跳到主要内容
约 11 分钟4 张卡

Agent 执行循环

Agent 执行循环是模型提出动作、程序执行动作,再把结果交回模型的重复过程。模型读取新的结果后,才能据此选择下一步。

本文沿一次工具交互说明模型与程序的分工,以及工具结果和错误如何影响下一步。随后介绍 ReAct、循环的停止条件,以及逐步决策与先规划再执行的适用任务。

1. 面试核心​

先记住这几条,再读正文
  • 执行循环。 ReAct 交替进行推理、动作和观测,模型提出工具调用后,程序负责执行并将结果写回上下文。卡 · 一次循环的执行链路
  • 停止条件。 程序用最大步数、总超时、Token 与费用预算限制循环,并在重复调用没有进展时停止。卡 · 停止条件与预算
  • 完成验收。 任务完成以验收结果为准。模型给出最终回答后,仍需检查实际产物和业务状态,未通过时继续修正或报告未完成。卡 · 完成校验
  • 计划与逐步决策。 ReAct 根据每一步的观测决定后续动作,Plan-and-Execute 则先生成计划,再逐项执行。卡 · ReAct 与 Plan-and-Execute

2. 一次循环的执行链路​

执行循环要把模型的一次输出变成可执行的动作,再把执行结果交给下一轮。这里的观测指工具返回的数据或错误,模型依靠它了解外部环境发生了什么。

下面是循环控制的伪代码。guarded_execute 负责校验参数、检查授权并执行工具。verify 则检查任务是否满足业务验收条件。

def run(task, tools, max_steps=20):
messages = [user(task)]
for step in range(max_steps):
if deadline_or_budget_exceeded():
return Stopped("budget", messages)
reply = model.generate(messages, tools=tool_schemas(tools))
messages.append(reply)
if not reply.tool_calls:
verdict = verify(task, reply)
if verdict.passed:
return Done(reply.text)
messages.append(validation_feedback(verdict))
continue
for call in reply.tool_calls:
result = guarded_execute(call, tools)
messages.append(tool_result(call.id, result))
return Stopped("max_steps", messages)

这段代码把一次模型调用算作一步,max_steps=20 只是示例上限。一轮可以包含多个工具请求,所以工具还需要单独的调用次数限制和超时。

模型返回调用请求后,程序依次完成以下三步:

  1. 校验工具名称和参数,并检查当前任务是否有权执行。
  2. 执行读取文件、调用接口或运行命令等动作,捕获执行异常。
  3. 按调用 ID 将结果追加到消息列表,让模型知道结果对应哪个请求。

下一轮读取这些结果后,模型可以继续调用工具,也可以给出回答。实际接口还会返回拒绝、截断或服务错误,程序需要分别处理这些状态。

工具失败也会形成观测。比如返回“参数缺少 order_id”,模型就能补齐订单号后重新请求。若程序直接抛出异常、结束任务,这次修正机会就会丢失。权限拒绝应返回受限原因,让模型调整方案,后续动作仍受原权限约束。

3. ReAct 的三个环节​

ReAct 将推理(Reasoning)和行动(Acting)交替进行。它让模型在每次行动后读取真实结果,再修正下一步判断。三个环节的分工如下:

环节内容作用
Thought对状态和下一动作的分析帮助选择动作
Action工具名及参数请求外部操作
Observation工具结果或错误为下一步提供反馈

假设用户问:“订单 A123 为什么还没发货?”下面用三轮交互展示模型怎样依据结果继续查询,最后给出回答。工具名、字段和返回值均为示意,Thought 只展示选择下一步的简短理由。

第 1 轮
Thought:先查订单状态,确认发货进度和负责的仓库。
Action:query_order(order_id="A123")
Observation:status="待发货", warehouse_id="WH2", sku="SKU-789"

第 2 轮
Thought:订单仍待发货,需要检查 WH2 的商品库存。
Action:query_warehouse(warehouse_id="WH2", sku="SKU-789")
Observation:stock=0, delay_reason="缺货", expected_restock="10月16日"

第 3 轮
Thought:已查到缺货原因和预计补货时间,信息足够回答。
Action:结束查询,给出最终回答。
最终回答:订单 A123 还在等待发货,因为发货仓库 WH2 的商品缺货。
仓库预计 10 月 16 日补货,实际发货时间仍需等待仓库确认。

第 2 轮查哪个仓库、哪件商品,取决于第 1 轮的观测。这就是 ReAct“边做边想”的含义:每次拿到结果,再决定下一步。回答中的订单状态和缺货原因都有工具结果作为依据,因此能减少凭空编造订单信息的机会。

原始 ReAct 用 Thought: 和 Action: 等文本标记表达推理和动作,程序解析后执行。上例沿用这些标记,便于读者理解交互过程。实际工具调用 API 中,Action 对应结构化调用字段,Observation 对应写回的工具结果消息。Thought 不一定以文本出现,推理是否可见取决于模型和接口。执行循环读取调用字段与结果即可,无须解析隐藏推理。

