当前位置: 首页 > news >正文

多轮对话实现

背景

要实现具有 “记忆力” 的 AI 应用,让 AI 能够记住用户之前的对话内容并保持上下文连贯性,我们可以使用Spring AI 框架的 对话记忆能力。
如何使用对话记忆能力呢?参考 Spring AI 的官方文档, Spring AI 提供了 ChatClient API 来和 AI 大模型交互。

一、ChatClient

官方示例代码,ChatClien‌t 支持更复杂灵活的链式调用‏(Fluent API):

@RestController
class MyController {private final ChatClient chatClient;public MyController(ChatClient.Builder chatClientBuilder) {this.chatClient = chatClientBuilder.build();}@GetMapping("/ai")String generation(String userInput) {return this.chatClient.prompt().user(userInput).call().content();}
}

Sprin‏g AI 提供了多؜种构建 ChatC​lient 的方式‌,比如自动注入、通‏过建造者模式手动构造:

// 方式1:使用构造器注入
@Service
public class ChatService {private final ChatClient chatClient;public ChatService(ChatClient.Builder builder) {this.chatClient = builder.defaultSystem("你是编程语言大师").build();}
}// 方式2:使用建造者模式
ChatClient chatClient = ChatClient.builder(chatModel).defaultSystem("你是编程语言大师").build();

ChatC‏lient 支持多؜种响应格式,比如返​回 ChatRes‌ponse 对象、‏返回实体对象、流式返回:

// ChatClient支持多种响应格式
// 1. 返回 ChatResponse 对象(包含元数据如 token 使用量)
ChatResponse chatResponse = chatClient.prompt().user("Tell me a joke").call().chatResponse();// 2. 返回实体对象(自动将 AI 输出映射为 Java 对象)
// 2.1 返回单个实体
record ActorFilms(String actor, List<String> movies) {}
ActorFilms actorFilms = chatClient.prompt().user("Generate the filmography for a random actor.").call().entity(ActorFilms.class);// 2.2 返回泛型集合
List<ActorFilms> multipleActors = chatClient.prompt().user("Generate filmography for Tom Hanks and Bill Murray.").call().entity(new ParameterizedTypeReference<List<ActorFilms>>() {});// 3. 流式返回(适用于打字机效果)
Flux<String> streamResponse = chatClient.prompt().user("Tell me a story").stream().content();// 也可以流式返回ChatResponse
Flux<ChatResponse> streamWithMetadata = chatClient.prompt().user("Tell me a story").stream().chatResponse();

可以给 C‏hatClient ؜设置默认参数,比如系​统提示词,还可以在对‌话时动态更改系统提示‏词的变量,类似模板的概念:

// 定义默认系统提示词
ChatClient chatClient = ChatClient.builder(chatModel).defaultSystem("You are a friendly chat bot that answers question in the voice of a {voice}").build();// 对话时动态更改系统提示词的变量
chatClient.prompt().system(sp -> sp.param("voice", voice)).user(message).call().content());

二、Advisors

Spring AI 使用 Advisors(顾问)机制来增强 AI 的能力,可以理解为一系列可插拔的拦截器,在调用 AI 前和调用 AI 后可以执行一些额外的操作,比如:

前置增强:调用 AI 前改写一下 Prompt 提示词、检查一下提示词是否安全
后置增强:调用 AI 后记录一下日志、处理一下返回的结果

直接为 ChatClient 指定؜默认拦截器,比如对话记忆拦截器 Me​ssageChatMemoryAdv‌isor 可以帮助我们实现多轮对话能‏力,省去了自己维护对话列表的麻烦。

var chatClient = ChatClient.builder(chatModel).defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build(), // chat-memory advisorQuestionAnswerAdvisor.builder((vectorStore).builder() // RAG advisor).build();var conversationId = "678";String response = this.chatClient.prompt()// Set advisor parameters at runtime.advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, conversationId)).user(userText).call().content();

Advisors 的原理图如下👇
在这里插入图片描述
1、官网原文翻译:

Spring AI 框架会根据用户的 Prompt(提示词)创建一个 AdvisedRequest,同时会生成一个空的 AdvisorContext(顾问上下文)对象。

框架中的每个 advisor(顾问/拦截器) 会依次处理这个请求,它们可以:

  • 修改请求内容。
  • 阻止请求继续传递(如果阻止,当前 advisor 需要自己生成并填充返回结果)。

如果请求一路通过,最后会由框架自带的最终 advisor 把请求发送给 Chat Model(聊天模型)。

聊天模型生成响应后,这个响应会沿着原路通过 advisor 链,最终被封装成 AdvisedResponse,这个响应中也包含之前共享的 AdvisorContext。

每个 advisor 也可以在响应阶段处理或修改返回结果。

最终,处理完的 AdvisedResponse 会被返回给客户端,客户端会从中提取出 ChatCompletion(聊天模型生成的回答内容)。

2、翻译

你可以把 Spring AI 的这套机制 想象成:

