基于Spring AI实现多轮对话系统架构设计

文章目录

基于Spring AI实现多轮对话系统架构设计

前言

一、多轮对话系统核心架构

1.1 架构概览

1.2 Spring AI核心优势

二、ChatClient与多轮对话设计

2.1 ChatClient的特性与角色

2.2 实现多轮对话方法

三、Advisors拦截器机制

3.1 Advisors概念与工作原理

3.2 对话记忆Advisor详解

四、对话记忆实现方案

4.1 ChatMemory接口

4.2 内存存储实现

4.3 文件持久化存储

4.4 数据库持久化实现

五、自定义增强Advisor实现

5.1 日志记录Advisor

5.2 内容安全Advisor

5.3 推理增强Advisor

六、结构化输出与业务应用

6.1 结构化报告生成

6.2 完整对话系统整合

七、性能优化与最佳实践

7.1 对话记忆性能优化

7.2 多轮对话系统设计要点

总结


基于Spring AI实现多轮对话系统架构设计

前言

随着大型语言模型(LLM)的迅速发展,构建具有持久记忆和上下文感知能力的对话系统成为AI应用开发的关键。Spring AI框架提供了简洁高效的组件,帮助开发者快速实现这类功能。本文将深入探讨如何基于Spring AI打造多轮对话系统,包括架构设计、核心组件和实现方法。

一、多轮对话系统核心架构

1.1 架构概览

多轮对话系统的核心在于维护对话上下文,使AI能够理解历史交互内容,实现连贯对话。基于Spring AI的多轮对话系统架构可概括为:

核心组件包括:

  • ChatClient:对话客户端,处理与LLM的交互

  • Advisor:拦截器链,处理请求前后的增强逻辑

  • ChatMemory:对话记忆,存储历史消息

  • PromptTemplate:提示词模板,构建结构化提示

1.2 Spring AI核心优势

基于Spring AI框架开发多轮对话系统具有以下优势:

  1. 链式调用API(Fluent API):简洁直观的调用方式

  2. 动态参数绑定:支持模板变量,提高灵活性

  3. 灵活的响应格式:支持实体映射和流式输出

  4. 可插拔拦截器:通过Advisors机制轻松扩展功能

  5. 内置记忆管理:开箱即用的对话记忆组件

二、ChatClient与多轮对话设计

2.1 ChatClient的特性与角色

@Component
@Slf4j
public class LoveApp {
​private static final String SYSTEM_PROMPT = "**恋爱大师·情感导航员**  \n" + "10年情感咨询经验,擅长亲密关系理论与沟通技巧...";private final ChatClient chatClient;
​public LoveApp(ChatModel dashscopeChatModel) {ChatMemory chatMemory = new InMemoryChatMemory();chatClient = ChatClient.builder(dashscopeChatModel).defaultSystem(SYSTEM_PROMPT).defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory)).build();}
}

ChatClient是Spring AI提供的核心对话客户端组件,负责维护与大语言模型的通信。它支持:

  • 链式调用:简化API调用流程

  • 动态参数注入:运行时传递控制参数

  • 多格式响应处理:文本、JSON、流式回复等

  • 拦截器机制:Advisors模式的扩展点

创建ChatClient示例:

 

2.2 实现多轮对话方法

通过ChatClient实现多轮对话的关键是正确配置对话记忆,并在每次交互中保持会话ID的一致性:

public String doChat(String message, String chatId) {ChatResponse response = chatClient.prompt().user(message).advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId).param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10)).call().chatResponse();String content = response.getResult().getOutput().getText();log.info("content: {}", content);return content;
}

这段代码的关键点在于:

  • 用户消息注入:通过.user(message)添加当前用户问题

  • 会话标识:通过CHAT_MEMORY_CONVERSATION_ID_KEY参数维护会话一致性

  • 上下文长度:通过CHAT_MEMORY_RETRIEVE_SIZE_KEY控制历史消息检索数量

  • 响应处理:获取并处理模型回复

三、Advisors拦截器机制

3.1 Advisors概念与工作原理

Advisors是Spring AI中基于责任链模式实现的拦截器机制,可以在调用大模型前后执行增强逻辑。其核心特性包括:

  • 责任链模式:多个拦截器按顺序执行

  • 顺序控制:通过getOrder()方法控制执行顺序

  • 前置/后置处理:可在请求发送前和响应接收后进行处理

  • 可扩展性:通过实现接口自定义拦截器

常用内置Advisor:

  • MessageChatMemoryAdvisor:维护对话历史

  • QuestionAnswerAdvisor:知识库检索增强

3.2 对话记忆Advisor详解

