Appearance
ReAct Agent vs DeepAgent:两种执行策略的深度对比
更新: 9/19/2026 字数: 0 字 时长: 0 分钟
开篇:为什么同样的任务,有的 Agent 又快又准,有的却"绕晕在半路"?
做 Agent 开发久了,你一定会碰到这样的分裂体验:
- 用某个 Agent 查个天气、算个汇率、调一次 API,它反应飞快,一两轮就搞定;
- 但一旦让它干"把这个 repo 重构一遍""写一份带调研的市场报告"这种大活,它就开始在决策链里打转——想一步、做一步、又忘了整体目标,几十轮下来上下文越堆越乱,最后跑偏或超时。
反过来,有的 Agent 处理复杂长任务稳如老狗,但你让它干个简单查询,它却小题大做:先列个五步计划、再拆子任务,一顿操作猛如虎,烧掉一堆 token 才回你一句本可以秒答的话。
这两种截然不同的"性格",背后正是两种主流的 Agent 执行策略:ReAct 和 DeepAgent。理解它们的差异,不是为了记概念,而是为了在实际项目里选对策略——用错了,轻则浪费 token 和延迟,重则任务根本跑不完。这篇文章,我们就从架构、实现到选型,把这两者彻底讲透。
一、ReAct:边想边做的"小步快跑"
ReAct(Reasoning + Acting,推理+行动)是目前最经典、最广泛使用的 Agent 范式。它的核心理念用一句话概括:把"思考"和"行动"交错进行,想一步、做一步、看一眼结果,再想下一步。[ReAct: Synergizing Reasoning and Acting in Language Models]
1.1 核心循环:Thought → Action → Observation
ReAct 的运行是一个紧凑的循环,前端开发者可以直接把它类比成一个 while 事件循环:
每一轮循环里,模型:
- Thought:基于当前已知信息,推理"下一步做什么"(比如"我需要先查一下这个城市的天气");
- Action:输出一个结构化的工具调用(比如
getWeather("北京")); - Observation:运行时执行工具,把结果塞回上下文;
- 回到第 1 步,直到模型认为任务完成。
1.2 关键特征
- 无全局规划:ReAct 不预先制定完整计划,而是走一步看一步。决策是"局部贪心"的——每一步只考虑眼下最该做什么;
- 状态即上下文:它的"记忆"就是不断累积的对话历史(Thought/Action/Observation 依次追加),没有独立的状态存储;
- 强反应性:能根据每一步的观察结果灵活调整方向,遇到工具报错也能当场"换个思路"。
优点:实现简单、响应快、对短任务极其高效、决策过程透明可追溯。缺点:任务一长,循环轮次暴增,历史上下文急剧膨胀,容易"中间迷失"、目标漂移,甚至陷入无效循环。
二、DeepAgent:先谋后动的"分层深耕"
DeepAgent(这里指以 Claude 的 Deep Agents、LangChain 的 deepagents 为代表的一类"深度智能体"架构)代表了另一种思路:先做完整规划,再分层、分工地深度执行。 它专为那种"步骤多、周期长、需要产出复杂交付物"的任务设计。
2.1 四大支柱
DeepAgent 通常由四个关键组件支撑,这套组合正是它能扛住复杂长任务的原因:
- ① 显式规划(Planning):开工前先调用一个"规划工具",把任务拆成一份完整的 todo 清单,并在执行中持续更新。这相当于给 Agent 装了一个始终在线的"目标锚点",防止它跑着跑着忘了要干嘛;
- ② 子代理(SubAgents):把子任务分派给专职的子 Agent 去做。每个子 Agent 有独立的上下文窗口,干完只把结论回传——这是上下文隔离,避免主 Agent 的上下文被细节撑爆;
- ③ 虚拟文件系统:用一个共享的"文件系统"(可以是内存里的键值存储)存放中间产物、草稿、笔记,而不是把所有东西都堆在对话历史里;
- ④ 详尽的系统提示:用一份很长、很结构化的 system prompt 规定角色、流程和规范。
2.2 关键特征
- 规划先行:有明确的"先想清楚全局,再动手"阶段,决策是"全局导向"的;
- 上下文隔离:通过子代理把不同子任务的上下文分开,主线程只保留高层信息,天然缓解长上下文问题;
- 持久化状态:借助文件系统和 todo 清单,状态独立于对话历史存在,可以跨很多步骤稳定追踪。
优点:扛复杂任务能力强、长任务不易跑偏、上下文管理更优雅、产出质量高。缺点:架构复杂、实现和维护成本高、简单任务下"杀鸡用牛刀",token 和延迟开销大。
三、正面对比:两种策略到底差在哪
最直观的差异:ReAct 是一条"链",DeepAgent 是一棵"树"。
ReAct 的执行轨迹是一条线性的链——想、做、看,想、做、看,一路走到底。DeepAgent 则是一棵树——先在根节点做整体规划,再向下分裂出多个子任务分支,每个分支可能又是一个小的执行过程。
从决策-行动的节奏看,两者的哲学正好相反:
| 维度 | ReAct | DeepAgent |
|---|---|---|
| 决策机制 | 局部贪心,走一步看一步 | 全局规划,先谋后动 |
| 执行结构 | 线性链式循环 | 分层树状(规划→子任务) |
| 规划阶段 | 无,隐式包含在每步思考里 | 有显式的规划工具/阶段 |
| 状态管理 | 累积在对话历史(单一上下文) | 独立文件系统 + todo 清单,上下文隔离 |
| 上下文压力 | 长任务下急剧膨胀 | 子代理隔离,主线程可控 |
| 工具调用 | 每轮一次,即时反应 | 主 Agent 调度 + 子 Agent 批量执行 |
| 适合任务 | 短、中等、需即时反应 | 长、复杂、多阶段交付物 |
| 实现复杂度 | 低 | 高 |
| 资源开销 | 短任务省,长任务反而浪费 | 短任务浪费,长任务更划算 |
四、实现方案与代码
下面用 TypeScript 实现三个核心模块,直观展示两种策略的差异点。
4.1 策略调度器:两种循环的骨架差异
先看共享的类型定义,再分别实现两种策略的执行器:
typescript
interface ToolCall { name: string; args: Record<string, unknown>; }
interface Message { role: 'system' | 'user' | 'assistant' | 'tool'; content: string; }
interface AgentStrategy {
run(task: string, tools: ToolRegistry): Promise<string>;
}
// ===== 策略一:ReAct —— 单一循环,历史累积 =====
class ReActStrategy implements AgentStrategy {
constructor(private llm: LLM, private maxSteps = 15) {}
async run(task: string, tools: ToolRegistry): Promise<string> {
const history: Message[] = [
{ role: 'system', content: 'You are a ReAct agent. Think step by step, then act.' },
{ role: 'user', content: task },
];
for (let step = 0; step < this.maxSteps; step++) {
// 每一轮:基于全部历史,让模型产出 Thought + Action
const resp = await this.llm.chat(history, { tools: tools.schemas() });
if (!resp.toolCall) {
return resp.content; // 无工具调用 → 任务完成
}
// 执行工具 → Observation 回填历史(上下文持续膨胀)
const observation = await tools.execute(resp.toolCall);
history.push({ role: 'assistant', content: JSON.stringify(resp.toolCall) });
history.push({ role: 'tool', content: JSON.stringify(observation) });
}
return '达到最大步数,任务未完成';
}
}
// ===== 策略二:DeepAgent —— 先规划,再分派子任务 =====
class DeepAgentStrategy implements AgentStrategy {
constructor(private llm: LLM, private fs: VirtualFS, private maxSteps = 8) {}
async run(task: string, tools: ToolRegistry): Promise<string> {
// 阶段 1:显式规划,生成 todo 清单
const plan = await this.planning(task);
this.fs.write('plan.json', JSON.stringify(plan));
// 阶段 2:逐个子任务分派给子代理(独立上下文)
for (const todo of plan.todos) {
const subResult = await this.runSubAgent(todo, tools);
this.fs.write(`result_${todo.id}.md`, subResult); // 产出存入文件系统
todo.done = true;
}
// 阶段 3:主 Agent 汇总所有子结果(只读高层结论,不含细节)
const results = plan.todos.map((t) => this.fs.read(`result_${t.id}.md`));
return this.llm.chat([
{ role: 'system', content: '汇总以下各部分结果,产出最终交付物。' },
{ role: 'user', content: results.join('\n---\n') },
]).then((r) => r.content);
}
private async planning(task: string) {
const resp = await this.llm.chat([
{ role: 'system', content: '把任务拆解成有序的 todo 清单,输出 JSON。' },
{ role: 'user', content: task },
]);
return JSON.parse(resp.content) as { todos: { id: string; desc: string; done: boolean }[] };
}
// 子代理:独立上下文,内部其实又跑一个小 ReAct 循环
private async runSubAgent(todo: { desc: string }, tools: ToolRegistry): Promise<string> {
const subAgent = new ReActStrategy(this.llm, 6); // 上下文隔离的关键
return subAgent.run(todo.desc, tools);
}
}差异一目了然:ReAct 是一个不断累积历史的循环;DeepAgent 是规划→分派→汇总的三段式,且子任务丢给独立的子代理执行(子代理内部可以复用 ReAct)。这也揭示了一个重要事实——DeepAgent 不是 ReAct 的替代品,而常常把 ReAct 作为它的"执行单元"包在里面。
4.2 工具调用适配器:统一两种策略的工具接口
无论哪种策略,工具调用都应统一抽象,方便切换:
typescript
type ToolFn = (args: Record<string, unknown>) => Promise<unknown>;
class ToolRegistry {
private tools = new Map<string, { fn: ToolFn; schema: object }>();
register(name: string, fn: ToolFn, schema: object) {
this.tools.set(name, { fn, schema });
}
schemas(): object[] {
return [...this.tools.values()].map((t) => t.schema);
}
async execute(call: ToolCall): Promise<unknown> {
const tool = this.tools.get(call.name);
if (!tool) return { error: `未知工具: ${call.name}` };
try {
// 带超时的工具执行,防止单点卡死
return await Promise.race([
tool.fn(call.args),
new Promise((_, rej) => setTimeout(() => rej(new Error('timeout')), 30000)),
]);
} catch (err) {
// 错误结构化返回:ReAct 会在下一轮"看到"并自我修正
return { error: String(err), recoverable: true };
}
}
}关键点:工具执行的错误不抛出,而是结构化返回。这对 ReAct 尤其重要——它能在下一轮 Observation 里"看到"错误,当场调整策略;对 DeepAgent,则由子代理消化错误,不污染主线程。
4.3 状态追踪管理器:单一上下文 vs 隔离状态
这是两种策略最本质的分野——如何管理"记忆":
typescript
// ReAct:状态 = 单一的线性历史
class ReActState {
private history: Message[] = [];
append(msg: Message) { this.history.push(msg); }
getContext(): Message[] { return this.history; } // 全量传给模型,越来越长
}
// DeepAgent:状态 = todo 清单 + 虚拟文件系统(隔离 + 持久)
class DeepAgentState {
private todos: { id: string; desc: string; done: boolean }[] = [];
private fs = new Map<string, string>();
setPlan(todos: typeof this.todos) { this.todos = todos; }
updateTodo(id: string, done: boolean) {
const t = this.todos.find((x) => x.id === id);
if (t) t.done = done;
}
// 主线程只需高层视图,不含子任务的执行细节
getHighLevelView(): string {
const progress = this.todos.filter((t) => t.done).length;
return `进度 ${progress}/${this.todos.length}:\n` +
this.todos.map((t) => `[${t.done ? 'x' : ' '}] ${t.desc}`).join('\n');
}
writeFile(path: string, content: string) { this.fs.set(path, content); }
readFile(path: string): string { return this.fs.get(path) ?? ''; }
}看这里的对比:ReAct 的 getContext() 每次返回全量历史,任务越长它越臃肿;DeepAgent 的 getHighLevelView() 只给主线程一个精简的进度视图,具体细节都沉淀在文件系统里按需读取。这就是 DeepAgent 能扛住长任务的底层秘密——用状态外置和上下文隔离,对抗上下文膨胀。
五、拓展:适用场景、混合策略与趋势
5.1 选型:什么任务用什么策略
| 任务类型 | 推荐策略 | 理由 |
|---|---|---|
| 单次查询、简单问答、一两次工具调用 | ReAct | 快、省、够用,别过度设计 |
| 中等任务(3~10 步,需即时反应) | ReAct | 灵活反应,实现简单 |
| 复杂长任务(代码库重构、深度调研报告) | DeepAgent | 规划锚定目标、上下文隔离防迷失 |
| 需要多个专业角色协作 | DeepAgent | 子代理天然对应角色分工 |
| 结果需高可靠、可追溯交付物 | DeepAgent | todo 清单 + 文件系统利于追踪审计 |
5.2 混合策略:两者不是对立,而是嵌套
前面代码已经埋了伏笔:最实用的架构往往是"DeepAgent 做骨架,ReAct 做血肉"——用 DeepAgent 的规划层锚定全局目标、隔离上下文,每个子任务内部则用轻量 ReAct 循环灵活执行。这样既有全局把控,又保留局部灵活性。
更进一步是自适应策略调度:先用一个轻量分类判断任务复杂度,简单任务直接走 ReAct,复杂任务才启用 DeepAgent 的完整规划-分派流程,把资源花在刀刃上。
typescript
async function adaptiveDispatch(task: string, llm: LLM): Promise<AgentStrategy> {
const complexity = await llm.chat([
{ role: 'system', content: '判断任务复杂度,只回 simple 或 complex' },
{ role: 'user', content: task },
]);
return complexity.content.includes('complex')
? new DeepAgentStrategy(llm, new VirtualFS())
: new ReActStrategy(llm);
}5.3 前沿趋势
- 自适应执行:Agent 根据任务实时在"快反应"和"深规划"之间动态切换,而非固定一种;
- 多 Agent 协作:DeepAgent 的子代理思想正在向更松耦合的多 Agent 系统演进,不同 Agent 有独立记忆和角色,通过消息协作;
- 上下文工程精细化:无论哪种策略,如何把有限的上下文"喂对",都成了比选策略本身更受关注的工程课题。
六、性能对比与选型 Checklist
性能特征速览
| 指标 | ReAct | DeepAgent |
|---|---|---|
| 短任务延迟 | 低 ✓ | 高(规划开销) |
| 长任务成功率 | 中(易跑偏) | 高 ✓ |
| Token 消耗(短任务) | 少 ✓ | 多 |
| Token 消耗(长任务) | 反而多(历史膨胀) | 相对可控 ✓ |
| 开发/维护成本 | 低 ✓ | 高 |
| 可观测/可调试性 | 高(链路线性) ✓ | 中(树状较复杂) |
选型评估 Checklist
- [ ] 任务步骤数:预计 <10 步 → 倾向 ReAct;>15 步或多阶段 → 倾向 DeepAgent;
- [ ] 是否需要全局规划:任务成败依赖"先想清楚整体" → DeepAgent;
- [ ] 上下文压力:是否会处理大量长文档/多来源资料 → DeepAgent 的隔离更稳;
- [ ] 实时性要求:要求秒级响应的交互场景 → ReAct;
- [ ] 交付物复杂度:需要结构化、可追溯的复杂产出 → DeepAgent;
- [ ] 团队与成本:团队小、要快速上线 → 先上 ReAct,复杂了再演进。
性能测试指南
- 分档测试:分别用"简单/中等/复杂"三档任务集测两种策略,别只用一种难度;
- 核心指标:任务成功率、平均步数/轮次、总 token 消耗、端到端延迟、跑偏率;
- 对照实验:同一任务集跑两种策略,横向对比,别只看绝对值。
常见问题排查
| 现象 | 排查方向 |
|---|---|
| ReAct 陷入无限循环 | 设 maxSteps 上限;检查工具错误是否被模型误读 |
| ReAct 长任务跑偏/失忆 | 上下文膨胀导致中间迷失 → 考虑切换 DeepAgent 或加摘要 |
| DeepAgent 简单任务太慢/太贵 | 规划阶段被滥用 → 加自适应调度,简单任务直接走 ReAct |
| DeepAgent 子任务结果对不上 | 检查 todo 拆解粒度;子代理产出格式是否统一 |
| 两者都频繁工具报错 | 工具适配器是否做了超时和结构化错误返回 |
一句话总结:ReAct 是"边想边做的短跑选手",DeepAgent 是"先谋后动的长跑教练"。 ReAct 用一条线性的"思考-行动-观察"循环,以简单、快速、灵活见长,是中短任务的首选;DeepAgent 用"显式规划 + 子代理 + 文件系统"的分层架构,靠上下文隔离和全局锚定扛住复杂长任务。二者并非对立——最成熟的工程实践,往往是用 DeepAgent 搭骨架、用 ReAct 填血肉,再加一层自适应调度,让每个任务都用上最划算的执行方式。选型的核心判断只有一个:你的任务,到底是"跑得快"重要,还是"想得全"重要。