多 Agent 协作实战:CrewAI / AutoGen 框架对比与生产级架构设计
前言: 上篇文章讲了单 Agent 的三大范式(ReAct / Plan-and-Execute / Reflexion),这篇聊聊更复杂的场景——多 Agent 协作。当单个 Agent 的能力不够用的时候,怎么让多个 Agent 配合工作?这篇文章对比主流框架,给出生产级架构设计的思路。
先说清楚:什么时候需要多 Agent?
多 Agent 不是银弹。能用单 Agent 解决的,不要用多 Agent。
多 Agent 的适用场景:
✅ 适合多 Agent 的场景:
├── 任务需要多种专业能力(写代码 + 搜索 + 发邮件)
├── 需要多人协作的工作流程(写稿 + 审核 + 发布)
├── 信息需要从多个来源汇总(多平台搜索 → 整合)
└── 需要相互校验的任务(生成 + 审查 + 修订)
❌ 不适合多 Agent 的场景:
├── 简单问答(直接问就行)
├── 单一步骤任务(不需要协作)
└── 实时性要求极高(多 Agent 有通信开销)
1. 多 Agent 协作模式
1.1 四种基本模式
"""
多 Agent 协作的四种基本模式
"""
# 模式1:层次式(Hierarchical)
# ┌─────────────┐
# │ Manager │ ← 负责任务分配、结果汇总
# └──────┬──────┘
# ┌───┴───┐
# ┌──┴──┐ ┌──┴──┐
# │Worker│ │Worker│ ← 负责执行具体任务
# └─────┘ └─────┘
HIERARCHICAL_PATTERN = """
特点:有一个中心协调者(Manager),分解任务给下属 Worker
优点:清晰、易理解
缺点:Manager 可能成为瓶颈
适用:任务明确、可分解的场景
"""
# 模式2:并行式(Parallel)
# ┌────────────────────┐
# │ Task │
# └─────────┬──────────┘
# ┌────┴────┐
# ┌──┴──┐ ┌──┴──┐
# │Agent│ │Agent│ ← 同时处理
# └──┬──┘ └──┬──┘
# └────┬────┘
# ┌────┴────┐
# ┌──┴──┐ ┌──┴──┐
# │Agent│ │Agent│ ← 结果汇总
# └─────┘ └─────┘
PARALLEL_PATTERN = """
特点:多个 Agent 同时工作,结果汇总
优点:速度快、并行度高
缺点:结果可能冲突,需要裁决机制
适用:信息收集、多方案生成
"""
# 模式3:环形协作(Round-robin)
# ┌──────────────────────────────────────┐
# │ │
# │ Agent-A → Agent-B → Agent-C → Agent-A│
# │ │
# │ 每一轮的输出是下一轮的输入 │
# └──────────────────────────────────────┘
ROUND_ROBIN_PATTERN = """
特点:Agent 轮流处理,每一轮的输出影响下一轮
优点:可以逐步完善、相互补充
缺点:速度慢,可能陷入循环
适用:创意生成、写作修订
"""
# 模式4:网状式(Mesh)
# ┌──────┐
# │Agent │
# │ A │◄──────►┌──────┐
# └───┬───┘ │Agent │
# │ │ B │
# ┌───┴───┐ └───┬───┘
# │Agent │◄──────►───┘
# │ C │
# └───────┘
MESH_PATTERN = """
特点:Agent 之间可以直接通信,没有固定顺序
优点:灵活、适应性强
缺点:通信复杂、难以追踪
适用:复杂、需要多领域知识融合的任务
"""
1.2 选择建议
| 模式 | 适用场景 | 框架选择 |
|---|---|---|
| 层次式 | 任务分解、流程明确 | CrewAI (默认) / LangGraph |
| 并行式 | 信息收集、投票决策 | CrewAI + parallel |
| 环形 | 迭代优化、内容创作 | LangGraph / Custom |
| 网状式 | 复杂协作、需要对话 | AutoGen |
2. CrewAI 框架实战
2.1 为什么选 CrewAI
CrewAI 是我目前用下来最顺手的多 Agent 框架:
优点:
✅ 概念清晰:Agent = 角色 + 目标 + 工具
✅ 配置简单:YAML 或代码都能定义
✅ 内置多种进程:Sequential / Parallel / Hierarchical
✅ 支持任务依赖:写清楚谁依赖谁
✅ 社区活跃:文档和示例比较全
缺点:
⚠️ 定制化有限(复杂逻辑可能不够用)
⚠️ 生产级监控/追踪要自己加
⚠️ 国内生态相对弱
2.2 完整代码示例
# crewai_demo.py
"""
CrewAI 完整示例:技术博客创作流程
流程:
1. Researcher:搜索最新 AI 技术动态
2. Writer:基于研究结果撰写文章
3. Editor:审核文章质量,给出修改意见
4. Publisher:发布到平台(模拟)
"""
from crewai import Agent, Task, Crew, Process
from crewai.tools import SerpDevTool, FileReadTool, FileWriteTool
def create_blog_crew(topic: str):
"""创建博客创作团队"""
# 定义工具
search_tool = SerpDevTool()
file_read = FileReadTool()
file_write = FileWriteTool()
# ========== Agent 定义 ==========
# 1. 研究员
researcher = Agent(
role="高级研究员",
goal=f"收集关于「{topic}」的全面、有价值的信息",
backstory="""
你是一位经验丰富的技术研究员,擅长:
- 从多个渠道收集一手信息
- 识别信息的可靠性和时效性
- 提炼关键观点和趋势
你搜索的资料会成为文章的基础,所以要尽量全面准确。
""",
tools=[search_tool],
verbose=True, # 打印详细日志
allow_delegation=False # 不需要委托给其他人
)
# 2. 写手
writer = Agent(
role="技术内容创作者",
goal="将研究成果转化为通俗易懂、有价值的文章",
backstory="""
你是一位专业的内容创作者,擅长:
- 用简洁清晰的语言解释复杂技术
- 结构化组织文章(开篇 / 正文 / 总结)
- 在专业性和可读性之间找到平衡
你的文章要:
- 有干货、有深度
- 语言生动、不枯燥
- 给读者带来实际收获
""",
tools=[file_read, file_write],
verbose=True,
allow_delegation=False
)
# 3. 编辑
editor = Agent(
role="资深编辑",
goal="确保文章质量达到发布标准",
backstory="""
你是一位严格的编辑,职责是:
- 检查文章逻辑是否清晰
- 指出需要补充或修改的地方
- 给出具体、可操作的修改建议
你要对读者负责,不要放过任何问题。
""",
verbose=True,
allow_delegation=False
)
# ========== Task 定义 ==========
# 任务1:研究
research_task = Task(
description=f"""
深入研究「{topic}」相关的内容:
1. 搜索最新进展和趋势
2. 找到3-5个有代表性的案例或应用
3. 整理关键信息点和技术要点
4. 评估信息的可靠性和时效性
输出:一份结构化的研究报告
""",
agent=researcher,
expected_output="包含最新动态、案例、技术要点的研究报告"
)
# 任务2:写作
writing_task = Task(
description="""
基于研究报告,撰写一篇完整的文章:
1. 标题要吸引人
2. 开篇引入要抓人
3. 正文结构清晰,有逻辑
4. 结尾有总结和行动指引
目标读者:有一定技术基础的学习者
字数:2000-3000字
""",
agent=writer,
expected_output="一篇完整的文章草稿",
context=[research_task] # 依赖研究任务的结果
)
# 任务3:编辑审核
editing_task = Task(
description="""
审核文章草稿:
1. 检查文章结构和逻辑
2. 指出需要改进的地方
3. 给出具体的修改建议
如果发现问题,标注出来让写手修改。
""",
agent=editor,
expected_output="包含修改建议的审核报告",
context=[writing_task] # 依赖写作任务的结果
)
# 任务4:修订(如果需要)
revision_task = Task(
description="""
根据编辑的反馈修订文章。
确保所有提到的问题都得到解决。
""",
agent=writer,
expected_output="修订后的最终文章",
context=[editing_task],
async_execution=False # 同步执行,等待编辑完成
)
# ========== Crew 定义 ==========
crew = Crew(
agents=[researcher, writer, editor],
tasks=[research_task, writing_task, editing_task, revision_task],
process=Process.sequential, # 顺序执行
verbose=True
)
return crew
def run_blog_creation(topic: str):
"""执行博客创作流程"""
print(f"🚀 开始创作:「{topic}」\n")
crew = create_blog_crew(topic)
result = crew.kickoff()
print("\n" + "="*60)
print("✅ 创作完成!")
print("="*60)
return result
if __name__ == "__main__":
result = run_blog_creation("AI Agent 开发实战")
# result 包含:
# - raw: 原始输出
# - tasks_output: 每个任务的输出
print(result.raw)
2.3 并行执行示例
# parallel_crew.py
"""
并行执行:多源信息收集
场景:同时从多个来源收集信息,然后汇总
"""
from crewai import Agent, Task, Crew, Process
def create_research_crew(topic: str):
"""创建并行研究团队"""
# 三个研究员,分别负责不同来源
researcher_news = Agent(
role="新闻研究员",
goal=f"收集「{topic}」的最新新闻动态",
backstory="你专注于追踪最新的新闻和事件..."
)
researcher_tech = Agent(
role="技术研究员",
goal=f"深入研究「{topic}」的技术原理和实现",
backstory="你专注于技术细节和实现方案..."
)
researcher_cases = Agent(
role="案例研究员",
goal=f"收集「{topic}」的真实应用案例",
backstory="你专注于寻找有代表性的实践案例..."
)
# 三个独立的研究任务
task_news = Task(
description=f"搜索「{topic}」的最新新闻",
agent=researcher_news,
expected_output="最新动态汇总"
)
task_tech = Task(
description=f"分析「{topic}」的技术原理",
agent=researcher_tech,
expected_output="技术原理说明"
)
task_cases = Task(
description=f"收集「{topic}」的应用案例",
agent=researcher_cases,
expected_output="案例列表"
)
# 汇总任务
synthesizer = Agent(
role="内容整合师",
goal="整合三个研究员的结果,生成完整报告",
backstory="你擅长将不同来源的信息整合成连贯的报告..."
)
task_synthesize = Task(
description="整合研究结果,生成完整报告",
agent=synthesizer,
expected_output="结构完整的综合报告",
context=[task_news, task_tech, task_cases]
)
# 并行执行前三个任务
research_crew = Crew(
agents=[researcher_news, researcher_tech, researcher_cases],
tasks=[task_news, task_tech, task_cases],
process=Process.parallel
)
# 再执行汇总
final_crew = Crew(
agents=[synthesizer],
tasks=[task_synthesize],
process=Process.sequential
)
return research_crew, final_crew
def run_parallel_research(topic: str):
"""执行并行研究流程"""
print(f"🔍 开始并行研究:「{topic}」\n")
research_crew, final_crew = create_research_crew(topic)
# 第一步:并行研究
print("📡 三个研究员同时工作...\n")
research_result = research_crew.kickoff()
# 第二步:汇总
print("📝 整合师正在汇总...\n")
final_result = final_crew.kickoff(
inputs={"previous_output": research_result.raw}
)
return final_result
3. AutoGen 框架实战
3.1 AutoGen 的特点
AutoGen(微软开源)和 CrewAI 的定位不太一样:
AutoGen 特点:
✅ 更灵活:Agent 之间可以真正「对话」
✅ 对话式:适合需要来回讨论的场景
✅ 微软背书:质量和持续性有保障
✅ 多种模式:对话 / 群聊 / 层级
CrewAI 特点:
✅ 配置更简单
✅ 概念更直观
✅ 任务管理更强
简单理解:
- CrewAI = 任务管理框架
- AutoGen = 对话框架
3.2 AutoGen 代码示例
# autogen_demo.py
"""
AutoGen 示例:代码生成 + 审查对话
流程:
1. User 发起请求
2. Assistant 生成代码
3. Critic 审查代码
4. Assistant 根据反馈修改
5. 循环直到 Critic 满意
"""
from autogen import AssistantAgent, UserProxyAgent, GroupChat, GroupChatManager
def create_code_review_system():
"""创建代码审查系统"""
# 1. 助手 Agent(生成代码)
assistant = AssistantAgent(
name="Coder",
system_message="""
你是一位经验丰富的 Python 开发者。
根据用户需求编写高质量的代码。
职责:
- 编写清晰、规范的代码
- 添加必要的注释
- 考虑边界情况
如果审查员提出问题,要积极修改。
""",
llm_config={
"model": "gpt-4o",
"api_type": "openai"
}
)
# 2. 审查员 Agent
critic = AssistantAgent(
name="Critic",
system_message="""
你是一位严格的代码审查员。
审查要点:
1. 代码正确性:逻辑是否正确?
2. 代码规范:是否符合 PEP8?
3. 安全性:是否有潜在风险?
4. 性能:是否有优化空间?
5. 可读性:是否易于理解?
审查方式:
- 如果代码有问题,明确指出问题所在
- 如果代码OK,说 "代码审查通过"
- 必要时给出修改建议
""",
llm_config={
"model": "gpt-4o",
"api_type": "openai"
}
)
# 3. 用户代理(模拟用户)
user_proxy = UserProxyAgent(
name="User",
human_input_mode="NEVER" # 不需要人工输入
)
return assistant, critic, user_proxy
def code_review_workflow(requirement: str):
"""执行代码审查工作流"""
assistant, critic, user_proxy = create_code_review_system()
# 初始化群聊
group_chat = GroupChat(
agents=[user_proxy, assistant, critic],
max_round=10, # 最多对话10轮
speaker_selection_method="round_robin" # 轮流发言
)
manager = GroupChatManager(groupchat=group_chat)
# 开始对话
user_proxy.initiate_chat(
manager,
message=f"""
请帮我实现以下功能:
{requirement}
完成后,等待审查员的反馈并根据需要修改代码。
"""
)
# 获取对话历史
chat_history = user_proxy.chat_messages.get(manager, [])
return chat_history
# ===== 更简单的两人对话示例 =====
def simple_conversation():
"""简单的两 Agent 对话"""
coder = AssistantAgent(
name="Coder",
system_message="你是一位 Python 开发者,简洁高效。"
)
tester = AssistantAgent(
name="Tester",
system_message="你是一位测试工程师,负责验证代码的正确性。"
)
# 测试场景:写一个排序函数
tester.initiate_chat(
coder,
message="""
写一个 Python 函数,对列表进行排序并返回结果。
然后测试它是否能正确排序 [3, 1, 4, 1, 5, 9, 2, 6]
"""
)
4. 生产级架构设计
4.1 整体架构
"""
多 Agent 系统的生产级架构
┌─────────────────────────────────────────────────────────────┐
│ API Layer │
│ (FastAPI / Flask / 各种 API) │
└──────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────────────┐
│ Router / Orchestrator │
│ 任务路由、负载均衡、异常处理 │
└──────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────────────┐
│ Agent Runtime │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Agent-A │ │ Agent-B │ │ Agent-C │ │
│ │ (专用) │ │ (通用) │ │ (专用) │ │
│ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ │
│ │ │ │ │
│ ┌─────▼────────────────▼────────────────▼─────┐ │
│ │ Tool Execution Layer │ │
│ │ (搜索引擎 / 数据库 / API / 文件系统) │ │
│ └─────────────────────────────────────────────┘ │
└──────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────────────┐
│ Memory Layer │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 短期记忆 │ │ 长期记忆 │ │ 情节记忆 │ │
│ │(上下文) │ │(向量DB) │ │(任务日志) │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────────────┐
│ Monitoring │
│ (日志 / 指标 / 追踪 / 告警) │
└─────────────────────────────────────────────────────────────┘
"""
# ===== 核心组件 =====
class AgentRuntime:
"""
Agent 运行时
管理 Agent 的生命周期
"""
def __init__(self):
self.agents: dict[str, Agent] = {}
self.tools: dict[str, Callable] = {}
self.memory = AgentMemory()
self.monitor = Monitor()
def register_agent(self, name: str, agent: Agent):
"""注册 Agent"""
self.agents[name] = agent
def register_tool(self, name: str, tool: Callable):
"""注册工具"""
self.tools[name] = tool
def execute_task(self, task: Task) -> Result:
"""执行任务"""
# 1. 选择合适的 Agent
agent = self._select_agent(task)
# 2. 获取相关记忆
context = self.memory.recall(task.description)
# 3. 执行
with self.monitor.track(agent.name, task.id):
result = agent.execute(task, context)
# 4. 存储结果到记忆
self.memory.remember(task.description, result)
return result
def _select_agent(self, task: Task) -> Agent:
"""根据任务类型选择 Agent"""
# 简单路由:按标签匹配
for agent in self.agents.values():
if any(tag in agent.capabilities for tag in task.tags):
return agent
# 默认返回通用 Agent
return self.agents.get("general")
4.2 记忆系统
# memory_system.py
"""
Agent 记忆系统
多层记忆:
1. Working Memory:当前任务的上下文
2. Short-term Memory:当前会话的历史
3. Long-term Memory:持久化的知识(向量数据库)
"""
from dataclasses import dataclass
from typing import Optional
import time
@dataclass
class MemoryItem:
"""记忆条目"""
content: str
memory_type: str # "working" | "short_term" | "long_term"
importance: float # 重要程度 0-1
timestamp: float
tags: list[str]
class AgentMemory:
"""
Agent 记忆系统
设计原则:
- 重要信息 → 强化存储
- 不重要信息 → 定期清理
- 失败经验 → 长期记住
"""
def __init__(self, vector_db=None):
self.working_memory: list[str] = [] # 当前上下文
self.short_term: list[MemoryItem] = [] # 短期记忆
self.long_term: list[MemoryItem] = [] # 长期记忆(简化版,生产用向量数据库)
self.vector_db = vector_db
def remember(
self,
content: str,
memory_type: str = "working",
importance: float = 0.5,
tags: list[str] = None
):
"""存储记忆"""
item = MemoryItem(
content=content,
memory_type=memory_type,
importance=importance,
timestamp=time.time(),
tags=tags or []
)
if memory_type == "working":
self.working_memory.append(content)
# 限制大小
if len(self.working_memory) > 20:
self.working_memory = self.working_memory[-20:]
elif memory_type == "short_term":
self.short_term.append(item)
if len(self.short_term) > 100:
self._compress_short_term()
elif memory_type == "long_term":
self.long_term.append(item)
if self.vector_db:
# 存入向量数据库
pass
def recall(self, query: str, limit: int = 5) -> list[str]:
"""检索记忆"""
results = []
# 工作记忆(直接返回)
results.extend(self.working_memory[-limit:])
# 短期记忆(关键词匹配)
for item in self.short_term[-20:]:
if query.lower() in item.content.lower():
results.append(item.content)
# 长期记忆(向量搜索)
if self.vector_db:
vector_results = self.vector_db.search(query, limit=limit)
results.extend([r["content"] for r in vector_results])
return results[:limit]
def remember_episode(
self,
task: str,
actions: list[str],
result: str,
success: bool,
lesson: str = ""
):
"""记录情节记忆(任务执行记录)"""
# 失败经验很重要,必须记住
if not success:
self.remember(
content=f"任务「{task}」失败的教训:{lesson}",
memory_type="long_term",
importance=0.9 # 失败经验重要度高
)
# 成功经验也记住
if success and lesson:
self.remember(
content=f"任务「{task}」成功的经验:{lesson}",
memory_type="long_term",
importance=0.7
)
def _compress_short_term(self):
"""压缩短期记忆,保留重要的"""
# 按重要性排序
sorted_memory = sorted(
self.short_term,
key=lambda x: x.importance,
reverse=True
)
# 保留最重要的 50 条
self.short_term = sorted_memory[:50]
5. 框架对比总结
| 特性 | CrewAI | AutoGen | LangGraph |
|---|---|---|---|
| 定位 | 任务导向 | 对话导向 | 状态机 |
| 上手难度 | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| 灵活性 | 中 | 高 | 最高 |
| 多 Agent 协作 | 强 | 强 | 强 |
| 监控/追踪 | 一般 | 一般 | 需自己加 |
| 适合场景 | 任务流水线 | 对话协作 | 复杂工作流 |
我的选择建议:
快速原型、业务流程 → CrewAI
多 Agent 对话、讨论 → AutoGen
复杂状态机、精确控制 → LangGraph
生产级、需要定制 → 组合使用 + 自定义
6. 踩坑和经验
坑1:Agent 之间通信要清晰
多个 Agent 协作时,最容易出问题是信息传递不清。
# ❌ 容易出问题
task_b = Task(
description="处理上一步的结果",
context=[task_a]
)
# ✅ 明确说明需要什么
task_b = Task(
description=f"""
基于以下研究结果,撰写文章:
{task_a.expected_output}
注意:重点关注XX方面,忽略YY方面
""",
context=[task_a]
)
坑2:不要让 Agent 产生过多对话
多 Agent 对话很灵活,但也很容易陷入无限循环。
解决:设置 max_round,并且设计好终止条件。
坑3:监控和日志必须做好
多 Agent 出问题很难排查,因为涉及多个 Agent 的交互。
必备:
- 每个 Agent 的输入输出日志
- 任务执行时间
- 错误追踪
结语
多 Agent 协作是 Agent 发展的下一个重点方向。从单 Agent 到多 Agent,不只是数量上的变化,而是从「一个聪明的工具」到「一个协作团队」的质变。
框架只是工具,核心还是理解什么场景需要协作、怎么协作。
希望这篇文章对你有帮助。下次聊点别的方向~
相关阅读:
AI Agent开发实战:ReAct / Plan-Execute / Reflexion 范式完整解析
前言: 最近在项目中落地 AI Agent,从最初的「LLM+工具调用」到现在的完整 Agent 架构,踩了不少坑。这篇文章把核心范式梳理清楚,配上可运行的代码,希望帮你少走弯路。
先说结论
做 Agent 这类项目,核心问题只有一个:LLM 怎么知道自己该干什么、该怎么干?
解决这个问题有三条成熟路径:
| 范式 | 核心理念 | 适合场景 | 复杂度 |
|---|---|---|---|
| ReAct | 边想边做,思考和行动交替 | 工具调用、信息检索 | ⭐⭐ |
| Plan-and-Execute | 先想好再做,计划和执行分离 | 复杂多步骤任务 | ⭐⭐⭐ |
| Reflexion | 做完了想一想,总结经验 | 需要自我改进的任务 | ⭐⭐⭐⭐ |
三条路不是非此即彼,可以组合使用。
1. 为什么需要 Agent 范式?
先看一个反常识的事实:直接让 LLM 调工具,效果往往很差。
比如你想让 LLM 帮你查资料然后写报告,最朴素的做法是:
用户: 帮我查一下最新的AI Agent进展
LLM: (调用搜索工具)
LLM: 好的,我查到了以下内容...(开始写)
这有什么问题?
问题1:LLM 可能不知道该不该调用工具
用户: 1+1等于多少
LLM: 调用搜索工具(query="1+1等于多少")
这种基础问题根本不需要搜索,但 LLM 可能会「想太多」。
问题2:LLM 可能不知道该用什么工具
LLM 面对「帮我查资料并写报告」这样的任务,它得决定:
- 先搜索还是先写?
- 用哪个搜索引擎?
- 搜到多少条够用?
问题3:LLM 不会反思和修正
如果第一次搜索结果不满意,LLM 不会主动换关键词重试。
Agent 范式就是为了解决这三个问题。
2. ReAct 范式:边想边做
2.1 核心思想
ReAct = Reasoning + Action
Thought: 我需要先搜索"AI Agent最新进展"
Action: search(query="AI Agent最新进展 2024")
Observation: 搜索到10条结果...
Thought: 第一条看起来最相关,我来读一下
Action: browse(url="...")
Observation: 文章内容是...
Thought: 内容够用了,我来写报告
Action: answer("基于搜索结果的报告...")
关键点:Thought 是 LLM 的「自言自语」,让它解释为什么要调用这个工具。
2.2 完整代码实现
# react_agent.py
import json
from dataclasses import dataclass, field
from enum import Enum
from typing import Callable
class ActionType(Enum):
"""Agent 能执行的动作类型"""
SEARCH = "search"
BROWSE = "browse"
READ_FILE = "read_file"
WRITE_FILE = "write_file"
CODE = "execute_code"
ANSWER = "answer" # 最终回答,结束标志
@dataclass
class Step:
"""执行步骤记录"""
step_num: int
thought: str
action: ActionType
action_input: dict
observation: str = ""
class ReActAgent:
"""
ReAct 范式的 Agent 实现
核心流程:
1. Think: 让 LLM 分析当前状态,决定下一步
2. Act: 执行选定的动作
3. Observe: 获取执行结果
4. 循环直到 LLM 说「回答完毕」
"""
def __init__(
self,
llm_client,
tools: dict[str, Callable],
max_iterations: int = 15
):
self.llm = llm_client
self.tools = tools
self.max_iterations = max_iterations
def think(self, history: list[Step]) -> tuple[ActionType, str, dict]:
"""
让 LLM 思考下一步该做什么
返回: (动作类型, 思考过程, 动作参数)
"""
# 构建历史上下文
history_text = self._format_history(history)
# 工具说明
tool_desc = "\n".join([
f"- {name}: {tool.__doc__ or '无描述'}"
for name, tool in self.tools.items()
])
prompt = f"""你是一个智能助手,正在帮用户完成任务。
请分析当前情况,决定下一步动作。
可用工具:
{tool_desc}
{history_text}
请严格按照以下 JSON 格式输出,不要输出其他内容:
{{
"thought": "你的思考过程,解释为什么选择这个动作",
"action": "工具名称(必须是可用工具之一) 或 'answer' 表示回答用户问题",
"action_input": {{"参数名": "参数值"}} // 如果是 answer 则为空对象
}}
"""
response = self.llm.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}],
temperature=0,
response_format={"type": "json_object"}
)
result = json.loads(response.choices[0].message.content)
action_type = (
ActionType.ANSWER
if result["action"] == "answer"
else ActionType(result["action"])
)
return action_type, result["thought"], result.get("action_input", {})
def act(self, action_type: ActionType, action_input: dict) -> str:
"""执行动作"""
if action_type == ActionType.ANSWER:
return "FINAL_ANSWER"
tool = self.tools.get(action_type.value)
if not tool:
return f"错误:工具 {action_type.value} 不存在"
try:
result = tool(**action_input)
return str(result)
except Exception as e:
return f"执行错误:{str(e)}"
def run(self, user_query: str) -> dict:
"""
执行完整的 ReAct 循环
"""
history: list[Step] = []
for i in range(self.max_iterations):
# Step 1: Think
action_type, thought, action_input = self.think(history)
# Step 2: Act
observation = self.act(action_type, action_input)
# 记录步骤
step = Step(
step_num=i + 1,
thought=thought,
action=action_type,
action_input=action_input,
observation=observation
)
history.append(step)
# Step 3: 检查是否结束
if action_type == ActionType.ANSWER:
return {
"status": "success",
"steps": history,
"answer": history[-1].observation
}
return {
"status": "max_iterations",
"steps": history,
"answer": "已达到最大迭代次数"
}
def _format_history(self, history: list[Step]) -> str:
if not history:
return "(暂无历史记录)"
lines = []
for step in history[-5:]:
lines.append(
f"步骤{step.step_num}:\n"
f" 思考:{step.thought}\n"
f" 动作:{step.action.value}({step.action_input})\n"
f" 结果:{step.observation[:100]}..."
)
return "\n".join(lines)
# ===== 使用示例 =====
def demo():
"""演示 ReAct Agent 的使用"""
# 模拟的工具函数
def search(query: str) -> str:
"""搜索工具"""
results = {
"AI Agent最新进展": "1. Anthropic Claude Agent\n2. OpenAI Swarm\n3. AutoGen\n4. CrewAI",
"AI Agent框架对比": "ReAct vs Plan-and-Execute vs Reflexion",
"LLM工具调用": "Function Calling 是主流方案"
}
return results.get(query, f"搜索到关于「{query}」的内容...")
def browse(url: str) -> str:
"""浏览网页"""
return f"正在浏览 {url},获取到文章内容..."
def calculate(expr: str) -> str:
"""计算器"""
try:
result = eval(expr)
return str(result)
except:
return "计算错误"
# 创建 Agent
agent = ReActAgent(
llm_client=None, # 实际使用时传入真实的 LLM client
tools={
"search": search,
"browse": browse,
"calculate": calculate
}
)
# 执行
result = agent.run("1+1等于多少?")
print(f"状态: {result['status']}")
print(f"回答: {result['answer']}")
# 打印执行过程
print("\n执行过程:")
for step in result["steps"]:
print(f"\n步骤 {step.step_num}:")
print(f" 思考: {step.thought}")
print(f" 动作: {step.action.value}")
if __name__ == "__main__":
demo()
2.3 ReAct 的实际效果
我在一个「技术博客助手」项目里用过 ReAct,效果对比:
| 场景 | 不用 ReAct | 用 ReAct |
|---|---|---|
| 简单问题 | 经常调用搜索(浪费) | 直接回答 |
| 复杂问题 | 执行一步忘一步 | 思路清晰,可追溯 |
| 错误处理 | 错了就错了 | 可以重试、修正 |
最明显的改善:Agent 的行为变得可解释了。每一步在做什么、为什么这么做,都有记录。
3. Plan-and-Execute:先想好再做
3.1 核心思想
ReAct 是「边想边做」,Plan-and-Execute 是「先想好完整计划,再按计划执行」。
用户任务:帮我准备一场技术分享
Plan:
1. 确定主题 → [需要:用户确认]
2. 搜索相关资料 → [需要:搜索引擎]
3. 整理资料写成大纲 → [需要:大纲模板]
4. 写 PPT → [需要:PPT 工具]
5. 检查并优化 → [需要:用户确认]
和 ReAct 的区别:
| ReAct | Plan-and-Execute | |
|---|---|---|
| 计划 | 没有全局计划 | 先制定完整计划 |
| 适应性 | 灵活,可随时调整 | 计划固定,灵活性差 |
| 可解释性 | 每步可解释 | 计划本身就很好看懂 |
| 适用场景 | 简单、探索性任务 | 复杂、流程固定任务 |
3.2 代码实现
# plan_and_execute.py
from dataclasses import dataclass
from typing import Optional
import json
@dataclass
class PlanStep:
"""计划步骤"""
step_id: int
task: str
tool: Optional[str]
params: dict
depends_on: list[int] = None # 依赖的前置步骤 ID
result: str = ""
class PlanAndExecuteAgent:
"""
Plan-and-Execute 范式的 Agent
核心流程:
1. Planning: 让 LLM 制定完整计划
2. Execution: 按计划执行(处理依赖关系)
3. Validation: 验证结果是否符合预期
"""
def __init__(self, llm_client, executor):
self.llm = llm_client
self.executor = executor # 实际执行器
def planning(self, goal: str) -> list[PlanStep]:
"""
制定执行计划
"""
prompt = f"""用户目标:{goal}
请将这个目标分解为可执行的步骤。
要求:
1. 每个步骤应该清晰、具体
2. 每个步骤对应一个工具
3. 标注步骤之间的依赖关系
请按以下 JSON 格式返回:
{{
"steps": [
{{
"step_id": 1,
"task": "具体任务描述",
"tool": "工具名称",
"params": {{"参数名": "参数值"}},
"depends_on": [] // 依赖的步骤 ID 列表
}}
]
}}
"""
response = self.llm.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}],
temperature=0,
response_format={"type": "json_object"}
)
result = json.loads(response.choices[0].message.content)
steps = []
for s in result.get("steps", []):
steps.append(PlanStep(
step_id=s["step_id"],
task=s["task"],
tool=s.get("tool"),
params=s.get("params", {}),
depends_on=s.get("depends_on", [])
))
return steps
def execute_plan(self, plan: list[PlanStep]) -> dict[int, str]:
"""
执行计划
处理步骤间的依赖关系
"""
results: dict[int, str] = {}
for step in plan:
# 等待依赖完成
while not all(dep in results for dep in step.depends_on):
# 实际应该用异步,这里简化处理
import time
time.sleep(0.01)
# 替换参数中的引用
params = self._substitute_params(step.params, results)
# 执行
if step.tool:
result = self.executor.execute(step.tool, params)
else:
result = "无工具,直接完成"
results[step.step_id] = result
print(f"✓ 步骤 {step.step_id} 完成: {step.task}")
return results
def _substitute_params(self, params: dict, results: dict) -> dict:
"""替换参数中的引用,比如 {2.output} 表示引用步骤2的输出"""
import re
new_params = {}
for k, v in params.items():
if isinstance(v, str):
# 替换 {step_id.output_key} 格式
matches = re.findall(r'\{(\d+)\.output\}', v)
for step_id in matches:
step_id = int(step_id)
if step_id in results:
v = v.replace(f'{{{step_id}.output}}', results[step_id])
new_params[k] = v
return new_params
def run(self, goal: str) -> dict:
"""完整的 Plan-and-Execute 流程"""
print(f"🎯 目标: {goal}")
print("📋 制定计划中...\n")
# 1. 制定计划
plan = self.planning(goal)
print("📋 计划:")
for step in plan:
deps = f" (依赖步骤 {step.depends_on})" if step.depends_on else ""
print(f" {step.step_id}. {step.task}{deps}")
print("\n🚀 开始执行...\n")
# 2. 执行计划
results = self.execute_plan(plan)
# 3. 汇总
final_answer = self._summarize(results, plan)
return {
"goal": goal,
"plan": plan,
"results": results,
"final_answer": final_answer
}
def _summarize(self, results: dict, plan: list[PlanStep]) -> str:
"""汇总执行结果"""
summary = []
for step in plan:
if step.step_id in results:
summary.append(f"[步骤{step.step_id}] {step.task}: {results[step.step_id][:50]}...")
return "\n".join(summary)
# ===== 使用示例 =====
def demo_plan_execute():
"""演示 Plan-and-Execute"""
class SimpleExecutor:
def execute(self, tool: str, params: dict) -> str:
tools = {
"search": f"搜索「{params.get('query', '')}」的结果",
"write": f"写入文件: {params.get('filename', '')}",
"format": f"格式化内容为 {params.get('format', '')}",
"email": f"发送到邮箱: {params.get('to', '')}"
}
return tools.get(tool, "未知工具")
agent = PlanAndExecuteAgent(
llm_client=None, # 实际使用传入
executor=SimpleExecutor()
)
result = agent.run("帮我搜索 AI Agent 最新进展,写成文章保存,然后发给我")
print("\n" + "="*50)
print("最终结果:")
print(result["final_answer"])
if __name__ == "__main__":
demo_plan_execute()
4. Reflexion:做完了想一想
4.1 核心思想
Reflexion = Act + Observe + Reflect
第1次尝试:
Action: search("AI Agent教程")
Observe: 搜到了10条结果
Reflect: 搜索关键词太宽泛,应该更具体
第2次尝试:
Action: search("AI Agent开发实战 ReAct")
Observe: 搜到了更精准的结果
Reflect: 这次效果好,继续
第3次尝试:
Action: browse("...")
Observe: 找到了想要的内容
Reflect: 任务完成,可以总结了
Reflexion 的关键创新:Agent 不仅观察结果,还评估「做得好不好」,并从错误中学习。
4.2 代码实现
# reflexion_agent.py
import json
from dataclasses import dataclass
from typing import Optional
@dataclass
class Reflection:
"""反思记录"""
goal: str
action: str
observation: str
evaluation: str # "成功" / "部分成功" / "失败"
analysis: str # 失败原因分析
strategy: str # 改进策略
should_retry: bool
retry_hint: Optional[str]
class ReflexionAgent:
"""
Reflexion 范式的 Agent
核心流程:
1. Execute: 执行动作
2. Observe: 观察结果
3. Reflect: 评估做得好不好
4. 如果需要重试,根据反思调整后重试
"""
def __init__(self, llm_client, tools: dict):
self.llm = llm_client
self.tools = tools
# 记忆:存储历史反思
self.reflections: list[Reflection] = []
def reflect(
self,
goal: str,
action: str,
observation: str
) -> Reflection:
"""
反思:评估动作效果,决定是否重试
"""
prompt = f"""任务目标:{goal}
执行的动作:{action}
观察到的结果:{observation}
请进行自我反思:
1. 这个动作是否有效推进了目标?
2. 如果效果不好,问题出在哪里?
3. 如果要重试,如何改进?
请按 JSON 格式返回:
{{
"evaluation": "成功 / 部分成功 / 失败",
"analysis": "详细分析",
"strategy": "改进策略(如果有)",
"should_retry": true / false,
"retry_hint": "具体如何重试"
}}
"""
response = self.llm.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}],
temperature=0,
response_format={"type": "json_object"}
)
result = json.loads(response.choices[0].message.content)
reflection = Reflection(
goal=goal,
action=action,
observation=observation,
evaluation=result["evaluation"],
analysis=result["analysis"],
strategy=result.get("strategy", ""),
should_retry=result["should_retry"],
retry_hint=result.get("retry_hint", "")
)
# 存储到记忆
self.reflections.append(reflection)
return reflection
def get_relevant_reflections(self, goal: str) -> list[Reflection]:
"""获取与当前任务相关的历史反思"""
# 简化版:返回最近的3条
# 实际可以用向量相似度匹配
return self.reflections[-3:]
def run(self, goal: str, max_attempts: int = 3) -> dict:
"""完整的 Reflexion 流程"""
history = []
for attempt in range(max_attempts):
# 获取相关反思经验
relevant = self.get_relevant_reflections(goal)
# 决定下一步动作
action = self._decide_action(goal, history, relevant)
# 执行
observation = self._execute(action)
# 反思
reflection = self.reflect(goal, action, observation)
history.append({
"attempt": attempt + 1,
"action": action,
"observation": observation,
"reflection": reflection
})
# 检查是否成功
if reflection.evaluation == "成功":
return {
"status": "success",
"result": observation,
"history": history,
"attempts": attempt + 1
}
# 如果失败且不应该重试
if not reflection.should_retry:
break
return {
"status": "failed",
"result": "多次尝试后仍未成功",
"history": history,
"attempts": max_attempts
}
def _decide_action(
self,
goal: str,
history: list,
relevant_reflections: list[Reflection]
) -> str:
"""决定下一步动作(需要结合 LLM)"""
reflection_hint = ""
if relevant_reflections:
reflection_hint = "\n\n之前的反思经验:\n"
for r in relevant_reflections:
if r.strategy:
reflection_hint += f"- {r.strategy}\n"
prompt = f"""目标:{goal}
{reflection_hint}
请决定下一步动作(工具调用)。
"""
# 实际应该调用 LLM 决定
return "search(query='AI Agent最新进展')"
def _execute(self, action: str) -> str:
"""执行动作"""
# 实际根据 action 调用对应工具
return "执行结果..."
5. 实战建议:怎么选、怎么用
5.1 选型建议
场景 推荐范式
──────────────────────────────────────────────
简单的单步任务 不需要 Agent
工具调用、信息检索 ReAct
复杂多步骤、流程固定 Plan-and-Execute
需要自我改进、长程任务 Reflexion
复杂 + 需要改进 ReAct + Reflexion
5.2 实际项目中我是怎么组合的
class HybridAgent:
"""
混合 Agent:结合多种范式的优点
"""
def __init__(self, llm_client, tools):
self.llm = llm_client
self.tools = tools
self.react = ReActAgent(llm_client, tools)
self.reflexion = ReflexionAgent(llm_client, tools)
def run(self, task: str, mode: str = "auto"):
"""
根据任务复杂度自动选择范式
mode:
- "auto": 自动选择
- "react": 强制使用 ReAct
- "reflexion": 强制使用 Reflexion
"""
if mode == "auto":
# 简单任务用 ReAct
if self._is_simple_task(task):
return self.react.run(task)
# 复杂任务用 Reflexion
else:
return self.reflexion.run(task)
elif mode == "react":
return self.react.run(task)
elif mode == "reflexion":
return self.reflexion.run(task)
def _is_simple_task(self, task: str) -> bool:
"""简单判断任务复杂度"""
# 简单指示词
simple_keywords = ["查", "找", "告诉", "回答"]
complex_keywords = ["分析", "比较", "评估", "优化"]
simple_score = sum(1 for k in simple_keywords if k in task)
complex_score = sum(1 for k in complex_keywords if k in task)
return simple_score > complex_score
6. 踩坑总结
做 Agent 这段时间,几个经验教训:
坑1:不要让 Agent 自己决定循环次数
LLM 不太擅长判断「我已经做够了」,容易陷入无限循环。加一个 max_iterations,并设置合理的上限。
坑2:工具描述要精确
LLM 靠工具描述来决定用什么工具。描述模糊会导致乱选工具。
# ❌ 不好
def search(query):
"""搜索"""
# ✅ 好
def search(query: str) -> str:
"""
在网络上搜索信息。
参数:
query: 搜索关键词,建议简洁准确
返回:
搜索结果摘要(最多10条)
用途:
用于查找最新信息、技术文档、新闻等
"""
坑3:Observation 要结构化
不要只返回「查到了」这种模糊结果,要返回具体的数据结构,方便 LLM 后续处理。
结语
ReAct、Plan-and-Execute、Reflexion 这三种范式,没有绝对的优劣之分,关键是看场景。
我的经验是先从 ReAct 开始,够用就好。等遇到瓶颈了,再根据具体问题引入其他范式。
下篇文章聊聊多 Agent 协作——当单个 Agent 不够用的时候,怎么让多个 Agent 配合工作。
相关阅读: