ReAct 与 Plan-Execute:两种 Agent 范式的实战对比

前言

在前面写了第一个 Agent Loop(think → act → observe),但它有个局限:只能做"一次工具调用 + 一次回复" 。这次我把 Agent 升级成两种真正的"范式"------ReAct 和 Plan-Execute,并且接入了 MCP(Model Context Protocol)让工具调用走标准协议。

这篇文章讲三个东西:两种范式怎么选、MCP 怎么接入、以及模型选型踩的一个大坑。


一、ReAct:边想边做

ReAct 让 LLM 交替输出「思考」和「行动」:

yaml 复制代码
Thought: 用户需要电子摇滚,BPM 130-150 → 调用 search_catalog
Action: search_catalog({style: 电子摇滚, bpm_min: 130, bpm_max: 150, budget: 3000})
Observation: 返回甜蜜蜜 (BPM 140) 和 Neon Pulse (BPM 135)
Thought: 两首都符合 → 回答用户
Action: Final Answer

和 Agent Loop 的区别 :Agent Loop 是"调用一次工具就回答",ReAct 是"可能连续调多个工具直到攒够信息"。所以 MAX_STEPS=6,比 Agent Loop 的 MAX_TURNS=3 大------给模型探索空间,但 6 轮封顶防死循环。

前端类比:ReAct ≈ 事件循环里每个 tick 决策------边跑边改,遇到新信息现场调整。

实测发现 :7b 模型会重复调用同一个工具(拿到数据后又调了一次同样的参数)。MAX_STEPS 正好兜住这种行为------浪费一轮但不死循环。这是 ReAct 在小模型上的常见失败模式。


二、Plan-Execute:先计划再执行

Plan-Execute 分两步:第一步让 LLM 生成完整计划,第二步逐步执行

bash 复制代码
Step 1: {"plan": ["确定 BPM 范围", "检索曲库", "筛选适合健身房的", "查询授权价"]}
Step 2-5: 逐条执行,记录每步的 done/result

和 ReAct 的区别

  • ReAct:路径不确定,边走边看------适合探索型任务("帮我查一下...")
  • Plan-Execute:步骤明确,先拆解再执行------适合复杂规划("写一个 RAG 架构文档")

前端类比:ReAct ≈ 每个 tick 决策的事件循环;Plan-Execute ≈ 先写 TODO list 再执行------任务拆解 + 逐个完成。

踩的坑(这个必须记录) :qwen2.5:7b 生成计划时输出了非法 JSON ------数组元素写成 ["步骤1": "内容"](带键值对),json.loads 直接抛错。根因是模型对 prompt 示例 ["步骤1", "步骤2"] 过度模仿,把序号当成了对象键。解法是解析层加修复:正则剥掉 "步骤N": 前缀。

教训:永远别指望小模型输出严格 JSON,解析层必须容错。这是 W3 JSON mode 的实战补充------schema 校验是"事后检查",修复层是"事前补救"。


三、MCP 接入:让工具调用走标准协议

项目要求接入至少一个 MCP server。我把 W4 的 search_catalog 注册成了 MCP 工具,然后写了适配器让 ReActAgent 通过 MCP 调用它。

MCP server@tool() 装饰器注册):

python 复制代码
server = MCPServer("music-catalog-server")

@server.tool(name="search_catalog", description="根据风格/BPM/预算检索曲库")
async def search_catalog(style: str, bpm_min: int, bpm_max: int, budget: int):
    ...

接入 Agent 的关键设计------适配器 :ReActAgent 只认 execute(name, arguments) → {"success", "result"/"error"} 这个接口。我写了个 MCPToolAdapter 实现同样的接口,内部走 MCP 协议:

ini 复制代码
# W4 手写:executor.execute(name, args) → 直接调本地函数
# MCP:adapter.execute(name, args) → JSON-RPC 到 server → 返回

# Agent 一行不改,只要注入不同的 executor:
agent = ReActAgent(executor=ToolExecutor(registry))   # W4 方式
agent = ReActAgent(executor=MCPToolAdapter(client))   # MCP 方式

前端类比 :手写 executor ≈ 直接 import 函数调用;MCP 适配器 ≈ 换成 HTTP API 调用------协议变了,接口不变。这就是适配器模式:依赖接口而非实现。

