Agent Loop 详解
感知、推理、行动、验证的循环,以及每一步的输入与输出。
一句话理解:Agent Loop 就是一个 while 循环:模型看当前信息、决定下一步动作,运行系统执行动作并把结果放回上下文,直到模型不再请求动作,或者撞上某个停止条件。
导论里的「初识 Agent Loop」建立了“观察、推理、行动、验证”的直觉。这一课把它拆到可以和工程师逐行讨论的程度:循环在代码里长什么样?每个阶段的输入和输出是什么?一条真实轨迹是怎么推进的?问题通常出在哪?
这一课之所以重要,是因为 Agent 的几乎所有问题(上下文爆炸、反复调用同一个工具、没验证就宣布完成)都发生在这个循环里。看不懂循环,就只能笼统地说“模型不够聪明”。
从一段伪代码开始
几乎所有 Agent 框架的核心,都可以简化成下面这几行:
messages = [system_prompt, user_task]
for turn in range(MAX_TURNS):
response = call_model(messages, tools=TOOL_DEFINITIONS) # 模型只“提议”动作
messages.append(response)
if not response.tool_calls: # 不再请求任何工具
return response.text # 本次运行结束
for call in response.tool_calls:
check_permission(call) # 运行系统:权限、审批、参数校验
result = execute(call) # 运行系统:真正执行
messages.append(tool_result(call.id, result)) # 结果放回上下文
return stop_with_reason("max_turns") # 撞上步数上限
读懂这段代码,就能纠正几个常见的错误认知:
- 模型本身不执行任何东西。 它只输出“我想调用
get_ticket,参数是……”,真正执行的是外面的运行系统(Harness)。权限检查、审批、超时、重试都发生在这一层,详见「什么是 Harness」。 - 一轮 = 一次往返。 Claude Agent SDK 文档的定义是:模型产生包含工具调用的输出,SDK 执行工具,结果自动回传给模型,这算一轮。
- 默认的结束条件是“模型不再调用工具”。 Claude Agent SDK 在模型给出不含工具调用的回复时结束循环;OpenAI Agents SDK 也类似,当模型输出符合预期类型的文本、并且没有工具调用时,就视为最终输出。换句话说,是否完成,默认由模型自己宣布。这正是后面「停止条件」一课要补上的缺口。
- 上下文只增不减。 每一轮的工具调用和结果都追加到
messages中,二十轮之后,模型面对的是一份很长的历史记录。
把这段伪代码画成图,模型与运行系统的分工一目了然:
四个阶段的输入与输出
把一轮循环展开,可以看到四个阶段。实际运行中它们是交织在一起的,Claude Code 文档描述自己的循环是“收集上下文、采取行动、验证结果”三个阶段,并指出这些阶段会相互融合。拆开来看是为了方便设计和排查:
| 阶段 | 输入 | 输出 | 由谁完成 | 设计时要问 |
|---|---|---|---|---|
| 观察 Observe | 任务、指令、历史消息、上一步的工具结果 | 模型对“现在是什么情况”的理解 | 运行系统组装上下文,模型阅读 | 该给的信息给了吗?不相关的信息是否太多? |
| 推理 Reason | 上述理解 | 下一步决策:调用哪个工具、传什么参数,或直接回答 | 模型 | 工具描述足够清楚吗?有没有需要遵守的顺序? |
| 行动 Act | 工具调用请求 | 工具执行结果或错误信息 | 运行系统 | 是否需要审批?失败时返回的错误能指导下一步吗? |
| 验证 Verify | 行动结果、预期目标 | 判断:继续、修正、完成,还是交给人 | 模型 + 程序检查 | 有没有客观的验证手段?验证不通过怎么办? |
四个阶段连起来是一个闭环,验证决定下一轮继续、修正、完成还是交给人:
最容易被省略的是验证。很多 Agent 的循环实际上只有“观察 → 推理 → 行动”,模型看到工具返回“成功”就宣布完成,没有回头确认结果是否真的达到目标。「为什么必须验证」会专门讨论这个问题。
一条完整轨迹:诊断一条迟迟没处理的工单
用户输入:“工单 T-1024 为什么还没关闭?”Agent 可以使用 5 个只读工具。(下面的数字均为示例)
| 轮次 | 模型的判断(推理) | 行动 | 结果(下一轮的观察) |
|---|---|---|---|
| 1 | 先确认工单基本状态 | get_ticket("T-1024") |
用户投诉“已付款未发货”,分类“发货延迟”,状态“待处理”,创建于 9 月 3 日 |
| 2 | 工单已挂两周仍未处理,同时查关联订单和支付记录(两者互不依赖,可以并行) | get_order("T-1024")、get_payment("T-1024") |
订单:9 月 3 日已付款,未出库;支付:9 月 3 日回调失败 |
| 3 | 回调失败可能不是个例,查同一支付渠道近期的其他订单是否也失败 | search_orders(channel="C", status="payment_callback_failed") |
9 月 1 日以后有 37 单回调失败 |
| 4 | 验证推断:这个渠道在 9 月 1 日之前的订单是否正常? | search_orders(channel="C", date_before="09-01", status="payment_callback_failed") |
0 单 |
| 5 | 证据充分,不再调用工具,给出结论 | —— | 输出:原因是渠道 C 在 9 月 1 日配置变更后支付回调未同步,影响 37 单;建议由支付团队核对渠道 C 的回调配置,附上各轮查询结果作为证据 |
把这五轮画在时间线上,可以看清哪一轮在并行、哪一轮是模型临时决定、哪一轮在验证:
值得注意的地方:
- 第 2 轮是并行调用。 两个查询互不依赖,模型在同一轮发出。Claude Agent SDK 文档提到,只读工具可以并发执行,修改状态的工具则按顺序执行。
- 第 3 轮是 Agent 与 Workflow 的分水岭。 “去查同一渠道的其他订单”这一步,不在任何预设流程里,是模型根据第 2 轮的结果临时决定的。
- 第 4 轮是验证。 模型没有在第 3 轮看到 37 单就急于下结论,而是主动找反例来排除“回调系统整体故障”的可能。好的指令和 Skill 应该鼓励这种行为。
同样的结构也出现在 Coding Agent 中:运行测试发现失败 → 读相关文件 → 修改代码 → 再运行测试确认通过 → 输出总结。区别只是工具换成了终端和文件编辑器。
循环中容易出问题的地方
| 问题 | 表现 | 应对方向 |
|---|---|---|
| 上下文膨胀 | 几十轮后,一个大文件或一大段日志挤占了大部分上下文,模型开始忽略早期的指令 | 工具返回精简结果、清理旧的工具输出、压缩历史、用子 Agent 隔离,见「长任务的上下文管理」 |
| 工具错误无法指导下一步 | 工具只返回“500 Internal Error”,模型反复用同样的参数重试 | 返回可行动的错误信息,如“工单号格式应为 T- 加四位数字”,见「工具设计原则」 |
| 缺少验证 | 工具返回“提交成功”就宣布完成,实际没有生效 | 行动后读回确认,或运行程序化检查 |
| 空转与循环 | 同样的工具、同样的参数调用了五次 | 设置步数和预算上限,检测重复调用,见「停止条件」 |
| 过早结束 | 证据不足就给出结论 | 在指令或 Skill 中写明完成标准,由程序检查结论是否引用了证据 |
| 人无法介入 | 发现方向错了,只能等它跑完 | 支持随时中断和插话。Claude Code 允许用户在任何时候打断并调整方向 |
常见误区
- “Agent Loop 就是模型自己在不停地跑。” 循环是运行系统写的代码,模型只负责每一轮的决策。权限、预算、审批、日志都在模型之外。
- “模型说完成了,任务就完成了。” 默认的结束条件只是“模型不再调用工具”,这并不等于任务真的完成。
- “循环跑得越久,结果越好。” 步数增加意味着上下文变长、错误累积、成本上升。Anthropic 在讨论 Agent 时专门提醒了成本升高和错误累积的风险。
设计要点
- 能画出你的 Agent 每一轮的上下文组成:指令、Skill、历史、工具结果各占多少。
- 每个工具在失败时都返回模型可以据此行动的错误信息。
- 写动作之后都有读回或程序化验证。
- 可以并行的只读查询,允许在同一轮发出。
- 循环有步数上限、预算上限,以及明确的停止原因记录。
- 完整轨迹(每一轮的输入、决策、动作、结果)都被记录下来,这是排查问题和做评测的基础,见「结果评估与轨迹评估」。
练习
标注一条真实轨迹(约 45 分钟):
- 用 Claude Code 或任何能展示工具调用过程的 Agent 完成一个小任务,比如“找出这个项目里所有没有单元测试的函数”。
- 把轨迹逐轮抄成上面那样的表格:模型的判断、行动、结果。
- 给每一轮标注它主要属于观察、推理、行动还是验证。
- 找出:哪一轮可以并行但没有并行?哪一步缺少验证?有没有重复或无效的调用?如果要把轮数减少三分之一,你会改指令、改工具,还是改上下文?
延伸阅读
- How the agent loop works(Claude Agent SDK 文档):轮次与消息的定义、结束条件、上下文累积与预算控制。
- How Claude Code works:Claude Code 的“收集上下文、采取行动、验证结果”循环。
- Running agents(OpenAI Agents SDK 文档):另一个框架对 Agent 循环与最终输出判定的描述。
下一步
循环的骨架是固定的,但“每一轮怎么想”有不同的范式。下一课「ReAct、Plan-and-Execute 与 Reflection」介绍三种经典做法。