Forge 开发笔记 03:让 Agent 学会被打回重做
Agent 最危险的时刻,往往不是它不知道怎么做,而是它以为自己已经做完了。
在普通聊天里,模型说“我完成了”只是一句话;但在 Coding Agent 里,这句话可能意味着系统停止循环、任务结束、代码被交给用户。如果没有外部验证,这个完成状态其实很脆弱。
Forge 的 v0.2 加入 Verifier gatekeeper,就是为了让“完成”变成一个可执行检查,而不是模型自己的声明。
源码版本:arjenzhou/forge@7b7045f,对应
v0.2.0-verifier。
设计思路:完成必须经过外部现实
v0.2 的核心原则很简单:模型可以申请完成,但不能批准自己完成。
模型的判断来自上下文和语言生成,它适合提出下一步行动、解释错误、生成补丁。但代码是否能运行、测试是否通过、语法是否正确,这些应该交给更客观的机制。Verifier 就是这个外部现实。
因此 runner 的结束逻辑要改成一个 gate:当模型不再请求工具时,不直接退出,而是先执行 verifier。通过才结束;失败就把报告重新送回上下文,让模型继续修。
代码落点
对应源码主要看这几处:
examples/demo_verifier.py # 故意制造语法错误,再让 verifier 打回
forge/runner.py # 模型想结束时触发 verifier
forge/verifier.py # 语法检查和测试命令
完成状态不能只由模型决定
在 v0.1 里,如果模型不再返回 tool calls,runner 就可以认为任务结束。v0.2 改了这个逻辑:模型可以申请结束,但系统先跑 verifier。
forge/runner.py 里最关键的分支是这个:
if not tool_calls:
is_passed, report = self.verifier.verify()
if is_passed:
trace.finish(content or "No final response provided.")
break
else:
context.add_assistant(content, None)
context.add_user(report)
step.tool_results.append({
"tool_call_id": "verifier_check",
"name": "auto_verifier",
"content": report
})
self.save_checkpoint(checkpoint_path, iteration, context, trace)
continue
这一段把 self-correction 从口号变成了机制:
- 模型尝试结束;
- runner 运行 verifier;
- verifier 失败;
- 失败报告被加回上下文;
- 下一轮模型看到错误后继续修。
Verifier 到底检查什么
forge/verifier.py 没有做复杂魔法。它先用 compile() 检查 Python 语法,再运行用户给的测试命令:
def verify(self):
syntax_passed, syntax_errors = self.verify_syntax()
if not syntax_passed:
return False, "[VERIFIER FAILED] Syntax verification failed! ..."
if self.test_command:
tests_passed, test_output = self.run_tests()
if not tests_passed:
return False, "[VERIFIER FAILED] Unit tests failed! ..."
return True, "[VERIFIER PASSED] All compilation and test checks passed successfully."
语法检查的核心也很朴素:
with open(filepath, 'r', encoding='utf-8') as f:
source = f.read()
compile(source, filepath, 'exec')
这恰好说明 verifier 不一定需要聪明。它只要足够客观,就能改变 Agent 行为。
Demo 里发生了什么
examples/demo_verifier.py 里的 VerifierMockModel 故意犯错。第三轮它把函数定义里的冒号删掉:
buggy_replacement = "def add(a, b)\n return a + b"
第四轮它不再调用工具,直接宣布完成:
return (
"I have updated main.py. I am finished with the task!",
None
)
这时 runner 不会直接相信它。运行 demo:
python examples/demo_verifier.py
能看到这样的输出:
[Runner] === Iteration 4 ===
[Model Thought/Message]: I have updated main.py. I am finished with the task!
[Verifier] Running verification checks...
[Verifier] Status: FAILED (Syntax Error)
[Runner] Verifier BLOCKED termination.
Syntax Error in main.py at line 1, col 14:
def add(a, b)
Error message: expected ':'
下一轮,模型读到 verifier report,又把冒号补回去:
[Runner] === Iteration 5 ===
[Model Thought/Message]: Oops, I introduced a syntax error...
[Runner] Requesting Tool (Sequential): apply_patch
最后再次尝试结束,verifier 通过:
[Verifier] Status: PASSED
[Runner] Verifier PASSED! Task complete.
用户也应该设计 Verifier
这件事会改变人和 Agent 的协作方式。
一个好的 agent task,最好不只有自然语言目标,还应该有可执行验证方式。例如:
- 修复 bug:给出测试命令;
- 实现函数:给出单元测试;
- 修改文档:给出格式检查或 diff 检查;
- 重构代码:给出编译、测试、静态检查。
没有 verifier,用户只能靠读最终回答判断质量;有 verifier,Agent 的完成状态就能被更具体地约束。
可靠系统不是从不犯错,而是犯错以后能被发现、能被反馈、能被修正。Forge v0.2 的重点就在这里:不要害怕 Agent 被打回重做。恰恰是这个被打回的机制,让 Agent 从一次性生成器,变成了可以在现实反馈中迭代的执行系统。