找回密码
 立即注册
首页 业界区 业界 Spring AI 玩转多轮对话

Spring AI 玩转多轮对话

步雪卉 2025-7-7 17:02:16
AI "失忆"怎么办?本文带你用 Spring AI 一招搞定多轮对话,让你的 AI 应用拥有超强记忆!从 ChatClient、Advisors 到实战编码,三步打造一个能记住上下文的智能历史专家。
大家好,我是程序员NEO。
你是否遇到过这样的 AI?上一秒刚告诉它你的名字,下一秒就问你是谁。这种“金鱼记忆”的 AI 简直让人抓狂!在智能客服、虚拟助手等场景,如果 AI 无法记住上下文,用户体验将大打折扣。
别担心,今天 NEO 就带你用 Spring AI 框架,彻底解决这个难题,轻松为你的 AI 应用植入“记忆芯片”!
为了方便演示,我们将一起创建一个“历史知识专家”AI。它不仅能对答如流,还能记住我们之前的对话,实现真正流畅的智能交流。
准备好了吗?让我们开始吧!
更强大的 ChatClient

要让 AI 拥有“记忆力”,首先得掌握与它高效沟通的工具。Spring AI 提供了 ChatClient API,这是我们与大模型交互的瑞士军刀。
很多同学可能习惯了直接注入 ChatModel,但 ChatClient 提供了功能更丰富、更灵活的链式调用(Fluent API),是官方更推荐的方式。
看看对比,高下立判:
  1. // 基础用法(ChatModel)
  2. ChatResponse response = chatModel.call(new Prompt("你好"));
  3. // 高级用法(ChatClient)
  4. ChatClient chatClient = ChatClient.builder(chatModel)
  5.     .defaultSystem("你是历史顾问")
  6.     .build();
  7.    
  8. String response = chatClient.prompt().user("你好").call().content();
复制代码
ChatClient 的构建方式也很灵活,可以通过构造器注入或使用建造者模式:
  1. // 方式1:使用构造器注入
  2. @Service
  3. public class ChatService {
  4.     private final ChatClient chatClient;
  5.    
  6.     public ChatService(ChatClient.Builder builder) {
  7.         this.chatClient = builder
  8.             .defaultSystem("你是历史顾问")
  9.             .build();
  10.     }
  11. }
  12. // 方式2:使用建造者模式
  13. ChatClient chatClient = ChatClient.builder(chatModel)
  14.     .defaultSystem("你是历史顾问")
  15.     .build();
复制代码
它还支持多种响应格式,无论是包含 Token 信息的完整响应、自动映射的 Java 对象,还是实现打字机效果的流式输出,都能轻松搞定。
  1. // ChatClient支持多种响应格式
  2. // 1. 返回 ChatResponse 对象(包含元数据如 token 使用量)
  3. ChatResponse chatResponse = chatClient.prompt()
  4.     .user("Tell me a joke")
  5.     .call()
  6.     .chatResponse();
  7. // 2. 返回实体对象(自动将 AI 输出映射为 Java 对象)
  8. // 2.1 返回单个实体
  9. record ActorFilms(String actor, List<String> movies) {}
  10. ActorFilms actorFilms = chatClient.prompt()
  11.     .user("Generate the filmography for a random actor.")
  12.     .call()
  13.     .entity(ActorFilms.class);
  14. // 2.2 返回泛型集合
  15. List multipleActors = chatClient.prompt()
  16.     .user("Generate filmography for Tom Hanks and Bill Murray.")
  17.     .call()
  18.     .entity(new ParameterizedTypeReference<List>() {});
  19. // 3. 流式返回(适用于打字机效果)
  20. Flux<String> streamResponse = chatClient.prompt()
  21.     .user("Tell me a story")
  22.     .stream()
  23.     .content();
  24. // 也可以流式返回ChatResponse
  25. Flux<ChatResponse> streamWithMetadata = chatClient.prompt()
  26.     .user("Tell me a story")
  27.     .stream()
  28.     .chatResponse();