一个多人协作的过安检流程,大家可以一起检查、修改,甚至拦下行李。

具体流程:

  1. 用户输入(Prompt) 就像是一个乘客的行李,交给系统处理。
  2. 系统会创建一个“请求包裹”(AdvisedRequest),和一个“共享便签”(AdvisorContext),这个便签可以让后面的每个人都写点东西上去(比如记录信息、加备注)。
  3. 这个行李会被传递给一组“安检员”(Advisor 链)。①每个安检员可以检查、调整,甚至拦住行李不让它继续走。②如果一个安检员拦下了行李,他要自己给出答案,不能再交给下一个人了。
  4. 如果行李一路畅通无阻,最后会被送到“AI 模型”(Chat Model),它会给出回复。
  5. 这时候,AI 的回答会被传回安检员们手里,他们还可以继续修改回答。
  6. 最后整理好的回答会被打包好(AdvisedResponse),送回给用户。

实际开发中,往往我们会用到多个拦截器,组合在一起相当于一条拦截器链条(责任链模式的设计思想)。每个拦截器是有顺序的,通过 getOrder() 方法获取到顺序,得到的值越低,越优先执行。

三、Chat Memory Advisor

前面我们提到‏了,想要实现对话记忆功能؜,可以使用 Spring​ AI 的 ChatMe‌moryAdvisor,‏它主要有几种内置的实现方式:

  • MessageChatMemoryAdvisor:从记忆中检索历史对话,并将其作为消息集合添加到提示词中
  • PromptChatMemoryAdvisor:从记忆中检索历史对话,并将其添加到提示词的系统文本中
  • VectorStoreChatMemoryAdvisor:可以用向量数据库来存储检索历史对话

1、Messag‏eChatMemoryAdvi؜sor

将对话历史作为一系列独​立的消息添加到提示中,保留原始‌对话的完整结构,包括每条消息的‏角色标识(用户、助手、系统)。

[{"role": "user", "content": "你好"},{"role": "assistant", "content": "你好!有什么我能帮助你的吗?"},{"role": "user", "content": "讲个笑话"}
]

2、Prom‏ptChatMemor؜yAdvisor

将对​话历史添加到提示词的系‌统文本部分,因此可能会‏失去原始的消息边界。

以下是之前的对话历史:
用户: 你好
助手: 你好!有什么我能帮助你的吗?
用户: 讲个笑话现在请继续回答用户的问题。

3、总结

一般情况下,更建议使用 MessageChatMemoryAdvisor。更符合大多数现代 LLM 的对话模型设计,能更好地保持上下文连贯性

四、Chat Memory

上述 ChatMemoryAdvisor 都依赖 Chat Memory 进行构造,Chat Memory 负责历史对话的存储,定义了保存消息、查询消息、清空消息历史的方法。

Sprin‏g AI 内置了几؜种 Chat Me​mory,可以将对‌话保存到不同的数据‏源中,比如:

  • InMemoryChatMemory:内存存储
  • CassandraChatMemory:在 Cassandra 中带有过期时间的持久化存储
  • Neo4jChatMemory:在 Neo4j 中没有过期时间限制的持久化存储
  • JdbcChatMemory:在 JDBC中没有过期时间限制的持久化存储

当然也可以‏通过实现 Chat؜Memory 接口​自定义数据源的存储‌。

五、总结

  • ChatClient 就是 Spring AI 调用大模型的客户端
  • Advisors 是用于增强 AI 调用能力的拦截器
  • Chat Memory Advisor 是对话记忆拦截器
  • Chat Memory 是对话记忆拦截器依赖的对话存储

相关文章:

  • 【大模型】RankRAG:基于大模型的上下文排序与检索增强生成的统一框架
  • 格恩朗超声波水表 绿色助农 精准领航​
  • 02.运算符
  • [ linux-系统 ] 进程控制
  • helm使用说明和实例
  • 赞助打赏单页HTML源码(源码下载)
  • 基于算法竞赛的c++编程(24)位运算及其应用
  • python3基础语法梳理(一)
  • 安全领域新突破:可视化让隐患无处遁形
  • Easy Excel
  • c语言(持续更新)
  • 使用DataX同步MySQL数据
  • OSPF域内路由
  • matlab时序预测并绘制预测值和真实值对比曲线
  • 6.9本日总结
  • DPC密度峰值聚类
  • PostgreSQL 与 SQL 基础:为 Fast API 打下数据基础
  • 卷积神经网络设计指南:从理论到实践的经验总结
  • CppCon 2015 学习:STL Algorithms in Action
  • CppCon 2015 学习:Transducers, from Clojure to C++
  • 郑州网站改版公司/郑州seo关键词
  • 建设一个网站要钱吗/天津seo推广服务
  • 网站服务器租用还是自买/新闻软文广告
  • 如何做一个自己的网站/网站建设多少钱
  • 网页设计与制作教程 刘瑞新/广东网络seo推广公司
  • 成都网站建设方案/申请自己的网站