大家好,我是锋哥。最近连载更新《LangChain4j 视频教程,开发Java Agent智能体》技术专题。

本课程主要介绍和讲解 LangChain4j 简介,阿里云百炼大模型平台接入,Ollama简介以及安装和使用,HelloWorld 实现,日志配置,集成SpringBoot,Ai Service 使用,对话与提示词工程(Prompt),结构化输出,会话记忆,工具调用(Function Calling),嵌入模型与向量数据库,RAG(检索增强生成),MCP(模型上下文协议),多模态支持。同时也配套视频教程 《2027版本 LangChain4j 视频教程,开发Java Agent智能体》
LangChain4j 提供了强大的会话记忆(Chat Memory)功能,用于解决大模型"无状态"的问题,能让你的 AI 应用在多轮对话中保持上下文连贯性。
我们测试下前面的代码,先问下一个 我是小锋。

继续再问一个 我是谁? 大模型已经不知道我是谁了,说明大模型默认是没有记忆功能的。

🧠 会话记忆的核心机制
- Memory vs. History :
ChatMemory是 LangChain4j 提供的核心机制,它管理与送入模型的上下文。与记录完整对话的History不同,ChatMemory会利用算法(如驱逐旧消息、摘要、注入信息等)来"记忆"部分信息,以适应模型的上下文限制 、控制调用成本 和降低响应延迟。
💡 内置的 ChatMemory 实现
LangChain4j 提供了两种开箱即用的 ChatMemory 实现,你也可以通过实现 ChatMemory 接口来扩展。
- MessageWindowChatMemory:作为滑动窗口保留最近 N 条消息,简单易懂,非常适合快速原型开发和轮次不多的对话场景。
- TokenWindowChatMemory:同样是滑动窗口,但以 Token 数为限制指标。它能更精细地控制发送给模型的上下文长度(尤其在 Token 敏感场景),但会完全驱逐超过限制的消息。
👤 会话隔离:@MemoryId 注解
默认情况下,ChatMemory 是所有用户/会话共享的,会导致"记忆串线"。@MemoryId 注解提供了优雅的解决方案。在 AI Service 接口方法中标注该注解,LangChain4j 会自动为每个 memoryId 创建并维护一个独立的 ChatMemory 实例。
先看一个简单会话实例
首先AssistantConfig里定义ChatMemoryProvider
scss
@Bean
public ChatMemoryProvider memoryChatMemoryProvider(){
return memoryId -> MessageWindowChatMemory.builder()
.id(memoryId) // 记忆id 会话隔离用
.maxMessages(20) // 窗口大小,可按需调整
.build();
}
再新建一个Assistant4Service,AIService里要配置 chatMemoryProvider
less
package com.java1234.service;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.spring.AiService;
import dev.langchain4j.service.spring.AiServiceWiringMode;
import reactor.core.publisher.Flux;
@AiService(
wiringMode = AiServiceWiringMode.EXPLICIT, // 手动指定接入模型 手动装配
streamingChatModel = "openAiStreamingChatModel", // 配置模型
chatMemoryProvider = "memoryChatMemoryProvider" // 会话记忆
)
public interface Assistant4Service {
/**
* 会话记忆
* @param memoryId 会话id
* @param question 提问
* @return
*/
Flux<String> chat(@MemoryId String memoryId, @UserMessage String question);
}
最后MyChatController里注入Assistant4Service以及实现chat6
java
@Autowired
private Assistant4Service assistant4Service;
typescript
@RequestMapping(value = "/chat6",produces = "text/html;charset=utf-8")
public Flux<String> chat6(String memoryId,String question) {
return assistant4Service.chat(memoryId,question);
}
再测试下:先提问,我是小锋。记得浏览器地址栏带上memoryId

再次提问,我是谁?,大模型已经记住我是小锋了。

本质上是第二次向大模型发送提问请求的时候,已经把历史提问和回答都一并作为提示词,请求大模型了。

再学习一个会话持久化实例
做会话持久化,可以存db或者redis,我们一般企业级开发用的是redis。
我们windows跑redis的话,一般都是跑docker里的,windows平台建议大家去安装一个Docker Desktop,比较方便。

接下来 直接 运行 安装,运行镜像命令:
arduino
docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 -v d:/redis-data:/data redis/redis-stack:latest
6379端口映射的是redis-stack服务,8001端口映射的是可视化服务。
首先第一步:pom.xml里加下redis依赖
xml
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-community-redis-spring-boot-starter</artifactId>
<version>1.15.0-beta25</version>
</dependency>
接着第二步:application.yml里配置redis连接配置
yaml
langchain4j:
community:
redis:
enabled: false # 关闭 Redis 向量库自动配置,仅保留手动配置的会话记忆
redis:
host: localhost
port: 6379
password: # 无密码留空
ttl-seconds: 3600 # 每个会话 key 过期时间,演示用 1 小时
接着第三步:新建redis会话配置类:RedisChatMemoryConfig
typescript
package com.java1234.config;
import dev.langchain4j.community.store.memory.chat.redis.RedisChatMemoryStore;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "langchain4j.redis")
@EnableConfigurationProperties(RedisChatMemoryConfig.class)
public class RedisChatMemoryConfig {
private String host;
private int port;
private String password;
private long ttlSeconds;
@Bean
public RedisChatMemoryStore redisChatMemoryStore() {
var builder = RedisChatMemoryStore.builder()
.host(host)
.port(port)
.ttl(ttlSeconds);
if (password != null && !password.isBlank()) {
builder.password(password);
// Redis 6+ ACL 可再加 .user("default")
}
return builder.build();
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public long getTtlSeconds() {
return ttlSeconds;
}
public void setTtlSeconds(long ttlSeconds) {
this.ttlSeconds = ttlSeconds;
}
}
最后:修改AssistantConfig,设置下chatMemoryStore
scss
@Bean
public ChatMemoryProvider memoryChatMemoryProvider(RedisChatMemoryStore redisChatMemoryStore){
return memoryId -> MessageWindowChatMemory.builder()
.id(memoryId) // 记忆id 会话隔离用
.chatMemoryStore(redisChatMemoryStore) // redis会话存储
.maxMessages(20) // 窗口大小,可按需调整
.build();
}
我们继续测试,先跟大模型说,我是小锋;

redis客户端里,我们看下,已经存储了会话信息。

我们再次向大模型提问,我是谁?

redis客户端里我们刷新看下,也进行了更新:
