大模型 Agent 架构设计:ReAct、Tool Use 与 Multi-Agent 的演进之路

引言:从“对话机器人”到“数字员工”的跨越

自从 ChatGPT 等大型语言模型(LLM)问世以来,我们与 AI 的交互方式发生了翻天覆地的变化。然而,早期的 LLM 更像是一个“封闭的超级大脑”——它拥有渊博的知识,却无法与外部世界交互,存在知识滞后、容易产生幻觉(Hallucination)以及无法执行实际操作等致命缺陷。

为了打破这一局限,Agent(智能体) 的概念应运而生。如果说 LLM 是一个拥有极高智商但足不出户的学者,那么 Agent 就是配备了对讲机、工具箱,并且能够在现实世界中执行任务的**“数字员工”**。

在构建大模型 Agent 的技术演进中,有三个核心概念构成了现代 Agent 架构的基石:

  1. ReAct(Reasoning and Acting):定义了智能体的思维与行动范式。
  2. Tool Use(工具调用):赋予了智能体连接真实世界的能力。
  3. Multi-Agent(多智能体):实现了从单体智能到群体智能的跨越。

本文将深入探讨这三大核心架构的设计理念、底层原理,并结合实际的代码示例,带你从零掌握大模型 Agent 的架构设计精髓。


一、 Agent 的灵魂:ReAct 范式

在 ReAct 出现之前,业界通常将大模型的能力分为两类:推理行动

  • 推理派(如 Chain of Thought, CoT):让模型“想一想”再回答。虽然提高了逻辑准确性,但模型依然只能停留在语言层面,无法获取新知识。
  • 行动派(如 Act-only):直接让模型调用工具。但缺乏先验的规划,导致工具调用往往非常盲目,错误率极高。

2022 年,耶鲁大学和 Google 团队联合发表了经典论文《ReAct: Synergizing Reasoning and Acting in Language Models》。ReAct 的核心思想极其优雅:让大模型将“推理”和“行动”交织在一起,形成一个迭代的闭环。

1. ReAct 的工作流拆解

ReAct 模式下,大模型会陷入一个持续的循环,直到任务完成:

  • Thought(思考):分析当前的问题、已有的观察结果,并决定下一步该做什么。
  • Action(行动):根据思考的结果,选择并调用一个外部工具(如搜索引擎、数据库查询)。
  • Observation(观察):接收工具返回的结果。

模型在这个 Thought -> Action -> Observation 的循环中不断螺旋上升,直到收集到足够的信息,最终输出答案。

2. ReAct 的 Prompt 设计魔法

ReAct 不需要复杂的微调,完全可以通过提示词工程来实现。下面是一个典型的 ReAct Prompt 模板骨架:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
你是一个优秀的任务规划助手。你需要回答用户的问题。
为了回答问题,你可以使用以下工具:

{tools_description}

请严格按照以下格式输出你的每一次回复:

Question: 用户输入的问题
Thought: 你思考下一步应该做什么
Action: 你决定使用的工具名称
Action Input: 传入工具的参数,必须是合法的 JSON
Observation: 工具执行返回的结果(这一步由系统填入,你无需生成)

... (Thought/Action/Action Input/Observation 可以重复多次)

Thought: 我现在已经知道最终答案了
Final Answer: 针对用户问题的最终回答

开始吧!

Question: {user_input}

3. ReAct 的代码实现示例(伪代码)

让我们用一段简单的 Python 代码来模拟 ReAct 的运行机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import openai

# 定义可用工具
def google_search(query):
# 实际应用中这里调用搜索 API
return f"关于 '{query}' 的搜索结果:2023年大模型Agent技术发展迅速。"

# Agent 系统消息
SYSTEM_PROMPT = """...""" # 填入上述的 ReAct Prompt 模板

def run_react_agent(user_query, max_iterations=5):
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_query}
]

for i in range(max_iterations):
# 1. 大模型生成 Thought 和 Action
response = openai.ChatCompletion.create(
model="gpt-4",
messages=messages,
temperature=0
)
assistant_reply = response.choices[0].message['content']
messages.append({"role": "assistant", "content": assistant_reply})
print(f"--- Iteration {i+1} ---")
print(assistant_reply)

# 2. 解析 Action 和 Action Input
if "Final Answer:" in assistant_reply:
print("任务完成!")
break

if "Action:" in assistant_reply and "Action Input:" in assistant_reply:
action_name = assistant_reply.split("Action:")[1].split("\n")[0].strip()
action_input = assistant_reply.split("Action Input:")[1].split("\n")[0].strip()

# 3. 执行工具获得 Observation
if action_name == "Google Search":
observation = google_search(action_input)
else:
observation = "未知工具"

