揭示多步 Agent 系统中每步积累对话历史导致后期上下文压力增大、输出质量下降的生产级陷阱。
上下文窗口管理,是每个多步骤 Agent pipeline 中看不见的成本。
在生产环境的咨询项目中,我反复看到同一种现象:一个 Agent 在测试时运行得非常顺畅——十个步骤、推理连贯、输出正确——上线六周后却开始逐渐退化。没有报错,也没有崩溃,只有悄无声息的偏移。输出越来越短,过去逻辑清晰的推理步骤开始变成含糊的总结。用户逐渐察觉,这个 Agent “感觉变慢了”。
根本原因几乎总是一样:上下文随着每一步不断增长,却没有人测量它。
默认情况下,多步骤 Agent 会累积完整的对话历史。每次工具调用的结果都会追加到上下文中,每个中间推理步骤都会被保存。传递给每次模型调用的 conversation 对象,会随着已执行步骤的数量线性增长。
在一个包含十个步骤的 pipeline 中,执行到第 8 或第 9 步时,模型每次调用接收到的内容可能已经占据上下文窗口的 90%。从技术上看,它仍未超出限制,但模型的注意力已经被分散到数千个 token 的早期推理内容上,而这些内容早已与当前步骤无关。
下面是这种情况在真实 pipeline 中的表现。假设一个研究 Agent 依次执行搜索、提取实体、消除歧义、检索文档、排序、生成初稿和事实核查,一共七个步骤。如果每一步的工具输出平均为 800 个 token,并且模型在每一步都会带上完整的推理轨迹,那么:
第 1 步之后:上下文中约有 1,200 个 token
第 3 步之后:约 4,800 个 token
第 5 步之后:约 9,600 个 token
第 7 步之后:约 15,600 个 token
对于上下文窗口为 16K 的模型,执行最后一步时,几乎已经没有空间留给真正的综合生成任务。对于上下文窗口为 128K 的模型,这些数字看起来宽裕得多——直到你意识到,每次调用都要为所有这些上下文付费,而且模型的注意力正在被那些早在三个步骤之前就失去相关性的内容稀释。
针对单次查询的测试永远暴露不了上下文饱和问题。开发者用一个测试集运行 pipeline 十次,看到输出都正确,就将它上线了。但他们实际测试的是十个全新的对话线程,每个线程都从零 token 开始。
生产负载完全不是这样。用户会连续执行任务。如果没有显式限制 session 历史,那么在同一个 session 中执行五次请求的 pipeline,会累积这五次运行的全部上下文。当 orchestration 层重试失败的步骤时,它会将失败相关的推理重新传入上下文。长时间运行的 Agent 在调用外部 API 并等待结果期间,也会不断累积工具日志。
真实查询量下的上下文增长,并不是通常意义上的扩展性问题,而是状态管理问题。pipeline 不知道哪些历史上下文仍然与当前步骤相关。
如果第 3 步需要第 1 步的输出,只传递结构化结果——不要传递第 1 步完整的 chain-of-thought。一个返回十条结果的搜索步骤,不需要把内部的排序依据继续传递给综合生成步骤。综合生成步骤真正需要的只是排序后的列表。应当在不同阶段的边界处积极截断上下文。
这要求你有意识地设计每个步骤的输出契约。如果输出是非结构化文本,默认情况下所有内容都会被继续传递。如果输出采用 typed schema,那么只有 schema 字段会在步骤之间流转。正是这种类型边界,强制执行了 token 预算。
当综合生成步骤运行时,检索步骤的详细推理无须继续保留在上下文中,但结构化摘要需要保留。在将控制权从一个阶段交给下一个阶段之前,先运行一个压缩步骤,将完整输出转换成固定大小的摘要。摘要负责传递关键事实,完整的推理轨迹仍可用于调试,但不会进入生产环境的调用链。
压缩步骤本身也是一次模型调用,因此会产生成本。但它几乎总是值得的。将一段包含 3,000 个 token 的推理轨迹压缩成 400 个 token 的结构化摘要,可以在 pipeline 后续的每一步中节省 2,600 个 token。对于一个每天处理数千次查询、包含十个步骤的 pipeline 来说,与节省的成本相比,压缩的成本微不足道。
无声的上下文溢出会产生细微错误的输出,而这些输出最终会抵达用户。当模型只剩下上下文窗口的 2% 可用于实际任务时,它不会返回错误。它会给出一个听起来合理、但质量已经退化的答案,而这个答案还能通过表层的质量检查。除非你主动测量每个步骤的上下文大小,否则这种故障模式完全不可见。
添加严格上限并不复杂:在每次模型调用前测量 token 数量,如果超过阈值,就抛出异常。这个阈值应该远低于模型的最大限制,而不是简单地按最大值的某个比例设定。如果某个步骤的预算是 2,000 个 token,而累积上下文已经达到 8,000 个 token,那么这个 pipeline 并不是“仍在限制以内”——它早就已经出问题了。
明确失败,意味着工程团队能够看到问题。无声退化,则意味着用户会比工程仪表盘更早发现问题。
只需要三个指标,就能在上下文增长影响用户之前发现问题:
这些指标并不难收集。只需在模型调用外封装一层,在发送请求前记录 prompt 的 token 数量即可。真正的问题在于,大多数 orchestration framework 默认并不暴露这些数据,你必须自己添加监控。
上下文增长之所以总让团队措手不及,是因为它看起来不像 bug。在无状态测试环境中正常运行的 pipeline 架构,恰恰也是在有状态生产负载下失败的那套架构。从测试到生产,唯一发生变化的就是不断累积的状态。
解决办法是将上下文视为一种具有预算的资源,就像对待内存或 API 调用一样。每个步骤都有输入预算和输出预算:输入预算限制该步骤可以接收多少内容,输出预算限制该步骤可以向后传递多少内容,而 pipeline 必须同时强制执行这两种预算。
如果一个 agentic pipeline 没有明确的上下文预算,那就意味着它在缺少一项关键运行约束的情况下工作。它会一直运行,直到某一天突然不再可靠;而故障信号又足够隐蔽,以至于用户会比监控仪表盘更早察觉。
面向生产环境 agentic system 的上下文窗口管理与监控,是我在 hannune.ai 提供的咨询服务之一。
如需采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。