AI Agent 的工程闭环 封面
AI 系列

AI Agent 的工程闭环:从 ReAct 到规划-执行-反思

读完这篇,你该能回答三个问题:一个 Agent 该不该上、它通常会死在哪一步、以及怎么用数字判断它到底好不好用——而不是停留在"看起来会自己干活"。

Agent 核心概念示意图
Agent 的核心组成:模型、记忆、工具与规划器如何协作
Agent 工作流程示意图
一轮 ReAct 循环:推理→行动→观察是怎样滚动的
Agent 实践检查示意图
上线前检查清单:边界、回退与可观测性

为什么 Agent 不是"会聊天的模型"

普通对话模型一次性生成回答,本质是"给一段输入,吐一段输出"。Agent 的区别在于它把"想"和"做"交错起来:模型先决定下一步要不要调用某个工具、调用什么、传什么参数,工具返回真实结果后再继续推理,如此循环直到任务完成。这种"推理+行动"交错的模式叫 ReAct(Reason + Act)

它解决的是纯思维链(Chain-of-Thought,让模型在内部一步步想)的根本缺陷:模型想得再多,也接触不到外部世界的真实状态。要查今天的订单、改一条数据库记录、发一封邮件,光靠"想"是做不到的,必须落到真实动作上。ReAct 用"行动结果"给推理提供了反馈锚点,这也是它能处理多步任务的原因。

为什么重要:ReAct 的价值不在"更聪明",而在"可纠错"。每一步 observation 都是一次现实校验——模型上一个判断对不对,工具返回立刻能说明,而不是等到全程结束才发现南辕北辙。

但 ReAct 有明显局限。它是串行的:上一步没返回,下一步想不了,长任务里 token 消耗与延迟线性累积;推理链会被工具结果打断,模型容易"忘记"最初的子目标;更麻烦的是错误会沿循环累积——一旦某步工具调用传错参数,后续每一步都建立在错误事实上,越走越偏。这也是为什么单靠 ReAct 撑不起严肃业务,需要下面这套更完整的循环。

规划—执行—反思循环

工程上更稳的做法是把 Agent 拆成三段职责:规划(Plan)把模糊目标拆成可验证的子任务;执行(Execute)调用工具逐个完成;反思(Reflect)在关键节点回头看是否偏离、是否该回溯重做。

任务分解的几种策略

最朴素的是自顶向下分解:让模型先列出步骤清单,再逐条执行。但平铺清单不表达依赖——第 3 步可能要等第 1 步的输出。更可靠的是依赖图(DAG):明确"哪些步骤可并行、哪些必须先做"。对探索型任务,可以用树状搜索(如 Tree-of-Thought),一条路走不通就剪枝换分支。2025 年后主流 Agent 框架(如 LangGraph、AutoGen、OpenAI Agents SDK)普遍支持把"规划"做成可暂停、可人工介入的显式节点,而非塞进一段 prompt。

踩坑提醒:不要让模型一次性规划 20 步然后闷头执行。规划越细,越容易在第三步就过时。务实做法是"规划一小段、执行、再规划",把重规划(re-planning)当成循环的一部分,而不是开头一次性动作。

反思不是可有可无

反思可以是同模型"你现在离目标还差多少",也可以是独立的小模型做校验器。它的关键作用是给出"停止/回溯/求助"的信号。没有反思的 Agent 容易陷入"看起来在努力、其实在打转"的状态,直到步数耗尽。

工具调用的执行闭环

Agent 真正的"执行"发生在工具调用这一环。一个健壮的闭环必须显式经过这几步,而不是把模型吐出的参数直接拿去跑:

AGENT 执行闭环
模型推理
(Thought)
工具调用请求
(Action)
参数校验
+权限判断
沙箱执行
(Observation)
结果回灌上下文
每一步都可能失败,失败要在环内被捕获并返回可理解的 observation,而不是让模型凭空编造

下面是一段最小可参考的 ReAct 循环实现,重点是显式兜底:到达步数上限必须返回明确结论,绝不允许静默失败。

def run_agent(task, max_steps=12):
    history = [{"role": "user", "content": task}]
    for step in range(max_steps):
        resp = llm.chat(history, tools=TOOL_SCHEMAS)
        # 模型不再请求工具调用 → 视为任务完成,返回最终答案
        if resp.tool_calls is None:
            return resp.content
        for call in resp.tool_calls:
            result = dispatch(call)        # 真实执行:校验→权限→沙箱→运行
            history.append({
                "role": "tool",
                "name": call.name,
                "content": result,          # observation 喂回上下文,形成闭环
            })
    return "未能在步数上限内完成"           # 必须显式兜底

记忆机制:短期、长期与压缩

Agent 要"记得"东西,但上下文窗口(context window)又贵又有限,所以记忆要分层:

# 长期记忆的写入与检索(伪代码)
vec_db.upsert(embed("用户偏好:报告用中文、不要表格"), meta={"user": uid})
hits = vec_db.search(embed("他上次怎么要求报告的"), top_k=3)
history += [{"role": "system", "content": "相关记忆:" + fmt(hits)}]
工程取舍:向量检索不是万能的。"精确回忆"(比如"把订单号 8842 的状态改掉")不该走向量库,应该用关键字/结构化查询直接定位——检索错了比不检索更糟。把"精确事实"和"模糊知识"分开存,是落地时的关键区分。

典型失败模式与对策

把 Agent 跑"通"很容易,跑"稳"难在这几类故障。下表是实战中最常遇见的:

失败模式表现对策
死循环反复调用同一工具、步骤不收敛设最大步数上限 + 检测重复 action 指纹 + 强制反思
幻觉工具调用调用了不存在的工具或捏造参数严格按 schema 校验,失败即返回错误而非放行
上下文溢出工具结果太长撑爆窗口结果截断/摘要、长期记忆外置、分页检索
错误传播某步失败,后续建立在其错误事实上每步校验 observation,失败即回溯而非硬撑
踩坑提醒:"幻觉工具调用"最危险。模型有时会信心十足地传一个不存在的参数名,如果你的 dispatch 直接 getattr 调用,就可能执行到意外函数。务必用白名单 + schema 双重校验,再放行执行。

怎么评估一个 Agent

不能只看"答得像不像"。要把 Agent 当成系统来度量,核心指标有四组:

指标含义为什么看它
任务成功率在 Benchmark / 真实样本上跑通的比例最根本的"能不能用"
平均步数完成一个任务平均调用几轮工具步数直接决定延迟与成本
Token / 成本单次任务消耗多少 token决定能否规模化、是否赔本
人工干预率需要人接手多少次反映真实自主性,越低越接近"可依赖"

2025–2026 年一个明显趋势是:用推理模型(如带长链思考的 o 系列、DeepSeek-R1 类)做规划,用小模型做单步工具执行,在"成功率"和"成本"之间找平衡。评估时也要分开测——规划模型的失误和工具执行层的失误要能归因,否则优化无从下手。

Agent 落地的关键,是把"会做事"变成"可度量、可兜底、可观察"的工程系统:用规划-执行-反思替代裸 ReAct,用分层记忆控制上下文,用显式闭环和失败对策把不确定性关进笼子。先把边界和回退设清楚,再谈自主,你的 Agent 才会从一次演示变成日常能依赖的流程。