本篇给同一个模型增加按会话隔离的记忆。先观察两组对话,再把代码放进业务服务;生成文件和执行代码不属于本例。
准备环境
沿用 LangChain4j 模型调用,使用 JDK 21 与本机 Ollama。Maven 加入同一版本的两个模块:
<dependency> <groupId>dev.langchain4j</groupId><artifactId>langchain4j</artifactId><version>1.22.0</version></dependency><dependency> <groupId>dev.langchain4j</groupId><artifactId>langchain4j-ollama</artifactId><version>1.22.0</version></dependency>不要把不同版本的 core、Spring Boot starter 和 reactor 适配模块拼在一起。本例不需要 Spring Boot starter。
多轮对话程序
保存 src/main/java/AiMemoryDemo.java:
import java.time.Duration;import dev.langchain4j.memory.chat.MessageWindowChatMemory;import dev.langchain4j.model.chat.ChatModel;import dev.langchain4j.model.ollama.OllamaChatModel;import dev.langchain4j.service.AiServices;import dev.langchain4j.service.MemoryId;import dev.langchain4j.service.UserMessage;
public class AiMemoryDemo { public interface Assistant { String chat(@MemoryId String conversationId, @UserMessage String question); }
public static void main(String[] args) { ChatModel model = OllamaChatModel.builder() .baseUrl("http://127.0.0.1:11434") .modelName("llama3.2:1b") .timeout(Duration.ofSeconds(60)) .build(); Assistant assistant = AiServices.builder(Assistant.class) .chatModel(model) .chatMemoryProvider(id -> MessageWindowChatMemory.withMaxMessages(10)) .build(); System.out.println(assistant.chat("session-A", "我叫小明,请记住本次对话中的名字。")); System.out.println(assistant.chat("session-B", "我叫小红,请记住本次对话中的名字。")); System.out.println(assistant.chat("session-A", "我在这次对话里说自己叫什么?")); }}在 IDE 运行 main。第三次调用沿用 A 的历史,不应把 B 的历史混进请求;具体回复仍由模型决定,可以通过调试观察传给模型的消息列表确认隔离。
@MemoryId 指定会话,@UserMessage 指定用户输入。窗口上限是消息数量,不是轮数或 token 数;超过窗口会淘汰旧消息,进程重启后内存历史会丢失。
放进后端之前
- 会话 ID 必须与已登录用户和会话归属校验绑定,不能让用户填写其他人的 ID 后读取历史。
- 同一会话的并发请求要协调,避免历史顺序交叉;不同会话也要限制总数量与存活期限。
- 持久化使用明确的数据格式和版本,避免把任意 Java 对象序列化文件当作长期协议。
- 模型生成的 HTML、文件名与代码均为不可信输入,展示、保存和执行需要分别验证。
流式调用使用 StreamingChatModel 与对应的服务方法返回类型,不能只把同步方法的 String 返回值改成 Flux 就假定完成。结构化输出也需要在最终文本齐全后按对应 API 转换并验证,不能随意用正则拆分模型代码块。
评论