负责维护对话上下文的拦截器主要有两种:

  • MessageChatMemoryAdvisor

    • 保留消息的角色(用户/助手/系统)

    • 将历史消息作为独立实体注入

    • 维护完整对话结构

  • PromptChatMemoryAdvisor

    • 将历史对话合并为文本

    • 作为系统提示的一部分注入

    • 可能丢失消息边界信息

在多轮对话系统中,通常选择MessageChatMemoryAdvisor以保留更多上下文信息:

ChatMemory chatMemory = new InMemoryChatMemory();
chatClient = ChatClient.builder(dashscopeChatModel).defaultSystem(SYSTEM_PROMPT).defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory)).build();

四、对话记忆实现方案

4.1 ChatMemory接口

ChatMemory是Spring AI提供的抽象接口,定义了对话记忆的核心操作:

public interface ChatMemory {void add(String conversationId, List<Message> messages);List<Message> get(String conversationId, int lastN);void clear(String conversationId);
}

这三个方法构成了对话记忆的基本功能:

  • 添加:保存新的对话消息

  • 获取:检索特定会话的历史消息

  • 清空:删除特定会话的所有记录

4.2 内存存储实现

最简单的实现是使用InMemoryChatMemory,适用于开发测试或短期会话:

ChatMemory chatMemory = new InMemoryChatMemory();

内存实现的优缺点:

  • 优点:速度快,配置简单

  • 缺点:服务重启数据丢失,不适合生产环境

4.3 文件持久化存储

对于需要跨服务重启保存对话的场景,可以实现基于文件的持久化:

@Slf4j
public class FileBasedChatMemory implements ChatMemory {
​private final String baseDir;private static final Kryo kryo;
​static {kryo = new Kryo();kryo.setRegistrationRequired(false);kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());}
​public FileBasedChatMemory(String dir) {this.baseDir = dir;new File(dir).mkdirs();}
​@Overridepublic void add(String conversationId, List<Message> messages) {var existingMessages = getOrCreateConversation(conversationId);existingMessages.addAll(messages);saveConversation(conversationId, existingMessages);}
​@Overridepublic List<Message> get(String conversationId, int lastN) {var allMessages = getOrCreateConversation(conversationId);return allMessages.stream().skip(Math.max(0, allMessages.size() - lastN)).toList();}
​// 其他辅助方法...
}

文件存储的优缺点:

  • 优点:简单易实现,无需额外服务

  • 缺点:并发性能有限,不适合高并发场景

4.4 数据库持久化实现

对于生产环境,尤其是多用户系统,数据库存储是最佳选择。以MySQL为例:

@Component
@Slf4j
public class MySQLChatMemory implements ChatMemory {
​private final JdbcTemplate jdbcTemplate;private final JSONConfig jsonConfig;
​public MySQLChatMemory(DataSource dataSource) {this.jdbcTemplate = new JdbcTemplate(dataSource);this.jsonConfig = new JSONConfig().setIgnoreNullValue(true);log.info("初始化MySQL对话记忆");}
​@Override@Transactionalpublic void add(String conversationId, List<Message> messages) {// 获取当前最大序号Integer maxOrder = getMaxOrder(conversationId).orElse(0);int nextOrder = maxOrder + 1;
​// 批量插入消息String insertSql = "INSERT INTO chatmemory (...) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";jdbcTemplate.batchUpdate(insertSql, messages, messages.size(), (ps, message) -> {// 设置参数...});}
​// 其他实现方法...
}

对于更复杂的场景,可以集成ORM框架如MyBatis-Plus:

@Component
@Slf4j
public class MybatisPlusChatMemory implements ChatMemory {
​private final ChatMemoryService chatMemoryService;
​public MybatisPlusChatMemory(ChatMemoryService chatMemoryService) {this.chatMemoryService = chatMemoryService;log.info("初始化Mybatis-Plus对话记忆");}
​@Overridepublic void add(String conversationId, List<Message> messages) {chatMemoryService.addMessages(conversationId, messages);}
​@Overridepublic List<Message> get(String conversationId, int lastN) {return chatMemoryService.getMessages(conversationId, lastN);}
​@Overridepublic void clear(String conversationId) {chatMemoryService.clearMessages(conversationId);}
}

五、自定义增强Advisor实现

5.1 日志记录Advisor

记录对话内容的自定义Advisor实现:

@Slf4j
public class MyLoggerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
​@Overridepublic String getName() {return getClass().getSimpleName();}
​@Overridepublic int getOrder() {return 0; // 执行顺序}
​// 请求前打印用户输入private AdvisedRequest before(AdvisedRequest request) {log.info("AI Request: {}", request.userText());return request;}
​// 响应后打印 AI 输出private void observeAfter(AdvisedResponse response) {log.info("AI Response: {}", response.response().getResult().getOutput().getText());}
​@Overridepublic AdvisedResponse aroundCall(AdvisedRequest req, CallAroundAdvisorChain chain) {req = before(req);AdvisedResponse res = chain.nextAroundCall(req);observeAfter(res);return res;}
​// 流式调用处理@Overridepublic Flux<AdvisedResponse> aroundStream(AdvisedRequest req, StreamAroundAdvisorChain chain) {req = before(req);Flux<AdvisedResponse> res = chain.nextAroundStream(req);return new MessageAggregator().aggregateAdvisedResponse(res, this::observeAfter);}
}

5.2 内容安全Advisor

检测违禁词的自定义Advisor实现:

 

5.3 推理增强Advisor

@Slf4j
public class ProhibitedWordAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
​private static final String DEFAULT_PROHIBITED_WORDS_FILE = "prohibited-words.txt";private final List<String> prohibitedWords;
​public ProhibitedWordAdvisor() {this.prohibitedWords = loadProhibitedWordsFromFile(DEFAULT_PROHIBITED_WORDS_FILE);log.info("初始化违禁词Advisor,违禁词数量: {}", prohibitedWords.size());}
​private AdvisedRequest checkRequest(AdvisedRequest request) {String userText = request.userText();if (containsProhibitedWord(userText)) {log.warn("检测到违禁词在用户输入中: {}", userText);throw new ProhibitedWordException("用户输入包含违禁词");}return request;}
​@Overridepublic AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {return chain.nextAroundCall(checkRequest(advisedRequest));}
​// 其他辅助方法...
}

