跳到主要内容

首次触达工具损耗:为什么你的智能体在执行任务前要先读 12 个文件

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的智能体刚刚花了 90 秒和几美元来修改一个只有三行代码的函数。在提交编辑之前,它列出了两个目录,打开了测试文件,运行了 grep 来查找调用者,读取了配置模块,检查了 CI 工作流,还调出了一个从未用过的类型定义。它产生的 diff 只有四行,而产生这个结果的 trace 却包含了 43 次工具调用。

这就是“首触工具损耗”(First-touch tool burn):一种当智能体被分配了一个范围明确的任务时,却表现得好像每个请求都是一个研究课题的模式。探索行为先行且力度极大 —— 在向文件写入单个字符之前,60% 到 80% 的 token 预算都花在了列出目录、grep 和读取上。团队在第一次查看 trace 时发现了这一点,并意识到智能体为一个两分钟的任务做了相当于两小时的入职培训。

这种行为并非某个特定模型的 bug。它是这些系统的训练和评估方式的必然产物,与生产环境发生了碰撞。而生产环境衡量的是训练从未衡量过的东西:这项工作是否便宜到值得去做的程度。

重写账单的训练奖励

编程智能体是在“先读取,后写入”是正确策略的轨迹上训练出来的。当基准测试中的问题模糊不清时,那些要求更多上下文、列出目录、立足于实际代码然后再采取行动的模型,往往会击败那些直接进入编辑环节的模型。这种轨迹在离线评估中得分很高。评审员将其评价为“谨慎”。它很少产生那种会被标记出来的“自信且错误”的编辑。

因此,经过多次迭代,训练过程会促使模型在采取行动前进行更多次读取。当你不知道自己身处何种环境时,这是正确的策略。但当任务描述已经指定了环境且 prompt 中包含了文件路径时,这就是错误的策略。

这种错位是结构性的。训练循环优化的是一种将探索视为免费的奖励,因为在离线评估中,token 预算本质上是无限的,而延迟的衡量标准是“它最终是否收敛到正确答案?”。生产环境优化的则是完全不同的奖励:这是否在用户关闭标签页之前完成了?它是否放进了上下文窗口?以及,一个微不足道的编辑产生的账单,是否保持在财务部门开始质询的阈值之下?

关于工具使用奖励设计的研究直接证实了这种不对称性。当探索性工具调用的惩罚较小时,模型会学到将其作为一种廉价的手段来对冲不确定性。同样的训练准则既防止了智能体倒向“总是编辑”的平庸策略,也教会了它“有疑虑时,多读一个文件”是低成本的保险。但成本并不低 —— 它只是在训练循环中不可见。

训练循环无法察觉的生产成本

一位开发者报告说,一个编程智能体为了回答关于三个函数的问题,读取了 25 个文件,这不是因为它不会写代码,而是因为它不知道哪三个函数才是关键。执行有意义工作的会话通常每个任务会烧掉数万个输入 token,其中很大一部分是从未影响过输出结果的探索行为。

三种具体的成本相互叠加:

破坏产品体验的延迟。 一个需要 5 秒模型时间加上 40 次工具调用(每次半秒)的任务,就是一个 45 秒的交互。用户觉得智能体慢,并不是因为模型推理慢,而是因为工具调用的扇出(fan-out)将延迟隐藏在一个单一指标无法捕捉的地方。你的 P95 聊天延迟看起来没问题,但你的 P95 端到端任务完成率却是一场灾难。

实际任务开始前的上下文窗口崩溃。 每一个工具结果都会进入上下文。读取 12 个文件,即使经过裁剪,也会将任务描述推到模型当前“思考”位置的上游三万个 token 处。关于上下文膨胀的研究直截了当:无关的上下文会主动降低性能,模型必须更努力地从加载的一堆垃圾中分离出信号。为了“谨慎”而读取更多的智能体,最终的准确率反而更低。

你仍需支付的废弃工作。 如果智能体探索了 60 秒后用户点击了停止,你仍然需要为产生的一切支付费用。每个已完成任务的 token 成本会随着每一次未能转化为编辑的额外读取而悄然增加。财务部门看到每个活跃用户的成本曲线向上弯曲,而工程团队却无法解释原因 —— 任务维度的核心指标看起来不错,但付费 token 与交付 token 的比例正在不断攀升。

为“大模型即函数”(LLM is a function)时代设计的可观测性仪表盘捕捉不到这些。它们衡量的是请求,而不是轨迹。信号隐藏在“消耗的 token”与“改变了输出的 token”之间的鸿沟中。

任务类别路由:并非每个任务都是研究型问题

解决这个问题的首要步骤是承认大多数任务并不模糊。“将此函数的日志级别更改为 warn” 不需要调查。“在文件的第 47 行添加空检查” 不需要列出目录。如果智能体将这些任务视为探索性问题,就是在为不存在的不确定性浪费预算。

任务类别路由意味着编排层在将工作交给智能体之前,先对输入的工作进行分类。分类器不需要很复杂 —— 一个简单的启发式规则就足够了:查看提示词是否包含具体的标识符(文件路径、函数名称、行号)和特定的动词(重命名、删除、添加、替换),这足以将 “直接做” 的任务与 “搞清楚” 的任务区分开。这两类任务会获得不同的工具访问权限、不同的系统提示词和不同的预算。

对于 “直接做” 类任务,除了提示词中指定的确切文件外,智能体应禁用读取和编辑工具。没有 grep,没有 list_files,也没有测试运行器。唯一的探索方式是请求澄清并停止运行,当任务描述自称具体但实际并非如此时,这才是正确的行为。