复制代码
更棒的是,你可以为 ChatClient 设置默认的“人设”(系统提示词),甚至在对话中动态替换模板变量,让 AI 的角色扮演更加生动。
  1. // 定义默认系统提示词
  2. ChatClient chatClient = ChatClient.builder(chatModel)
  3.         .defaultSystem("You are a friendly chat bot that answers question in the voice of a {voice}")
  4.         .build();
  5. // 对话时动态更改系统提示词的变量
  6. chatClient.prompt()
  7.         .system(sp -> sp.param("voice", voice))
  8.         .user(message)
  9.         .call()
  10.         .content());
复制代码
Advisors 拦截器

如果说 ChatClient 是 AI 的躯体,那 Advisors(顾问)就是给它加持的各种“外挂”和“Buff”。
你可以把 Advisors 理解为一系列可插拔的拦截器。在请求发给 AI 前或收到 AI 响应后,它们可以执行各种骚操作:

  • 前置增强:悄悄改写你的提问,让它更符合 AI 的胃口;或者进行安全检查,过滤掉危险问题。
  • 后置增强:记录调用日志,或者对 AI 的回答进行二次加工。
用法非常简单,直接在构建 ChatClient 时配置 defaultAdvisors 即可。比如,MessageChatMemoryAdvisor 就是我们实现对话记忆的关键“外挂”。
  1. var chatClient = ChatClient.builder(chatModel)
  2.     .defaultAdvisors(
  3.         new MessageChatMemoryAdvisor(chatMemory), // 对话记忆 advisor
  4.         new QuestionAnswerAdvisor(vectorStore)    // RAG 检索增强 advisor
  5.     )
  6.     .build();
  7. String response = this.chatClient.prompt()
  8.     // 对话时动态设定拦截器参数,比如指定对话记忆的 id 和长度
  9.     .advisors(advisor -> advisor.param("chat_memory_conversation_id", "678")
  10.             .param("chat_memory_response_size", 100))
  11.     .user(userText)
  12.     .call()
  13.         .content();
复制代码
Advisors 的工作原理就像一条精密的流水线(责任链模式):
1.png

流水线流程解读:

  • 用户的请求进来,被包装成一个 AdvisedRequest。
  • 请求在 Advisor 链上依次传递,每个 Advisor 都可以对它进行处理或修改。
  • 最终,请求被发送给 ChatModel。
  • 模型的响应再沿着流水线反向传回,每个 Advisor 也可以处理响应。
  • 最后,客户端收到经过层层“加持”的最终结果。
注意:Advisor 的执行顺序由其 getOrder() 方法决定,值越小,优先级越高,跟代码书写顺序无关哦!
2.png

Chat Memory Advisor

要实现对话记忆,ChatMemoryAdvisor 是我们的不二之选。它有几种实现方式,最常用的是 MessageChatMemoryAdvisor。

  • MessageChatMemoryAdvisor:将历史对话作为完整的消息列表(包含用户和 AI 的角色)添加到提示中。这是最符合现代大模型交互方式的选择。
  • PromptChatMemoryAdvisor:将历史对话拼接成一段文本,塞进系统提示词里。
  • VectorStoreChatMemoryAdvisor:使用向量数据库来存储和检索历史对话,适用于更复杂的场景。
3.png

MessageChatMemoryAdvisor 保留了对话的原始结构,能让 AI 更好地理解上下文,因此 强烈推荐使用
Chat Memory

ChatMemoryAdvisor 只是“搬运工”,真正存储对话历史的是 Chat Memory。Spring AI 提供了多种“记忆仓库”:

  • InMemoryChatMemory:内存存储,简单快捷,适合测试(我们今天就用它)。
  • JdbcChatMemory, CassandraChatMemory, Neo4jChatMemory:持久化存储,可将对话历史保存在数据库中,适合生产环境。
打造一个“历史学家”AI

理论讲完了,上代码!
初始化 ChatClient