提高模型推理能力的自定义Advisor:

public class ReReadingAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
​private AdvisedRequest before(AdvisedRequest advisedRequest) {Map<String, Object> advisedUserParams = new HashMap<>(advisedRequest.userParams());advisedUserParams.put("re2_input_query", advisedRequest.userText());
​return AdvisedRequest.from(advisedRequest).userText("""{re2_input_query}Read the question again: {re2_input_query}""").userParams(advisedUserParams).build();}
​@Overridepublic AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {return chain.nextAroundCall(this.before(advisedRequest));}
​// 其他方法实现...
}

六、结构化输出与业务应用

6.1 结构化报告生成

Spring AI支持将模型输出直接映射为Java对象,实现结构化数据处理:

public LoveReport doChatWithReport(String message, String chatId) {LoveReport loveReport = chatClient.prompt().system(SYSTEM_PROMPT + "每次对话后都要生成恋爱结果,标题为{用户名}的恋爱报告,内容为建议列表").user(message).advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId).param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10)).call().entity(LoveReport.class);log.info("loveReport: {}", loveReport);return loveReport;
}

这种方式可以直接将模型生成的内容解析为Java对象,便于后续业务处理。

6.2 完整对话系统整合

将所有组件整合的多轮对话系统示例:

@Component
@Slf4j
public class EnhancedLoveApp {
​private static final String SYSTEM_PROMPT = "**恋爱大师·情感导航员**...";private final ChatClient chatClient;
​public EnhancedLoveApp(ChatModel dashscopeChatModel, ChatMemoryService chatMemoryService) {// 使用MyBatis-Plus持久化对话记忆ChatMemory chatMemory = new MybatisPlusChatMemory(chatMemoryService);chatClient = ChatClient.builder(dashscopeChatModel).defaultSystem(SYSTEM_PROMPT).defaultAdvisors(// 对话记忆能力new MessageChatMemoryAdvisor(chatMemory),// 记录日志new MyLoggerAdvisor(),// 违禁词检测new ProhibitedWordAdvisor(),// 复读强化阅读能力new ReReadingAdvisor()).build();}
​public String doChat(String message, String chatId) {return chatClient.prompt().user(message).advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId).param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10)).call().chatResponse().getResult().getOutput().getText();}// 其他业务方法...
}

七、性能优化与最佳实践

7.1 对话记忆性能优化

在高并发场景下,对话记忆的存取可能成为性能瓶颈,几个优化策略:

  1. 分级缓存

    • 热点会话使用内存缓存

    • 冷数据使用数据库存储

    • 采用 Redis 等分布式缓存

  2. 异步写入

    • 响应用户后再异步保存对话历史

    • 使用消息队列减轻数据库压力

  3. 记忆裁剪

    • 定期清理过期会话数据

    • 只保留最近N轮对话

    • 实现智能摘要压缩历史内容

