如何实现 AI 多轮对话功能?如何解决对话记忆持久化问题?
- 标签:后端、编程导航
🏛 业务架构师
多轮对话的实现分上下文组装与记忆管理两层:
上下文组装:每轮请求把历史消息(system + 历史对话 + 新输入)拼进 messages。关键参数是上下文窗口——模型只能"看见"窗口内的内容,超长就要截断或压缩。
记忆管理的分级策略(这是成本与体验的核心权衡):
- 滑动窗口:保留最近 N 轮——便宜但"失忆",用户半小时前提的要求模型忘了,体验崩塌;
- 摘要压缩:老对话用模型压缩成摘要注入 system——用一次便宜的总结调用换长期记忆,token 成本可控;
- 分层记忆:短期(近几轮原文)+ 长期(用户偏好、关键事实,抽取后结构化存储)——类似人类的记忆模型。
持久化的业务考量:
- 存储选型:会话消息按会话 ID 落关系库(用户要能翻历史记录——对话历史是可追溯的产品资产),热会话的窗口快照放 Redis 加速组装;
- 生命周期:留存期限对齐合规(个保法语境下用户数据可删除)——"记忆"既是资产也是合规责任,删除会话要级联清理摘要与向量;
- 成本模型:记忆越长 token 越贵,轮次 × 上下文长度是线性增长的成本曲线——产品定价(订阅额度)必须基于这条曲线测算,很多 AI 产品亏钱就亏在没有算清这笔账。
一句话:多轮对话的架构 = 窗口管理(技术)+ 记忆分层(体验)+ 成本测算(商业),三者缺一就不成立。
🏗 系统架构师
多轮对话 = 上下文管理问题:LLM 无状态,每轮都要把历史"喂回去"。实现分三层:
1. 消息组装:每轮请求携带 messages 数组(system 人设 + 历史对话 + 当前问题)。Spring AI 里用 ChatMemory(MessageWindowChatMemory 保留最近 N 轮 / MessageChatMemoryAdvisor 自动注入)。
2. 记忆持久化(关键):
- 存储结构:
conversation_id → List<Message>,落关系库(消息表:会话 ID、角色、内容、token 数、时间); - 加载策略:窗口截取(最近 20 轮)+ 摘要压缩(超窗历史用 LLM 摘成一段系统提示,压缩 token)——长对话成本与效果的平衡点;
- 会话管理:多聊天室 = 多 conversation_id,用户切换会话即切上下文。
3. 增强策略:
- 关键信息提取:从对话中抽取用户偏好/事实存结构化档案(画像记忆),比全文回灌更省 token 更精准;
- Redis 缓存活跃会话最近窗口(热数据),DB 存全量(冷数据),读写分离。
工程权衡:上下文窗口是稀缺资源——每轮全量回灌最准但贵且慢;窗口+摘要是性价比方案;档案化记忆适合长期陪伴类产品。失效场景:用户清会话/换设备 → 按 user_id 关联会话列表恢复;摘要损失细节 → 关键决策(如已确认的订单参数)单独结构化存储,不依赖对话记忆。
💻 全栈开发专家
多轮对话 = 维护会话消息列表传给模型;本质是把历史 messages(system + 多轮 user/assistant)每次完整发送——模型本身无状态,"记忆"全靠应用层。
实现要点:
java
List<Message> messages = new ArrayList<>();
messages.add(new SystemMessage("你是项目助手..."));
messages.addAll(historyDao.load(conversationId)); // 历史消息
messages.add(new UserMessage(question));
// 调用后把 user + assistant 消息再追加落库持久化方案对比(我的选型过程):
- 内存/Redis:快但重启丢、容量小——只做加速层;
- MySQL 持久化(我的落地):会话表 + 消息表,
conversation_id关联,天然支持历史查询、分页加载、审计;
sql
CREATE TABLE chat_message (
id BIGINT PRIMARY KEY,
conversation_id VARCHAR(64) NOT NULL,
role VARCHAR(16) NOT NULL, -- system/user/assistant/tool
content TEXT,
create_time DATETIME,
KEY idx_conv (conversation_id, create_time)
);- 上下文长度控制(关键):模型窗口有限且越长越贵。我的策略:最近 N 轮原文 + 更早历史的 LLM 摘要拼接,比如近 10 轮原样带、之前的压成一段 summary,token 稳定可控。
工程细节:Spring AI 用 MessageChatMemoryAdvisor + 自定义 ChatMemoryRepository(实现 load/append 到 MySQL)即可,读时按窗口截断;会话切换/删除要做并发保护(同一会话串行写)。