为什么 Claude Code 不用 ReAct

问题从哪里来

ReAct(Reasoning + Acting)几乎是 AI Agent 入门课的标配。Thought → Action → Observation 三步循环,模型先输出一段」思考」,再选工具,再拿到结果,周而复始。

很多 Agent 框架 ——LangChain、AutoGPT、CrewAI—— 都直接或间接地采用了这个模式。看起来天经地义。

所以当我读 Claude Code 的源码时,第一反应是困惑:它没有 ReAct。没有显式的 Thought 步骤。主循环就是一个 while(true),模型直接返回 tool_useend_turn

这不是偷懒。这是刻意设计的。

真正卡住的是哪一步

要理解 Claude Code 为什么不用 ReAct,先得理解 ReAct 到底有什么问题。不是 ReAct「不好」,而是它在面对强模型时会暴露出三个实实在在的缺陷。

第一个问题:Token 浪费。 ReAct 要求模型每一轮都输出一段 Thought 文本。对于编程 Agent 来说,一次任务可能循环 50 轮,每轮都写一句」我打算先读取这个文件然后分析它的结构……」,加起来就是好几万 Token。这些 Token 不是免费的 —— 它们要作为上下文的一部分发给 API,占用宝贵的窗口空间。

第二个问题:应用层代码复杂。 实现 ReAct 意味着你需要解析模型的输出,区分」哪部分是 Thought、哪部分是 Action」,然后提取 Action 调用工具,再把 Observation 拼回去。这个解析过程脆弱 —— 模型输出的格式不一定标准,一旦解析失败整个循环就崩了。

第三个问题是最关键的:ReAct 是为弱模型设计的。 当模型的推理能力不够强时,用显式的 Thought 来」强迫」它一步步思考是有意义的。但 Claude Opus 级别的模型,推理能力已经足够强了,它完全可以在内部完成推理,不需要在输出里显式地写出来。

这三个问题加起来,揭示了一个更深层的事实:ReAct 的设计哲学是」帮模型思考」,而 Claude Code 面对的是不需要帮忙的模型。

我最后怎么处理

Claude Code 的做法出奇简单,核心就是一个 while(true) 循环:

while (true) {
response = claude.messages.create(messages)

if (response.stop_reason === "end_turn") {
break // 模型说完了,跳出循环
}

if (response.stop_reason === "tool_use") {
result = executeTool(response.tool_call)
messages.push({ role: "tool", content: result })
// 继续循环
}
}

没有 Thought 步骤。模型在内部通过 Extended Thinking 完成推理(这是 Claude 的一个能力,模型在生成回复前会在内部进行一段不可见的深度推理,不占用上下文空间),然后直接返回两种结果之一:

  • tool_use:」我要用某个工具」,应用层执行工具,结果拼入消息列表,继续循环
  • end_turn:」我说完了」,跳出循环,返回最终结果

应用层不需要解析任何文本格式。tool_useend_turn 是 API 层面的原语,语义清晰,不需要正则表达式、不需要格式检查。

两者的区别用一个表格能看得更清楚:

维度 ReAct Tool-Use Loop
推理方式 显式 Thought 文本 模型内部 Extended Thinking
工具调用 解析文本提取 Action API 原生 tool_use
终止判断 检测「Final Answer」等标记 API 原生 end_turn
Token 开销 每轮输出 Thought 无额外开销
编排复杂度 高(需要解析 Thought / Action) 低(只需要 if / else)
适合场景 弱模型 + 简单工具 强模型 + 复杂工具集

这个设计的核心哲学就一句话:信任模型的推理能力,把应用层框架做得尽可能简单。

它不试图教模型怎么想,不试图在应用层做推理,不试图用复杂的编排逻辑弥补模型的不足。它让推理留在模型内部,让执行留在工具层,让编排只做最简单的事 —— 调 API、执行工具、再调 API。

这种」大道至简」的设计,反而最高效。

这件事留下的经验

读完 Claude Code 的 Tool-Use Loop,我反思了之前做过的一些 Agent 项目。一个很典型的错误是:在应用层做了太多本该模型做的事。

比如自己写一套推理流程、自己定义状态机、自己解析模型输出 —— 这些本质上都是在」帮模型思考」。但对强模型来说,最好的帮助就是别帮倒忙。给它清晰的工具描述、明确的终止信号,剩下的交给它自己。

另一个收获是:架构的简洁性依赖于底层的可靠性。 Tool-Use Loop 之所以能用 while(true) + if/else 这么简单的结构,前提是 Claude API 稳定地提供了 tool_useend_turn 这两种 response type。如果 API 层面没有这些原语,应用层就会被迫自己解析 —— 然后就回到了 ReAct 的老路。

所以选 Agent 框架的时候,一个挺好的判断标准是:看它的核心循环有多复杂。如果主循环超过 50 行,很可能在应用层做了模型该做的事。

这也延伸到另一个话题 ——Claude Code 的 Plan Mode。Plan Mode 本质上也只是一个工具调用(EnterPlanMode / ExitPlanMode),引擎层完全不需要做特殊处理。同样的 while(true) 循环,同样的 if / else 判断。「工具即能力」—— 新增能力只需要新增工具,引擎永远不变。

简洁不是简陋,是把复杂度放在正确的位置。