Agent 长任务的瓶颈,是上下文工程
长任务跑不稳,往往不是模型不会推理,而是它每一轮看到的状态不对。真正难的是把上下文当成工作记忆来管理:该放什么、压缩什么、记住什么、缓存什么。
aibuddy 团队
运行时
上下文窗口是工作记忆,不是存储
每一次模型调用,都是一次新的推理。模型当然可以借助数据库、文件系统、搜索结果、记忆和工具输出,但前提是 Harness 先把相关内容取出来,再放进这一轮的上下文里。没有进入上下文的东西,对这一轮推理来说就是不可见的。
所以,上下文窗口更像工作记忆,不像硬盘。它容量有限,生命周期短,而且很容易被污染。
这正是长任务里最常见的误区:看到模型上下文窗口变大,就以为可以把所有东西都塞进去。实际上,上下文不是越多越好。低价值 token 会和高价值 token 争夺注意力。过期的工具输出、重复日志、已经放弃的假设、不相关的文档,不只是花钱,它们还会稀释模型对关键信息的注意力。
Anthropic 把 context engineering 定义为:选择最有可能让模型产生目标行为的上下文配置。我们自己的 docs 里也用了一个更直白的说法:模型能力是基础,上下文质量决定上限。一个强模型如果拿不到项目架构、业务规则和内部约定,就像一个聪明的新员工进了信息混乱的团队,能力再强也发挥不出来。
所以,真正的问题不是“窗口能塞多少”,而是:
在这一轮调用里,模型最需要看到哪一小块高信号状态?
真正难的是 context lifecycle
Agent 不是一次 prompt,而是一个循环。
模型读取上下文,提出动作;Harness 执行工具,把结果放回轨迹;下一轮模型调用再基于新的上下文继续。每一轮,上下文里都有稳定前缀、工具定义、消息历史、当前任务状态、检索出来的记忆,以及上一轮刚产生的新信息。
如果系统只会不断追加,上下文迟早会撞上两个边界。第一个很明显:窗口装不下。第二个更隐蔽:还没装满,质量已经开始变差。长上下文里,模型对具体信息的召回和使用会下降,这就是 context rot。
因此,长任务需要的不是更长的一段 prompt,而是一套上下文生命周期管理。每一次模型调用前,runtime 都要决定:
- 哪些内容保留原文
- 哪些内容变成摘要
- 哪些内容写入记忆或文件
- 哪些内容需要时再加载
- 哪些探索应该交给子 agent 隔离完成
- 哪些前缀必须保持字节稳定,方便 prompt cache 命中
这些不是实现细节,而是产品和架构决策。它们决定了一个 agent 能不能在两小时任务里保持连贯,还是跑到第四十步就忘了最初目标。
压缩不是截断
缩短上下文最简单的办法,是删掉旧消息。但这通常不是好办法。
任务开头往往包含目标、约束、用户偏好和早期关键决策。最近几轮反而可能是低价值历史记录:很长的工具输出、临时日志、已经用完的探索结果。如果只是“从最早开始删”,很可能删掉承重信息,留下噪声。
压缩不是这样。压缩是把原始历史变成一份更小的派生视图。真实记录应该只追加、不重写;被改变的,是每一轮送给模型看的投影。
这个区分很关键。用户和系统仍然保留完整历史;重试和恢复时,可以从原始记录重新推导出同一份视图;模型当前看不到的内容,也仍然可以从原始记录或 artifact 中找回。
好的压缩应该是分层的。先把大块工具结果缩成真正有用的行号、路径、标识符和结论。再把已完成的小段工作总结成几句话。只有在真正有压力时,才把大段历史折叠成紧凑的任务摘要。即便到了这一步,也应该保护最近几轮,因为模型继续当前动作时最依赖的,往往就是最近几步。
摘要应该保留结论,而不是保留过程的味道:用户请求、长期约束、关键决策及理由、交付物路径、验证状态、未完成 TODO、已经排除的路线。这些是锚点,不能丢。大量过程细节通常可以丢。
记忆和状态,是不能只靠窗口承载的东西
有些事实不应该只活在滚动轨迹里。
用户最初的目标、开头说过一次的约束、通过搜索发现的项目约定、比较两种方案后做出的选择、不能悄悄回退的 TODO。如果它们只存在于对话历史里,压缩迟早会让它们处在风险里。
这就是 memory 和 structured notes 的位置。它们不是 transcript。Transcript 是按时间排列的证据;memory 是可复用的知识;structured notes 是被外部化的工作记忆,用来保存当前计划、关键决策、开放问题、已验证事实和任务进展。
Anthropic 在讲 long-running agents 时也强调 structured notes:让 agent 写状态文件,再在上下文重置后读回来,能显著改善长任务连续性。我们 docs 里的“Agent 状态栏”也是同一类思想:Harness 把散落在上下文里的隐式状态,提炼成一小段显式状态,放在上下文末尾,比如当前 TODO、尝试次数、已用时间、当前环境状态。
核心原则很简单:不要让模型每次都从原始历史里重新发现状态。能用代码算出来的,就用代码算好;能写成状态的,就写成状态;需要时,再把相关部分放回上下文。
Cache 是同一件事的成本侧
上下文工程也关乎成本。
长任务会反复发送一大段前缀:系统指令、工具定义、项目上下文、消息历史、检索出来的状态。厂商可以缓存重复前缀,但都有自己的规则。OpenAI 对足够长、重复出现的前缀自动启用 prompt caching,并在 usage 里报告 cached tokens。Anthropic 既支持自动缓存,也支持显式 cache breakpoints;是否精确匹配、缓存存活多久、断点放在哪里,都会影响命中。
架构含义很清楚:稳定内容放前面,变化内容放后面。不要把时间戳、会话状态、随机排序、不断变化的提醒塞进 system head。动态状态应该追加到末尾。如果压缩频繁改写可缓存前缀,你省了窗口空间,却牺牲了缓存命中。如果永远不压缩,前缀也许更稳定,但上下文会越来越大、越来越脏。
所以,成本不是由某一个神奇指标决定的,而是两根杠杆共同决定:
- token 数量:靠裁剪、隔离、记忆和压缩控制
- input token 单价:靠 prompt caching 和字节稳定的前缀降低
缓存命中率是价格侧最重要的指标。上下文占用和信号密度,则是长度与质量侧最重要的指标。两边都要看。
真正的能力,是决定此刻让模型看什么
压缩、记忆、状态栏、子 agent、工具结果清理、prompt caching,看起来是不同技术,其实都在回答同一个问题:
下一次决策前,模型的工作记忆里应该有什么?
有时答案是最近几轮原文。有时是摘要。有时是一个文件路径,而不是文件全文。有时是一条 structured note。有时什么都不该进主上下文,因为中间探索应该交给子 agent,最后只返回结论。
这就是为什么 Agent 长任务的瓶颈是上下文工程。更强的模型当然重要,它会提高模型利用好上下文的上限。但“让模型看到哪一份状态”仍然是工程问题。给错状态,模型会很聪明地解决错问题。给对状态,小模型也常常能把事情做成。
Agent 不需要看到一切。它需要在正确的时间,看到正确的东西,而且是以它能直接使用的形式看到。
参考来源
- Effective context engineering for AI agents —— Anthropic 对 context engineering 的总体框架。
- Context engineering: memory, compaction, and tool clearing —— Anthropic Cookbook 对 memory、compaction、tool clearing 的区分。
- Prompt caching —— Claude API 关于自动缓存、显式断点、精确前缀匹配和 TTL 的说明。
- Prompt Caching in the API —— OpenAI 关于自动 prompt caching 和 cached tokens 的说明。
相关阅读
- 智能体需要 Harness,不只是更强的模型 —— 更强的模型替你解决不了的那些协同问题。