print(f"Observation: {observation}")

# 4. 将结果追加到上下文,开启下一轮循环
messages.append({"role": "user", "content": f"Observation: {observation}"})
else:
break

# 运行测试
run_react_agent("什么是大模型Agent?它的最新发展现状是什么?")

ReAct 的痛点与优化:
尽管 ReAct 极大提升了逻辑可靠性,但它要求大模型具有极强的指令遵循能力。早期的 3B/7B 模型很容易在严格的格式要求(如 Action:Action Input: 的解析)下崩溃。直到 OpenAI 推出了 Function Calling(函数调用) 机制,才彻底解决了格式解析的噩梦,这也就是如今统称的 Tool Use


二、 连接物理世界:Tool Use(工具调用)架构

如果说 ReAct 是 Agent 的灵魂,那么 Tool Use 就是 Agent 的手和脚。

早期的 Agent 框架(如早期的 LangChain)通过正则表达式或 JSON 解析来提取大模型输出中的工具调用意图。这种方式极其脆弱。2023 年下半年,主流大模型厂商(OpenAI、Anthropic、智谱等)开始在模型层面原生支持 Function Calling

1. Function Calling 的工作原理

原生 Function Calling 改变了传统的文本补全机制。当系统识别到用户的意图需要调用工具时,LLM 不会生成包含工具参数的文本字符串,而是直接生成一段结构化的 JSON 对象,由 SDK 自动拦截并解析。

执行流程如下:

  1. 用户输入查询。
  2. 开发者将可用工具的 JSON Schema(包含名称、描述、参数类型)连同用户输入一起发送给 LLM。
  3. LLM 判断是否需要调用工具。
    • 如果不需要:直接返回文本回复。
    • 如果需要:返回一个特殊的 tool_calls 对象,其中包含工具名称和预填好的 JSON 参数(此时 LLM 停止生成)。
  4. 本地代码拦截该对象,执行对应的 Python 函数。
  5. 将函数执行的结果作为 tool 角色的消息,再次发送给 LLM。
  6. LLM 根据工具返回的结果,总结出最终的自然语言回复。

2. 现代化的 Tool Use 实战代码

下面是基于 OpenAI API 最新版 SDK 的 Function Calling 实现。假设我们要构建一个天气查询 Agent:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import json
from openai import OpenAI

client = OpenAI(api_key="your-api-key")

# 1. 定义本地工具函数
def get_current_weather(location, unit="celsius"):
"""获取指定城市的天气(模拟数据)"""
if "北京" in location.lower():
return json.dumps({"location": "北京", "temperature": "25", "unit": unit})
elif "上海" in location.lower():
return json.dumps({"location": "上海", "temperature": "28", "unit": unit})
else:
return json.dumps({"location": location, "temperature": "unknown"})

# 2. 描述工具的 JSON Schema (这是 Agent 架构设计中最关键的一环)
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "获取指定地点的当前天气情况",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市或区县,例如:北京市",
},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["location"],
},
},
}
]

# 3. Agent 主循环
def run_tool_use_agent(user_query):
messages = [{"role": "user", "content": user_query}]

# 第一次调用大模型:判断是否需要调用工具
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
tool_choice="auto" # auto 表示由模型自己决定
)

response_message = response.choices[0].message
tool_calls = response_message.tool_calls

# 检查模型是否决定调用工具
if tool_calls:
# 将大模型的工具调用指令追加到历史记录
messages.append(response_message)

# 遍历所有工具调用(支持并行调用)
for tool_call in tool_calls:
function_name = tool_call.function.name
function_to_call = globals()[function_name] # 获取本地函数对象

# 解析大模型生成的参数
function_args = json.loads(tool_call.function.arguments)

print(f"准备执行工具: {function_name},参数: {function_args}")

# 在本地执行真实函数
function_response = function_to_call(
location=function_args.get("location"),
unit=function_args.get("unit"),
)

# 将工具执行结果追加到历史记录中,role 为 "tool"
messages.append(
{
"tool_call_id": tool_call.id,
"role": "tool",
"name": function_name,
"content": function_response,
}
)

# 第二次调用大模型:让模型根据工具返回的结果总结回答
second_response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
)
return second_response.choices[0].message.content
else:
# 无需工具,直接返回模型原有的文本回复
return response_message.content

# 测试
print(run_tool_use_agent("今天北京和上海的天气怎么样?"))

3. Tool Use 的架构设计原则

