那天我给一个负责调度技能的 Agent 布置了一个看起来很大的任务:把当天的协作记录都看一遍,找出它过去走错的入口,再训练它更准确地理解用户意图。表面上,这是一次错误汇总;真正做起来,我很快发现,最先需要解决的不是它“知道得不够多”,而是它经常在开始行动前就走错了路。
它会把技能脚本猜到一个通用工具目录里,也会去已经不用的旧目录寻找入口,还会根据用户的一句话自造一个并不存在的技能名。更麻烦的是,有时候它明明知道某个任务有指定的正门,却绕过去自己写一套替代方案。等到错误命令真正执行,后面每一步都可能建立在错误前提上。
这件事让我重新意识到,路由型 Agent 的可靠性,不是“记住更多名字”这么简单。在真正调用之前,先把入口、命名和执行环境核对清楚,往往比再写一段更长的提示词更有用。
它不是不会做,而是先走错了
我一开始以为,只要把技能说明写得更完整,Agent 就能慢慢记住正确路径。于是我先让它读取当天的协作记录,想从失败结果里找出规律。记录很快暴露出一个反复出现的场景:用户说的是一个自然语言任务,Agent 需要把它路由到某个技能;但它还没完成路由,就已经开始凭经验拼目录、拼文件名、拼命令了。
有一种错误尤其典型。技能真正的脚本在技能自己的目录里,Agent 却把它当作普通工具,直接去全局工具目录下找。另一个错误是沿用旧环境的记忆,去一个历史目录里继续搜索。还有时候,用户口头说了一个近似的名称,它没有先查名称索引,而是直接判断“本机没有这个技能”,甚至让用户重新提供路径。
如果不处理这些问题,后果不只是一次调用失败。错误入口会让 Agent 误以为能力不存在,接着自己写替代脚本;替代脚本又可能漏掉原技能里的校验、授权和交付要求。最后看起来像是“做了很多”,实际却绕过了用户指定的工作流,甚至留下难以审计的副作用。
真正要修的是调用前的一瞬间
继续看记录后,我把问题拆成了几层。第一层是名称解析:用户说的名字可能有缩写、同音或轻微拼写差异。第二层是入口查找:脚本可能不在默认目录,而在对应技能的脚本目录。第三层是命令完整性:有些脚本需要在技能根目录运行,有些需要特定解释器,不能只凭文件名猜测。
这几层如果都交给模型临场判断,错误就会被放大。模型可能在第 1 步猜错,第 2 步又把这个猜测当作事实,最后生成一串看似合理、实际不存在的命令。表面上每一步都很有解释,真正的问题却发生在最开始:它没有先确认自己要调用的对象。
所以我选择在调用前增加一道守卫。守卫的职责不是替 Agent 完成任务,而是在任务真正落地前确认“这个入口确实存在,而且是用户要求的那条正门”。 这也是我没有继续堆系统提示词的原因:提示词可以提醒,但不能可靠地证明某个路径真的存在;守卫可以把检查变成确定动作。
守卫不能只会说“不存在”
第一版守卫很快就暴露了边界问题。技能名拼错时,原来的解析规则会把名字的一部分误当成分类;模糊匹配虽然能找到相近项目,却可能把相似名称引到错误入口;还有一些常用脚本只写了文件名,没有带上技能前缀,普通查找也会漏掉。
这些问题说明,拦截本身不是终点。如果守卫只返回“路径不存在”,Agent 仍然要重新猜。更好的失败反馈应该同时告诉它:原调用哪里不对,实际可用的入口是什么,为什么应该改成这个入口,以及下一步可以直接执行什么。错误信息必须能推动下一次行动,而不只是记录一次失败。
我先修了名称解析和模糊匹配,再补上缺前缀脚本的查找规则,最后用历史错例回放。回放不是只测一个正确样例,而是故意把错误目录、旧目录、近似技能名和缺前缀脚本都送进去,看守卫能不能挡住并给出可执行纠正。这样做的价值在于,测试的不是“它能不能找到”,而是“它会不会把常见错误重新放行”。
这次我还保留了修改前的备份,并把守卫装入两套执行环境后分别检查。一个可靠的拦截器必须让人看得见它改了什么、为什么改,以及失败时如何恢复。 否则,自动修正本身也会变成另一个无法解释的黑箱。
不绕正门,才算真正理解任务
随着错误样本增加,我发现路径问题只是表层。更深一层的模式是,Agent 常常把“自己能写出来”误认为“自己应该这样做”。用户已经点名某个技能,它却绕过正门,直接手写一套实现;已有现成能力,它又从零重复造一遍;某个任务要求使用专门入口,它却选择自己熟悉的通用命令。
这里最容易误判的是:替代方案有时真的能运行,所以看上去比查索引更快。但能运行不等于完成了用户交代的流程。正门里可能包含参数约束、隐私处理、环境准备、回执方式和失败恢复。绕开这些内容,短期少走几步,长期却把风险转移到用户身上。
我判断“先查索引、再走正门”应该成为默认规则,是因为它同时解决了两个问题:一方面减少错误路径,另一方面保留技能本身积累下来的边界。只有确认正门不存在、不可用,或者用户明确要求另一个方案时,才有理由进入替代路径;而且这个理由应该留下来,方便后续人工审计。
这也改变了我对“聪明”的看法。一个 Agent 能写出更多代码,并不代表它更聪明。它是否知道什么时候不要自己发挥,是否能在不确定时先查证,是否会把一次纠正变成下一次的规则,反而更能说明它是不是可靠。
把失败变成下一步的路标
从这次复盘里,我整理出一套可以复用的方法。第一步,收集真实调用中的错误,不只看最终报错,也记录用户在哪一步纠正了 Agent。第二步,把错误按名称、入口、解释器、正门和意图分层,不要把所有失败都归结为“模型能力不足”。第三步,建立一张技能名、实际入口和运行要求之间的索引,让路由有可查询的依据。
第四步,在调用前做存在性、命名和正门校验。命中正确入口就继续;遇到常见错法,就返回实际入口和可执行修正;遇到无法判断的模糊匹配,就停在安全边界内,给出候选而不是擅自选择。第五步,用历史错例回放,覆盖旧目录、错误前缀、相似名称和绕过正门等情况。第六步,把每次被人工纠正的原因回灌到索引和测试样本中。
这套流程的关键,不是建立一张永远不会变化的表,而是让表和失败样本一起更新。入口会移动,命名会变化,新的技能也会加入。如果只把正确路径写进提示词,过一段时间仍然会漂移;如果把路径核对变成调用前动作,变化就能在真正执行前暴露出来。
真正可复用的不是某个目录答案,而是“先查全局索引、再校验正门、失败后返回纠正、最后回放错例”的顺序。 这个顺序同样适用于命令路由、插件选择和多环境执行:凡是一步走错会让后面全部失真的地方,都值得在入口处设置守卫。
下次我会怎么做
下次再训练一个需要调用大量能力的 Agent,我会把“路径守卫”当成基础设施,而不是等错误积累后才补的补丁。第一件事不是写更长的说明,而是确认有没有一份可查询的全局索引,能回答名称对应什么入口、需要什么解释器、是否存在指定正门。
我会先拿少量真实错例做零成本回放,尤其检查它会不会把脚本猜到通用目录、回到旧目录,或者自造一个听起来合理的名字。守卫通过后,再接入实际环境,并保留备份、审计记录和回归样本。这样做并不会让 Agent 永远不犯错,但能把最昂贵的错误挡在调用之前。
这件事让我重新意识到,一个流程成熟的标志,不是它顺利时能跑通一次,而是它在错误发生前就知道该在哪里停下。Agent 变聪明,未必是让它拥有更多自由;很多时候,是让它在不确定时先查证,在走偏时及时回头,并把这次回头留下的理由交给下一次行动。