对于 “搞清楚” 类任务,可以使用完整的探索工具包,但预算上限更严、更透明。在系统提示词中告诉智能体还剩下多少次读取机会,以及预算耗尽时会发生什么。Google 和加州大学圣塔芭芭拉分校(UC Santa Barbara)的研究表明,在智能体中加入显式的预算跟踪,可以在保持准确性的同时,减少 40% 的搜索调用和约 30% 的总成本。预算本身并不是关键的杠杆 —— 让智能体意识到预算的存在才是。

探索预算上限与 “先计划后执行” 门控

预算上限需要被强制执行,而不仅仅是传达。系统提示词里说 “尽量不要读取太多文件” 只是一个愿望,而不是政策。可执行的版本存在于工具层:在单个任务上执行 N 次读取后,读取工具开始返回一个合成响应,内容是:“探索预算已耗尽 —— 在读取另一个文件之前,请声明你接下来的操作。”

这种约束机制完美契合 “先计划后执行” 模式。一些编程智能体产品已经采用了显式的双模式分离,智能体在执行编辑之前必须生成一份计划 —— 一份关于它打算更改什么以及为什么要更改的书面假设。计划本身就是门控。一旦确定了计划,智能体剩余的工具调用将根据该计划进行解释:支持计划的读取操作可以通过,而那些看起来像是在盲目搜索的读取行为将被限流。

“先计划后执行” 有时被批评为增加了摩擦,对于简单的任务来说确实如此 —— 这就是为什么任务类别路由要排在第一位。计划门控不是为了 “重命名这个变量”,而是为了那些探索虽然合理但往往会无限扩张的长尾任务。计划通过让智能体在读取到足以产生 “自信的错误” 之前做出承诺,从而限制了搜索空间。

第二种强制机制是 “输出优于输入” 的压力。告知智能体它在该任务上的得分是 “diff 中的 token 数除以读取的 token 数”。虽然这个指标孤立来看是退化的(零 diff 的回答可以轻而易举地优化它),但结合正确性检查,它能强力抑制探索膨胀。采用这种方式调优智能体的从业者报告称,模型开始在前期提出更精准的问题,而不是进行十次投机性的读取并寄希望于其中一次能消除歧义。

导致这种行为的评估集反模式

团队之所以从训练过程中继承了过度探索的智能体,是因为在后训练(post-training)中使用的评估集本身就存在选择偏误。大多数编程基准测试都是由探索才是正确答案的任务构建的:不熟悉的代码库、模糊的需求、需要追踪多个文件才能发现的 bug。这些基准测试奖励 “在编辑前读取 12 个文件”,因为在这些任务中,第 12 个文件确实是关键。

而基准测试几乎从未包含平衡类别:即那些范围明确、探索纯属浪费的任务。一条执行了 19 次读取才完成两行代码修改的轨迹,并不会因为有一条零读取且得到相同答案的轨迹存在而被降分,因为测试环境不知道零读取就足够了。两条轨迹都产生了正确的 diff,所以得分都是 1。

这就是过度探索行为被固化的过程。模型并不是因为浪费 token 而受到奖励,而是在一个 token 效率从未被纳入评分标准的分布中,因其正确性而受到奖励。修复评估集,行为就会开始转变。具体来说,这意味着增加任务对,即同一个正确输出既可以由极简轨迹产生,也可以由臃肿轨迹产生,并将臃肿轨迹评定为较差。完成这项工作的团队报告称,效果比预期的要大 —— 一旦信号进入损失函数,模型愿意用几点原始准确率来换取探索量的大幅减少。

如果你是模型的使用者而非训练者,你可以通过在自己的评估环境中运行这两类轨迹,并在提示词工程中推广高效的轨迹来模拟这一点。上一节提到的探索预算上限本质上是评估集修复的运行时替代方案 —— 它是你对一个在不关心成本的基准测试中训练出来的模型所做的补偿。

值得关注的值班信号

这种故障的缓慢版本不会表现为一次事故,而是表现为一种“漂移”:同一个任务在本季度消耗的 Token 数是上季度的三倍,但完成质量却毫无变化。如果你只跟踪“智能体是否完成了任务”以及“用户是否满意”,你将完全忽略这一点。如果你跟踪随时间变化的每个任务类别的 Token 消耗情况,你就能在它演变成预算讨论的数周前发现这种漂移。

你应该放在值班人员面前的监控面板是这样的:针对每个任务类别,绘制“输出差异中的 Token 数”与“任务期间消耗的 Token 总数”的比率,采用 90 天滚动数据。当该比例呈下降趋势时,你的智能体正在学习——无论是由于提示词的细微变化、上游模型更新,还是检索漂移——在每单位产出的工作量中进行更多的探索。解决方法很少是重新训练。通常是因为抽象泄漏了(工具描述变得模糊、检索索引噪声变大、路由器被禁用),而智能体正通过增加阅读量来补偿这些缺失。

底层的架构调整是将“探索”视为一种带有预算的一等资源,而不是智能体启动任务时的一种隐性副作用。做到这一点的团队会发现,首次工具调用的消耗下降了,延迟改善了,而且“为什么这个智能体做这点事要花这么多钱”的问题也不再出现在每个季度的业务回顾中了。当你让智能体“节食”时,它的工作表现并不会变差。它反而会变得更快,因为原本消耗的那些“卡路里”并没有真正滋养到工作本身。

References:Let's stay in touch and Follow me for more thoughts and updates