在真实的企业级应用中,设计 Tool Use 架构需要遵循以下原则:

  • 工具描述是新的 Prompt:大模型完全依赖 description 字段来决定何时、如何使用工具。描述必须清晰、没有歧义。比如,区分 查询DB搜索Web 的边界。
  • 沙箱隔离:Agent 拥有了调用代码(如 Python REPL)或操作系统的能力后,极其危险。必须在 Docker 容器或安全沙箱中执行工具,防止模型被 Prompt 注入攻击后执行恶意命令(如 rm -rf /)。
  • 错误重试与兜底机制:工具调用可能由于网络、API 频率限制而失败。架构设计需包含重试机制,并将错误信息作为 Observation 反馈给大模型,让它尝试修正参数或更换工具。

三、 突破单体极限:Multi-Agent(多智能体)架构

随着任务复杂度的指数级上升,单体 Agent 架构(将所有的工具和 Prompt 塞给一个大模型)开始暴露出严重的瓶颈:

  1. 上下文窗口污染:工具过多导致模型迷失,准确率下降。
  2. 缺乏专业深度:要求一个模型既懂写代码,又懂法律合规,还懂财务分析,这在 Prompt 上很难做到。
  3. 容错率低:单点 Agent 一旦陷入死循环,整个任务失败。

自然界的蚁群、蜂群启示了我们:通过多个职责单一、专业的智能体相互协作,能够涌现出解决复杂问题的强大能力。 这就是 Multi-Agent 系统(MAS)。

1. 主流的 Multi-Agent 拓扑结构

设计一个多智能体系统,首要任务是确定它们之间的通信与控制拓扑结构

  • 集中式(中心化控制)
    有一个 “Supervisor”(主管)Agent 负责拆解任务、分发任务,并收集其他 Worker Agent 的结果进行汇总。这是目前企业内落地最广、最可控的架构。
  • 网状式(去中心化对话)
    Agent 之间直接对话,自主决定将任务传递给谁。例如微软的 AutoGen 就是典型的网状结构,开发者作为一个 Agent 加入群聊。
  • 流水线式(工作流编排)
    任务按照预定义的 DAG(有向无环图)流转。类似于传统的 ETL 或 Airflow 工作流,只不过节点由代码服务变成了 LLM Agent。

2. 经典框架剖析:CrewAI 与 LangGraph

目前开源社区有众多优秀的 Multi-Agent 框架,最具代表性的是基于集中式/流水线模式的 CrewAI 和基于图计算的 LangGraph

以**开发一个虚拟的“科技公司软件开发团队”**为例,我们可以使用类 CrewAI 的思想设计如下 Agent:

  • Product Manager (PM):负责分析用户需求,拆解 Epic。
  • Architect:负责设计技术栈和系统架构。
  • Senior Developer:负责编写代码。
  • QA Engineer:负责测试并生成测试用例。

核心调度器架构代码示例:

以下代码演示了一个简化的 中心化 Supervisor 架构。Supervisor 负责接收用户任务,将其路由给不同领域的专家 Agent,并决定何时结束任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import json
from openai import OpenAI

client = OpenAI(api_key="your-api-key")

# 模拟不同的专家 Agent
# 实际应用中,这些 Agent 会拥有不同的 System Prompt 和 Tools
EXPERT_AGENTS = {
"Researcher": {"name": "Researcher", "role": "负责在互联网上搜集相关资料和数据。"},
"Coder": {"name": "Coder", "role": "负责根据需求编写 Python 代码。"},
"Reviewer": {"name": "Reviewer", "role": "负责检查代码的逻辑漏洞和安全性。"}
}

# 定义 Supervisor 眼中的“工具”,这里的工具其实是其他的 Agent
agent_tools = [
{
"type": "function",
"function": {
"name": name,
"description": agent_info["role"],
"parameters": {
"type": "object",
"properties": {
"task_description": {
"type": "string",
"description": f"分配给 {name} 的具体任务描述"
}
},
"required": ["task_description"]
}
}
} for name, agent_info in EXPERT_AGENTS.items()
]

# Supervisor 的系统提示词
SUPERVISOR_PROMPT = """
你是一个项目经理(Supervisor)。你的目标是管理一个由专家组成的团队来完成用户的复杂任务。
你可以通过调用工具来把任务分配给不同的专家成员。
当所有子任务都已完成,并且你收集到了足够的信息来回答用户最初的问题时,请直接回复最终的总结结果。
"""

def run_multi_agent_system(user_task):
messages = [
{"role": "system", "content": SUPERVISOR_PROMPT},
{"role": "user", "content": user_task}
]

while True:
# 1. Supervisor 思考下一步该分派什么任务,或者是否结束
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=agent_tools,
tool_choice="auto"
)

message = response.choices[0].message
messages.append(message)

if not message.tool_calls:
# 如果没有工具调用,说明 Supervisor 认为任务已经完成
print("\n=== 最终项目汇报 ===")
return message.content

