AI Agent 四大核心模块深度拆解:ReAct、Planning、Memory 与 Tool Use
前言
如果说大语言模型(LLM)是 AI Agent 的"大脑",那么 ReAct、Planning、Memory、Tool Use 就是让这个大脑能够思考、规划、记忆、行动的四大认知模块。
这四个模块共同构成了 Agent 的认知闭环:
┌──────────────────────────────────────────────────────────┐
│ AI Agent 认知闭环 │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Planning│ ───▶ │ ReAct │ ───▶ │Tool Use │ │
│ │ (规划) │ │ (推理) │ │ (行动) │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────┐ │ │
│ └─────────▶│ Memory │◀──────────┘ │
│ │ (记忆) │ │
│ └─────────┘ │
└──────────────────────────────────────────────────────────┘
本文将从技术原理、核心算法、代码实现、最佳实践四个维度,对这四大模块进行系统性深度拆解。
一、Planning(规划模块):Agent 的"大脑皮层"
1.1 规划的本质
Planning 模块解决的核心问题是:
给定一个目标,如何将其分解为可执行的步骤序列?
这对应人类认知中的"执行功能"(Executive Function)------我们不会直接行动,而是先在脑中"预演":要做什么?先做什么?后做什么?每步预期什么结果?
1.2 规划的三种范式
范式一:任务分解(Task Decomposition)
将复杂目标拆解为原子任务。
经典方法:Plan-and-Solve
输入:复杂目标 G
输出:任务序列 [T1, T2, T3, ..., Tn]
算法:
1. LLM 分析目标 G,识别关键子目标
2. 对每个子目标,判断是否可原子化执行
3. 若不可,递归分解;若可,加入任务队列
4. 输出有序任务序列
Prompt 模板示例:
python
PLAN_PROMPT = """
你是一个任务规划专家。请将以下目标分解为具体可执行的步骤。
目标:{goal}
约束条件:
1. 每个步骤应当是原子操作,不可再分
2. 步骤之间有明确的依赖关系
3. 每个步骤应当有明确的成功标准
请按以下格式输出:
Step 1: [描述] | 依赖: 无 | 成功标准: [标准]
Step 2: [描述] | 依赖: Step 1 | 成功标准: [标准]
...
"""
范式二:思维链规划(Chain-of-Thought Planning)
让模型显式输出推理过程,而非直接给出行动计划。
CoT 的数学形式化:
设问题为 QQQ,答案为 AAA,中间推理步骤为 s1,s2,...,sns_1, s_2, ..., s_ns1,s2,...,sn
传统方法:P(A∣Q)P(A|Q)P(A∣Q) ------ 直接预测答案
CoT 方法:P(s1,s2,...,sn,A∣Q)=∏i=1nP(si∣Q,s<i)⋅P(A∣Q,s1:n)P(s_1, s_2, ..., s_n, A | Q) = \prod_{i=1}^{n} P(s_i | Q, s_{<i}) \cdot P(A | Q, s_{1:n})P(s1,s2,...,sn,A∣Q)=∏i=1nP(si∣Q,s<i)⋅P(A∣Q,s1:n)
关键洞察: 显式的中间步骤 sis_isi 充当了"思维脚手架",让模型在复杂推理中不迷失方向。
范式三:树状搜索规划(Tree-of-Thought)
当任务存在多种可能路径时,用树搜索探索最优解。
目标
│
┌───────────┼───────────┐
▼ ▼ ▼
路径A 路径B 路径C
│ │ │
┌───┴───┐ ┌───┴───┐ ┌───┴───┐
▼ ▼ ▼ ▼ ▼ ▼
A1 A2 B1 B2 C1 C2
│ │ │ │ │ │
评分 评分 评分 评分 评分 评分
│ │ │ │ │ │
└───┬───┘ └───┬───┘ └───┬───┘
▼ ▼ ▼
选择最优路径继续扩展...
ToT 算法伪代码:
python
def tree_of_thought(problem, max_depth=5, beam_width=3):
# 初始化:根节点为问题本身
root = Node(state=problem, depth=0)
frontier = [root]
for depth in range(max_depth):
candidates = []
for node in frontier:
# 生成多个可能的下一步思考
thoughts = llm_generate_thoughts(node.state, n=beam_width)
for thought in thoughts:
child = Node(state=thought, parent=node, depth=depth+1)
# 评估这个思考的质量
child.score = llm_evaluate_thought(thought)
candidates.append(child)
# 保留 top-k 高分节点继续扩展
frontier = sorted(candidates, key=lambda x: x.score)[:beam_width]
# 检查是否找到解
if any(is_solution(node) for node in frontier):
return backtrack_solution(node)
return best_path(frontier)
1.3 规划的挑战与解决方案
| 挑战 | 描述 | 解决方案 |
|---|---|---|
| 规划幻觉 | 模型生成了不可执行的步骤 | 添加步骤可行性验证器 |
| 依赖错误 | 步骤顺序违反依赖关系 | 用 DAG 建模依赖,拓扑排序 |
| 规划僵化 | 执行中遇到意外无法调整 | 引入动态重规划机制 |
| 粒度不当 | 步骤过大或过细 | 自适应粒度控制 |
二、ReAct(推理-行动循环):Agent 的"认知引擎"
2.1 ReAct 的诞生
ReAct(Reason + Act)由 Yao et al. 在 2023 年的论文《ReAct: Synergizing Reasoning and Acting in Language Models》中提出。
核心思想:让模型在"思考"和"行动"之间交替,形成推理-行动循环。
2.2 ReAct 的形式化框架
状态定义:
- ttt:当前时间步
- oto_tot:第 ttt 步的观察(Observation,来自环境)
- ata_tat:第 ttt 步的行动(Action,对环境的操作)
- τt\tau_tτt:第 ttt 步的思考(Thought,内部推理)
ReAct 循环:
初始化:context = 用户输入
循环直到任务完成:
1. 思考:τ_t = LLM(context + 历史轨迹)
2. 决策:a_t = extract_action(τ_t)
3. 执行:o_t = Environment.execute(a_t)
4. 更新:context = context + τ_t + a_t + o_t
5. 判断:若 τ_t 包含 "任务完成",退出循环
2.3 ReAct 的完整实现
python
import re
from typing import List, Tuple, Optional
from dataclasses import dataclass
@dataclass
class ReActStep:
thought: str # 思考内容
action: Optional[str] = None # 行动名称
action_input: Optional[str] = None # 行动参数
observation: Optional[str] = None # 观察结果
class ReActAgent:
def __init__(self, llm, tools: dict, max_iterations: int = 10):
self.llm = llm
self.tools = tools
self.max_iterations = max_iterations
self.history: List[ReActStep] = []
def build_prompt(self, question: str) -> str:
"""构建 ReAct Prompt"""
prompt = f"""
你是一个使用 ReAct 范式解决问题的 AI Agent。
可用工具:
{self._format_tools()}
问题:{question}
请按以下格式思考和行动:
Thought: [你的思考过程]
Action: [工具名称]
Action Input: [工具输入参数]
或者当你认为问题已解决时:
Thought: [最终思考]
Final Answer: [最终答案]
历史轨迹:
{self._format_history()}
现在请继续:
"""
return prompt
def parse_response(self, response: str) -> ReActStep:
"""解析 LLM 响应,提取 Thought/Action/Observation"""
step = ReActStep(thought="")
# 提取 Thought
thought_match = re.search(r'Thought:\s*(.+?)(?=Action:|Final Answer:|$)',
response, re.DOTALL)
if thought_match:
step.thought = thought_match.group(1).strip()
# 提取 Action
action_match = re.search(r'Action:\s*(.+?)\n', response)
if action_match:
step.action = action_match.group(1).strip()
# 提取 Action Input
input_match = re.search(r'Action Input:\s*(.+?)(?=\n|$)', response)
if input_match:
step.action_input = input_match.group(1).strip()
# 提取 Final Answer
final_match = re.search(r'Final Answer:\s*(.+?)$', response, re.DOTALL)
if final_match:
step.observation = f"FINAL: {final_match.group(1).strip()}"
return step
def execute_action(self, step: ReActStep) -> str:
"""执行工具调用"""
if step.action and step.action in self.tools:
tool = self.tools[step.action]
try:
result = tool(step.action_input)
return f"Observation: {result}"
except Exception as e:
return f"Observation: Error - {str(e)}"
return "Observation: Invalid action"
def run(self, question: str) -> str:
"""运行 ReAct 循环"""
for i in range(self.max_iterations):
# 1. 构建 Prompt 并获取 LLM 响应
prompt = self.build_prompt(question)
response = self.llm(prompt)
# 2. 解析响应
step = self.parse_response(response)
self.history.append(step)
# 3. 检查是否完成
if step.observation and step.observation.startswith("FINAL:"):
return step.observation[6:].strip()
# 4. 执行行动并获取观察
if step.action:
observation = self.execute_action(step)
step.observation = observation
print(f"[Step {i+1}] Thought: {step.thought[:50]}...")
if step.action:
print(f" Action: {step.action}({step.action_input})")
print(f" {observation[:100]}...")
return "达到最大迭代次数,任务未完成"
# 使用示例
def search_tool(query: str) -> str:
# 模拟搜索
return f"搜索结果:关于 '{query}' 的相关信息..."
def calculate_tool(expression: str) -> str:
try:
result = eval(expression)
return f"计算结果:{result}"
except:
return "计算错误"
tools = {
"search": search_tool,
"calculate": calculate_tool,
}
# agent = ReActAgent(llm=your_llm, tools=tools)
# result = agent.run("2024年世界杯冠军是谁?他们赢了多少场比赛?")
2.4 ReAct vs Chain-of-Thought vs Plan-and-Execute
| 维度 | Chain-of-Thought | Plan-and-Execute | ReAct |
|---|---|---|---|
| 交互性 | 无外部交互 | 先规划后执行 | 边推理边交互 |
| 适应性 | 静态推理 | 静态规划 | 动态调整 |
| 错误恢复 | 无法恢复 | 需重规划 | 自动反思调整 |
| 适用场景 | 纯推理问题 | 结构化任务 | 开放式任务 |
| 计算成本 | 低 | 中 | 高 |
2.5 ReAct 的关键设计原则
- 思考先行:每次行动前必须有显式的 Thought,防止盲目行动
- 观察驱动:每次行动后必须等待 Observation,基于反馈决策下一步
- 轨迹记忆:完整保留历史 Thought-Action-Observation,避免重复
- 终止条件:明确的 Final Answer 或最大迭代次数,防止死循环
三、Memory(记忆模块):Agent 的"长期记忆系统"
3.1 为什么 Agent 需要记忆?
没有记忆的 Agent 就像金鱼------每轮对话都从零开始,无法:
- 记住用户偏好和历史上下文
- 从过往经验中学习和改进
- 在多步任务中保持状态一致性
3.2 记忆的三层架构
┌─────────────────────────────────────────────────────────┐
│ Agent Memory System │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Sensory Memory (瞬时记忆) │ │
│ │ • 当前输入的原始感知 │ │
│ │ • 存活时间:毫秒级 │ │
│ │ • 实现:直接传入 LLM Context │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Working Memory (工作记忆) │ │
│ │ • 当前任务相关的上下文 │ │
│ │ • 存活时间:当前会话 │ │
│ │ • 实现:Conversation Buffer / Sliding Window │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Long-Term Memory (长期记忆) │ │
│ │ • 跨会话的持久化知识 │ │
│ │ • 存活时间:永久 │ │
│ │ • 实现:Vector DB + Embedding 检索 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
3.3 工作记忆的实现
方案一:固定窗口(Fixed Window)
python
class FixedWindowMemory:
def __init__(self, max_messages: int = 10):
self.max_messages = max_messages
self.messages = []
def add(self, role: str, content: str):
self.messages.append({"role": role, "content": content})
# 超过限制时,保留系统消息 + 最近的 N 条
if len(self.messages) > self.max_messages:
system_msgs = [m for m in self.messages if m["role"] == "system"]
other_msgs = [m for m in self.messages if m["role"] != "system"]
self.messages = system_msgs + other_msgs[-(self.max_messages - len(system_msgs)):]
def get_context(self) -> list:
return self.messages
方案二:Token 预算(Token Budget)
python
class TokenBudgetMemory:
def __init__(self, max_tokens: int = 4000, tokenizer=None):
self.max_tokens = max_tokens
self.tokenizer = tokenizer # tiktoken 或类似
self.messages = []
def count_tokens(self, messages: list) -> int:
text = " ".join(m["content"] for m in messages)
return len(self.tokenizer.encode(text))
def add(self, role: str, content: str):
self.messages.append({"role": role, "content": content})
self._trim()
def _trim(self):
"""从旧到新删除消息,直到符合 token 预算"""
while self.count_tokens(self.messages) > self.max_tokens and len(self.messages) > 1:
# 删除最早的非系统消息
for i, msg in enumerate(self.messages):
if msg["role"] != "system":
self.messages.pop(i)
break
方案三:摘要压缩(Summary Compression)
python
class SummaryMemory:
def __init__(self, llm, max_tokens: int = 4000):
self.llm = llm
self.max_tokens = max_tokens
self.raw_messages = []
self.summary = ""
def add(self, role: str, content: str):
self.raw_messages.append({"role": role, "content": content})
# 当超过 token 限制时,压缩旧消息
if self._estimate_tokens() > self.max_tokens:
self._compress()
def _compress(self):
"""将旧消息压缩为摘要"""
to_compress = self.raw_messages[:-4] # 保留最近 4 条
keep = self.raw_messages[-4:]
prompt = f"""请将以下对话历史压缩为简洁的摘要,保留关键信息:
对话历史:
{self._format_messages(to_compress)}
当前摘要:
{self.summary}
请输出更新后的摘要:"""
self.summary = self.llm(prompt)
self.raw_messages = keep
def get_context(self) -> list:
"""返回摘要 + 最近消息"""
context = []
if self.summary:
context.append({"role": "system", "content": f"[历史摘要] {self.summary}"})
context.extend(self.raw_messages)
return context
3.4 长期记忆的实现
长期记忆的核心是语义检索:用向量相似度找到与当前查询最相关的历史记忆。
完整实现:基于 Chroma 的长期记忆
python
import chromadb
from chromadb.config import Settings
from typing import List, Optional
import uuid
class VectorLongTermMemory:
def __init__(self,
persist_directory: str = "./memory_db",
collection_name: str = "agent_memory",
embedding_function=None):
# 初始化 Chroma 客户端
self.client = chromadb.PersistentClient(path=persist_directory)
self.collection = self.client.get_or_create_collection(
name=collection_name,
embedding_function=embedding_function # 默认用 Chroma 的
)
def save(self,
content: str,
metadata: Optional[dict] = None,
memory_type: str = "episodic") -> str:
"""
保存一条记忆
Args:
content: 记忆内容
metadata: 元数据(如时间戳、任务ID、重要性等)
memory_type: 记忆类型(episodic=情景, semantic=语义)
Returns:
记忆ID
"""
memory_id = str(uuid.uuid4())
if metadata is None:
metadata = {}
metadata["memory_type"] = memory_type
metadata["timestamp"] = datetime.now().isoformat()
self.collection.add(
ids=[memory_id],
documents=[content],
metadatas=[metadata]
)
return memory_id
def recall(self,
query: str,
n_results: int = 5,
where: Optional[dict] = None) -> List[dict]:
"""
检索相关记忆
Args:
query: 查询文本
n_results: 返回数量
where: 元数据过滤条件
Returns:
相关记忆列表
"""
results = self.collection.query(
query_texts=[query],
n_results=n_results,
where=where
)
memories = []
for i, doc in enumerate(results["documents"][0]):
memories.append({
"content": doc,
"metadata": results["metadatas"][0][i],
"id": results["ids"][0][i],
"distance": results["distances"][0][i] if "distances" in results else None
})
return memories
def forget(self, memory_id: str):
"""删除一条记忆"""
self.collection.delete(ids=[memory_id])
def update(self, memory_id: str, new_content: str, new_metadata: Optional[dict] = None):
"""更新一条记忆"""
self.collection.update(
ids=[memory_id],
documents=[new_content],
metadatas=[new_metadata] if new_metadata else None
)
# 使用示例
memory = VectorLongTermMemory(persist_directory="./agent_memory")
# 保存情景记忆
memory.save(
content="用户偏好使用 Python 进行数据分析,熟悉 pandas 和 numpy",
metadata={"category": "user_preference", "importance": "high"},
memory_type="episodic"
)
# 保存语义记忆(知识)
memory.save(
content="ReAct 是一种让 LLM 在推理和行动之间交替的范式,由 Yao et al. 2023 提出",
metadata={"category": "technical_knowledge", "source": "paper"},
memory_type="semantic"
)
# 检索相关记忆
relevant = memory.recall("用户喜欢用什么编程语言?", n_results=3)
for m in relevant:
print(f"- {m['content']} (distance: {m['distance']:.3f})")
3.5 记忆的遗忘机制
不是所有记忆都值得永久保留。智能遗忘机制至关重要:
python
class ForgettingMechanism:
"""基于 Ebbinghaus 遗忘曲线的记忆衰减"""
def __init__(self, decay_rate: float = 0.3):
self.decay_rate = decay_rate
def compute_retention(self,
initial_strength: float,
time_elapsed_hours: float,
recall_count: int = 0) -> float:
"""
计算记忆保留强度
R = S * e^(-t/d) * (1 + boost * recall_count)
Args:
initial_strength: 初始记忆强度 (0-1)
time_elapsed_hours: 距离上次访问的小时数
recall_count: 被回忆的次数(强化因子)
Returns:
当前保留强度 (0-1)
"""
import math
# Ebbinghaus 遗忘曲线
decay = math.exp(-time_elapsed_hours / (24 * self.decay_rate))
# 回忆强化
boost = 0.1 * recall_count
retention = initial_strength * decay * (1 + boost)
return min(retention, 1.0) # 上限为 1
def should_forget(self,
memory: dict,
threshold: float = 0.1) -> bool:
"""判断是否应该遗忘"""
retention = self.compute_retention(
initial_strength=memory.get("initial_strength", 0.5),
time_elapsed_hours=memory.get("hours_since_access", 0),
recall_count=memory.get("recall_count", 0)
)
return retention < threshold
四、Tool Use(工具调用):Agent 的"行动能力"
4.1 工具调用的本质
Tool Use 让 Agent 突破"只输出文本"的限制,能够:
- 获取实时信息(搜索、API 调用)
- 执行计算和代码
- 操作文件和数据库
- 与外部系统交互
4.2 工具调用的两种范式
范式一:Function Calling(结构化调用)
OpenAI、Anthropic Claude、国产大模型普遍支持的方式。
工作流程:
1. 用户定义工具 schema
2. LLM 输出结构化的函数调用参数
3. Agent 执行函数调用
4. 将结果返回 LLM 继续推理
工具 Schema 定义:
python
tools_schema = [
{
"type": "function",
"function": {
"name": "search_web",
"description": "搜索互联网获取实时信息",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词"
},
"num_results": {
"type": "integer",
"description": "返回结果数量",
"default": 5
}
},
"required": ["query"]
}
}
},
{
"type": "function",
"function": {
"name": "execute_python",
"description": "执行 Python 代码进行计算或数据处理",
"parameters": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "要执行的 Python 代码"
}
},
"required": ["code"]
}
}
}
]
完整调用流程:
python
import openai
def run_with_tools(user_message: str, tools: list, tool_implementations: dict):
messages = [{"role": "user", "content": user_message}]
while True:
# 1. 调用 LLM
response = openai.chat.completions.create(
model="gpt-4-turbo",
messages=messages,
tools=tools,
tool_choice="auto"
)
message = response.choices[0].message
messages.append(message)
# 2. 检查是否需要工具调用
if not message.tool_calls:
# 无工具调用,返回最终答案
return message.content
# 3. 执行工具调用
for tool_call in message.tool_calls:
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
print(f"调用工具: {function_name}({function_args})")
# 执行实际的工具函数
if function_name in tool_implementations:
result = tool_implementations[function_name](**function_args)
else:
result = f"错误:未知工具 {function_name}"
# 4. 将结果返回给 LLM
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": str(result)
})
# 工具实现
def search_web(query: str, num_results: int = 5) -> str:
# 实际搜索逻辑
return f"关于 '{query}' 的搜索结果..."
def execute_python(code: str) -> str:
try:
# 安全的代码执行(实际应用需要沙箱)
local_vars = {}
exec(code, {"__builtins__": {}}, local_vars)
return str(local_vars.get("result", "执行成功"))
except Exception as e:
return f"执行错误: {e}"
tool_implementations = {
"search_web": search_web,
"execute_python": execute_python
}
# 运行
result = run_with_tools(
"帮我搜索最新的 AI Agent 论文,并统计有多少篇",
tools_schema,
tool_implementations
)
范式二:Prompt-based Tool Use(ReAct 风格)
不依赖模型的 Function Calling 能力,通过 Prompt 引导模型输出工具调用。
Prompt 模板:
python
TOOL_USE_PROMPT = """
你是一个可以使用多种工具的 AI Agent。
可用工具:
{tool_descriptions}
使用工具的格式:
Action: [工具名称]
Action Input: [JSON 格式的参数]
示例:
Action: search_web
Action Input: {{"query": "AI Agent 最新进展", "num_results": 5}}
现在请处理用户的请求:
{user_request}
请先思考,然后决定是否需要使用工具。
"""
4.3 工具设计最佳实践
原则一:单一职责
每个工具只做一件事,职责清晰。
python
# ❌ 不好:一个工具做太多事
def process_data(action: str, data: any) -> any:
if action == "read":
return read_file(data)
elif action == "write":
return write_file(data)
elif action == "transform":
return transform(data)
# ... 职责混乱
# ✅ 好:每个工具单一职责
def read_file(path: str) -> str:
"""读取文件内容"""
pass
def write_file(path: str, content: str) -> bool:
"""写入文件"""
pass
def transform_data(data: any, operation: str) -> any:
"""数据转换"""
pass
原则二:明确的 Schema
工具的参数、返回值、错误情况都要有清晰的文档。
python
def search_web(
query: str,
num_results: int = 5,
site: Optional[str] = None
) -> List[SearchResult]:
"""
搜索互联网获取信息
Args:
query: 搜索关键词,不能为空
num_results: 返回结果数量,1-20
site: 限定搜索站点,如 "github.com"
Returns:
SearchResult 列表,每个包含 title, url, snippet
Raises:
ValueError: query 为空或 num_results 超出范围
NetworkError: 网络请求失败
"""
pass
原则三:错误处理与降级
工具调用可能失败,需要优雅处理。
python
class ToolExecutor:
def __init__(self, tools: dict, max_retries: int = 3):
self.tools = tools
self.max_retries = max_retries
def execute(self, tool_name: str, **kwargs) -> ToolResult:
if tool_name not in self.tools:
return ToolResult(
success=False,
error=f"未知工具: {tool_name}",
fallback="请检查工具名称是否正确"
)
tool = self.tools[tool_name]
for attempt in range(self.max_retries):
try:
result = tool(**kwargs)
return ToolResult(success=True, result=result)
except Exception as e:
if attempt == self.max_retries - 1:
return ToolResult(
success=False,
error=str(e),
fallback=self._get_fallback(tool_name, e)
)
time.sleep(2 ** attempt) # 指数退避
def _get_fallback(self, tool_name: str, error: Exception) -> str:
"""根据错误类型提供降级建议"""
if "timeout" in str(error).lower():
return f"工具 {tool_name} 超时,建议稍后重试或使用替代方案"
elif "permission" in str(error).lower():
return f"权限不足,请检查 {tool_name} 的访问权限"
else:
return f"工具 {tool_name} 执行失败,请尝试其他方法"
原则四:权限控制
工具调用必须有权限边界,防止滥用。
python
class PermissionController:
"""工具调用权限控制器"""
def __init__(self):
self.permissions = {
"read_file": ["~/Documents/**", "~/Desktop/**"], # 只允许读取特定目录
"write_file": ["~/Documents/output/**"], # 只允许写入输出目录
"execute_python": ["safe_functions"], # 只允许安全函数
"search_web": ["*"], # 无限制
}
def check_permission(self, tool_name: str, **kwargs) -> bool:
"""检查是否有权限执行"""
if tool_name not in self.permissions:
return False
allowed = self.permissions[tool_name]
if tool_name == "read_file":
path = kwargs.get("path", "")
return self._match_path(path, allowed)
elif tool_name == "execute_python":
code = kwargs.get("code", "")
return self._is_safe_code(code)
return True # 其他情况默认允许
def _match_path(self, path: str, patterns: list) -> bool:
"""检查路径是否匹配允许的模式"""
import fnmatch
expanded = os.path.expanduser(path)
for pattern in patterns:
expanded_pattern = os.path.expanduser(pattern)
if fnmatch.fnmatch(expanded, expanded_pattern):
return True
return False
def _is_safe_code(self, code: str) -> bool:
"""检查代码是否安全(简化版)"""
dangerous = ["import os", "import subprocess", "eval(", "exec(", "__import__"]
return not any(d in code for d in dangerous)
4.4 MCP:工具调用的新范式
MCP(Model Context Protocol) 是 Anthropic 在 2024 年底开源的工具调用协议,正在成为 AI Agent 工具生态的事实标准。
MCP 的核心优势:
| 特性 | 传统 Function Calling | MCP |
|---|---|---|
| 协议标准化 | 各厂商自定义 | 统一开放协议 |
| 工具发现 | 手动定义 schema | 自动 discovery |
| 跨模型兼容 | 仅特定模型支持 | 任何支持 MCP 的模型 |
| 生态复用 | 各自为战 | 共享工具生态 |
MCP 架构:
┌─────────────────┐ MCP Protocol ┌─────────────────┐
│ MCP Client │ ◄─────────────────────► │ MCP Server │
│ (AI Agent) │ │ (Tool Host) │
├─────────────────┤ ├─────────────────┤
│ • 发起请求 │ │ • 提供工具列表 │
│ • 接收响应 │ │ • 执行工具调用 │
│ • 管理连接 │ │ • 返回结果 │
└─────────────────┘ └─────────────────┘
五、四大模块的协同:构建完整的 Agent
5.1 模块协作流程
python
class CompleteAgent:
def __init__(self, llm, tools, memory_system):
self.llm = llm
self.tools = tools
self.memory = memory_system # 包含工作记忆 + 长期记忆
self.planner = Planner(llm)
self.executor = ToolExecutor(tools)
def run(self, goal: str) -> str:
# 1. Planning:分解目标
plan = self.planner.decompose(goal)
# 2. 检索相关长期记忆
relevant_memories = self.memory.long_term.recall(goal)
context = self._build_context(relevant_memories)
results = []
for step in plan:
# 3. ReAct 循环
while not step.completed:
# 思考
thought = self._think(step, context, results)
# 决定行动
if thought.needs_tool:
# Tool Use
action_result = self.executor.execute(
thought.tool_name,
**thought.tool_args
)
results.append(action_result)
# 更新工作记忆
self.memory.working.add("assistant", thought.content)
# 检查步骤是否完成
step.completed = self._check_completion(step, results)
# 4. 保存重要结果到长期记忆
if step.important:
self.memory.long_term.save(
content=step.summary,
metadata={"step": step.name, "goal": goal}
)
return self._synthesize_results(results)
5.2 模块间的信息流
用户输入
│
▼
┌─────────────────────────────────────────────────────────┐
│ Planning Module │
│ 输入:用户目标 + 长期记忆检索结果 │
│ 输出:任务分解计划 │
└────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ ReAct Loop (for each task) │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Thought │──▶│ Action │──▶│Observe │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌────────────┐ │ │
│ │ │ Tool Use │ │ │
│ │ └────────────┘ │ │
│ │ │ │ │
│ └─────────────┴─────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Working Mem │ ◄── 更新上下文 │
│ └──────────────┘ │
└────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Long-Term Memory │
│ • 保存重要经验 │
│ • 更新知识库 │
│ • 记录任务结果 │
└─────────────────────────────────────────────────────────┘
│
▼
最终输出
六、总结与展望
6.1 四大模块的核心要点
| 模块 | 核心问题 | 关键技术 | 最佳实践 |
|---|---|---|---|
| Planning | 如何分解目标? | CoT、ToT、Plan-and-Solve | 动态重规划、可行性验证 |
| ReAct | 如何边推理边行动? | Thought-Action-Observation 循环 | 思考先行、观察驱动 |
| Memory | 如何保持上下文? | 工作记忆 + 长期记忆 + 向量检索 | 智能遗忘、摘要压缩 |
| Tool Use | 如何与外部交互? | Function Calling、MCP | 单一职责、权限控制 |
6.2 未来趋势
- Planning:从静态规划到动态自适应规划,引入强化学习优化策略
- ReAct:与多模态结合,支持图像/视频理解和行动
- Memory:更高效的压缩算法、跨 Agent 的共享记忆
- Tool Use:MCP 生态爆发、工具自动发现与组合
6.3 学习路径建议
入门阶段
├── 理解 ReAct 原理,阅读原始论文
├── 用 LangChain 实现简单的 Tool Use
└── 搭建基础的对话记忆系统
进阶阶段
├── 实现 ToT 规划算法
├── 集成向量数据库构建长期记忆
└── 学习 MCP 协议,搭建 MCP Server
高级阶段
├── 设计多 Agent 协作架构
├── 实现自适应规划和动态重规划
└── 优化记忆检索效率,引入遗忘机制
参考资料
- Yao, S., et al. "ReAct: Synergizing Reasoning and Acting in Language Models" (ICLR, 2023)
- Wei, J., et al. "Chain-of-Thought Prompting Elicits Reasoning in Large Language Models" (NeurIPS, 2022)
- Yao, S., et al. "Tree of Thoughts: Deliberate Problem Solving with Large Language Models" (NeurIPS, 2023)
- Shinn, N., et al. "Reflexion: Language Agents with Verbal Reinforcement Learning" (NeurIPS, 2023)
- Anthropic. "Model Context Protocol (MCP)" --- https://modelcontextprotocol.io
- LangChain Documentation --- Memory Module
- CrewAI Documentation --- Agent Architecture