这种逐轮交互有两个典型代价:

  • 上下文增长:每轮观测都追加进上下文,保留全部历史时,长任务的输入会越来越长。这会推高费用和延迟,控制方法见上下文压缩。
  • 步骤增多:模型容易偏离原始目标,或围绕同一问题反复尝试。程序需要用停止条件与预算限制无效循环。

4. 停止条件与预算​

模型会过早宣布完成,也会在几个动作之间反复尝试。因此,程序需要自行判断任务进展,并区分四种状态:

  • 完成:验收通过后,任务进入 done,向调用方返回结果。
  • 等待:缺少用户输入或需要人工批准时,先保存进度,再等待回复。
  • 达到上限:步数、总时长、Token 或费用达到上限时,停止继续执行。
  • 重复失败:相同调用持续失败且没有新观测时,停止重试或转人工处理。

步数上限应按任务类型,从成功任务的执行轨迹中统计。轨迹就是每次模型调用、工具执行及其结果的记录。比如,评测中成功任务的 P95 为 12 步,表示 95% 的成功任务在 12 步内结束。可以先试设 20 步上限,再结合每步成本,检查成功率与单任务总费用。

重复检测要同时记录工具名、参数、错误和任务进展。相同参数反复调用,或连续 N 次出现同一错误且没有进展时,应触发处理。这里的 N 是按任务设置的阈值。工具重试与子任务的消耗也要计入总预算,执行中的请求则需要超时或取消机制。

触发上限后,可以返回 stopped: max_steps 这样的结构化状态。同时保存已完成动作、剩余任务和最后一次错误,便于调用方重试或转人工。若展示部分结果,也要说明哪些工作尚未完成。

需要批准的动作应在执行前暂停,具体分级见工具授权与执行沙箱。

4.1 完成校验​

完成校验要回答的是:任务要求的结果是否已经实现?模型没有请求工具,只说明本轮生成给出了回答,任务完成还需要外部证据。比如,编码任务检查目标测试和回归结果;信息抽取任务检查必填字段及业务约束。

验收失败时,程序把具体差异写回上下文,模型才知道还要修正什么。剩余预算足够时继续修正,否则返回未完成状态。如果验证器自身故障,应标记验收不可用,保留已有结果供后续核验。

5. ReAct 与 Plan-and-Execute​

长任务可以每一步都根据观测作决定,也可以先列出计划。Plan-and-Execute 采用后一种方式:先让模型生成计划,再由执行器逐项完成。观测偏离计划时,系统重新规划。

维度ReActPlan-and-Execute
决策时机每步根据新观测决策先规划,必要时重规划
适用任务故障排查、交互探索子任务及依赖较清楚的任务
模型选择按每步难度选择规划与执行可使用不同模型
主要代价决策轮数增加规划与重规划增加开销

ReAct 适合排障或浏览网页,因为下一步依赖刚拿到的结果。比如,查完日志才知道要检查哪台机器。逐步决策需要持续调用模型,长任务中还要防止偏离目标。

Plan-and-Execute 适合多源调研或报表生成,因为子任务及依赖比较清楚。比如,生成一份周报需要查询订单、退款和客服工单,再计算指标并汇总。规划器可以先输出下面的计划,其中 id 标识步骤,task 描述要做的事。depends_on 列出该步骤需要等待的前置步骤,空数组表示没有依赖。这里的字段用于示意计划结构。

{
"steps": [
{ "id": 1, "task": "查询本周订单数据", "depends_on": [] },
{ "id": 2, "task": "查询本周退款数据", "depends_on": [] },
{ "id": 3, "task": "查询本周客服工单", "depends_on": [] },
{ "id": 4, "task": "计算订单量、退款金额和工单量", "depends_on": [1, 2, 3] },
{ "id": 5, "task": "汇总指标并生成周报", "depends_on": [4] }
]
}

执行器读取依赖关系后,可以并行执行步骤 1、2、3。步骤 4 要等三项查询全部成功后再执行,步骤 5 则等待指标计算完成。明确的计划便于事先审计查询范围,也能让独立查询并行执行。不过,初始计划出错会造成返工,因此执行器需要根据结果触发重规划。

如果步骤 2 返回“退款表字段已改名”,执行器就把失败结果交回规划器。新计划把步骤 2 改为“查询新的表结构 → 按新字段重新查询退款数据”,复用步骤 1、3 的成功结果。

触发重规划的常见条件有:

  • 步骤失败:例如字段改名导致退款查询报错,原步骤需要修改。
  • 观测与计划的假设矛盾:例如计划假设金额都以人民币记录,结果发现退款中包含美元,需要增加币种换算。
  • 出现新信息:例如工单中发现集中投诉的新问题,需要补充分类统计。

如果用每轮只决定一个动作的串行 ReAct 做这份周报,每查完一个数据源,都要再调用模型决定下一步。在这种执行方式下,三个查询只能依次进行,完整路径也难以事先审计。支持一轮请求多个工具的实现可以并行独立调用,区别见前文一次循环的执行链路。

规划可以使用能力较强的模型,简单执行步骤则交给成本较低的模型或固定代码。ReAct 也能按每步难度选择模型。成本是否下降,要比较完整轨迹,因为规划、拆分和重规划本身也会增加调用。

