Agent 构建模式
LLM GEN
为什么需要特殊的"架构"?
要回答这个问题,首先要理解一个核心矛盾:
LLM 是"一次对话"式的——你问一句,它答一句,上下文用完就结束。
现实任务是多步骤的——需要查资料、算数据、写文件、反复修正。
Agent 架构本质上解决的是:如何让 LLM 突破"单次对话"的局限,在一个循环中自主完成复杂任务。
底层涉及几个关键能力:
- 工具调用(Tool Use)——模型输出结构化指令来调用外部函数
- 多步推理(Multi-step Reasoning)——不靠一次生成,靠"想一步、做一步、再看结果"的迭代
- 状态管理(State Management)——在多次调用间维护上下文、记忆中间结果
- 自我修正(Self-correction)——发现错误后调整策略而非直接放弃
不同架构的区别,归根结底是在以上四个维度上做了不同的权衡。
第一种:ReAct(Reasoning + Acting)
底层原理
ReAct 是 2022 年由谷歌提出的论文方法,核心思想极其简单:让模型在每一步同时输出"思考"和"动作",而不是直接输出答案。
循环体(每次调用 LLM):
├── Thought: 模型当前的想法(自然语言推理)
├── Action: 决定调用的工具和参数
└── Observation: 工具返回的结果
↓
把 thought + action + observation 追加到上下文
进入下一次循环
论文中的经典例子是问"除了 Apple Remote,还有什么设备能控制它?"
| 模式 | 结果 |
|---|---|
| 直接回答(Standard) | 幻觉,回答 iPod ❌ |
| 只推理(Reason Only) | 思考很多但没法验证 ❌ |
| 只调用工具(Act Only) | 搜了多次但不会关联结果 ❌ |
| ReAct | 搜索 Apple Remote → 发现是 Front Row 用的 → 搜索 Front Row → 找到 Keyboard ✅ |
ReAct 的三种实现形态
graph LR
subgraph "1. 原始 ReAct(文本解析)"
A1[LLM 输出文本] --> B1[暴力 split 关键词] --> C1[提取 thought/action]
end
subgraph "2. ReAct + Function Calling"
A2[LLM 输出 JSON] --> B2[原生解析 tool_calls] --> C2[稳定可靠]
end
subgraph "3. ReAct + MCP Tools"
A3[MCP 协议] --> B3[动态发现工具] --> C3[标准化工具生态]
end- 原始 ReAct(2022):模型输出
Thought: ... Action: Search[xxx]这样的文本,靠正则或字符串 split 解析。不稳定,对弱模型几乎不可用。 - ReAct + Function Calling(2023+):OpenAI 发布 function calling 后,模型原生支持输出结构化 JSON
tool_calls,解析成功率接近 100%。这是如今最普遍的形式——你实际上每天都在用,只不过没人叫它 ReAct 了。 - ReAct + MCP(2025+):通过 MCP 协议,工具不再硬编码在代码里,而是由 MCP Server 动态提供,模型在运行时发现可用工具。
核心特点
| 维度 | 说明 |
|---|---|
| 工具 | 必须提前全部定义好,无法动态生成 |
| 组合能力 | 弱——每次只能调一个工具,不能嵌套 |
| 稳定性 | 高(搭配 Function Calling)——JSON 结构化输出保证了解析 |
| 调用次数 | O(n)——每个子任务都需要一次完整的 LLM 调用 |
| 适合场景 | 调用外部 API(搜索、查 DB、发邮件) |
优势
- 简单可靠——思路直白,实现容易,几乎所有 LLM 框架都支持
- 工具调用原生稳定——Function Calling 保证了工具调用的成功率
- 可审计——每一步的 thought 都可以看到,容易 debug
劣势
- 慢——每一步都要调一次 LLM,复杂任务可能需要几十次调用
- 工具集是静态的——只能调用预定义的工具,无法在运行时发现新能力
- 无法组合——没法写出
multiply(subtract(15,9), 8)这样的嵌套调用 - 表达式能力有限——没有循环、条件分支等编程结构
第二种:CodeAct(Coding + Acting)
底层原理
CodeAct 的核心洞察极其锐利:大模型已经会写代码了,为什么不直接让它写代码?
循环体(每次调用 LLM):
├── Thought: 模型思考需要做什么
├── Code: 模型直接输出代码片段(Python / JavaScript)
└── Execution Result: 沙箱运行代码后的输出或报错
↓
把结果追加到上下文,进入下一次循环
graph LR
A[LLM] -->|输出代码| B[沙箱环境]
B -->|运行结果 / 报错| C{是否还有代码?}
C -->|是| A
C -->|否| D[输出最终答案]和 ReAct 的本质区别
| 维度 | ReAct | CodeAct |
|---|---|---|
| 工具来源 | 预定义的有限工具集 | 语言标准库 + 任意第三方库 |
| 组合能力 | 不能嵌套 | 可以任意嵌套、循环、条件 |
| 一次调用的信息量 | 一个工具结果 | 一次完整计算的输出 |
| 对模型要求 | 较低 | 较高——需要模型能写出正确代码 |
| 安全风险 | 低——工具是你定义的 | 高——代码可能做危险操作,需要沙箱 |
为什么 CodeAct 效率更高?
回到之前的复杂表达式:(15 - 9) × 8 + 48 ÷ 6
ReAct 需要 5 次 LLM 调用(减法 → 乘法 → 除法 → 加法 → 总结答案)。
CodeAct 只需要 2 次甚至 1 次:
// 第一次调用:模型直接输出完整代码
const result = (15 - 9) * 8 + 48 / 6;
console.log(result); // → 65
两者的时间复杂度差距随着任务复杂度增长而放大。CodeAct 论文中的数据表明,在复杂任务上 CodeAct 的调用次数减少约 60-80%,任务成功率提升 20-40%。
CodeAct 的关键设计问题
沙箱安全
模型生成的代码必须在一个隔离环境中运行,不能访问文件系统、网络等。最简单的沙箱使用 eval() / exec()(仅限演示),生产环境需使用:
- Docker 容器
- Pyodide(浏览器端 Python)
- Deno(安全性内建)
- gVisor / Firecracker(Google 级别隔离)
错误处理
CodeAct 有一个 ReAct 不具备的优势:错误信息本身就是有效的反馈。
用户:画一个正弦波
模型第一次输出:
```python
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
plt.plot(x, np.sin(x))
plt.show()
沙箱报错:plt.show() 在非交互环境中不工作
模型第二次输出:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
plt.plot(x, np.sin(x))
plt.savefig('/tmp/sine_wave.png')
print("图片已保存")
#### 对模型能力的要求
CodeAct 要求模型具备较强的代码生成和调试能力。2022 年(ReAct 提出时)模型写代码能力不够,CodeAct 不现实。2024-2026 年(GPT-4、Claude 3/4 时代)模型写代码能力已经足够,CodeAct 成为可行且强大的选择。
### 优势
- **效率极高**——用代码代替多次工具调用,大幅减少 LLM 调用次数
- **表达力无限**——可以写循环、条件、递归,调用任何标准库
- **组合自然**——嵌套调用是代码基本功,不需要额外设计
- **自修正**——报错本身就是反馈,模型可以自己 debug
### 劣势
- **对模型要求高**——代码能力弱的模型不适用
- **安全风险大**——需要可靠的沙箱隔离
- **不适合纯 API 调用**——如果任务是"搜索网页、查数据库",写代码反而绕弯路
- **结果解析有额外开销**——需要从代码输出中提取有用信息
---
## 第三种:Plan-and-Execute(先规划再执行)
### 底层原理
ReAct 和 CodeAct 都是"边想边做"。Plan-and-Execute 反过来:**先制定完整计划,再按计划执行。**
这其实模拟了人类处理复杂任务的方式——先想清楚怎么做,再动手。
阶段一:规划
LLM 接收任务 → 输出完整步骤列表(每一步的依赖关系)
阶段二:执行
按计划逐步执行(可以结合 ReAct 或 CodeAct)
如果某一步失败 → 回到规划阶段重新调整
### 示例
用户:分析这篇论文的主要内容,做一个摘要,再用中文翻译
规划阶段输出:
- 读取论文 PDF(工具:read_file)
- 提取关键段落(工具:llm_extract)
- 生成英文摘要(工具:llm_summarize)
- 翻译为中文(工具:llm_translate)
- 保存到文件(工具:write_file)
执行阶段:
[第1步] read_file → 成功
[第2步] llm_extract → 成功
[第3步] llm_summarize → 成功
[第4步] llm_translate → 成功
[第5步] write_file → 成功 → 完成
```mermaid
graph TD
A[用户输入] --> B[规划器: 制定步骤]
B --> C[执行步骤 1]
C --> D{成功?}
D -->|是| E[执行步骤 2]
D -->|否| B
E --> F{全部完成?}
F -->|否| G[执行下一步]
G --> D
F -->|是| H[输出最终结果]
Plan-and-Execute vs ReAct
| ReAct | Plan-and-Execute | |
|---|---|---|
| 规划时机 | 边走边看 | 先想好再走 |
| 灵活性 | 高——随时可以调整 | 中——计划可能过时 |
| 可预测性 | 低——不知道会走多少步 | 高——一开始就知道总步骤 |
| 适合任务 | 探索性、不确定性高的任务 | 流程明确、确定性高的任务 |
优势
- 可预测——用户一开始就能看到全部计划,心里有数
- 可打断——可以审查计划后调整再执行
- 适合长流程——几十步的任务也能清晰管理
劣势
- 计划可能过时——复杂任务中,执行到中间发现计划不合理,需要重新规划
- 额外开销——规划本身就是一次 LLM 调用
- 对规划能力要求高——模型需要能预见所有步骤
第四种:Multi-Agent(多智能体协作)
底层原理
核心思想:用一个模型解决所有事情是低效的,让多个专业模型各司其职。
每个 Agent 有自己的:
- 角色定义(system prompt)——"你是一个资深 Python 工程师"
- 工具集——研究员 Agent 有搜索工具,程序员 Agent 有代码沙箱
- 记忆——独立或共享的上下文
┌─────────────────┐
│ Orchestrator │
│ (协调者 Agent) │
└────────┬────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 研究员 │ │ 程序员 │ │ 审查员 │
│ Agent │ │ Agent │ │ Agent │
└──────────┘ └──────────┘ └──────────┘
经典架构类型
-
Supervisor / Worker(主管 + 工人)
- 一个主管 Agent 负责任务拆解和分配
- 多个 Worker Agent 各司其职
- 结果汇总给主管做最终输出
-
Debate(辩论式)
- 多个 Agent 就同一个问题各自输出
- 互相审查、质疑、反驳
- 最终达成共识或由裁决者决定
-
Pipeline(流水线式)
- Agent A 的输出 → Agent B 的输入 → Agent C 的输入
- 适用于有明确前后依赖的任务
为什么 Multi-Agent 现在很火?
核心原因:一个 prompt 不可能同时擅长所有事。
"你是一个擅长写代码、做研究、写文档、测试、部署的全栈工程师。"
→ 实际上模型会在多个角色间摇摆,哪个都做不精。
Agent A: "你是一个严谨的代码审查员,只关注代码质量和安全性。"
Agent B: "你是一个富有创造力的产品设计师,专注于用户体验。"
Agent C: "你是一个高效的工程师,专注于快速实现功能。"
→ 每个 Agent 聚焦一个角色,输出质量明显更高。
代表框架
| 框架 | 特点 |
|---|---|
| CrewAI | Python 原生,最易上手,定义 Role + Goal + Task 即可 |
| AutoGen (Microsoft) | 支持复杂对话模式,适合研究场景 |
| LangGraph | 最灵活,可以定义任意拓扑结构 |
| OpenAI Swarm | 轻量级实验框架,手写逻辑,透明可控 |
关键挑战
Multi-Agent 不是"Agent 越多越好"。每增加一个 Agent 意味着:
- 多一次 LLM 调用——成本和延迟线性增加
- 多一个故障点——一个 Agent 犯错可能带偏整个任务
- 多一层协调开销——Agent 间通信本身就是额外的 token 消耗
并不是所有任务都需要多 Agent。对于简单任务,一个 Agent 加好 prompt 就足够了。多 Agent 只有在任务需要多个不同专业视角协作时才真正发挥价值。
优势
- 专业化——每个 Agent 专精一个领域,输出质量高
- 可并行——独立 Agent 可以同时工作
- 可扩展——新增角色只需新增 Agent 定义
- 可解释——每个 Agent 的输入输出都可审计
劣势
- 成本高——多次 LLM 调用,token 消耗大
- 延迟高——串行通信时延迟叠加
- 协调复杂——Agent 间通信、冲突解决、结果合并都是难题
- 过度设计——大部分场景不需要多 Agent
第五种:Workflow / DAG(有向无环图工作流)
底层原理
这个模式在理念上和前几种完全不同:不是让模型自己决定下一步做什么,而是开发者预先画好流程图,模型按图执行。
graph LR
A[用户输入] --> B[分类器]
B -->|技术问题| C[搜索知识库]
B -->|账户问题| D[查询用户信息]
B -->|投诉| E[转人工]
C --> F[生成回答]
D --> F
F --> G[输出]和 Agentic Loop 的关键区别
| Agentic Loop | Workflow / DAG | |
|---|---|---|
| 决策者 | 模型自己决定下一步 | 开发者预先决定 |
| 灵活性 | 高——模型可以自由选择路径 | 低——路径固定 |
| 可预测性 | 低 | 高——行为完全确定 |
| 控制粒度 | 粗——模型决定一切 | 细——可以精确控制每个节点 |
| 适用场景 | 开放式探索任务 | 确定性业务流程 |
为什么 Workflow 模式在企业级很流行?
因为企业不喜欢"黑箱"。
一个自由发挥的 Agent 可能今天走这条路,明天走那条路,QA 没法覆盖所有路径。而 Workflow 的路径是确定的,每个分支都可以单独测试。
代表产品
- LangGraph——功能最强,支持条件分支、循环、并行、人工介入
- 扣子(Coze)——字节跳动出品,可视化编排
- Amazon Bedrock Prompt Flows——AWS 生态
- Dify——开源 LLMOps 平台
优势
- 完全可预测——行为确定,适合生产环境
- 可测试——每条路径都可以单独测试
- 精确控制——可以在任意节点插入逻辑、人工审核
- 适合复杂业务逻辑——审批流、多步验证等
劣势
- 缺乏灵活性——无法处理预期外的场景
- 维护成本高——节点多时图变得复杂难以维护
- 不适用于探索性任务——研究、分析类任务不适合固定流程
最佳实践:推荐范式
不需要在以上模式中"选一个"。 现代生产级 Agent 都是多种模式的混合体。
推荐架构:分层混合模式
┌─────────────────────────────────────────────┐
│ Orchestrator Layer │
│ Plan-and-Execute 风格 │
│ (先规划,再按计划调度子任务) │
└──────────────────┬──────────────────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 工具调用 │ │ 代码执行 │ │ 子Agent │
│ ReAct 风格│ │CodeAct 风格│ │ 多 Agent │
│(API 类) │ │(计算/处理)│ │(复杂协作)│
└──────────┘ └──────────┘ └──────────┘
具体建议
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 简单工具调用(搜网页、查天气) | ReAct + Function Calling | 简单直接,几乎零开销 |
| 数据处理/计算/绘图 | CodeAct | 一次代码顶 N 次工具调用 |
| 复杂多步任务 | Plan-and-Execute + ReAct | 先规划稳定大方向,再灵活执行 |
| 多角色协作(代码生成+审查) | Multi-Agent(2-3 个 Agent) | 专业角色分工提升质量 |
| 生产环境业务流(客服、审批) | Workflow / DAG | 确定性优先,每条路径可测试 |
| 需要调用外部工具生态 | MCP 协议 | 标准化工具接入,不绑定特定实现 |
避免反模式
"你是一个可以写代码、上网搜索、查数据库、发送邮件、做数据分析的全能助手。"
→ 模型会迷失方向,什么都做不精。
应该:用 Orchestrator + 专用 Agent / Tool 的架构,每个能力独立管理。
"这个任务太复杂了,我们上 10 个 Agent 吧。"
→ 每个 Agent 都是额外的成本和故障点。大部分任务 1-3 个 Agent 足够了。
应该:从单个 Agent 开始,只有确实需要多个视角时才拆分。
"用户可能说任何话,所以我的流程图覆盖所有分支。"
→ 分支数量会指数级膨胀,最终无法维护。
应该:Workflow 只处理确定路径,意外情况交给 Agentic Loop 兜底。
底层原理总结
所有 Agent 架构的本质都可以概括为:
Agent = LLM + 循环 + 工具 + 状态管理
| 维度 | 说明 |
|---|---|
| LLM | 推理引擎,负责"思考" |
| 循环 | 突破单次对话限制,让任务可以多步完成 |
| 工具 | 扩展 LLM 的能力边界(搜索、计算、操作文件) |
| 状态管理 | 在多次调用间维护上下文、记忆、中间结果 |
不同的架构只是对这四要素的组合方式不同:
| 架构 | LLM 任务 | 循环驱动力 | 工具形式 | 状态管理 |
|---|---|---|---|---|
| ReAct | 思考+决策 | Tool calls 触发 | 预定义函数 | 上下文累积 |
| CodeAct | 写代码 | 代码执行结果触发 | 编程语言+标准库 | 上下文累积 |
| Plan-and-Execute | 先规划后执行 | 计划步骤触发 | 任意 | 计划+上下文 |
| Multi-Agent | 专业化分工 | Agent 间通信 | 各 Agent 自有 | 独立+共享 |
| Workflow/DAG | 按图执行 | 图的边触发 | 各节点自有 | 图引擎管理 |
写在最后
没有银弹。最好的架构取决于你具体要解决什么问题。
- 做一个聊天机器人 + 简单工具调用?ReAct + Function Calling 就够了
- 做一个数据分析助手?CodeAct 是核心
- 做一个企业级客服系统?Workflow / DAG
- 做一个 AI 编程助手?ReAct(工具)+ CodeAct(代码)的混合
- 做一个复杂的自动化系统?Plan-and-Execute + Multi-Agent
最关键的是理解底层原理,然后根据实际需求灵活组合,而不是盲目追随某种"流行模式"。
参考
- ReAct 论文 — "Synergizing Reasoning and Acting in Language Models" (2022)
- CodeAct 论文 — "CodeAct: Making LLM Agents Code-driven" (2024)
- MCP 协议 — Model Context Protocol
- LangGraph — 图工作流框架
- CrewAI — 多 Agent 框架
claude code 的一个典型代表:
graph TD
A[用户输入] --> B[Agentic Loop]
B --> C{模型决定方式}
C -->|调工具| D[ReAct: Read / Write / Bash / Grep]
C -->|写代码| E[CodeAct: 写代码 → 终端执行 → 看结果]
C -->|查资料| F[ReAct: WebSearch / MCP工具]
D --> G{还有更多?}
E --> G
F --> G
G -->|是| B
G -->|否| H[输出答案]curcor:
graph TD
A[用户输入] --> B{Agent 模式}
B --> C[写/改代码]
B --> D[执行终端命令]
B --> E[读取上下文]
C --> F[感知结果]
D --> F
E --> F
F --> G{还需要改?}
G -->|是| B
G -->|否| H[提交更改]