Agent Loop 是把”理解目标—调用工具—评估结果”串成闭环的标准执行循环,其关键阶段可归纳为 Plan(规划)、Act(行动)、Observe(观察)、Reflect(反思)四步;少任何一步 Agent 都会退化为一次性问答或失控的死循环。生产级 Agent 在每一步都有明确的核心职责:Plan 决定”下一步做什么”,Act 决定”动手做什么”,Observe 决定”看到什么结果”,Reflect 决定”还要不要继续”。
一、为什么 Agent Loop 不是”问一句答一句”
LLM 自身只能”想”,不能”动”。要让 Agent 真正完成任务,必须把它放进一个循环:想一次、做一次、看一次、评一次,再决定下一次。ReAct 框架最早把这套流程形式化,2026 年主流 Agent 平台(Azure Logic Apps Agent Loop、Claude Agent SDK、LangGraph、Semantic Kernel)都把 Plan→Act→Observe→Reflect 作为默认骨架。
循环的价值不只是”多做几步”,而是把”我以为我做完了”和”我确实做完了”区分开。一次性问答里没有”对照目标”的机会,Agent 容易在第一步就宣称完成;循环强制每一步都回到目标校验,是把 Agent 从玩具推向生产的关键。
二、四阶段的核心职责对照
四阶段不是顺序清单,而是一个有反馈的闭环。下面这张表把每个阶段的核心职责、典型输入、典型输出、与 LLM 的关系列清楚。
| 阶段 | 核心职责 | 典型输入 | 典型输出 | 与 LLM 的关系 |
|---|---|---|---|---|
| Plan | 把目标拆成可执行步骤,选下一步动作 | 目标、上下文、可用工具列表 | 编号步骤 + 工具选择 + 调用参数 | LLM 做推理与决策 |
| Act | 调用一个工具或执行一段代码 | Plan 选中的工具 + 参数 | 工具原始输出(API 响应、文件内容) | 通常不调 LLM,由 SDK 调度 |
| Observe | 把工具输出结构化、剔除噪声 | Act 原始输出 | 摘要后的 observation 注入回上下文 | 多为格式化代码,少量 LLM 摘要 |
| Reflect | 对照目标评估进度,决定继续 / 重试 / 终止 | 累积 observation + 目标 | 状态判定:on_track / done / stuck | LLM 做评估与决策 |
这张表的隐藏含义是:LLM 只在 Plan 和 Reflect 两步真正”思考”,Act 与 Observe 是”动手”与”看结果”,由 SDK 与运行时承载。把 LLM 的角色与执行器的角色分开,是 Agent 框架与单纯 prompt 调用的本质区别。
三、Plan 阶段:把目标拆成可执行的步骤
Plan 阶段最容易踩的坑是”一次生成 30 步大计划”。世界会变,过长的计划在第 3 步就可能失效。主流做法是让 Planner 输出 3–5 步的小计划,并显式声明”在第 N 步之后重新规划”。
Plan 的输入必须包含三件事:用户的最终目标、当前已知上下文(含历史 observation)、可用工具及其 schema。输出建议采用结构化格式(编号步骤 + 每步要调的工具 + 关键参数),便于下游 Act 阶段直接消费。
# Plan 阶段输出(结构化示例)
Goal: 给用户写一份关于"分布式锁"的对比文档
Steps:
1. 搜索内部知识库关键词"分布式锁",取前 5 条结果
2. 对每条结果调用 summarize 工具,提取 200 字以内摘要
3. 按"实现方式 / 一致性 / 性能"三维度生成对比表
4. 输出 Markdown 文档到 /tmp/lock.md
ReplanAfter: step 2
replan_after 是关键设计:明确告诉 Agent 在哪一步要回头重新评估,避免一口气执行到底才发现第 2 步的工具就用错了。
四、Act 与 Observe 阶段:动手与看结果
Act 阶段的核心是”一次只做一个原子操作”。一次调用写入多个文件、一次 SQL 同时改多张表,会让回滚成本陡增。SDK 在这一步通常禁用 LLM,直接把 Plan 输出的参数传给工具运行时。
Observe 阶段容易被低估。它的职责不只是”把工具输出塞回上下文”,还要做三件事:解析工具原始输出(JSON / 二进制 / 异常)、按需截断或摘要(防止上下文爆炸)、对工具错误做归因(是参数错了、权限不够、还是外部系统故障)。这三件事直接决定 Agent 的稳定性。
# Observe 阶段的最小实现(伪代码)
def observe(act_result: ToolResult) -> Observation:
if act_result.exit_code != 0:
return Observation(
status="error",
summary=act_result.stderr[:500],
hint="检查工具参数或权限",
)
return Observation(
status="ok",
summary=summarize(act_result.stdout, max_tokens=300),
raw=act_result.stdout if len(act_result.stdout) < 2000 else None,
)
Observe 输出的 Observation 会被注入下一轮 Plan 的上下文,模型据此决定下一步动作。
五、Reflect 阶段:评估进度,决定是否继续
Reflect 是四阶段里最被低估的一环,也是生产 Agent 与 Demo Agent 的分水岭。Reflect 阶段要回答三个问题:目标达成没?上一步成功没?若失败是否可恢复?对应三种动作:done / continue / escalate。
Reflect 必须与 Executor(执行器)解耦。把”反思”塞进 Act 后的同一次 LLM 调用,会引发乐观偏差——刚成功的执行器倾向于宣称成功。生产做法是把 Reflect 设为独立的 prompt,甚至用不同模型做交叉评估。
# Reflect 阶段输出(结构化)
Question: 当前是否达成目标?
-> Yes: 输出最终答案
-> No : 继续
Question: 上一步是否成功?
-> Yes: 按计划继续下一步
-> No : 进入重试或升级路径
Question: 是否卡在重复失败上?
-> Yes: 升级到人类处理
-> No : 继续
done / continue / escalate 三态判定必须显式返回,便于运行时记账与对账。
六、循环运行的关键工程约束
把四阶段串起来后,还需要给循环加上工程边界,否则 Agent 会”跑飞”或”烧光预算”。
- 设置最大步数(如 25 步),超过即强制终止;
- 设置最大 token / 美元预算,预算耗尽即终止;
- 设置最大墙钟时间,单轮工具调用超时即中断;
- 为 Reflect 阶段的 escalate 提供人类接管入口,不要让 Agent 自循环到资源耗尽;
- 把每一步的 Plan / Act / Observe / Reflect 写入可观测日志,便于事后复盘。
常见问题(FAQ)
Q1:Plan 阶段能不能省掉,直接进 Act?
简单任务可以省,但只要任务跨多个工具调用,省掉 Plan 容易在第二步就失去方向感。
Q2:Reflect 一定要单独调用 LLM 吗?
可用规则判定替代简单场景,但涉及语义评估(”答案是否合理”)时仍需 LLM 介入。
Q3:循环跑飞(陷入死循环)怎么防?
同时设置最大步数、最大 token、最大重复失败次数三道闸门,触发任一即终止并升级。