两者还可以组合:外层计划定义阶段,阶段内部用 ReAct 探索。比如修复接口故障时,外层计划分成“定位问题—修复—验证”三个阶段。定位阶段根据日志逐步缩小范围,修复阶段根据代码和报错调整改动。验证阶段再根据测试结果决定是否补充检查,每个阶段内部都用 ReAct 处理不确定的细节。

两种结构都可以加入评审步骤(Critic),检查执行结果是否满足要求,并指出需要修改的地方。

面试中还会追问以下两种规划类变体:

  • ReWOO:规划时一次写出全部工具调用,用 #E1 这类占位符引用前面步骤的结果。执行器取得结果后替换占位符,省去逐步调用模型决定下一步的过程,因此减少模型调用和 Token 消耗。代价是执行中难以根据新观测调整计划,更适合能够预先拆解的任务。
  • LLMCompiler:把计划表示成依赖图,图中的节点是工具调用,连线表示谁要等待谁。执行器并行运行没有依赖的调用,从而降低延迟。代价是需要维护依赖图、调度任务和传递结果,执行器比串行循环更复杂。

6. 常见问题​

6.1 除了 ReAct,还有哪些交互模式?​

除了前文的 Plan-and-Execute,还可以使用 Reflexion、评审与修改循环,以及多 Agent 分工。它们的区别在于谁来规划、何时检查,以及何时停止,工具仍由程序执行。ReWOO 和 LLMCompiler 这类规划变体见 ReAct 与 Plan-and-Execute。

Reflexion 在失败后生成文字反思,把失败原因和调整建议存入记忆,供下一次尝试使用。评审与修改循环则按明确标准检查产物,再把反馈交给模型修改。

需要动态拆分并分派任务时,见多 Agent 协作。

6.2 工具失败后,由模型重试还是程序重试?​

7 · 面试题卡片​

本页题目与参考回答

7.1 ReAct Agent 的核心执行流程是什么?Thought、Action、Observation 分别承担什么作用?​

考察重点

  • 解释模型怎样提出动作,以及程序怎样校验并执行动作。
  • 说明观测怎样回到上下文,并影响下一步选择。

相关内容:一次循环的执行链路、ReAct 的三个环节。

参考回答

ReAct 通过推理、动作和观测交替推进任务,让模型根据执行结果决定下一步。

模型提出工具名与参数,程序校验权限后执行,再按调用 ID 把结果写回上下文。下一轮据此继续查询、修正参数或生成回答。例如,先查询订单状态,再根据返回的仓库信息查询库存。

原始 ReAct 显式输出推理文本,结构化接口用调用字段表达动作。程序依靠工具请求执行,并用预算和业务验收决定何时停止。

7.2 如何避免 Agent 陷入无限循环?最大步数怎么确定?​

考察重点

  • 说明程序为什么需要独立于模型的停止条件。
  • 用轨迹数据确定步数上限,并解释触发上限后怎样处理。

相关内容:停止条件与预算。

参考回答

程序通过步数、总时限、Token 和费用预算限制循环,并检测没有进展的重复调用。

步数上限可参考成功轨迹:若 P95 为 12 步,可试设 20 步,再核对总成本。这只是示例,各类任务需分别统计,重试和子任务也计入预算。

相同调用持续失败且没有新信息时,停止或转人工。模型自报完成仍需验收;达到上限则报告停止原因、已完成动作和最后错误。多 Agent 还需约束委派深度与归属。

7.3 ReAct 和 Plan-and-Execute 分别适合什么场景?​

考察重点

  • 根据任务对中间结果的依赖,解释逐步决策与预先规划的取舍。
  • 说明什么情况下需要重规划,以及怎样比较两种方式的成本。

相关内容:ReAct 与 Plan-and-Execute。

参考回答

ReAct 适合下一步依赖观测的任务,Plan-and-Execute 适合子任务及依赖较清楚的任务。

  • ReAct 根据反馈逐步决定动作。 排障时查完日志才知道下一步检查什么,适合这种结构;代价是调用轮次增加,长任务容易偏离目标。
  • Plan-and-Execute 先列计划再执行。 周报可先安排订单、退款和工单查询,再汇总。执行结果偏离预期时,需要修改计划。

两者可以组合成外层计划、内层 ReAct,也可按步骤难度选择模型。成本比较应包含规划、重规划和执行的全部消耗。

7.4 模型输出最终回答后,怎样判断任务已经完成?​

考察重点:区分本轮生成结束与业务任务完成,并说明验收失败后怎样继续处理。

相关内容:完成校验。

参考回答

任务是否完成,应以外部验收证据为准。

模型停止调用工具,只说明它给出了本轮回答。编码任务还要检查测试和回归结果,抽取任务则检查字段及业务约束。未达标时,程序把具体差异交回模型,让它在剩余预算内修正。

预算耗尽时报告未完成,并保留已有结果。验证器自身故障时,应标记验收不可用,等待后续核验。

8. 参考资料​