给 Agent Loop 加 Max Iteration
昨天我们手写了一个 100 行 Agent Loop。
今天给它加 max iteration。
很多教程会把这一步写得很轻:
for (let i = 0; i < maxSteps; i++) {
// call model
// call tools
}
这当然要写。
但如果你的理解停在这里,max iteration 就只是一个计数器。真正上线后,它会变成用户看到的那句:
Agent stopped due to max iterations.
这句话很熟。LangChain、LangFlow、n8n、各种 agent 项目的 issue 里都能看到类似问题。Agent 一直尝试工具调用,最后被最大迭代次数拦住。用户不知道它为什么停,工程师也不一定知道。
所以今天要讲的不是'怎么加一个 for 循环'。
是 Agent Loop 到底应该怎么停。
maxSteps 是保险丝,不是诊断报告
没有 maxSteps 的 Agent Loop 是事故入口。
模型可能重复读同一个文件。可能在工具失败后换个参数继续试。可能一直调用搜索工具。也可能拿到无用 observation 后继续绕圈。
maxSteps 的作用,是防止它无限跑下去。
但保险丝跳了,不等于你知道哪里短路。
如果最终只返回:
达到最大迭代次数。
用户什么也学不到。工程师也只能翻日志。
更好的做法,是把停止原因拆开:
type StopReason =
| "final"
| "max_steps"
| "repeated_error"
| "repeated_tool_call"
| "no_new_information"
| "budget_exhausted"
| "permission_denied"
| "user_cancelled";
max_steps 仍然保留,但它不应该吃掉所有问题。
Agent 连续三次读同一个不存在的文件,不该最后只说 max_steps。它应该停在 repeated_error。Agent 连续调用同一个工具、同一组参数,也不该跑满 20 步才停。它应该更早触发 repeated_tool_call。
最小实现:先把 stop reason 写进 state
昨天的 RunState 可以扩一下:
type StopReason =
| "final"
| "max_steps"
| "repeated_error"
| "repeated_tool_call";
= {
: ;
: [];
: [];
?: ;
?: ;
: ;
: <, >;
};

