看到那组“UMass等176组实验”的标题时,我第一反应是——这结论怕不是研究组为了搏眼球编出来的。但顺着公开的技术分享和实验图表往下看,我慢慢把嘴闭上了。Coding Agent的隐藏瓶颈是上下文管理,这句话确实戳中了我在真实开发里反复撞到的墙。
过去一年里,我把主流编程Agent几乎都试了一遍,从IDE插件到命令行工具,体感是:模型本身写单段代码的能力早就够用了,真正让人血压升高的场景永远是“一个多文件、多步骤任务走到一半,Agent突然失忆”。它上一秒还在跟你讨论需求,下一秒就能一本正经地改一个完全无关的函数。这组实验恰好把这个问题从玄学变成了可量化的结论。
这篇文章我会拆三块内容:176组实验到底在测什么,为什么上下文管理会成为隐形的天花板,以及我结合实验和实际踩坑整理出的几种改善方法。无论你是拿Agent写业务的开发者,还是在做智能体工具链的工程师,后面这些实操建议都可以直接抄作业。
1. 176组实验到底暴露了什么?
1.1 实验设计:不是“补一行代码”的玩具任务
这176组实验不是那种“给一个函数加注释”的人工测试题。从公开报告看,他们用的是GitHub上的真实开源项目,挑出了一批带有明确验收条件的issue,比如“修复并发安全漏洞”“给现有API增加向后兼容参数”。这类任务的链路普遍很长:先定位相关文件,再理解调用关系,然后改多个源文件,接着跑测试,最后根据失败信息迭代。这跟程序员日常工作节奏基本一致,也恰好是Agent最容易翻车的场景。
每一组实验里,研究者会记录Agent的每一步动作、上下文长度变化、工具返回内容以及最终是否通过测试。单独看某一组,可能只是“某个模型在某个任务上失败了”,可把176组放在一起后,规律就浮现出来了:模型在短任务集上表现亮眼,一旦任务步数超过一定阈值,失败率就肉眼可见地爬升。而且失败原因并不是生成了语法错误的代码,而是动作方向错了——Agent改了一个跟任务毫无关系的函数,或者一直在沿用早期探索时留下的错误假设。
1.2 核心结论:一次“失忆”毁掉所有正确步骤
报告里最扎眼的结论是:上下文的保留与重建质量,直接决定了任务能不能收尾。他们对比了几种不同的上下文策略,比如把所有中间工具输出一股脑塞给模型、设置固定长度后强制截断、以及把关键信息手动提取到一份状态摘要里。结果显示,用了状态摘要的那一档,176组任务里的最终通过率明显高于另外两档。换句话说,当外部帮Agent把上下文做了“减脂”之后,模型原本的代码能力才真正被释放出来。
我后来在自己项目里也做了个小规模复现,印证了同一个结论。同样一个模型,不加任何上下文约束,它埋头折腾半天后给我改了三个互不相关的文件;我强制它先写“任务目标+变更清单+当前风险”到记忆文件里再动手,整个流程顺了很多。所谓“隐藏瓶颈”,就是模型自己不会区分信息该留多久,而我们过去总在模型参数里找原因,忽略了Agent循环里这层隐形的调度问题。
2. 为什么上下文管理会成为Coding Agent的隐形天花板
2.1 上下文窗口的“虚假安全感”
现在的模型动不动给128k、200k的上下文窗口,宣传口径都是“可以一次读完一本小说”。但长窗口造出的最危险东西,是虚假安全感。很多团队在架构上完全不考虑上下文取舍,心想反正窗口那么大,把文件全部塞进去就行。实验里一个典型失败案例是:Agent读入了项目里所有相关源码,加起来有十几万token,而初始的issue描述只有几百token。等到它执行到第八步时,它已经开始纠结一个无关配置项到底该不该改,把真正要解决的问题抛到九霄云外。
原因出在注意力机制的分配方式上。Transformer做self-attention时,token之间的关系是两两计算的,信息多了之后,重要信号会被大量噪声稀释。生活里有个很接近的体验:你面前摊开100本参考书,让你在里面找出三条关键法规,你能找到,但很难保证最后引用时不把这三条和其他兜底条款混在一起。模型也是一样的,长上下文不等于“记住”,更多时候只是一种“可检索”,而且是检索概率不稳定、结果容易串味的可检索。
2.2 遗忘曲线:Agent循环里的记忆衰减
另一个容易被忽略的点是,Agent每执行一个动作,新的工具结果都会不断改写到已有上下文里。比如第一轮读取了一个函数的实现,第二轮跑测试,第三轮测试报了一个堆栈错误,第四轮为了理解报错又去grep了一堆调用点。这时候,模型视野里最靠前、最“新鲜”的,往往是最后一轮grep出来的碎片内容,而不是最开头的问题描述。
这很像人类的短期记忆衰减:不是一下子全忘,而是新信息挤进来后,旧信息占的位置越来越小,真要使用的时候只能凭模糊印象。实验中多次出现这样的场景:Agent在活动日志里反复强调“我决定增加一个参数”,于是后续所有修改都在围绕这个参数打转,但它忘了这个参数提出的前提是“为了兼容旧的调用方”,而不是“新功能需要”。方向从开始就偏了,而后续每一次工具调用都在强化这个偏误。所以上下文管理本质上是在做两件事:保留关键约束,及时纠正偏差。
2.3 工具调用链中的上下文污染
说到Coding Agent,工具调用是绕不开的一环。它需要看文件、跑命令、查日志,这些环节都会给上下文带来大量“工业废水”。我见过一个特别典型的例子:Agent跑了一次测试,测试框架打印了3000行日志,其中有用的报错只有一行“AttributeError: 'NoneType' object has no attribute 'strip'”,但Agent把这3000行全部保留,导致后面几轮对话里模型反复引用日志里的无关路径和环境信息。上下文污染的直接后果,就是模型开始过度拟合工具输出里的偶然细节,在错误方向上越走越远。
更麻烦的是,这种污染是累积的。每轮工具调用都会往上下文里追加新token,旧的关键信息如果不做“归档”,就会在token流的深处慢慢消失。实验里统计了176组任务中的上下文token变化,绝大多数失败任务的上下文长度都在前期快速膨胀,直到逼近窗口限制,模型开始丢弃一些看似“不重要”的早期信息——而这些被丢掉的通常恰好是需求描述或验收标准。上下文管理不到位,等于让模型一边写代码一边失忆。
3. 改善上下文管理的实操方法(基于实验启发)
3.1 先拆成短任务,再交给Agent
实验里状态摘要策略之所以有效,是因为它等价于人为打断Agent的长期记忆负担。实操里我觉得最好用的办法是:不要一个Agent会话干到底。把任务拆成理解、修改、验证三个阶段。理解阶段只让Agent读取文件,输出一份“问题定位和改动计划”;修改阶段把这份摘要作为上下文起点,让它只做改动;验证阶段再让它跑测试,根据失败信息决定下一步。每一步都开一个新的对话或会话,不让前一步的噪声无限制传递。
举个例子,我曾经处理过一个“用户登录接口偶发超时”的问题。启动Agent前,我手动把相关日志和调用链里可能出现超时的三个位置列了出来,然后对Agent说:“不要给我看日志,先只读gateway.py和auth/session.py,告诉我你认为哪里有问题。”这等于帮它提前把候选范围缩小,大大降低了上下文被无关文件占满的概率。很多刚上手的朋友总觉得喂给Agent的信息越多它就越聪明,实验已经证明这是幻觉。
3.2 用外部文件作为Agent的“工作台”
另一个很有效的方法,是在项目目录里维护一个类似AGENT_STATE.md的文件,强制Agent在关键节点把当前状态写进去。这个文件承担两件事:作为外部记忆,避免所有信息都堆在对话里;作为可被后续步骤重新读取的“锚点”,降低模型遗忘早期决策的概率。我一般会这样提示Agent:“在开始修改前,先把你的计划和最终验收条件写入AGENT_STATE.md;每完成一个子任务,更新文件里的‘已完成’和‘待办’两节。”
这个“工作台”不是给Agent写流水账,而是聚焦三类信息:当前任务的目标、已经做的决定、尚未确认的疑问。有了这个文件后,即使Agent的上下文因为某种原因被截断或者被新内容冲垮,你也能在新会话里用一句“读取AGENT_STATE.md继续”把任务接回来。我在几个短周期项目里用过这招,最直观的变化是,跑偏的次数少了一大半。
3.3 压缩工具输出,主动给上下文“瘦身”
实验里大量失败案例都源于工具输出过多。所以替Agent过滤掉无关信息,是收益最高的动作之一。不要直接把整个测试日志丢给它,而是先用命令把日志压缩成关键行。比如运行pytest时,我通常会这样:
pytest tests/test_auth.py --tb=short 2>&1 | tail -n 50这样Agent只会看到最后50行,而不是几千行的完整输出。如果日志特别长,还可以进一步用grep把“Error”“Traceback”等关键词筛出来,再扔给模型。这个操作看起来简单,但对上下文干净度的帮助极大。有些命令行Agent支持设置单文件读取的最大token数,你也可以把限制调低,超过的部分让Agent先问你要不要继续读。
还有一点经验:对于那些已经确认没用的中间输出,比如Agent自己打印的长列表、它执行过的无关命令记录,最好在下一轮提示词里明确说“忽略上一步中关于XXX的内容,只需要保留结论”。绝大多数模型会服从这个“注意力重定向”指令,让自己更聚焦。
3.4 每个关键轮次结束时,让Agent主动复述目标
这是一个成本极低但效果出奇好的技巧。在完成一个子步骤后,不要急着让它做下一步,而是要求它用不超过两句话复述当前目标、已改动内容和下一步打算。这么做的作用是“强制记忆巩固”:通过让模型生成对目标的显式描述,提高它在后续生成中的概率权重。你可以这样写提示词:
在完成这个修改后,先不要继续。请用两句话概括:1)原始需求是什么;2)截止目前改了哪些文件,哪些行为会受影响。然后等我确认。
我在用命令行Coding Agent的时候,经常在任务中段加这层“checkpoint”,像硬性检查点。它不见得每次都能完美复述,但只要出现偏差,我能第一时间发现并纠正,而不是让Agent在跑偏的路上狂奔两千行代码后再回头返工。对于需要长时间无人值守的任务,这个技巧几乎可以说是保命用的。
4. 实验中的常见误区和排查实录
4.1 误区一:上下文管理就是“加大窗口”
很多团队看到实验结果后的第一反应是:是不是上下文窗口不够大,换个更大的型号不就行了?我也这么想过。但实验和实测都证明,单纯加大窗口并没有显著提升长任务成功率。原因很简单:大窗口解决的是“放不放得下”的问题,而瓶颈真正卡在“放进来之后还记不记得住”的问题。模型不会因为窗口从64k变成128k,就对中间某处关键的那两三行代码产生更高的注意力权重;反而因为塞进来的无用信息更多,关键信号的密度又被稀释了一次。
直观一点说,增大窗口相当于把办公桌从两平米换成四平米,但桌上同时多堆了三倍的文件。你找东西不一定更快,甚至可能更慢。真正的解决方向是减少桌上要放的东西,而不是把桌子换大。
4.2 误区二:失败后直接重试,却不清理上下文
另一个在复现时会踩的坑,是Agent失败后直接给它发一句“再试一次”,而不重置上下文。这会让模型带着上一轮所有错误尝试的记忆进入新一轮,错误模式很容易复现。正确做法是清空会话,或者给Agent一个全新的“失败摘要”,把已经排除的路径明确标注出来,让它不要重复。
我印象很深的一次,Agent连续三次用同一个错误方案修改一个函数,直到我手动在提示词里写道:“你已经尝试过在__init__里初始化属性,但这个方案的报错是XXX,请不要再走这条路。”它才跳出来换了个正确思路。所以复现实验时,我建议记录每个任务的失败历史,并在重试时显式告诉Agent哪些方法已经失败。这个操作至少能减少一半的无脑循环。
4.3 一个典型排查实录:跨文件接口修改失败
拿一个真实踩坑经历来说。任务是给一个内部组件增加超时重试逻辑,需要同时修改配置文件、调用方和实现方三个文件。Agent第一次输出改得很好,接口名、参数都对上了。可到了验证阶段,测试报了一个跟超时无关的“依赖注入失败”,Agent开始转头去改注入代码,改了几轮后发现把原实现里的超时参数也删了。
我回看上下文,问题出在三点:
- 测试输出的错误堆栈里有大量无关框架代码,Agent被带偏了;
- 最开始的需求描述“增加超时重试”被埋在了几轮调试输出后面,模型对它的注意力迅速降低;
- 后来我强制它重新读取AGENT_STATE.md并复述目标,才把它拉了回来。
这个过程我后来总结成一张排查表,供参考:
| 症状 | 可能原因 | 快速排查 |
|---|---|---|
| Agent中途开始修改无关函数 | 原始需求被工具输出淹没 | 检查近2000个token里是否还有需求描述 |
| 重复执行相同的失败步骤 | 历史失败信息未显式标记 | 在提示词里列出“已排除方案” |
| 使用的接口签名前后不一致 | 多文件读取顺序导致版本混淆 | 强制Agent先输出接口签名摘要再动手 |
| 不断追加冗余代码 | 上下文里污染严重 | 用外部状态文件收敛信息量 |
4.4 复现实验时的几个环境级注意事项
如果你也想跑类似实验,有几个环境层面的细节需要留意。第一,模型temperature要调低,最好设为0或0.1,否则同样的上下文条件下,结果方差会大到让结论失真。第二,每次运行要保证起始会话干净,不能复用之前的聊天历史,最好用独立容器,防止Agent读到上次任务留下的临时文件。第三,固定随机种子和采样参数,让过程可复现。我在做对比时还会记录每一步的上下文token数,不然很难观察到关键信息到底是什么时候开始丢失的。
另外要特别提醒:不要用“模型A成功、模型B失败”这种单一结果下结论,最好每种策略跑多次。176组数字之所以有说服力,是因为统计效应。你本地做粗糙验证时,至少同一个任务跑三到五遍,看趋势,而不是看个例。
5. 我的实操心得与后续可以怎么玩
5.1 不同形态Coding Agent的上下文策略差异
现在市面上的Coding Agent大概分两类。一类是IDE插件形态,像那些自动读取当前打开文件、在侧边栏给建议的工具;另一类是命令行形态,典型如OpenAI的Codex CLI这类需要登录ChatGPT账号后使用的终端Agent。两者上下文策略差别很大。IDE插件天然优势是能感应到你正在编辑的文件,但也容易把一堆根本无关的打开文件塞进上下文;命令行Agent通常更“按需读取”,它会先搜索再决定读哪些文件,优点是省token,缺点是可能遗漏项目里隐含的依赖关系。
我现在习惯是:涉及多文件、架构一致性的改动,用命令行Agent加状态文件来管;做局部小改动,直接用IDE插件更轻。实验里那种多步issue任务,本质上更贴近命令行Agent的使用方式,所以它们暴露出的上下文问题也最多。
5.2 三条铁律:给自己定的上下文管理守则
踩了这么多坑之后,我给自己定了三条铁律,分享出来供参考。
第一,任务必须先拆后做。哪怕是让Agent“修一个bug”,也要拆成“定位问题—提出方案—实施—验证”。每一步一个会话,不在一个会话里从头硬扛到底。
第二,关键约束必须文件化。所有不可妥协的需求、验收标准、接口约定,都放到AGENT_STATE.md这类文档里,让Agent自行读取,而不是靠它在几十轮对话里“记住”。
第三,验证后必须清理。每轮测试通过后,主动删除已被证明无用的工具输出,用一句“测试通过,所有改动集中在src/auth.py”之类的摘要替代。这个动作等于给下一轮Agent清理出干净的工作内存。
5.3 这个思路还能扩展到哪些场景
上下文管理这个事,我觉得不止适用于写代码的Agent。任何需要多步骤决策的智能体,比如数据分析、自动运维、甚至写长篇文档的Agent,都会碰到同样的信息遗忘问题。176组实验真正给人的启发,不是某个模型不行,而是告诉我们:当外部工具链和任务复杂度上来以后,如何组织信息流,比模型参数更重要。
我最近在试一个更“重”的方案,给Agent维护一个外部键值状态库,让它每完成一个子任务就写一条结构化记录,下一轮决策前先做一次“最近相关的状态查询”。这相当于把上下文管理从会话层搬到架构层。目前还在小范围实验,效果有待统计,但方向上我认为是对的。
在你们实际使用Coding Agent的时候,有没有遇到过“明明前面说得好好的,后面突然失忆”的时刻?如果有,不妨按上面提到的办法先调一轮,看看上下文瘦身之后,它是不是突然就变聪明了。对我来说,这正是那176组实验最大的后劲。