从零开始掌握 LangChain4J 与大模型交互的核心技术。涵盖基础调用、Spring Boot集成、AI Services、流式输出、会话记忆等完整知识链。
环境准备
- JDK: 17+
- Spring Boot: 3.2.0
- LangChain4J: 1.0.1-beta6
- Redis: 用于会话持久化(可选)
第一步:快速入门 - 普通方式调用大模型
1.1 创建 Maven 项目
创建一个普通的 Maven 项目,在 pom.xml 中引入依赖:
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>1.0.1</version>
</dependency>
1.2 编写代码调用大模型
public class App {
public static void main(String[] args) {
// 构建 OpenAiChatModel 对象
OpenAiChatModel model = OpenAiChatModel.builder()
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.apiKey(System.getenv("API-KEY")) // 从环境变量获取 API-KEY
.modelName("qwen-plus")
.logRequests(true) // 开启请求日志
.logResponses(true) // 开启响应日志
.build();
// 调用 chat 方法与大模型交互
String result = model.chat("你好");
System.out.println(result);
}
}
重点说明:
API-KEY建议配置在系统环境变量中,避免硬编码在代码里导致泄露风险logRequests/logResponses用于调试,查看请求和响应的详细内容baseUrl使用阿里云百炼平台的兼容模式端点
第二步:Spring Boot集成LangChain4J
2.1 创建 Spring Boot 项目并引入依赖
<!-- LangChain4J Spring Boot Starter -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
<version>1.0.1-beta6</version>
</dependency>
2.2 配置文件 application.yml
langchain4j:
open-ai:
chat-model:
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
api-key: ${API-KEY} # 从环境变量读取
model-name: qwen-plus
log-requests: true # 请求日志
log-responses: true # 响应日志
logging:
level:
dev.langchain4j: debug # 设置日志级别
核心原理:
- 起步依赖会自动检测配置信息
- 自动向 IOC 容器中注入一个
OpenAiChatModelBean 对象
2.3 开发接口调用大模型
@RestController
public class ChatController {
@Autowired
private OpenAiChatModel model;
@RequestMapping("/chat")
public String chat(String message) {
return model.chat(message);
}
}
启动应用后访问 http://localhost:8080/chat?message=你好 即可测试。
第三步:AI Services 工具类封装
为什么需要 AI Services?
直接使用 OpenAiChatModel 的 chat 方法在实际开发中并不常用。因为如果要实现会话记忆、RAG知识库、Tools工具等高阶功能,在调用 chat 方法前需要做很多准备工作,比较复杂。
LangChain4J 提供了 AiServices 工具类,封装了 model 对象和其他功能,用起来非常简单。
3.1 引入 AiServices 依赖
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring-boot-starter</artifactId>
<version>1.0.1-beta6</version>
</dependency>
3.2 声明用于封装聊天方法的接口
public interface ConsultantService {
// 用于聊天的方法,message 为用户输入的内容
String chat(String message);
}
3.3 使用 AiServices 创建接口的动态代理对象
@Configuration
public class CommonConfig {
@Autowired
private OpenAiChatModel model;
@Bean
public ConsultantService consultantService() {
ConsultantService cs = AiServices.builder(ConsultantService.class)
.chatModel(model) // 设置对话时使用的模型对象
.build();
return cs;
}
}
3.4 Controller 中注入并使用
@RestController
public class ChatController {
@Autowired
private ConsultantService consultantService;
@RequestMapping("/chat")
public String chat(String message) {
return consultantService.chat(message);
}
}
第四步:AI Services 声明式使用
为了进一步简化使用,LangChain4J 提供了声明式方法:在接口上添加 @AiService 注解,LangChain4J 扫描到后会自动创建该接口的代理对象并注入到 IOC 容器中。
4.1 修改接口添加注解
@AiService(
wiringMode = AiServiceWiringMode.EXPLICIT, // 手动装配
chatModel = "openAiChatModel" // 指定模型 Bean 名称
)
public interface ConsultantService {
// 用于聊天的方法,message 为用户输入的内容
String chat(String message);
}
注解说明:
wiringMode:装配模式
-
AUTOMATIC(默认):自动装配EXPLICIT:手动装配(推荐,更清晰)
chatModel:指定对话时使用的模型对象在 IOC 容器中的名字
注意:虽然可以省略这些属性使用自动装配,但手动设置更容易理解原理,建议都采用手动装配的方式。
4.2 简化后的优势
使用声明式后,之前的 CommonConfig 配置类中的 consultantService() Bean 定义就可以省略了,LangChain4J 会自动处理。
第五步:流式调用
5.1 流式调用 vs 阻塞式调用
之前演示的都是阻塞式调用,结果是一次性响应的。接下来学习如何使用 LangChain4J 发起流式调用,实现边生成边输出的效果。
5.2 引入依赖
<!-- WebFlux 用于支持响应式流式输出 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- LangChain4J Reactor 支持 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-reactor</artifactId>
<version>1.0.1-beta6</version>
</dependency>
5.3 配置流式模型对象
在 application.yml 中添加流式模型配置:
langchain4j:
open-ai:
chat-model:
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
api-key: ${API-KEY}
model-name: qwen-plus
log-requests: true
log-responses: true
streaming-chat-model: # 流式模型配置
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
api-key: ${API-KEY}
model-name: qwen-plus
log-requests: true
log-responses: true
5.4 调整 ConsultantService 接口
@AiService(
wiringMode = AiServiceWiringMode.EXPLICIT,
chatModel = "openAiChatModel",
streamingChatModel = "openAiStreamingChatModel" // 指定流式模型
)
public interface ConsultantService {
// 返回值改为 Flux<String> 支持流式输出
Flux<String> chat(String message);
}
关键点:
streamingChatModel属性指定流式聊天模型对象,值为openAiStreamingChatModel- 返回值类型改为
Flux<String>(响应式流类型)
5.5 调整 ChatController
@RestController
public class ChatController {
@Autowired
private ConsultantService consultantService;
@RequestMapping(value = "/chat", produces = "text/html;charset=utf-8")
public Flux<String> chat(String message) {
return consultantService.chat(message);
}
}
说明:
produces = "text/html;charset=utf-8"解决中文乱码问题- 浏览器会接收到流式数据并逐步显示
第六步:消息注解(SystemMessage & UserMessage)
6.1 场景说明
假设我们开发一个 AI 顾问,它只能回答特定领域的问题,如果用户问其他问题则不予回答。这时就需要通过设定系统消息来实现。
6.2 SystemMessage 注解
@SystemMessage 用于设置系统消息,有两种使用方式:
方式一:直接在注解中书写
@AiService(
wiringMode = AiServiceWiringMode.EXPLICIT,
chatModel = "openAiChatModel",
streamingChatModel = "openAiStreamingChatModel"
)
public interface ConsultantService {
@SystemMessage("你是精通AI编程方向的资深讲师")
Flux<String> chat(String message);
}
方式二:从外部文件加载(推荐)
@AiService(
wiringMode = AiServiceWiringMode.EXPLICIT,
chatModel = "openAiChatModel",
streamingChatModel = "openAiStreamingChatModel"
)
public interface ConsultantService {
@SystemMessage(fromResource = "system.txt")
Flux<String> chat(String message);
}
在 src/main/resources 目录下创建 system.txt 文件,写入系统消息内容:
你是精通AI编程方向的资深讲师。
你的职责是为用户提供专业的咨询服务。
请保持友好、耐心、细致的回答风格。
好处:
- 系统消息与代码分离,便于维护
- 支持复杂的多行消息
- 非开发人员也可修改
6.3 UserMessage 注解
@UserMessage 用于在用户消息前后拼接预设内容。
基本用法:
@UserMessage("你是精通AI编程方向的资深讲师。{{it}}")
Flux<String> chat(String message);
{{it}}是固定写法,用于动态获取用户传递的消息- 可以在
{{it}}前后拼接任意内容
自定义参数名:
@UserMessage("你是精通AI编程方向的资深讲师。{{msg}}")
Flux<String> chat(@V("msg") String message);
- 使用
@V注解给参数起别名 {{msg}}就会对应到message参数
注意:测试完毕后记得注释掉 @UserMessage,保留 @SystemMessage(fromResource = "system.txt") 设置。
第七步:会话记忆(Chat Memory)
7.1 为什么需要会话记忆?
大模型本身不具备记忆能力,每次会话都是独立的。要想让大模型产生记忆的效果,唯一的方法就是把之前聊天的内容和新的内容一起发送给大模型。
LangChain4J 能够帮我们自动记录聊天消息并自动发送!
7.2 会话记忆原理
用户提问 → Web 后端 → 存储到 ChatMemory 对象 → 获取所有会话记录 → 发送给大模型
↑ ↓
←←←←←←←←← 大模型响应结果 ←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
↓
复制到 ChatMemory → 响应用户
每次用户提问时:
- Web 后端自动把消息存放到
ChatMemory存储对象中 - 获取存储对象中所有的会话记录,一并发送给大模型
- 大模型根据所有历史记录生成答案
- Web 后端把响应消息拷贝一份到存储对象,再发送给用户
7.3 ChatMemory 接口
LangChain4J 提供了 ChatMemory 接口:
public interface ChatMemory {
Object id(); // 记忆存储对象的唯一标识
void add(ChatMessage var1); // 添加一条会话记忆
List<ChatMessage> messages(); // 获取所有会话记忆
void clear(); // 清除所有会话记忆
}
两个实现类:
TokenWindowChatMemory:基于 Token 数量限制MessageWindowChatMemory:基于消息条数限制(常用)
7.4 实现会话记忆
步骤 1:定义会话记忆对象
在 CommonConfig 中构建 MessageWindowChatMemory 对象并注入到 IOC 容器:
@Bean
public ChatMemory chatMemory() {
return MessageWindowChatMemory.builder()
.maxMessages(20) // 最大保存 20 条会话记录
.build();
}
为什么要限制最大数量?
- 大模型的上下文有限(一般 10w tokens 左右)
- 会话记录太多会导致费用越高(按 token 收费)
- 超过限制后,最早的消息会被淘汰,只保留最新的 20 条
步骤 2:配置会话记忆对象
在 ConsultantService 接口的 @AiService 注解中配置:
@AiService(
wiringMode = AiServiceWiringMode.EXPLICIT,
chatModel = "openAiChatModel",
streamingChatModel = "openAiStreamingChatModel",
chatMemory = "chatMemory" // 配置会话记忆对象(Bean 名称)
)
public interface ConsultantService {
@SystemMessage(fromResource = "system.txt")
Flux<String> chat(String message);
}
现在启动测试,就能实现连续对话了!
第八步:会话记忆隔离
8.1 问题分析
前面实现的会话记忆有个问题:所有用户共用同一个会话记忆对象,无法区分不同用户的会话记录。
那怎么办?借助 ChatMemory 的 id() 方法实现会话记忆隔离!
8.2 隔离原理
用户 A (memoryId=1) → 查找 id=1 的 ChatMemory → 不存在 → 创建新的 → 存储会话
用户 B (memoryId=2) → 查找 id=2 的 ChatMemory → 不存在 → 创建新的 → 存储会话
用户 A (memoryId=1) → 查找 id=1 的 ChatMemory → 已存在 → 复用 → 继续存储
LangChain4J 内部维护一个容器,存储所有会话记忆对象。根据用户传递的 memoryId 查找复用的对象,找不到就创建新的。
8.3 实现会话隔离
步骤 1:定义会话记忆对象提供者
ChatMemoryProvider 会在找不到指定 id 的 ChatMemory 时被调用:
@Bean
public ChatMemoryProvider chatMemoryProvider() {
ChatMemoryProvider chatMemoryProvider = new ChatMemoryProvider() {
@Override
public ChatMemory get(Object memoryId) {
return MessageWindowChatMemory.builder()
.id(memoryId) // 设置 id 值
.maxMessages(20) // 最大会话记录数量
.build();
}
};
return chatMemoryProvider;
}
步骤 2:配置会话记忆对象提供者
在 @AiService 注解中指定,并注释掉之前的 chatMemory 配置:
@AiService(
wiringMode = AiServiceWiringMode.EXPLICIT,
chatModel = "openAiChatModel",
streamingChatModel = "openAiStreamingChatModel",
// chatMemory = "chatMemory", // 注释掉
chatMemoryProvider = "chatMemoryProvider" // 配置提供者
)
public interface ConsultantService {
@SystemMessage(fromResource = "system.txt")
Flux<String> chat(@MemoryId String memoryId, @UserMessage String message);
}
关键变化:
chatMemoryProvider属性指定会话记忆对象提供者chatMemory配置不再需要
步骤 3:添加 @MemoryId 参数
在 ConsultantService 接口的 chat 方法中添加 memoryId 参数:
@AiService(...)
public interface ConsultantService {
@SystemMessage(fromResource = "system.txt")
Flux<String> chat(@MemoryId String memoryId, @UserMessage String message);
}
注解说明:
@MemoryId:标识该参数用于指定ChatMemory对象的 id@UserMessage:明确标识哪个参数是用户消息(多参数时必须)
细节注意:
- 如果
chat方法只有一个参数,LangChain4J 默认把它当作用户消息 - 如果有多个参数,必须手动指定哪个是用户消息
步骤 4:Controller 接收 memoryId 参数
@RequestMapping(value = "/chat", produces = "text/html;charset=utf-8")
public Flux<String> chat(String memoryId, String message) {
return consultantService.chat(memoryId, message);
}
步骤 5:前端提交 memoryId
如果使用现成的前端页面(如 index.html),通常已经包含了 memoryId 参数的提交逻辑,无需额外修改。
现在启动测试,不同用户(不同 memoryId)就有各自独立的会话记忆了!提示词
第九步:会话记忆持久化(Redis)
9.1 问题分析
实现了会话记忆隔离后,还有一个问题:后端重启后会话记忆就丢失了。
为什么会这样?分析一下:
- 我们使用的
MessageWindowChatMemory内部维护了一个ChatMemoryStore成员变量 - 真正存储会话记录的就是这个
ChatMemoryStore对象 - 默认使用的
SingleSlotChatMemoryStore实现类是用ArrayList在内存中存储 - 服务器重启,内存数据自然丢失
public class MessageWindowChatMemory implements ChatMemory {
private final String id;
private final ChatMemoryStore store; // 真正存储会话的对象
public void add(ChatMessage message) {
this.store.updateMessages(this.id, messages);
}
}
// 默认使用的实现类
class SingleSlotChatMemoryStore implements ChatMemoryStore {
private List<ChatMessage> messages = new ArrayList(); // 内存存储!
// ...
}
9.2 解决方案
思路很清晰:自己提供一个 ChatMemoryStore 实现类,把数据存储到外部存储器(如 Redis)中。
ChatMemoryStore 接口提供了三个方法:
public interface ChatMemoryStore {
List<ChatMessage> getMessages(Object memoryId); // 获取会话记录
void updateMessages(Object memoryId, List<ChatMessage> messages); // 更新会话记录
void deleteMessages(Object memoryId); // 删除会话记录
}
9.3 实现 Redis 持久化
步骤 1:准备 Redis 环境
使用 Docker 快速安装 Redis:
# 安装 Redis 容器
docker run --name redis -d -p 6379:6379 redis
步骤 2:引入 Redis 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
步骤 3:配置 Redis 连接信息
在 application.yml 中添加:
spring:
data:
redis:
host: localhost
port: 6379
步骤 4:实现 ChatMemoryStore 接口
@Repository
public class RedisChatMemoryStore implements ChatMemoryStore {
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public List<ChatMessage> getMessages(Object memoryId) {
// 从 Redis 获取 JSON 字符串
String json = redisTemplate.opsForValue().get(memoryId.toString());
// 反序列化为 List<ChatMessage>
return ChatMessageDeserializer.messagesFromJson(json);
}
@Override
public void updateMessages(Object memoryId, List<ChatMessage> messages) {
// 序列化为 JSON
String json = ChatMessageSerializer.messagesToJson(messages);
// 存储到 Redis,设置 1 天过期期
redisTemplate.opsForValue().set(memoryId.toString(), json, Duration.ofDays(1));
}
@Override
public void deleteMessages(Object memoryId) {
// 从 Redis 删除
redisTemplate.delete(memoryId.toString());
}
}
关键技术点:
- 使用
ChatMessageDeserializer.messagesFromJson()反序列化 - 使用
ChatMessageSerializer.messagesToJson()序列化 - 设置过期时间避免数据永久存储
步骤 5:配置 ChatMemoryStore
在 CommonConfig 中将自定义的 ChatMemoryStore 配置给 MessageWindowChatMemory:
@Configuration
public class CommonConfig {
@Autowired
private ChatMemoryStore redisChatMemoryStore;
@Bean
public ChatMemoryProvider chatMemoryProvider() {
ChatMemoryProvider chatMemoryProvider = new ChatMemoryProvider() {
@Override
public ChatMemory get(Object memoryId) {
return MessageWindowChatMemory.builder()
.id(memoryId)
.maxMessages(20)
.chatMemoryStore(redisChatMemoryStore) // 配置自定义 Store
.build();
}
};
return chatMemoryProvider;
}
}
9.4 效果验证
现在重新启动应用,测试会话记忆:
- 用户提问几次,产生会话记录
- 重启后端服务
- 再次访问,发现之前的会话记录依然存在!
达成效果:
- ✅ 会话数据持久化到 Redis
- ✅ 服务重启后可恢复历史对话
- ✅ 支持分布式部署共享会话
- ✅ 自动过期清理(1 天后)
总结与回顾
核心知识点回顾
- 快速入门:普通 Maven 项目直接构建
OpenAiChatModel调用大模型 - Spring Boot集成:使用 starter 自动配置,简化开发
- AI Services:工具类封装,简化高阶功能使用
- 声明式使用:
@AiService注解自动创建代理对象 - 流式调用:WebFlux +
Flux<String>实现边生成边输出 - 消息注解:
-
@SystemMessage:设置系统消息(fromResource 加载外部文件)@UserMessage:在用户消息前后拼接预设内容({{it}}或{{msg}})
- 会话记忆:
ChatMemory接口自动记录历史消息 - 会话隔离:
@MemoryId+ChatMemoryProvider实现多用户隔离 - 会话持久化:自定义
ChatMemoryStore集成 Redis
最佳实践建议
- ✅ API-KEY 安全:配置在环境变量中,不要硬编码
- ✅ 日志控制:开发环境开启日志,生产环境关闭
- ✅ 会话数量限制:设置合理的
maxMessages(如 20)控制成本和上下文长度 - ✅ 持久化必选:生产环境务必使用 Redis 等外部存储
- ✅ 声明式优先:优先使用
@AiService声明式,代码更简洁 - ✅ 流式体验:面向用户的场景尽量使用流式输出
进阶扩展方向
- RAG知识库:结合向量数据库实现私有知识检索
- Function Calling:让 AI 调用外部 API 或执行代码
- 多模态:支持图片、文件等多模态输入
- 监控统计:Token 用量、响应时间、成本统计
参考资源:
- LangChain4J 官方文档:docs.langchain4j.dev/
- GitHub:github.com/langchain4j…
- 阿里云百炼平台:help.aliyun.com/product/421…