跳到正文
返回

工具返回成功,不等于任务完成

4分钟阅读

假设让一个 Agent 在博客仓库里创建草稿,要求很具体:文件放进指定目录,draft 必须是 true,不能覆盖已有改动,最后还要通过构建。

随后,写文件工具返回退出码 0,并报告文件已经创建。这个动作成功了吗?成功了。任务完成了吗?还不知道。

文件可能被写到了正确位置,却把 draft 设成了 false;也可能内容完全正确,却顺手改了另一个文件。工具没有撒谎。它准确报告了自己负责的那次操作,但我们向它问了一个它无法回答的问题。

这正是构建可靠 Agent 时容易混淆的两件事:工具输出是 Observation,任务成败则要经过 Evaluation 才能成为 Feedback。观察告诉我们发生了什么;反馈才说明这件事相对于目标意味着什么。

目录

展开目录

工具只能报告它看见的局部事实

一次工具调用通常有清晰的边界。文件工具知道目标路径、写入内容和执行结果,命令行知道标准输出、标准错误与退出码,浏览器知道页面上出现了哪些元素。Harness 再把这些环境状态转换成 Agent 能读懂的文本。

这些都是事实,但事实的语义很窄:

它们都没有自动蕴含“用户要的结果已经得到”。一个总是返回 200、正文却是错误数据的接口,并不会因为传输层面成功就变得正确。类似地,构建通过可以排除一批语法和结构错误,却不能证明文章的中心命题成立。

ReAct 论文把推理与作用于外部环境的行动交错起来:行动让模型取得新的外部信息,再据此更新后续推理。这个循环解决了“模型如何看见行动结果”,但看见结果只是下一轮判断的输入,不是判断本身。

“好不好”来自目标,不来自观察

同一份观察可以对应相反的反馈。文件的 draftfalse:如果任务是“保存草稿”,这是失败;如果任务是“发布已审批文章”,它反而可能是必要条件。文件内容没有变化,变化的是我们拿什么标准解释它。

因此,Objective 不能只有一句模糊愿望。至少要区分三部分:

只有拿到这些标准,系统才能把“文件存在”“差异只涉及新文章”“构建退出码为 0”等观察组合起来,判断任务是否完成。OpenAI 的 Evals 接口也把一次评估定义为测试标准与数据来源的组合:待测输出本身不够,还需要明确用什么条件给它判定结果。

这里甚至不必引入一个更聪明的模型。很多完成标准都能直接编码成确定性检查:解析 Frontmatter 确认 draft: true,读取版本差异确认修改范围,再运行项目已有的检查命令。模型擅长处理开放问题,机器检查擅长守住明确边界;把两者接起来,比让模型对着“成功”二字自行领会可靠得多。

反馈是带着判定依据的观察

观察与反馈不是两份完全不同的数据。反馈往往就是观察经过目标解释后的结果。

例如,构建命令输出“0 errors”。作为一行终端文本,它是观察;当完成标准写着“构建不得出现错误”,它才成为支持通过的反馈。测试失败也一样:失败日志先描述哪里不一致,评估再指出这个差异违反了哪条预期。

关键不在于输出由工具、测试、人类还是另一个模型产生,而在于其中是否存在一条可追溯的判定链:

Action
  → 环境发生变化
  → Observation
  → 按 Objective 的完成标准进行 Evaluation
  → Feedback
  → 更新下一步行动或结束任务

如果把观察直接当反馈,这条链会在最需要语义判断的地方短路。Agent 会偏爱最容易读取的成功信号:工具返回了、文件出现了、命令结束了。于是局部操作越顺利,过早宣布完成的诱惑反而越大。

完成不是停止行动,而是证据闭环

解决办法不是在每次行动后再追加一句“请仔细检查”。那仍然把完成标准藏在自然语言期待里。更可靠的做法,是在行动前就问清楚:什么状态才算完成,哪些状态绝不能出现,最后用哪些证据区分它们。

这样,Agent 的停止条件就不再是“我已经做过所有计划步骤”,而是“现有观察已经满足完成标准,并且没有违反约束”。计划描述准备做什么,证据才决定能不能停。

这个区分也不只适用于 Agent。持续集成显示绿色、数据管道没有报错、接口返回成功,都只是某一层系统对局部事实的观察。要把它们提升为真正的成功信号,必须能回答:它检查了哪个目标,又遗漏了哪些目标。

所以,看到一个漂亮的成功标记时,最值得追问的不是“它有没有成功运行”,而是“这个信号凭什么代表我在意的事情已经发生”。