上一篇我们用 Fowler 的 2×2 矩阵把 Harness 的各种组件分了类。末尾我提了一句:下一篇聊上下文工程,因为它是 Harness 里最容易卡住整体性能的那一块。

        今天就来兑现这个承诺。

        先说一个让我印象深刻的现象。

        我用 Claude Code 处理过一个比较长的任务——给一个老项目做类型迁移,涉及大概 80 个文件。前半段跑得很顺,Agent 思路清晰,每改完一批文件就记录下来,方向感很强。

        但大概到了 40 个文件之后,它开始走样。开始重复检查已经改完的文件,开始把之前定好的类型命名规则搞混,甚至有一次给我改了一个它自己十分钟前刚改完的文件——改的方向还跟之前相反。

        我当时以为是模型的问题。换了个更大的模型,同样的现象,只是推迟了一点出现的时间。

        后来才明白:问题不在模型,在上下文。


        一、上下文的瓶颈        

        O(n²):数学上写死的瓶颈

        Transformer 架构有一个逃不掉的特性:Attention 的计算复杂度是 O(n²)。上下文长度翻倍,计算量变成四倍。

        上下文长度翻倍,计算量四倍——Agent 跑了 30 分钟,积累了大量工具调用输出、中间推理、修改历史,上下文窗口已经撑得接近上限,此时每一步推理的成本,远比刚开始时贵得多,质量还在下滑。

        Anthropic 的工程团队记录了一个具体的「35分钟墙」:Agent 连续运行超过 35 分钟后,性能开始明显退化。不是模型能力变弱,是上下文太长、信息密度太高,模型的注意力开始稀释,重要的早期信息被淹没在大量后续输出里。

        我当时踩的坑,就是这个。

        上下文管理决定 Agent 能跑多远、跑多稳、跑多便宜。这才是 Harness 里真正的核心——不是最花哨的那块,是最容易被忽视、出问题最安静的那块。


        二、四板斧:把上下文管好

        Anthropic 和 OpenAI 在他们的工程实践里,都反复提到四个具体技术。我按照「出手时机」来排:先是防止上下文膨胀的两招,再是降低成本的两招。

        2.1 第一招:Compaction(压缩)

        上下文快满了怎么办?暴力截断?不行,早期的关键信息会丢失。

        Compaction 的思路是智能摘要:在接近上下文限制之前,把当前窗口里的内容压缩成一个结构化摘要,然后用这个摘要替换掉原始内容,继续往前跑。

        Anthropic 已经把这个做成了 API 级别的能力——你不需要自己实现压缩逻辑,只需要告诉系统「在 80% Token 占用时触发压缩」,框架自动处理。

        其中有一项更精细的技术叫 ACON(Anchored Context Optimization Network)——锚定式迭代摘要

        普通的摘要是把全文压缩成一段话,问题是细节大量丢失。ACON 的做法是:先识别「锚点」——任务列表、已完成项、关键约定、已知约束这些必须保留的信息——然后围绕锚点做有选择的压缩,舍弃的是冗余的中间步骤,保留的是影响后续决策的核心状态。

        效果是 26-54% 的峰值 Token 降低。换算成成本:一个原本需要 100 万 Token 完成的任务,用 ACON 可能只需要 50-70 万。

        但 Compaction 有它的代价:压缩必然有信息损失。压缩越激进,信息损失越多。好的 Compaction 策略需要在「压缩比」和「信息保留」之间找平衡——这本身就是一个需要精心设计的 Harness 组件。

        我自己的经验是:任务早期的关键约定(命名规范、架构决策、禁止事项)必须标记为「锚点」不可压缩,中间的工具执行细节可以大胆压缩。

        2.2 第二招:Tool Call Offloading(工具输出卸载)

        Agent 运行时有一类特别危险的上下文污染源:大体量工具输出

        想象 Agent 调用了 grep 搜索整个代码库,返回了 500 行匹配结果。这 500 行会直接进上下文窗口,吃掉大量空间,而真正有用的可能只有其中 5 行。

        Tool Call Offloading 的解法很直接:保留头尾,全量卸载到文件系统

        具体逻辑:工具返回大体量输出时,Harness 自动截取前 50 行和后 50 行放入上下文(让模型知道输出的大致结构),完整内容写到临时文件(比如 .agent-workspace/tool-output-123.txt)。需要深查时,Agent 显式读文件;不需要时,这 500 行不占上下文一个 Token。

        这个技术听起来简单,效果却相当显著——特别是在涉及大量文件读取、搜索、日志分析的 Agent 任务里,上下文膨胀速度可以降低 60% 以上。

        Claude Code 就是这么设计的。你在用它的时候,注意观察它处理大文件时的行为——它不会把整个文件塞进上下文,而是把文件「记在外面」,按需拉取。

        2.3 第三招:Progressive Disclosure(渐进式披露)

        这一招不是在任务执行过程中起作用,而是在任务开始时起作用。

        很多人建 Harness 的时候,会把所有东西塞进系统提示:所有工具的完整文档、所有规则、所有示例、所有注意事项……结果系统提示动不动就几千甚至上万 Token。任务还没开始,上下文窗口已经用掉了相当一部分。

        Progressive Disclosure 的思路是:系统提示只是地图,不是百科全书

        入口文件保持短小(大约 100 行左右):核心角色定义、关键约束、可用工具的简短列表、以及「如何获取更多信息」的指引。

        工具文档、详细规则、示例代码——全部外链,按需加载。Agent 需要用某个工具时,去读那个工具的文档文件;遇到特定场景需要更多指引时,去读对应的规则文件。

        Claude Code 的 Skills 系统就是这个思路。每个 Skill 是一个独立的文件,系统提示里只有 Skill 的名字和一句话描述。Agent 决定要用某个 Skill 时,才把那个文件加载进来——不用的 Skill 一个 Token 都不占。

        效果:同样功能完备的 Harness,用 Progressive Disclosure 设计,系统提示的初始大小可以从 8000 Token 压到 500 Token 以内。

        2.4 第四招:Prompt Caching(提示缓存)

        这一招直接冲着成本去的。

        大多数 Agent 任务有一个特点:系统提示(角色定义、规则、工具描述)在整个会话期间是不变的。每次 Agent 调用都带着这一大段相同的内容,每次都要重新计算——浪费。

        Prompt Caching 让模型对「不变的前缀」做一次计算,结果缓存下来,后续调用直接复用缓存。

        Anthropic API 支持显式标记缓存断点(cache_control: ephemeral)。在恰当位置打上标记之后:

  •  输入成本降低约 90%(缓存命中的 Token 不重新计费)

  •  延迟降低约 75%(缓存命中不重新计算)

        对于有大量工具描述或长系统提示的 Harness,这几乎是免费的优化——代码改动不超过十行,收益立竿见影。

        有一个坑要注意:Prompt Cache 的 TTL(生存时间)是 5 分钟。如果 Agent 两次调用之间间隔超过 5 分钟,缓存失效,下次重新计算。设计 Agent 的执行节奏时要考虑这一点——如果有频繁的长间隔,缓存收益会打折扣。


