AI Agent 开发 06|多轮对话能接上,靠的是怎样保存和取回信息
第一轮告诉助手项目用 pnpm,第二轮问“那我应该运行什么命令”,应用得把前面的信息带回请求。只发送第二句话,模型就缺少“那”指向的内容。
对话记忆由应用保存、选择,再提供给模型。弄清这三个动作,才知道信息为什么会遗失,或者为什么会出现在不该出现的会话里。
最近消息先解决眼前的追问
最直接的做法是保存消息历史,每次请求带上最近一部分。当前会话连续讨论同一个项目时,这已经很有用。
历史继续增长以后,输入会变长,也会夹进已经结束的话题。此时需要决定保留哪些原文,哪些内容可以压缩。这个决定应该根据后续任务需要什么来做。
知识库助手刚读过一个接口定义,用户接着问其中某个字段,可以保留对应片段和最近问题。几小时前讨论过的界面配色,通常没有必要一直跟着这次请求。
摘要能节省空间,也会遗漏细节
原教程介绍 Mastra 的观察记忆,用 Observer 把长对话整理成观察笔记,再由 Reflector 合并和提炼积累的内容。
摘要适合保留阶段决定和话题关系。精确字段名、执行结果及尚未解决的约束,则需要能回查原文或结构化状态。把“只在测试环境运行”压成“已经确认可以运行”,后面的行动就可能越过原本范围。
压缩后可以抽样核对。原来明确要求保留的限制有没有留下,未完成的任务有没有被写成完成,谁提出的判断有没有被混在一起。摘要本身也是一次生成结果,需要检查。
用户资料与当前任务分开保存
工作记忆可以是一份持续更新的资料。默认使用中文、主要写 TypeScript 这类偏好,可能跨会话仍有帮助。某个项目今天使用的启动命令,则应该跟项目绑定。
原教程用 resourceId 和 threadId 区分资源与会话范围。理解它们时,可以先看信息允许被哪些请求共享,再映射到具体框架的配置。
用户 A
会话 1 讨论项目甲
会话 2 讨论项目乙
用户 B
会话 3 讨论自己的项目用户 A 的语言偏好可以在合适配置下共享。项目甲尚未解决的错误,不该自动变成项目乙的背景。用户 B 更不能看到用户 A 的资料。
这些 ID 只负责定位。服务端仍要验证当前登录用户是否有权访问会话。浏览器传来一个 thread ID,无法单凭这个值证明归属。
旧话题需要另一种找法
最近消息适合追问眼前的内容。如果用户想找三个月前讨论的分块方案,只取最近十条消息通常找不到。语义召回可以从保存的历史中寻找相关内容,再把其中一部分带进来。
召回结果仍要保留时间和来源。旧项目的决定可能已经过期,语义相近也可能属于另一份文档。把召回结果直接当成当前事实,会混淆时间和适用范围。
不必一开始就加入所有记忆形式。先让同一会话的追问接得上,再保存明确需要跨会话的偏好。长对话出现实际成本时再压缩;确实要找旧话题时再做语义召回。
存进数据库以后,还要验证能不能恢复
保存与召回是不同动作。数据库中有记录,并不意味着请求已经使用了它。需要检查写入是否成功、查询条件是否正确,以及最终上下文里有没有对应内容。
原教程的 LangChain 示例使用内存型 checkpointer。它可以在同一进程里保留状态,进程重启以后就会丢失。需要长期运行时,要选择持久化存储,并测试断开、重连和重启后的恢复。
可以按下面四种情况验收知识库助手。
| 操作 | 需要核对的结果 |
|---|---|
| 同一会话追问 | 能引用刚才讨论的内容 |
| 同一用户另开会话 | 只共享明确允许共享的信息 |
| 换另一个用户 | 无法读取前一位用户的资料 |
| 重启应用 | 持久化状态恢复,临时状态按设计丢弃 |
删除也需要测试。用户要求移除一条偏好后,原消息、摘要和长期记忆可能分别保存过相关信息。应用要按自己的数据策略处理这些副本,并验证后续请求不会继续召回已经删除的内容。
做完测试,再打印一次最终发给模型的消息结构。原文、摘要和用户资料应当保留各自来源,方便回查。出现错误记忆时,这份记录会告诉你该修保存、召回,还是压缩过程。
延伸阅读
本篇依据冴羽《AI Agent 开发实战》学习整理稿中的四类记忆、会话范围与持久化说明重写。原教程见 Mastra 记忆功能 和 LangChain.js 会话示例。