# 2. 处理分配的任务
for tool_call in message.tool_calls:
agent_name = tool_call.function.name
task_desc = json.loads(tool_call.function.arguments)["task_description"]

print(f"\n[Supervisor] 正在将任务分配给: {agent_name}")
print(f"[Task] {task_desc}")

# 3. 模拟专家 Agent 工作(在实际架构中,这里会发起另一次 LLM 调用或调用工具)
# 我们这里用一个 mock 函数模拟专家的输出
if agent_name == "Researcher":
agent_result = f"关于 '{task_desc}' 的调研报告:Python的FastAPI框架是目前最快的Web框架之一。"
elif agent_name == "Coder":
agent_result = f"根据任务 '{task_desc}' 编写的代码如下:\n`print('Hello World')`"
else:
agent_result = f"对任务的审查结果 '{task_desc}':代码存在安全漏洞,建议修改。"

print(f"[{agent_name}] 工作完成: {agent_result}")

# 4. 将专家的工作成果作为 tool 的返回结果反馈给 Supervisor
messages.append({
"tool_call_id": tool_call.id,
"role": "tool",
"name": agent_name,
"content": agent_result
})

# 运行 Multi-Agent 系统
final_report = run_multi_agent_system("帮我开发一个安全的 Hello World Web 接口。")
print(final_report)

3. Multi-Agent 架构设计的避坑指南

虽然 Multi-Agent 看起来极具科幻感,但在生产环境中极其容易演变成一场灾难。

  • 过度的“认知失调”:Agent 之间如果出现互相推诿或指令冲突,会导致无休止的内部对话循环(即系统一直打印日志却不干正事)。必须引入 轮次限制强制终止条件
  • 状态共享难题:Agent 之间如何共享上下文?把所有对话历史都塞给每个 Agent 会导致上下文溢出,只给一部分又会导致信息断层。解决方案通常是引入一个共享的短期记忆黑板 或使用向量数据库作为长期记忆
  • 成本控制:多一次 Agent 交互,就多消耗千级别的 Token。一个 Supervisor 调度 3 个 Agent 走完一个完整的 ReAct 流程,消耗的 Token 可能是单体 Agent 的数十倍。优化系统提示词、精简不必要的信息流转是控制成本的关键。

四、 企业级 Agent 架构的工程化考量

在掌握了 ReAct、Tool Use 和 Multi-Agent 之后,我们已经能构建出一个能在本地跑通的 Demo。但在真实的商业场景中,从 Demo 到生产级系统,还需要一套强大的外围基建。

1. 记忆架构

  • 短期记忆:主要依赖 LLM 的上下文窗口。通过有效的消息截断、滑动窗口算法,确保当前对话不超出 Token 限制。
  • 长期记忆:将用户偏好、历史操作记录存入向量数据库(如 Milvus、Qdrant、Pinecone)。每次 Agent 思考时,先通过 RAG(检索增强生成)技术从数据库中捞取相关记忆注入 Prompt 中。MemGPT 架构是目前解决长期记忆的优秀方案。

2. 规划与任务拆解

面对庞大任务(如“帮我写一份年度财报分析PPT”),直接使用 ReAct 会因为步骤过多而彻底崩溃。现代 Agent 架构通常采用 Plan-and-Solve 策略:

  • 规划阶段:大模型一次性生成一个全局的有向无环图(DAG)或执行计划清单。
  • 执行阶段:按照计划一步步执行 Tool Use。如果遇到错误,再局部使用 ReAct 进行修正。

3. 可观测性与评估

大模型的输出具有高度的不可预测性(黑盒)。构建 Agent 离不开强大的追踪系统。业界标准是集成 LangSmithOpenTelemetry,详细记录每一次 Prompt 的输入、耗时、Token 消耗以及 Tool 的返回结果。开发者需要通过这些日志不断微调工具的描述和系统提示词。


总结

从简单的对话模型到复杂的多智能体系统,大模型 Agent 架构的演进展现了软件工程向“AI 原生”转变的深刻趋势。

我们可以将这三种架构视为 Agent 设计模式的递进:

  1. ReAct 提供了单体思考与行动的微观闭环
  2. Tool Use 打通了微观闭环与外部世界的物理接口
  3. Multi-Agent 则通过分工与协同,在宏观层面重构了复杂系统的解法

未来,随着大模型底层能力的提升(尤其是长上下文、逻辑推理和指令遵循能力的增强),Agent 架构将变得越发高效与鲁棒。作为开发者和架构师,深入理解这些底层机制,不仅有助于我们更好地使用 LangChain、AutoGen、CrewAI 等框架,更能让我们在 AI 应用爆发的浪潮中,游刃有余地设计出下一代数字员工和企业级智能工作流。