我们通过构造器注入 ChatModel,然后构建 ChatClient。在构建时,设定好“历史学家”的人设(SYSTEM_PROMPT),并装上我们的记忆“外挂”——MessageChatMemoryAdvisor。
  1. /**
  2. * @author 程序员NEO
  3. * @version 1.0
  4. * @description 历史知识专家应用
  5. * @since 2025-07-07
  6. **/
  7. @Component
  8. @Slf4j
  9. public class HistoryExpertApp {
  10.     private final ChatClient chatClient;
  11.     private static final String SYSTEM_PROMPT = "你是一位风趣幽默的历史知识专家,学识渊博。" +
  12.             "你需要根据用户的提问,生动、清晰地回答相关的历史知识。" +
  13.             "如果用户的问题不清晰,你需要引导用户提供更多信息。";
  14.     public HistoryExpertApp(ChatModel chatModel) {
  15.         // 初始化基于内存的对话记忆
  16.         ChatMemory chatMemory = new InMemoryChatMemory();
  17.         chatClient = ChatClient.builder(chatModel)
  18.                 .defaultSystem(SYSTEM_PROMPT)
  19.                 .defaultAdvisors(
  20.                         new MessageChatMemoryAdvisor(chatMemory)
  21.                 )
  22.                 .build();
  23.     }
  24.     // ... doChat 方法
  25. }
复制代码
这里我们使用了 InMemoryChatMemory,它将对话历史存在内存里。对于生产环境,记得换成 Redis 或数据库等持久化方案。
编写对话方法

核心的 doChat 方法接收用户消息(message)和会话 ID(chatId)。chatId 是区分不同对话的关键,确保每个用户的聊天记录相互独立。
  1. /**
  2. * 执行聊天操作,处理用户消息并返回 AI 的响应。
  3. *
  4. * @param message 用户发送的消息
  5. * @param chatId  对话 ID,用于标识当前会话
  6. * @return AI 的响应内容
  7. */
  8. public String doChat(String message, String chatId) {
  9.     ChatResponse chatResponse = chatClient
  10.             .prompt()
  11.             .user(message)
  12.             .advisors(spec -> spec
  13.                     .param(MessageChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, chatId) // 设置对话 ID
  14.                     .param(MessageChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10)) // 设置记忆容量
  15.             .call()
  16.             .chatResponse();
  17.     String content = chatResponse.getResult().getOutput().getContent();
  18.     log.info("AI Response: {}", content);
  19.     return content;
  20. }
复制代码
在 .advisors() 方法中,我们传入了两个关键参数:

  • CHAT_MEMORY_CONVERSATION_ID_KEY: 会话 ID,确保每个用户的对话历史是隔离的。
  • CHAT_MEMORY_RETRIEVE_SIZE_KEY: 对话记忆检索大小。设置为 10 表示 AI 在回答时,会参考最近的 10 条消息(5 轮对话)。
见证奇迹的时刻!

我们用一个单元测试来验证 AI 是否真的拥有了记忆。
  1. @SpringBootTest
  2. public class HistoryExpertAppTest {
  3.     @Resource
  4.     private HistoryExpertApp historyExpertApp;
  5.     @Test
  6.     void testChat() {
  7.         String chatId = UUID.randomUUID().toString();
  8.         
  9.         // 第一轮对话
  10.         System.out.println("--- 第一轮对话 ---");
  11.         String message1 = "我叫NEO,我最喜欢的数字是7。";
  12.         System.out.println("我: " + message1);
  13.         String answer1 = historyExpertApp.doChat(message1, chatId);
  14.         Assertions.assertNotNull(answer1);
  15.         System.out.println("AI: " + answer1);
  16.         // 第二轮对话
  17.         System.out.println("\n--- 第二轮对话 ---");
  18.         String message2 = "我叫什么名字?我最喜欢的数字是几?";
  19.         System.out.println("我: " + message2);
  20.         String answer2 = historyExpertApp.doChat(message2, chatId);
  21.         Assertions.assertNotNull(answer2);
  22.         System.out.println("AI: " + answer2);
  23.     }
  24. }
复制代码
场景一:拥有完整记忆

当 CHAT_MEMORY_RETRIEVE_SIZE_KEY 设置为 10 时,AI 能轻松记住我们在第一轮对话中提供的信息。
测试结果
[code]--- 第一轮对话 ---我: 我叫NEO,我最喜欢的数字是7。AI: 哈哈,Neo!很高兴认识你!7确实是一个神奇的数字——不仅是上帝创造世界的天数,也是彩虹的颜色数、一周的天数,甚至还是詹姆斯·邦德的代号!看来你和神秘事物很投缘啊!既然你喜欢7,那我考考你:你知道人类历史上有哪些著名的"七"吗?比如七大奇迹、七星瓢虫,或者...《七龙珠》?
来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除

相关推荐

您需要登录后才可以回帖 登录 | 立即注册