7.2 多轮对话系统设计要点

构建生产级多轮对话系统的关键考量:

  1. 会话标识与隔离

    • 使用稳定唯一的会话标识

    • 避免跨会话数据泄露

    • 支持多设备同步

  2. 上下文窗口管理

    • 根据模型能力调整窗口大小

    • 实现滑动窗口减少token成本

    • 处理长对话的上下文压缩

  3. 异常处理

    • 模型超时与重试机制

    • 会话恢复能力

    • 敏感内容过滤

  4. 监控与评估

    • 对话质量监控指标

    • 性能监控

    • A/B测试框架

总结

基于Spring AI框架构建多轮对话系统,关键在于正确设计对话记忆机制和合理组织Advisor拦截器链。通过灵活选择不同的持久化方案,可以满足从开发测试到大规模生产环境的不同需求。


最后我叫 lenyan~ 也会持续学习更进 AI知识。让我们共进 AI 大时代。

 作者:lenyan GitHub:lenyanjgk (lenyanjgk) · GitHub CSDN:lenyan~-CSDN博客 

觉得有用的话可以点点赞 (/ω\),支持一下。

如果愿意的话关注一下。会对你有更多的帮助。

每周都会不定时更新哦 >人< 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/80617.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/80617.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++中的虚表和虚表指针的原理和示例

一、基本概念 1. 什么是虚函数&#xff08;virtual function&#xff09;&#xff1f; 虚函数是用 virtual 关键字修饰的成员函数&#xff0c;支持运行时多态&#xff08;dynamic polymorphism&#xff09;。通过基类指针或引用调用派生类重写的函数。 class Base { public:…

FPGA:XILINX FPGA产品线以及器件选型建议

本文将详细介绍Xilinx&#xff08;现为AMD的一部分&#xff09;当前的FPGA产品线及其主要特点&#xff0c;并提供器件选型的建议。以下内容基于Xilinx FPGA的最新信息&#xff0c;涵盖产品系列、特性及选型指导。由于Xilinx已被AMD收购&#xff0c;产品线以AMD Xilinx品牌为主&…

【C++】多线程和多进程

在C++中,多线程通信(同一进程内的线程间交互)和进程间通信(IPC,不同进程间的数据交换)是构建并发系统的核心技术。以下是两种通信机制的详细介绍和典型实现: 一、多线程通信(线程间同步与数据共享) 1. 共享内存与同步原语 通过全局变量或对象成员变量实现数据共享,…

PC Cleaner软件,它能帮助用户轻松清理和优化电脑,提升系统性能。

不用破解就能用&#xff01;这款超神的电脑清理 Pro 版&#xff0c;绝了&#xff01; 宝子们&#xff0c;我是你们的数码小助手蓝木云&#xff01;不知道大家有没有这种感觉&#xff0c;电脑用久了&#xff0c;就像住久了没打扫的屋子&#xff0c;越来越 “乱”&#xff0c;运…

linux中fork()函数的小问题

问题描述&#xff1a;分析下列代码&#xff0c;分别能产生多少a // 1 for(int i0; i<3; i){ printf("a\n"); fork(); }// 2 for(int i0; i<3; i){ fork(); printf("a\n"); }// 3 for(int i0; i<3; i){ fork(); printf("a"); } fflus…

阿克曼-幻宇机器人系列教程2- 机器人交互实践(Topic)

在上一篇文章中&#xff0c;我们介绍了两种登录机器人的方式&#xff0c;接下来我们介绍登录机器人之后&#xff0c;我们如何通过topic操作命令实现与机器人的交互。 1. 启动 & 获取topic 在一个终端登录树莓派后&#xff0c;执行下列命令运行机器人 roslaunch huanyu_r…

51c嵌入式~电路~合集27

我自己的原文哦~ 一、7805应用电路 简介 如上图&#xff0c;7805 集成稳压电路。 7805是串联式三端稳压器&#xff0c;三个端口分别是电压输入端&#xff08;IN&#xff09;&#xff0c;地线&#xff08;GND&#xff09;&#xff0c;稳压输出&#xff08;OUT&#xff09;…

Vitrualbox完美显示系统界面(只需三步)

目录 1.使用vitrualbox的增强功能&#xff1a;​编辑 2.安装增强功能&#xff08;安装完后要重启虚拟机&#xff09;&#xff1a; 3. 调整界面尺寸&#xff08;如果一个选项不行的话&#xff0c;就多试试其他不同的百分比&#xff09;&#xff1a; 先看看原来的&#xff0c;…

2025年第十六届蓝桥杯软件赛省赛C/C++大学A组个人解题

