AI Agent 设计模式:从单一工具调用到多智能体协作
很多人以为 Agent 就是让模型自己调工具,但这只是冰山一角。
Agent 的核心不是"自动化",而是决策边界的管理。一个真正意义上的 Agent,必须回答三个问题:何时决策、如何决策、何时停止。如果这三个问题的答案完全由人工预设,那不是 Agent,是自动化脚本;如果完全交给模型,那不是 Agent,是赌博。
Agent 的本质
Agent 是一个具备环境感知、自主决策、行动执行、反馈迭代能力的系统。它区别于传统软件的根本特征在于:行为路径在运行时确定,而非编码时固定。
从工程角度看,Agent 的本质是决策闭环:
观测 → 推理 → 行动 → 反馈 → 观测 → ...
这个闭环有三个关键参数:
- 决策粒度:每次行动前是否需要重新规划,还是沿用既定策略
- 反馈深度:环境反馈是否改变内部状态,还是仅作为下一个观测输入
- 终止条件:谁来定义"任务完成",是外部信号还是内部判断
很多人把 Agent 理解为"LLM + 工具调用",这忽略了最关键的状态管理和策略演化。一个成熟的 Agent 系统,其复杂度不在工具层,而在决策层:如何让模型在不确定的环境中,做出可预期的行为。
五种主流 Agent 设计模式
ReAct(Reasoning + Acting)
ReAct 是最接近人类思维模式的 Agent 架构。其核心机制是思维-行动交替:
Thought: 我需要查询北京今天的天气
Action: search("北京天气 2026年8月1日")
Observation: 北京今天晴,气温28-35°C
Thought: 用户问的是否适合户外活动,我需要判断温度范围
Answer: 适合,但需注意防暑
适用场景:
- 任务可分解为明确步骤
- 环境反馈相对确定性(如 API 调用)
- 不需要长程规划,单步决策即可推进
局限:
- 无全局规划,容易陷入局部最优
- 依赖高质量 Observation,环境噪声会放大决策失误
- Token 消耗随步数线性增长,长任务成本高
ReAct 的优势在于简单可控,劣势在于无前瞻性。它适合"走一步看一步"的场景,不适合需要全局协调的复杂任务。
Plan-and-Execute
Plan-and-Execute 将决策拆分为两个阶段:先规划,再执行。
Plan:
1. 收集用户需求文档
2. 分析技术栈选型
3. 生成架构设计方案
4. 输出详细技术文档
Execute:
Step 1: 调用文件读取工具,获取需求文档
Step 2: 调用知识库,检索相关技术方案
...
适用场景:
- 任务有明确起点和终点
- 步骤间依赖关系清晰
- 执行环境相对稳定,无需频繁调整计划
局限:
- 计划阶段的质量决定上限,一次规划失误会导致全盘失败
- 执行阶段缺乏灵活调整能力,环境变化时需要重新规划
- 过度依赖初始信息,新信息难以动态融入
Plan-and-Execute 的本质是用确定性对抗不确定性。它假设环境足够稳定,规划足够准确,这在很多场景下是强假设。
Multi-Agent(多智能体协作)
Multi-Agent 将复杂任务拆解到多个 Agent,每个 Agent 承担特定角色:
[ProductManager] 接收用户需求,生成产品文档
[TechLead] 审查文档,生成技术方案
[Developer] 实现方案,输出代码
[Tester] 执行测试,反馈问题
适用场景:
- 任务复杂度超出单 Agent 决策能力
- 需要跨领域知识协作
- 有明确的角色分工和协作流程
局限:
- 协调成本高,Agent 间通信开销大
- 角色边界模糊时会产生推诿或重复劳动
- 整体稳定性取决于最弱环节
Multi-Agent 的核心挑战不是"如何分工",而是"如何对齐"。多个 Agent 之间的认知偏差会逐级放大,最终导致协作失败。成功的 Multi-Agent 系统,必然有严格的接口契约和结果验收机制。
Self-Reflection(自我反思)
Self-Reflection 让 Agent 在执行后自我评估,形成迭代改进:
Action: generate_code(prompt)
Output: def foo(): return 1
Reflection: 代码缺少类型注解,不符合最佳实践
Revision: def foo() -> int: return 1
适用场景:
- 任务有明确的成功标准
- 反馈机制可自动化(如单元测试、代码检查)
- 迭代收益大于成本
局限:
- 自我评估的准确性受模型能力限制,模型无法发现自己不知道的问题
- 迭代次数难以预测,可能无限循环
- 对简单任务过度设计,性价比低
Self-Reflection 的前提是Agent 知道什么是"好结果"。这个标准不能完全交给模型定义,必须由工程系统提供客观反馈。
Tool Use(工具调用)
Tool Use 是 Agent 的基础能力,但作为设计模式时,强调的是工具的选择与组合策略:
User: 分析这个 CSV 文件中的销售数据
Agent:
- 工具选择: pandas_reader (数据加载)
- 工具选择: data_visualizer (可视化)
- 工具组合: 串联执行,传递中间结果
适用场景:
- 工具边界清晰,功能正交
- 工具数量可控(<50个)
- 任务可映射到现有工具集
局限:
- 工具描述质量决定选择准确性,描述不当会导致误用
- 工具组合依赖模型推理能力,复杂组合易出错
- 工具失败时的降级策略难以设计
Tool Use 的本质是能力扩展,但它没有解决"何时使用"的问题。纯粹的 Tool Use 模式需要外部决策系统驱动。
代码实战:一个完整的 ReAct Agent
以下实现一个最小可运行的 ReAct Agent,包含工具定义、推理循环和终止判断:
import json
import re
from typing import Callable, Dict, List, Optional
class Tool:
"""工具基类"""
def __init__(self, name: str, description: str, func: Callable):
self.name = name
self.description = description
self.func = func
def run(self, args: str) -> str:
try:
return self.func(args)
except Exception as e:
return f"Error: {str(e)}"
class ReActAgent:
"""ReAct Agent 实现"""
PROMPT_TEMPLATE = """You are a ReAct agent. Follow this format:
Question: the input question
Thought: your reasoning step
Action: tool_name[args]
Observation: tool result
... (repeat Thought/Action/Observation)
Thought: I now know the final answer
Answer: the final answer
Available tools:
{tools_desc}
Question: {question}
{history}"""
def __init__(self, llm_call: Callable[[str], str], tools: List[Tool], max_steps: int = 10):
self.llm_call = llm_call
self.tools = {t.name: t for t in tools}
self.max_steps = max_steps
def _parse_action(self, text: str) -> Optional[tuple]:
"""解析 Action 和参数"""
pattern = r"Action:\s*(\w+)\[(.*?)\]"
match = re.search(pattern, text, re.DOTALL)
if match:
return match.group(1), match.group(2).strip()
return None
def _parse_final_answer(self, text: str) -> Optional[str]:
"""解析最终答案"""
pattern = r"Answer:\s*(.+?)(?:\n|$)"
match = re.search(pattern, text, re.DOTALL)
return match.group(1).strip() if match else None
def _build_tools_desc(self) -> str:
"""构建工具描述"""
lines = [f"- {name}: {tool.description}" for name, tool in self.tools.items()]
return "\n".join(lines)
def run(self, question: str) -> str:
"""执行推理循环"""
history = ""
for step in range(self.max_steps):
prompt = self.PROMPT_TEMPLATE.format(
tools_desc=self._build_tools_desc(),
question=question,
history=history
)
response = self.llm_call(prompt)
history += response + "\n"
# 检查是否得出最终答案
answer = self._parse_final_answer(response)
if answer:
return answer
# 解析并执行工具调用
action = self._parse_action(response)
if not action:
history += "Observation: Invalid action format\n"
continue
tool_name, args = action
if tool_name not in self.tools:
history += f"Observation: Unknown tool '{tool_name}'\n"
continue
result = self.tools[tool_name].run(args)
history += f"Observation: {result}\n"
return f"Max steps ({self.max_steps}) reached. Last response:\n{response}"
# 示例工具
def calculator(expression: str) -> str:
"""安全计算数学表达式"""
try:
# 仅允许基本数学运算
allowed = set("0123456789+-*/().e ")
if not all(c in allowed for c in expression):
return "Error: Invalid characters in expression"
return str(eval(expression))
except Exception as e:
return f"Error: {str(e)}"
def search_mock(query: str) -> str:
"""模拟搜索工具"""
mock_db = {
"python": "Python 3.12 released in 2023, with performance improvements",
"agent": "AI Agent is a system with perception, reasoning, and action",
"react": "ReAct pattern: Reasoning + Acting interleaving"
}
for key in mock_db:
if key in query.lower():
return mock_db[key]
return "No relevant information found"
# 使用示例
if __name__ == "__main__":
# 模拟 LLM 调用(实际使用时替换为真实 API)
def mock_llm(prompt: str) -> str:
# 简化示例:实际应调用 GPT/Claude 等
if "2+3" in prompt or "calculate" in prompt.lower():
return "Thought: I need to use the calculator\nAction: calculator[2+3]"
elif "agent" in prompt.lower():
return "Thought: I should search for agent information\nAction: search[AI Agent]"
return "Thought: I now know the final answer\nAnswer: Task completed"
tools = [
Tool("calculator", "Evaluate math expressions", calculator),
Tool("search", "Search for information", search_mock)
]
agent = ReActAgent(mock_llm, tools)
result = agent.run("What is 2+3?")
print(result)
这个实现包含以下关键设计:
- 严格的格式约束:通过正则解析 Action,避免模型输出不稳定导致的解析失败
- 步数限制:
max_steps防止无限循环 - 错误隔离:工具执行异常不影响 Agent 主流程
- 历史管理:维护完整的 Thought-Action-Observation 链
实际生产中需要补充:LLM API 接入、更复杂的历史压缩、工具参数验证、并发调用等。
Agent 开发框架对比
LangChain
优势:
- 生态最完善,工具链丰富
- 抽象层次清晰,从简单 Chain 到复杂 Agent
- 文档和社区支持强
劣势:
- 抽象过度,源码阅读成本高
- 版本迭代快,API 稳定性差
- 复杂场景下性能调优困难
LangChain 适合快速原型验证和标准场景落地。如果需求超出预设抽象,扩展成本会显著上升。
AutoGen
优势:
- Multi-Agent 原生支持,角色对话模式自然
- 人机协作设计成熟,支持中断和干预
- Microsoft 背书,长期维护有保障
劣势:
- 单 Agent 场景能力平庸
- 配置复杂,学习曲线陡峭
- 调试困难,多 Agent 状态追踪成本高
AutoGen 适合明确的多角色协作场景。如果不需要 Multi-Agent,它的价值难以体现。
CrewAI
优势:
- 角色定义直观,Task-Assignment 模式清晰
- 轻量级,代码侵入性低
- 快速搭建 Demo 体验好
劣势:
- 生产级特性不足(如容错、监控)
- 社区规模小,问题解决依赖官方
- 扩展能力受限,复杂定制困难
CrewAI 适合中小规模项目和快速迭代阶段。大规模生产环境需要自行补齐工程能力。
踩坑点
无限循环
现象:Agent 在某个状态反复执行相同 Action,无法推进。
原因:
- 终止条件定义模糊,模型无法判断何时结束
- 工具返回结果与预期不符,Agent 陷入"重试-失败"循环
- 规划失败,Agent 反复尝试错误的路径
解决:
- 硬性步数限制(如
max_steps=15) - 状态去重:记录已执行 Action,重复时强制终止
- 显式终止信号:要求模型在满足条件时输出特定标记
Token 爆炸
现象:单次调用 token 数超限,或累计成本不可控。
原因:
- 历史记录无压缩,Observation 过长
- Multi-Agent 通信开销累积
- 工具返回结果未精简
解决:
- 历史压缩:只保留关键 Thought 和最终 Observation
- 工具输出截断:限制返回内容长度
- 分层规划:高层策略用小模型,低层执行用大模型
工具调用失败处理
现象:工具执行异常导致 Agent 中断或输出错误结果。
原因:
- 工具参数验证缺失,模型生成无效输入
- 工具依赖环境不稳定(如网络、文件系统)
- 错误信息不明确,Agent 无法理解失败原因
解决:
- 工具层防御:参数校验 + 异常捕获 + 明确错误消息
- 降级策略:工具失败时提供替代方案或要求人工干预
- 工具隔离:单个工具失败不影响其他工具和主流程
作者观点与选型建议
Agent 不是万能解,它是对确定性工程的补充,而非替代。
选型决策树:
-
任务是否需要运行时决策?
- 否 → 传统软件工程,不需要 Agent
- 是 → 继续
-
决策路径是否可枚举?
- 是 → ReAct,单步决策足够
- 否 → 继续
-
任务是否可分解为独立子任务?
- 是 → Plan-and-Execute 或 Multi-Agent
- 否 → Self-Reflection + 人工监督
-
是否有明确的成功标准和自动化反馈?
- 是 → Self-Reflection 可行
- 否 → 降低自动化预期,引入人工验收环节
框架选择:
- 标准场景 + 快速落地 → LangChain
- 多角色协作 → AutoGen
- 中小规模 + 快速迭代 → CrewAI
- 特殊需求 + 团队技术强 → 自研
Agent 的成熟度,不取决于模型能力,而取决于决策边界的设计和工程兜底的能力。把不确定性交给模型,把确定性留给工程,这是 Agent 落地的核心原则。
更多推荐



所有评论(0)