最近在做一个带工具调用的Agent,用的GPT-4o。单轮任务还行,但一进入多轮工具调用就崩——比如用户连续让AI查天气、订餐厅、再改时间,后面AI就开始“失忆”,甚至把上一轮的工具参数串到下一轮。我自己试过把历史消息全塞进system prompt,结果token爆了不说,模型反而被无关信息干扰。也试过简单的摘要压缩,但摘要丢细节,比如用户中途改过的偏好就丢了。想请教下大家,生产级的Agent一般怎么做上下文管理?是分层记忆(短期+长期)?还是用RAG专门存关键状态?或者有什么工程上的trick(比如把工具调用记录单独结构化存储)?求个大致思路或开源项目参考,感谢!
Agent里多轮对话越聊越乱,有没有靠谱的上下文管理方案?
全部回复
共 6 条结构化存工具调用记录是关键,再配合分层记忆,比硬塞历史靠谱多了。
结构化存工具调用记录这个方向我觉得是对的,参数串场多半是因为历史里混了太多自然语言噪音。我们之前是把每轮tool call和结果单独抽出来,按时间戳维护一个状态机,只把当前任务相关的状态喂给模型,token省了不少。不过摘要压缩丢偏好那个问题,建议搞个分层的key-value记忆,用户明确改过的偏好单独存,比让模型从长文本里自己找靠谱。想问下你试过把最近几轮原始消息+更早的摘要混着用吗?比例怎么调比较稳?
我之前也被这个问题折磨过,试了一圈下来感觉单纯堆历史或者粗暴摘要确实不行。你现在这种“改时间”的偏好丢失,本质上是缺了一个状态追踪层,把用户意图的变更和当前任务的关键参数单独拎出来维护。我的做法是把对话历史、工具调用记录和“工作记忆”分开存,工作记忆是个结构化JSON,每次工具调用后强制更新,比如订餐厅后用户说改时间,就直接更新这个JSON里的时间字段,而不是靠模型从长对话里自己找。这样模型每次只需要读最近几轮加当前工作记忆,token省了,准确率也稳。至于长期偏好,建议定期把确认过的信息抽出来存到向量库里,但别全塞给模型,只在需要时检索。你可以看看LangChain的ConversationSummaryBufferMemory,但生产上我更推荐自己写个轻量状态机,或者参考一下CrewAI的上下文管理方式,他们有个叫做ContextRouting的机制。想再问下你,现在工具调用结果返回后,你是让模型自己提炼必要信息,还是直接把原始结果丢回历史里?这点我觉得影响也很大。
试试把工具调用结果单独存结构化状态,对话流只留轻量摘要,能省不少token。
我们项目用分层记忆,短期存会话,长期落库,靠向量检索拉取历史偏好,稳很多。
这个问题挺典型的,我踩过类似的坑。你说的把历史全塞system prompt确实是反模式,模型注意力会被稀释,工具参数串轮次基本就是这么来的。我现在用的做法是把上下文拆成几层:对话历史只保留最近N轮原文,更早的做结构化摘要而不是自然语言摘要,比如把用户偏好、已确认的约束、已完成动作存成JSON字段,这样细节不会丢。工具调用记录我强烈建议单独存,不要混在message历史里,每次只把当前任务相关的工具schema和最近一次调用结果注入,能省很多token也更稳。RAG存关键状态可行但别当万能药,检索噪音反而会引入新的串扰,适合存跨会话的长期偏好,不适合存当前任务的临时状态。可以看看LangGraph的checkpointer机制和MemGPT的思路,前者解决状态持久化,后者解决分层记忆调度,但生产里最后还是得自己写一层状态机来管槽位。你们现在是单Agent多工具还是多Agent拆分?如果是后者,上下文隔离会容易很多。
我最近也在踩这个坑,说下目前试下来比较管用的做法:把工具调用记录单独拎出来用结构化存,每轮只把当前任务相关的工具结果重新注入,别把所有历史都往里堆。另外维护一个独立的slot memory存用户偏好和已确认的状态,摘要只用来压缩闲聊部分,关键字段绝不走摘要。LangGraph的checkpoint机制配合状态机挺适合这种场景,可以把每轮的工具输入输出挂在graph state上,比硬塞prompt干净多了。