文章目录 题目A题目C&#xff1a;抽奖题目D&#xff1a;红黑树题目E&#xff1a;黑客题目F&#xff1a;好串的数目 https://www.dotcpp.com/oj/train/1166/ 题目A 找到第2025个素数 #include <iostream> #include <vector> using namespace std; vector<i…

电机控制储备知识学习(一) 电机驱动的本质分析以及与磁相关的使用场景

目录 电机控制储备知识学习&#xff08;一&#xff09;一、电机驱动的本质分析以及与磁相关的使用场景1&#xff09;电机为什么能够旋转2&#xff09;电磁原理的学习重要性 二、电磁学理论知识1&#xff09;磁场基础知识2&#xff09;反电动势的公式推导 附学习参考网址欢迎大家…

JMeter同步定时器 模拟多用户并发访问场景

同步定时器 JMter同步定时器的作用主要在于模拟多用户并发访问的场景&#xff0c;确保多个线程能够同时执行某个操作&#xff0c;达到真正的并发效果。 当多个线程同时启动时&#xff0c;它们可能会在不同的时间间隔内执行&#xff0c;这样就无法达到真正的并发效果。&#xff…

C++11异步编程 --- async

C11异步编程 — async和future C11引入了async和future机制&#xff0c;用于简化异步编程和并发操作。这两个组件位于<future>头文件中&#xff0c;提供了高级的异步任务管理接口。 一、async 1.定义 std::async std::async是一个函数模板&#xff0c;用于启动一个异…

(七)深度学习---神经网络原理与实现

分类问题回归问题聚类问题各种复杂问题决策树√线性回归√K-means√神经网络√逻辑回归√岭回归密度聚类深度学习√集成学习√Lasso回归谱聚类条件随机场贝叶斯层次聚类隐马尔可夫模型支持向量机高斯混合聚类LDA主题模型 一.神经网络原理概述 二.神经网络的训练方法 三.基于Ker…

[Java实战]Spring Boot 整合 Swagger2 (十六)

[Java实战]Spring Boot 整合 Swagger2 &#xff08;十六&#xff09; 一、Swagger 的价值与痛点 为什么需要 API 文档工具&#xff1f; 开发阶段&#xff1a;前后端高效协作&#xff0c;实时验证接口测试阶段&#xff1a;提供标准化测试用例维护阶段&#xff1a;降低新人理解…

系统稳定性之上线三板斧

&#x1f4d5;我是廖志伟&#xff0c;一名Java开发工程师、《Java项目实战——深入理解大型互联网企业通用技术》&#xff08;基础篇&#xff09;、&#xff08;进阶篇&#xff09;、&#xff08;架构篇&#xff09;清华大学出版社签约作家、Java领域优质创作者、CSDN博客专家、…

题海拾贝:P1833 樱花

Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;《编程之路》、《数据结构与算法之美》、《题海拾贝》、《C修炼之路》 欢迎点赞&#xff0c;关注&am…

摆脱拖延症的详细计划示例

以下是一个以一周为周期&#xff0c;帮助你摆脱拖延症的详细计划示例&#xff0c;你可以根据自己的实际情况进行调整和完善。 --- # 摆脱拖延症一周计划 ## 一、计划目标 通过一系列有针对性的方法和行动&#xff0c;逐步克服拖延习惯&#xff0c;提高任务执行效率和自我管理…

实物工厂零件画图案例(上)

文章目录 滑台气缸安装板旋转气缸安装板张紧调节块长度调节块双轴气缸安装板步进电机安装板梯形丝杆轴承座 简介&#xff1a;案例点击此处下载&#xff0c;这次的这几个案例并没有很大的难度&#xff0c;练习这几个案例最为重要的一点就是知道&#xff1a;当你拿到一个实物的时…

【Nova UI】十六、打造组件库之滚动条组件(中):探秘滑块的计算逻辑

序言 在上篇文章中&#xff0c;我们完成了滚动条组件开发的前期准备工作&#xff0c;包括理论推导、布局规划和基础设置。现在&#xff0c;我们将把这些准备转化为实际代码&#xff0c;开启滚动条组件的具体开发之旅&#x1f31f;。我们会详细阐述如何实现各项功能&#xff0c…

laravel 使用异步队列,context带的上下文造成反序列化出问题

2025年5月8日17:03:44 如果你是单个应用&#xff0c;异步递交任务&#xff0c;是在应用内部使用&#xff0c;一般不会发生这样的问题 但是现在app项目是 app是一个应用&#xff0c;admin是一个应用&#xff0c;app吧为了接口性能吧异步任务丢给admin去执行&#xff0c;如果两个…