Agent 走的,是它看得见的那条路
它走的不是最好的路,是它看得见的那条。而它看得见什么,由你决定。
上周要写一个销售数据的导入脚本,先得把业务方给的那张真实表读顺:五家分公司,三个 sheet,合并单元格、日期、货币格式、公式一样不少,底下还有一张汇总。我要先把它们并成一张、跟汇总对一遍,再拆成三份喂给系统。这活儿在 Excel 里点一遍,我自己二十分钟能完;交给它,想着几分钟该够了。它跑了 12 分钟。
12 分钟不算离谱。让我后背发凉的是它为什么走了这条路。
我盯着它一步步往下走,越看越不对——它没有打开 Excel,它在解压文件:把 xlsx 当成压缩包解开,从里面几个 XML 文件里捞出文字和数字,再拼回一张表。十几步。
我第一反应是没装库吧。查了一下:openpyxl、pandas 都在,只是装它们的那个 python 不在 PATH 上。PATH 是系统找命令时按顺序翻的目录列表——它调的那个 python3,恰好是没装库的那个。
我不信邪,让它 pip install(python 装包的命令),撞墙了:
1 | error: externally-managed-environment |
意思是:这个 python 归系统管,别往里装东西。所以它调的那个 python3 里确实没有 openpyxl,也确实装不上。它手上只有那一条路可走。
第二件事是我顺手翻了它的思考过程。在决定解压之前,它评估过别的选项,但每一条都被自己否掉了:可能没装、可能打扰用户、可能不安全——连 python 标准的「先建个虚拟环境」也落在「要装东西、得先问人」那条里。最后它选了唯一确定能跑通的那条。
它想错了吗?没有。每一步推理都成立。它只是对环境的认知是错的——而这个认知是我造成的。
所以这篇文章只讲一件事:人真正能控制的,不是它的判断,是它的选项。 让它看见对的路、让它用上对的工具、让它不必返工。
先把一个词说白,后面全靠它:层。修 agent 的问题,可以修在不同的层,从下往上大致是:环境、工具、提示词、模型。
为什么环境是「下」?越往下,你改的越是机器世界里的事实——PATH 里有没有这个命令、装没装这个库。事实不需要被谁理解,成立就是成立。越往上,你改的越是话——提示词怎么写、模型多聪明。话要靠它读到、读懂、还愿意照做,每一环都可能掉链子。所以越往下修,它越不用猜;越往上修,你越是在赌。
一、先算账,别先怪模型
先把「慢」说清楚:一个任务从交出去到拿到能用的结果,一共花了多久。开发一个功能、修一个 bug、出一版报告,形状都一样——要跑多少步、每步多贵、有多少步白跑:
1 | 总时长 ≈ 步数 × 每步成本 × (1 + 返工率) |
返工率是乘在整条链上的放大器:一个 15 步的任务,在第 14 步发现方向错了,前面 13 步全废。
直觉会说「换更快的模型」。粗算一下:把返工率从 0.5 降到 0.1,整条链的时间降到原来的 73%;把生成速度提快三成,降到 77%。同一量级——而且这个比较还偏向了换模型一边:提速只作用于生成那一步,跑工具的时间纹丝不动,而返工率乘的是整条链。降返工率常常只是修一个环境问题,不花钱;换模型要花钱。
那为什么先怀疑环境?因为返工的大头往往不是「它推错了」,而是「它一开始就站错了地方」——没看见更好走的那条路。
有一项研究正好量过「设置里哪个最有用」。研究者让 agent 去调用各领域的专用科学模型(测星系红移、预测基因表达这类任务),把五个设置挨个调了一遍:给它多少任务信息、保留多少推理、要不要它自我检查、给多少时间、用多强的模型,八千多次运行。结果排第一的是任务信息——每个任务上都是第一,而且幅度明显超过换更强的模型;保留推理平均排第三,比时间预算更有用;垫底的是「要求它自我检查」。
这项研究还有个比排名更实用的数字:同一套配置原样重跑,分数自己就会抖,这种抖动占了整体差异的一半以上。换句话说,你改了一处、分数涨了 3 分,很可能什么都没发生。
所以任何对比之前,先做一件事:同一套配置重复跑 3 到 5 次,看它自己能抖多少。抖出来的「提升」,不是提升。
(说明:它测的是「你告诉它什么」,不是「你环境里装了什么」。但它从另一个角度印证了主题:它只能在你给它的范围里选。)
账算完了,落点只有一个:在换模型之前,先把返工率压下去——收益同量级,成本差着数量级。而返工是从哪来的,就是下一节的事。
二、病根:修错了层
回到开头那个 Excel:它没打开表格,而是把文件解了压。这类问题——在错误的层上,手工重建已经有人做对的东西——我给它们起了个名字:层次错配。它有两个方向:
- 环境缺了东西,你在提示词里写一句「记得用 openpyxl」:用高层补低层。
- 它判断手上没有能用的工具,于是自己写一份更差的替代品——解压 xlsx 就是这一种:用低层补高层。
方向看着相反,说的其实是同一件事,只是重建的人不同:前者是你在重建,后者是它在重建。
回头看那个 Excel,是三件事叠在一起:
| 缺什么 | 具体表现 |
|---|---|
| 对的 python | 装了库的解释器不在 PATH 上 |
| 对的报错 | 报错只说了「不许往这儿装」,它给的替代路——建虚拟环境——仍然需要装东西、需要问人 |
| 经验 | 当时连一套现成的做法都还没沉淀 |
三件事都该在环境和工具那一层解决。而我当时的打算,是靠一句提示词绕过去——写进规则它就照做吗?照做了下个项目还成立吗?没人知道。而环境层的手段是确定的:PATH 里放对了解释器,它想错都错不了。
所以我自己的经验是:遇到「agent 很笨」,先花五分钟排除环境。命中率比我预期高得多。当然不是每次都中——第七节讲它不管用的时候。
三、病例一:它自己解了 zip
解压不是我的选择,是它自己算出来的路。要判断这条路有多差,光讲故事不够,我做了个复现(代码在 xlsx-unzip-vs-openpyxl):造一张同样结构的表(1100 行、三个 sheet、合并单元格、日期、货币格式、公式),出 6 道题,让三种做法都答一遍,只看答对几个。
- A:随手解压读——模拟一个赶时间的 agent 会写成什么样
- B:同样自己解压,但认真做到全对
- C:用 openpyxl,一次调用
结果
| 做法 | 代码行数 | 答对 |
|---|---|---|
| A 随手解压 | 22 | 1/6 |
| B 解压但做全对 | 76 | 6/6 |
| C openpyxl | 13 | 6/6 |
A 错在哪(真实输出):
1 | C4 的日期 期望 2025-07-15 实际 45853 |
它唯一答对的那题——合并区里 B1 是什么——也是碰巧:那个格子压根不在文件里,它返回空,空恰好就是答案。
贵在反复改,不在跑一次
B 那 76 行,我修了四轮才全对——括号笔误、属性顺序看反、路径多拼一层、格式表键值写反。四个错没有一个报过异常,全是静默的错答案,靠对着期望输出一个个排出来;而且这还是在我已经知道全部正确答案的情况下。真实任务里没有答案表,错的输出看起来偏偏又都像对的:45853 是个正常数字,空值是个正常空值。
用库则把轮数压到 1:一次调用,对话里只多一行。手写省下的那点执行时间——毫秒到秒——相对任务的分钟到小时,完全可以忽略。真正贵的从来是那几轮「读回代码、看输出、查文档、再改」,每轮的代码、输出、文档片段都要重新进一遍上下文,还会一直留在对话里,拖慢后面的每一步。
它没有报错。它只是给了你一个错的数字。
麻烦不在解压,在那些没写在表面的规矩
解压本身不难。难的是读对一张表格,得同时知道一堆没写在表面的规矩:
- 数字格式和文字都可能不在格子里,而在别的对照表或清单文件里,格子上只留个编号——这份测试表恰好把文字直接写在格子里,算它客气;Excel 自己存的表大多不是。
- 日期在文件里是个数字,从 1899 年底起算的天数。
- 公式在文件里没有开头的等号。
- 合并区只有左上角那个格子有值,其他格子压根不出现。
这些事没有一个是「难」,问题是漏了任何一条都不会有提示。判断标准不是「这个格式复杂不复杂」,而是「我错了会不会知道」。
这类事也不只发生在表格上。缺一个命令、缺一个权限、缺一个登录态,agent 的反应都一样:自己手写一个更差的替代品——用 sed 硬改 JSON、手拼 SQL 转义、翻 cookie 绕登录。所以值得记住一个症状识别:看到它开始手拼某种解析、转义或绕过,多半就是层次错配——环境里缺了它本该够得着的那个东西。共同点是,不报错,只是悄悄错。
什么时候手写也够用?看你要多少东西:
1 | 值 → 类型 → 格式 → 公式与结构 |
只是把两列数字取出来求和,22 行完全够,甚至比装库还快——文本另说,它得先过对照表那一关。要的是第四层,就得用库——或者接受自己重写一个库。
四、病例二:AGENTS.md 越写越长
AGENTS.md(放进仓库根目录、每次对话都会被 agent 读进去的说明文件)这件事,我自己身上就有一个。
一开始很自然:每次出问题,就补一条规则。Excel 路径一条、编码规范一条、部署流程一条,三个月后长成了几百行。然后我发现每一步都变慢了——因为每加一行,之后每一次调用都要重新读一遍。几百行常驻上下文,租金刚好抵消了它偶尔带来的那点收益。
它到底有没有收益?有人做过一项对照实验:在 Claude Code 和 Codex 上跑 17 个真实任务(3 个 Python 仓库),测三种给法——不给、每次都给、按需给。正确率上没测出差别。两个保留要说清:一是任务太少,小差别测不出来,所以这是「没看出区别」,不是「证明没区别」;二是它只测了「怎么给」,没测「写什么」,而且样本里的文件是通用的风格说明——针对具体任务写的上下文有没有用,它回答不了。
比「有没有用」更有信息量的是它后半段的失败归因:agent 栽在干活的手艺上——功能怎么设计、用哪种模式、线怎么接——而不是「不了解仓库里的事」。他们还做了个对照:把仓库真实的 AGENTS.md 再喂一遍,「差一点就过」的任务没有一次变成「过」。
所以别拿「实验说上下文没用」当免死金牌,也别拿它给几百行背书。靠谱的读法是:你随手补的那条规则,多半回答的不是它真正失败的原因。一条规则值不值得写,看两点就够:它描述的是一类失败,还是某一次的个案?它能不能写成环境里的自动检查?个案的、能检查的,都别留在文档里占地方。
五、修复阶梯:能往下修的,绝不往上修
按「靠不靠得住、占不占上下文」,问题可以修在四个地方。这张表从上到下,就是梯子从顶到底——最底下那行是地基:
| 层 | 手段 | 靠不靠得住 | 占不占上下文 |
|---|---|---|---|
| 模型(顶层) | 换模型、调参数 | 不确定 | — |
| 提示词 | 对话里写一句 | 低,靠自觉 | 一直占着 |
| 工具与知识 | 封装的命令、skill、AGENTS.md | 高,前提是找得到 | 低到中 |
| 环境(底层) | PATH、权限、预装库、自动检查、登录好的浏览器 |
事实一定成立 | 不占 |
原则就一句:能往下修的,绝不往上修。同一个问题,能在最底下那行解决的,就不要用上面任何一行去补。(表里说环境层「事实一定成立」,指的是手段不依赖它配合;它是不是真走上了这条路,第六节再验。)
(skill:写好的一套做法,放在那里,agent 干这类活时自己去取。它和 AGENTS.md 的差别是「什么时候进上下文」,第五节末尾说。)
环境层还有两种修法,后面一直会用到:
- 不打招呼的修法:改它已经在走的那条路。把对的 python 放进
PATH,它调python3就对了——它不需要知道你改过什么。 - 要打招呼的修法:新加一个东西——新命令、新路径——得先告诉它有这么个东西。
拿三个情况回填一下。
Excel:我原本想在 AGENTS.md 里写「处理 Office 用某某路径的 python」,那是提示词层;该做的是把那个 python 放进 PATH,环境层,不打招呼。
AGENTS.md:那几百行里,凡是「必须用某路径」「永远不要跑全量测试」这类能写成检查的,都该降到环境层做成自动脚本。留在文档里,就是每次调用都在付租,还可能不被遵守。
浏览器登录:让 agent 上浏览器做验证,没人告诉它登录态从哪来,它就会去翻 cookie、试各种绕过,十几步全花在探索上。修法同样不在提示词:给它一个已登录的浏览器配置(不打招呼),或者明说「遇到登录页就停下等人」——把不能自动化的那一步划给人。
第三个情况还带出一句话:能力和状态都是环境。它一开始处于什么状态——已登录、已有数据、已配好变量——和环境里装了什么,是一回事。
skill 的位置
那次之后,我给这台机器装了 Anthropic 官方的 xlsx skill。它写得相当好:什么任务用 openpyxl、什么用 pandas、什么时候用别的工具看一眼就好,列成了路由表;合并单元格只写左上角、重算通过不等于公式正确,这些坑也都写成了清单。
skill 和 AGENTS.md 有个关键差别(就是前面说的「什么时候进上下文」):AGENTS.md 一定在场,skill 可能根本没被用上。AGENTS.md 写进去,每次调用都在读;skill 只有被找对才进上下文。省掉了常驻成本,风险换成了「不被发现」——任务开场如果只是一句「处理一下这个表格」,很难命中任何 skill。
但注意它开头那句话:「openpyxl、pandas 已预装——别先跑 pip install,直接写脚本导入;只有 import 失败了,再装缺的那个包」。这条指引在它假设的环境里是对的,在这台机器上是灾难:PATH 上的 python 没有 openpyxl,import 必然失败;按它说的去 pip install,撞的正是开头那堵墙。就算那天已经装了这个 skill,结局多半一样——它会被引去装包,撞墙,然后退回解压那条路。
这是模型厂商自己写的 skill,踩坑清单足够专业,也救不了一个断了的环境层——因为它假设的环境是他们的沙箱,不是你的机器。
skill 解决了「知不知道」,没解决「调不调得到」。知识层的正确,依赖环境层的正确。修法也不是去改 skill——那是别人维护的,改了会被下次更新覆盖——而是把环境修成它假设的样子:让 PATH 上的 python 就是装了 openpyxl 的那个。修一次环境,所有 skill 一起受益。
最好的修复,是不占上下文的修复。
六、怎么确认修对了
有个很便宜的验收动作:把相关提示词删掉,重开一个干净会话,跑同一个任务,重复 3 到 5 次——为什么是多次,第一节说过。它每次都走对,才算修在了环境里;它又走错,说明你修的只是「存在」,不是「可达」。
这个判断在那项研究里也有对应。要求 agent 自我检查,从「不要求」加到「硬性要求」,它真去核对的比率只从 19% 涨到 22%,大多数时候只是看了看答案格式。而给它一个检查工具——研究中是一个能对标准答案打分的「阅卷器」,现实中通常弱一些,比如一条能跑的测试命令——参照式核对比率大约翻了三倍。在提示词里要求它更努力,和给它一个顺手的检查工具,是两个量级的事。这和环境层与提示词层的差别,本质上是同一件事。
到这里,散在前面各节的几件事可以钉成一个闭环了:
- 识别症状:它卡住,或者开始手拼替代品——解压 xlsx、
sed硬改 JSON、手拼转义、翻 cookie 绕登录(第三节末说过:看到手拼,就是层次错配的信号)。 - 定位:翻 trace,看卡住的那一步——是猜错了前提,还是能力到此为止(下一节专门讲这两种错)。
- 修:猜错前提的,找出它缺的那个「事实」,修在能修的最低层(第五节阶梯);能写成自动检查的,降级成脚本,别留在文档里。
- 验:删提示词,干净会话重跑 3 到 5 次。每次都走对才算修对;走不通,回到第 2 步换一层再修。
整个闭环里,没有一步是「提醒它更努力」。
七、这套框架不管用的时候
先分清两种错:
- 推理错:前提是对的,推错了。
- 前提错:推理完全成立,但它的前提不成立——它认定「这里没有 openpyxl」,而得看是哪个 python:它调的那个确实没有,你机器上另有一个装了库的。
后者更常见也更隐蔽,因为它看起来毫无问题,而且基本不是模型能自己修好的:前提来自环境、文档、上下文。分不清的时候,看运行记录(trace,agent 每一步调了什么、拿到什么的流水)里它卡住的那一步:是猜错了前提,还是能力到此为止。
框架盖不住的,还有三种情况:
- 能力不够。看它是「一直走错方向」还是「走对了但走不完」。前者查环境和信息,后者才是模型的地盘。
- 对错要专家才能判。验标准写得出,前提是你知道什么是对的。写到自己也不确定的地方(一条业务规则的边界),能自动验收的标准就写不出来了。
- 链条太长。十个 95% 的步骤串起来只有 60% 的成功率。这类失败不来自单点,来自概率相乘——修环境抬得了单步正确率,改不了这个乘法。该做的是缩短链条、加中间检查点。
结尾
回到开头那个 Excel,最后怎么解决的?很朴素:把装了库的那个 python 加进了 PATH,一行改动。后来装上官方的 xlsx skill,它开头写着「openpyxl 已预装,直接 import」——这句话对这台机器来说,从那一刻起才变成真的。那之后它处理表格,再没走过解压那条路。
你想让它想得对,得先让它站的地方是对的。
这些东西都不酷:把对的解释器放进 PATH、把验收标准写清楚、留一条三十秒能跑完的测试。但它们决定了 agent 是在推进任务,还是在原地打转。所以留一个判断习惯就够了:
遇到问题,先问「能不能在更低的层彻底消灭它」,而不是「该怎么用提示词缓解它」。
数据来源
- 《Agents Are Systems, Not Models: Rethinking Agentic Evaluation》(arXiv:2610.01618):五个设置哪个最有用、重跑抖动占整体差异一半以上、自我检查的实际触发率从 19% 到 22%、给检查工具后的提升。注意它的任务是科学任务,不是日常编程。
- 《Do Context Files Help Coding Agents? A Two-Agent Ablation Study on Real Repositories》(arXiv:2607.27250):AGENTS.md 三种给法的对照实验与失败归因。
- 文中的 xlsx skill 指 anthropics/skills 仓库里的官方 xlsx skill。
- 第三节的对比为本文复现,代码在 zavier/xlsx-unzip-vs-openpyxl:openpyxl 生成的一张 31 KB、1100 行的表,三个实现分别 22 / 76 / 13 行,6 道题判分。