踩的坑(这个最有价值) :一开始适配器的 list_tools() 只返回了工具名和描述,没返回参数 schema 。结果模型完全不知道工具要什么参数,瞎猜了 4 次(genre/bpm_range/budget_per_song)全部失败。把 input_schema 加进返回后,模型一次就猜对了style/bpm_min/bpm_max/budget)。

教训:schema 必须传给 LLM------这和 W4 的"JSON Schema 给模型的菜单"是同一个设计:不给菜单,模型只能瞎点菜。

MCP SDK 新版 API 的坑 :旧版 fastmcp / Client(read, write) 全变了。新版是 MCPServer + Client(in-memory/URL 高层封装)+ ClientSession(stdio 流)。ClientSession 必须显式 await client.initialize() ,否则 tools/list 报 Invalid request parameters------这是我精读时没覆盖到的细节,实测才发现。


四、模型选型:任务形态决定模型

默认模型换了几次,踩了个大坑:

模型 耗时 结果
qwen2.5:7b 5.8s ✅ 快,但 JSON 不稳、ReAct 重复调工具
deepseek-r1:8b >113s ❌ 慢到不可接受
qwen2.5:14b 16.6s ✅ JSON 稳定,定为默认

为什么 r1:8b 不可用 :ReAct 的"思考"已经写死在 prompt 里 (让模型输出 thought 字段),它不需要 R1 那样的隐式思维链。R1 把大量 token 花在 CoT 上,拖慢速度还干扰 JSON 输出------推理模型的强项在这里用不上,短板(慢 + JSON 不稳)却正中要害

选型原则:模型选型要匹配任务形态。推理模型(R1)适合深度分析场景;执行模型(qwen)适合"结构化输出 + 工具调用"场景。ReAct 是执行型任务,不是推理型任务。


五、多轮记忆

之前每次 run() 都从零开始,用户说"对,就是那首"时"那首"没有上下文。前面补全两个记忆:

  • 短期记忆(ShortTermMemory):会话内的消息历史,放 messages 里------≈ React 组件内 state
  • 长期记忆(LongTermMemory):跨会话的用户事实,存 JSON 文件,启动时加载------≈ localStorage

长期记忆的持久化有个细节:__init__ 必须先读文件再初始化 ,否则重开实例数据全丢。测试 test_persistence 专门验证这个。


结论

ReAct 是"边想边做"的探索者,Plan-Execute 是"先计划再执行"的规划师------任务形态决定范式选择。

这周最大的三个收获:schema 必须传给 LLM (不然它只能瞎猜)、小模型 JSON 必须容错 (修复层不是可选项)、模型选型匹配任务形态(推理模型不是万能的)。

从 手写 Agent Loop,到 ReAct + Plan-Execute + MCP------ Agent 终于有了"范式"和"协议",不再是单次调用。

相关推荐
刘发财37 分钟前
前端2秒生成500页矢量PDF,rust真的强到没朋友
前端·javascript·rust
郑州光合科技余经理1 小时前
国际版外卖系统:税率字段怎么和订单主流程解耦
android·java·开发语言·前端·后端·php·ai编程
梦想平凡2 小时前
百游棋牌源代码开发搭建教程(十):隔离部署、备份恢复与双端验收
java·前端·javascript·数据库·源代码管理
yume_sibai3 小时前
06-Rust Web 开发实战(Axum 框架 + 数据库 + JWT 认证 + 中间件 + 部署)
前端·数据库·rust
冬奇Lab4 小时前
DeepSeek Harness 系列(07):能力 Seam——换一行配置,能力全换
人工智能·agent·deepseek
计算机魔术师4 小时前
特朗普上台打给黄仁勋:AI末日论是骗局,我们绝不让它发生
前端
troy1284 小时前
Python 基础语法(八):Web 后端开发、数据分析与可视化、网络爬虫、人工智能 / 大模型应用
前端·python·数据分析
计算机魔术师4 小时前
CEO说要慢下来,黑客说别做梦了——同一篇论文,两种命运
前端
kyriewen5 小时前
我花3天抓了一个幽灵bug,凶手藏在第4层
前端·javascript·程序员
IT_陈寒6 小时前
Java里用Stream.parallel()翻车实录,这性能还不如单线程
前端·人工智能·后端