三、把四板斧拼在一起

        光讲四个技术还不够,关键是它们怎么组合。

一个完整的上下文管理策略是这样的:

任务开始
    │
    ▼
系统提示(Progressive Disclosure)
    │  只有核心入口,~100 行
    │  按需加载工具文档 / 规则文件
    │
    ▼
执行过程
    │
    ├── 工具输出大?  → Tool Call Offloading
    │    头尾入上下文,全量写文件系统
    │
    ├── 上下文到 80%?→ Compaction
    │    保留锚点,压缩冗余
    │    干净上下文继续跑
    │
    └── 每次 LLM 调用 → Prompt Caching
         系统提示缓存命中
         90% 成本直接省掉

        四招不是并列关系,是分工:

  •  Progressive Disclosure 控制起点,让上下文从一开始就精简

  •  Tool Call Offloading 控制增速,防止执行过程中的膨胀

  •  Compaction 控制上限,临近满载时及时清理

  •  Prompt Caching 控制成本,把重复计算变成零边际成本

        缺任何一招,都会有对应的漏洞。

四板斧上下文管理全景:四招触发时机与分工


五、一个实战数字对比

        说个数字,我自己跑过的一个项目。

        任务背景:让 Agent 分析一个中型代码仓库(约 200 个文件),找出所有违反某一架构规则的调用点,生成改造方案,逐文件修改。

        5.1 没有上下文管理(原始跑法):

  •  任务到 60% 左右开始出现重复操作

  •  到 75% 上下文已经接近上限,Agent 开始频繁输出「我需要先确认一下之前的进度」

  •  任务最终没有跑完,需要人工接管

        5.2 加入四板斧之后

  •  全程无需人工介入,任务一次跑完

  •  总 Token 消耗减少约 45%(Compaction + Offloading)

  •  API 调用成本减少约 62%(Prompt Caching + Token 减少叠加)

  •  任务执行时间反而缩短了 30%(缓存命中降低延迟)

        不是玄学,是工程。

上下文管理有 vs 无:实战数字对比


        六、上下文是稀缺资源,要像内存一样管

        我最近在和一个做 AI Infra 的朋友聊,他说了一句很准确的话:

        「上下文窗口就是 Agent 的内存,但大多数人用它的方式像是从不释放内存的程序员。」

        传统软件里,程序员知道内存是有限的,会主动分配、使用、释放。但在 Agent 开发里,很多人把上下文当成无限大的草稿纸——往里塞,塞满了换一个,然后抱怨模型不够聪明。

        上下文管理不是 Harness 的「高级选项」,是基础设施——就像你的程序必须管好内存,你的 Harness 必须管好上下文。

        四板斧用熟了,你会开始有自己的感觉:哪些信息绝不能压缩,哪些工具输出从来不需要看全量,哪段系统提示可以抽出来做缓存。这种感觉是 Harness 工程师的直觉,练出来之后干活会快很多。

        但有一句话可以先记住:每一个进上下文的 Token,都应该值得占那个位置。


        下一篇讲工具设计。上下文管好了,Agent 还是得靠工具跟世界打交道——工具设计得好,它的「手」就精准;设计得差,它就是一头力气很大但到处打翻东西的熊。

参考文献:

第4篇:上下文工程深度解析 —— 越跑越蠢的 Agent,败在了这里

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