一、引言:为什么 2026 年我们仍在讨论 Agent 模式?
2023 年是“Chat”的元年,2024 年是“Copilot”的元年,而 2025–2026 年,则是“Agent 工程化”的元年。
我们正在经历一次重要的范式迁移:从「对话式 AI(Conversational AI)」走向「行为式 AI(Action-oriented AI)」。
过去,我们关注的是模型“说得对不对”;现在,我们更关心它“做得成不成”。
如果把大模型看作一个强大的大脑,那么 Agent 就是:把推理(Reasoning)与行动(Acting)进行工程化封装的系统结构。
Agent 的本质不是“让模型更聪明”,而是通过结构设计,降低对模型原生能力的依赖,提高:
- 稳定性(Stability)
- 可控性(Controllability)
- 可扩展性(Scalability)
- 成功率(Success Rate)
本文将系统拆解当前主流的 5 种 Agent 架构模式:
- ReAct
- CodeAct
- Plan Mode
- Reflection Mode
- Multi-Agent
你将看到它们如何从“单线程思考”逐步演进为“团队级协作系统”。
二、ReAct 模式
核心逻辑
ReAct = Reasoning + Acting
其经典流程:
Thought -> Action -> Observation -> Thought -> ...
也就是:
- 思考下一步做什么
- 调用工具执行
- 观察结果
- 再思考下一步
这是第一个真正意义上的 Agent 结构。
ReAct 原理图:
ReAct 示例代码
import os
from dotenv import load_dotenv
from openai import OpenAI
def get_client_and_model():
load_dotenv()
api_key = os.getenv("DEEPSEEK_API_KEY")
base_url = os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com/v1")
model = os.getenv("DEEPSEEK_MODEL", "deepseek-chat")
if not api_key:
raise RuntimeError("缺少 DEEPSEEK_API_KEY,请先在 .env 中配置。")
client = OpenAI(api_key=api_key, base_url=base_url)
return client, model
REACT_PROMPT = """
你是一个严格遵循 ReAct(Reasoning and Acting)模式的智能体。
你必须按以下循环回答问题:
Thought: 你的思考
Action: 要调用的工具名
Action Input: 工具入参(JSON)
PAUSE
当你收到 Observation 后,再继续下一轮 Thought/Action,直到得到最终答案。
结束时输出:
Final Answer: 你的最终答案
可用工具如下:
{tools}
规则:
1. 若是问候/闲聊,不调用工具,直接给出 Final Answer。
2. 若需要外部信息,优先调用工具。
3. 如果问题可直接回答,也可以不调用工具,直接 Final Answer。
4. 每次只调用一个工具;多工具需求分多轮。
5. Action Input 必须是合法 JSON。
示例:
Question: 比较青岛啤酒和贵州茅台的收盘价
Thought: 我需要分别查询两只股票的收盘价
Action: get_closing_price
Action Input: {"name":"青岛啤酒"}
PAUSE
Observation: 67.92
Thought: 还需要贵州茅台的收盘价
Action: get_closing_price
Action Input: {"name":"贵州茅台"}
PAUSE
Observation: 1488.21
Final Answer: 贵州茅台收盘价更高(1488.21 > 67.92)。
现在开始。
Question: {input}
"""
from typing import Any
TOOLS = [
{
"name": "get_closing_price",
"description": "获取指定股票的收盘价",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "股票名称,例如:青岛啤酒、贵州茅台",
}
},
"required": ["name"],
},
}
]
def get_closing_price(name: str) -> str:
mock_data = {
"青岛啤酒": "67.92",
"贵州茅台": "1488.21",
}
return mock_data.get(name, "未搜到该股票")
def run_tool(tool_name: str, action_input: dict[str, Any]) -> str:
if tool_name == "get_closing_price":
return get_closing_price(action_input["name"])
return f"未知工具: {tool_name}"
import json
import re
from typing import Any, Optional, Tuple
from llm import get_client_and_model
from prompt import REACT_PROMPT
from tools import TOOLS, run_tool
FINAL_ANSWER_PATTERN = re.compile(r"Final Answer:\s*(.*)", re.DOTALL)
ACTION_PATTERN = re.compile(r"Action:\s*([a-zA-Z_][\w]*)")
ACTION_INPUT_PATTERN = re.compile(r"Action Input:\s*(\{.*\}|\".*\")", re.DOTALL)
class ReActAgent:
def __init__(self, max_steps: int = 6) -> None:
self.client, self.model = get_client_and_model()
self.max_steps = max_steps
def _send_messages(self, messages: list[dict[str, str]]) -> str:
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
)
content = response.choices[0].message.content
return content or ""
def _extract_final_answer(self, text: str) -> Optional[str]:
match = FINAL_ANSWER_PATTERN.search(text)
if not match:
return None
return match.group(1).strip()
def _extract_action(self, text: str) -> Optional[Tuple[str, dict[str, Any]]]:
action_match = ACTION_PATTERN.search(text)
input_match = ACTION_INPUT_PATTERN.search(text)
if not action_match or not input_match:
return None
tool_name = action_match.group(1).strip()
action_input_text = input_match.group(1).strip()
try:
parsed_input = json.loads(action_input_text)
except json.JSONDecodeError:
return None
if isinstance(parsed_input, str):
parsed_input = {"input": parsed_input}
if not isinstance(parsed_input, dict):
return None
return tool_name, parsed_input
def run(self, query: str, verbose: bool = True) -> str:
prompt = REACT_PROMPT.format(
tools=json.dumps(TOOLS, ensure_ascii=False, indent=2),
input=query,
)
messages: list[dict[str, str]] = [{"role": "user", "content": prompt}]
for step in range(1, self.max_steps + 1):
output = self._send_messages(messages)
if verbose:
print(f"\n=== Step {step} ===")
print(output)
final_answer = self._extract_final_answer(output)
if final_answer:
return final_answer
action = self._extract_action(output)
if not action:
return f"模型未按 ReAct 格式输出,原始输出:\n{output}"
tool_name, action_input = action
observation = run_tool(tool_name, action_input)
if verbose:
print(f"Observation: {observation}")
messages.append({"role": "assistant", "content": output})
messages.append({"role": "user", "content": f"Observation: {observation}"})
return f"超过最大循环次数({self.max_steps})仍未得到 Final Answer。"
from agent import ReActAgent
def main() -> None:
default_query = "请比较青岛啤酒和贵州茅台的股票收盘价谁更高?"
query = input(f"请输入问题(直接回车使用默认问题):\n[{default_query}]\n> ").strip()
if not query:
query = default_query
agent = ReActAgent(max_steps=6)
final_answer = agent.run(query=query, verbose=True)
print("\n=== Final Answer ===")
print(final_answer)
if __name__ == "__main__":
main()
技术背景:为什么它重要?
在 ReAct 出现之前,大模型存在两个明显问题:
- 容易“幻觉”
- 无法获取实时信息
ReAct 的核心突破是:允许模型边思考边调用工具。
例如:
- 搜索 API
- 数据库查询
- 计算器
- 外部系统接口
这让模型从“纯语言生成器”变成了“可执行系统”。
适用场景
- 搜索助手
- API 编排
- 简单自动化任务
- 单轮或短路径工具调用
优点
- 架构简单
- 容易实现
- 逻辑直观
缺点
- 容易陷入循环
- 长路径任务容易“走一步忘一步”
- 对 Prompt 设计依赖极强
ReAct 是 Agent 世界的 “Hello World”,但远不是终点。
三、CodeAct 模式
ReAct 的行动通常是:输出一段 JSON,调用一个 API。
CodeAct 则更进一步:不输出“调用指令”,而是直接生成并执行代码。
例如:
- Python
- Bash
- SQL
核心逻辑
生成代码 -> 执行代码 -> 读取结果 -> 再生成代码
代码成为 Agent 的“中间表示语言(IR)”。
CodeAct 原理图:
技术优势
1. 精度更高
自然语言描述计算步骤容易歧义。但:sum(df["revenue"]) 没有歧义。
在:
- 数学计算
- 数据分析
- 复杂逻辑处理
场景中,代码远比文本可靠。
2. 可扩展性更强
代码意味着:
- 可以调用任何 Python 库
- 可以访问文件系统
- 可以控制操作系统
- 可以构建复杂流程
这让 Agent 成为真正的“自动化执行体”。
CodeAct 示例代码
SYSTEM_PROMPT = """
你是一个严格遵循 CodeAct 模式的智能体。
你的循环流程必须是:
1. 思考任务并规划求解步骤
2. 生成 Python 代码
3. 根据执行反馈观察结果并更新代码
4. 必要时继续修正,直到得到正确答案
输出规则:
1. 当需要执行代码时,只输出一个 Python 代码块(```python ... ```)。
2. 代码必须把关键结果写入变量 result。
3. 你会收到执行反馈(包含 stdout、result、error)。
4. 如果结果已经正确,输出:Final Answer: 你的最终答案
5. 不要输出与上述格式无关的内容。
注意:
- 优先使用 Python 标准库。
- 如果执行报错,先修复报错再继续。
"""
import contextlib
import io
def execute_python(code: str) -> dict[str, str]:
stdout_buffer = io.StringIO()
local_vars: dict[str, object] = {}
try:
with contextlib.redirect_stdout(stdout_buffer):
exec(code, {}, local_vars)
result = local_vars.get("result", "执行成功,但未设置 result 变量")
stdout_text = stdout_buffer.getvalue().strip()
return {
"status": "ok",
"stdout": stdout_text,
"result": str(result),
}
except Exception as exc:
stdout_text = stdout_buffer.getvalue().strip()
return {
"status": "error",
"stdout": stdout_text,
"error": f"{type(exc).__name__}: {exc}",
}
import json
import re
from typing import Optional
from llm import get_client_and_model
from prompt import SYSTEM_PROMPT
from tools import execute_python
FINAL_ANSWER_PATTERN = re.compile(r"Final Answer:\s*(.*)", re.DOTALL)
PYTHON_BLOCK_PATTERN = re.compile(r"```python\s*(.*?)```", re.DOTALL | re.IGNORECASE)
class CodeActAgent:
def __init__(self, max_steps: int = 8) -> None:
self.client, self.model = get_client_and_model()
self.max_steps = max_steps
def _send_messages(self, messages: list[dict[str, str]]) -> str:
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
)
content = response.choices[0].message.content
return content or ""
def _extract_final_answer(self, text: str) -> Optional[str]:
match = FINAL_ANSWER_PATTERN.search(text)
if not match:
return None
return match.group(1).strip()
def _extract_python_code(self, text: str) -> Optional[str]:
match = PYTHON_BLOCK_PATTERN.search(text)
if not match:
return None
return match.group(1).strip()
def run(self, query: str, verbose: bool = True) -> str:
messages: list[dict[str, str]] = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": query},
]
for step in range(1, self.max_steps + 1):
output = self._send_messages(messages)
if verbose:
print(f"\n=== Step {step} / LLM Output ===")
print(output)
final_answer = self._extract_final_answer(output)
if final_answer:
return final_answer
code = self._extract_python_code(output)
if not code:
return f"模型未按 CodeAct 约定输出代码或 Final Answer,原始输出:\n{output}"
execution_result = execute_python(code)
observation = json.dumps(execution_result, ensure_ascii=False, indent=2)
if verbose:
print("\n=== Execution Observation ===")
print(observation)
messages.append({"role": "assistant", "content": output})
messages.append(
{
"role": "user",
"content": (
"以下是你刚才代码的执行反馈,请继续下一轮。\n"
"如果结果已满足用户问题,请输出:Final Answer: ...\n"
"如果报错或结果不满足,请输出修正后的 Python 代码块。\n\n"
f"{observation}"
),
}
)
return f"超过最大循环次数({self.max_steps})仍未得到 Final Answer。"
from agent import CodeActAgent
def main() -> None:
default_query = "请用 Python 计算 1 到 100 的和,并给出计算过程的简要说明。"
query = input(f"请输入问题(直接回车使用默认问题):\n[{default_query}]\n> ").strip()
if not query:
query = default_query
agent = CodeActAgent(max_steps=8)
final_answer = agent.run(query=query, verbose=True)
print("\n=== Final Answer ===")
print(final_answer)
if __name__ == "__main__":
main()
实战案例
- 数据分析 Agent
- 自动化运维 Agent
- 报表生成 Agent
- AI 自动编程助手
工程风险
- 代码安全
- 沙箱隔离
- 资源控制
- 无限循环风险
CodeAct 提升了能力上限,同时也提升了系统复杂度。
四、计划模式(Plan Mode):复杂任务的“指挥官”
当任务路径变长时,ReAct 会出现典型问题:每一步都只看当前局部。
Plan Mode 的核心思想是:先规划,再执行。
核心逻辑
任务拆解 -> 生成执行计划 -> 分步执行 -> 汇总结果
类似:
- 架构师出蓝图
- 工程师按图施工
Plan Mode 原理图:
代表架构
- Plan-and-Execute
- LLMCompiler
解决的核心痛点
ReAct 的问题:
- 容易偏航
- 中途遗忘目标
- 长任务成功率下降
Plan Mode 引入:
- 全局视角
- 中间检查点
- 结构化步骤管理
适用场景
- 研究报告编写
- 复杂项目规划
- 多步骤数据处理
- 长链路业务流程
Plan Mode 示例代码
PLANNER_PROMPT = """
你是 PlanMode 里的“计划器(Planner)”。
给定用户目标后,你需要拆解出一个最小可执行的多步计划。
约束:
1. 步骤必须按执行顺序排列。
2. 每一步都要可执行、可验证。
3. 计划总步数控制在 3-5 步。
4. 输出必须是 JSON,且只能输出 JSON。
输出格式:
{
"steps": ["步骤1", "步骤2", "步骤3"]
}
"""
EXECUTOR_PROMPT = """
你是 PlanMode 里的“执行器(Executor)”,需要严格执行当前步骤。
你可用的工具定义如下:
{tools_json}
请根据“当前步骤”决定执行动作,并只输出 JSON:
1) 如果需要调用工具:
{
"action": "tool",
"tool_name": "工具名",
"tool_input": { "参数": "值" }
}
2) 如果当前步骤已可直接完成:
{
"action": "final",
"content": "本步骤的完成结果"
}
"""
REPLAN_PROMPT = """
你是 PlanMode 里的“计划更新器(Replanner)”。
你需要根据已完成步骤,判断是否可以结束,或返回剩余步骤。
规则:
1. 如果任务已完成,返回 response。
2. 如果任务未完成,返回剩余 steps(不要重复已完成步骤)。
3. 输出必须是 JSON,且只能输出 JSON。
输出格式二选一:
{
"actions": {
"response": "最终回答"
}
}
{
"actions": {
"steps": ["剩余步骤1", "剩余步骤2"]
}
}
"""
FINAL_RESPONSE_PROMPT = """
你是 PlanMode 的总结器。
请根据用户目标和已完成步骤结果,整合出最终回答。
输出必须是 JSON:
{
"response": "最终回答"
}
"""
from typing import Any
TOOL_SPECS = [
{
"name": "search_knowledge",
"description": "检索主题基础知识,返回适合写作的要点",
"parameters": {
"type": "object",
"properties": {
"topic": {"type": "string", "description": "检索主题"},
},
"required": ["topic"],
},
},
{
"name": "build_outline",
"description": "根据主题和要点生成文章提纲",
"parameters": {
"type": "object",
"properties": {
"topic": {"type": "string", "description": "文章主题"},
"key_points": {"type": "string", "description": "关键要点"},
},
"required": ["topic", "key_points"],
},
},
{
"name": "write_draft",
"description": "根据提纲生成内容草稿",
"parameters": {
"type": "object",
"properties": {
"outline": {"type": "string", "description": "文章提纲"},
"audience": {"type": "string", "description": "目标读者"},
},
"required": ["outline"],
},
},
{
"name": "polish_article",
"description": "润色文章,使其更符合技术博客风格",
"parameters": {
"type": "object",
"properties": {
"draft": {"type": "string", "description": "待润色草稿"},
"style": {"type": "string", "description": "风格偏好"},
},
"required": ["draft"],
},
},
]
def search_knowledge(topic: str) -> str:
return (
f"主题:{topic}\n"
"要点:\n"
"1. 量子比特可同时处于多个状态(叠加)。\n"
"2. 多个量子比特之间可形成纠缠,产生强关联。\n"
"3. 量子门操作可构建量子算法,如 Shor、Grover。\n"
"4. 量子计算适合特定问题,并非替代所有经典计算。"
)
def build_outline(topic: str, key_points: str) -> str:
return (
f"# {topic}\n"
"## 1. 为什么关注量子计算\n"
"## 2. 三个核心概念:叠加、纠缠、量子门\n"
"## 3. 典型应用场景与现实边界\n"
"## 4. 结语:未来机会与挑战\n"
f"\n参考要点:\n{key_points}"
)
def write_draft(outline: str, audience: str = "技术爱好者") -> str:
return (
f"面向读者:{audience}\n\n"
"量子计算并不是“更快的普通计算机”,而是一种新的计算范式。"
"它利用量子比特的叠加和纠缠,在某些任务上有潜在优势。"
"例如在组合优化、量子化学模拟等方向,量子方法可能显著缩短求解时间。\n\n"
"但现实中,量子硬件仍受噪声、纠错成本和规模化难题限制。"
"因此更准确的判断是:量子计算会与经典计算长期协同,"
"先在少数高价值场景突破,再逐步扩展。\n\n"
"以下是本文提纲:\n"
f"{outline}"
)
def polish_article(draft: str, style: str = "技术博客") -> str:
return (
f"[风格:{style}]\n"
"量子计算是一种利用量子力学规律进行信息处理的计算方式。"
"它最值得关注的地方,不是“全面替代经典计算”,而是在特定问题上"
"提供更优求解路径。\n\n"
"从原理看,叠加让量子系统具备并行表达能力,纠缠让变量之间形成强耦合,"
"量子门则像经典逻辑门一样,承担可控计算的职责。"
"从工程看,噪声与纠错仍是主要瓶颈,短期内更可行的路线是“经典+量子”混合架构。\n\n"
"如果你把量子计算看作“下一代专用加速器”,而不是“万能机器”,"
"你会更容易识别它的真实机会。\n\n"
"---- 原始草稿 ----\n"
f"{draft}"
)
TOOL_REGISTRY = {
"search_knowledge": search_knowledge,
"build_outline": build_outline,
"write_draft": write_draft,
"polish_article": polish_article,
}
def run_tool(tool_name: str, tool_input: dict[str, Any]) -> str:
tool_fn = TOOL_REGISTRY.get(tool_name)
if not tool_fn:
return f"工具不存在: {tool_name}"
try:
return tool_fn(**tool_input)
except TypeError as exc:
return f"工具参数错误: {exc}"
import json
import re
from typing import Any, Optional
from llm import get_client_and_model
from prompt import EXECUTOR_PROMPT, FINAL_RESPONSE_PROMPT, PLANNER_PROMPT, REPLAN_PROMPT
from tools import TOOL_SPECS, run_tool
class PlanModeAgent:
def __init__(self, max_rounds: int = 8) -> None:
self.client, self.model = get_client_and_model()
self.max_rounds = max_rounds
def _send_messages(self, messages: list[dict[str, str]]) -> str:
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
temperature=0.2,
)
content = response.choices[0].message.content
return content or ""
def _extract_json_object(self, text: str) -> Optional[dict[str, Any]]:
text = text.strip()
for candidate in (text, self._extract_fenced_json(text), self._extract_brace_json(text)):
if not candidate:
continue
try:
parsed = json.loads(candidate)
except json.JSONDecodeError:
continue
if isinstance(parsed, dict):
return parsed
return None
def _extract_fenced_json(self, text: str) -> str:
match = re.search(r"```json\s*(.*?)```", text, re.DOTALL | re.IGNORECASE)
return match.group(1).strip() if match else ""
def _extract_brace_json(self, text: str) -> str:
start = text.find("{")
end = text.rfind("}")
if start == -1 or end == -1 or end <= start:
return ""
return text[start : end + 1]
def _default_plan(self, query: str) -> list[str]:
return [
f"检索与“{query}”相关的基础知识",
f"基于知识要点制定“{query}”的文章提纲",
f"根据提纲撰写“{query}”短文草稿",
"检查与润色草稿,输出最终文章",
]
def _plan(self, query: str) -> list[str]:
messages = [
{"role": "system", "content": PLANNER_PROMPT},
{"role": "user", "content": f"用户目标:{query}"},
]
output = self._send_messages(messages)
data = self._extract_json_object(output)
if not data:
return self._default_plan(query)
steps = data.get("steps")
if not isinstance(steps, list):
return self._default_plan(query)
clean_steps = [step.strip() for step in steps if isinstance(step, str) and step.strip()]
return clean_steps if clean_steps else self._default_plan(query)
def _build_default_tool_call(
self,
query: str,
step: str,
past_steps: list[dict[str, str]],
) -> tuple[str, dict[str, Any]]:
if "检索" in step or "知识" in step:
return "search_knowledge", {"topic": query}
if "提纲" in step:
key_points = past_steps[-1]["result"] if past_steps else "无"
return "build_outline", {"topic": query, "key_points": key_points}
if "草稿" in step or "撰写" in step:
outline = past_steps[-1]["result"] if past_steps else f"# {query}\n## 主题内容"
return "write_draft", {"outline": outline, "audience": "技术博客读者"}
if "润色" in step or "检查" in step:
draft = past_steps[-1]["result"] if past_steps else ""
return "polish_article", {"draft": draft, "style": "技术博客"}
return "search_knowledge", {"topic": query}
def _execute_step(
self,
query: str,
full_plan: list[str],
step: str,
past_steps: list[dict[str, str]],
) -> str:
tools_json = json.dumps(TOOL_SPECS, ensure_ascii=False, indent=2)
prompt = EXECUTOR_PROMPT.format(tools_json=tools_json)
messages = [
{"role": "system", "content": prompt},
{
"role": "user",
"content": (
f"用户目标:{query}\n"
f"完整计划:{json.dumps(full_plan, ensure_ascii=False)}\n"
f"当前步骤:{step}\n"
f"已完成步骤:{json.dumps(past_steps, ensure_ascii=False)}"
),
},
]
output = self._send_messages(messages)
data = self._extract_json_object(output) or {}
action = data.get("action")
if action == "final":
content = data.get("content", "")
if isinstance(content, str) and content.strip():
return content.strip()
if action == "tool":
tool_name = data.get("tool_name")
tool_input = data.get("tool_input", {})
if isinstance(tool_name, str) and isinstance(tool_input, dict):
return run_tool(tool_name, tool_input)
tool_name, tool_input = self._build_default_tool_call(query, step, past_steps)
return run_tool(tool_name, tool_input)
def _replan(
self,
query: str,
remaining_plan: list[str],
past_steps: list[dict[str, str]],
) -> tuple[bool, str, list[str]]:
messages = [
{"role": "system", "content": REPLAN_PROMPT},
{
"role": "user",
"content": (
f"用户目标:{query}\n"
f"当前剩余计划:{json.dumps(remaining_plan, ensure_ascii=False)}\n"
f"已完成步骤:{json.dumps(past_steps, ensure_ascii=False)}"
),
},
]
output = self._send_messages(messages)
data = self._extract_json_object(output) or {}
actions = data.get("actions", {})
if isinstance(actions, dict):
response = actions.get("response")
if isinstance(response, str) and response.strip():
return True, response.strip(), []
steps = actions.get("steps")
if isinstance(steps, list):
clean_steps = [step.strip() for step in steps if isinstance(step, str) and step.strip()]
return False, "", clean_steps
if not remaining_plan:
return True, "", []
return False, "", remaining_plan
def _final_response(self, query: str, past_steps: list[dict[str, str]]) -> str:
messages = [
{"role": "system", "content": FINAL_RESPONSE_PROMPT},
{
"role": "user",
"content": (
f"用户目标:{query}\n"
f"步骤执行结果:{json.dumps(past_steps, ensure_ascii=False)}"
),
},
]
output = self._send_messages(messages)
data = self._extract_json_object(output) or {}
response = data.get("response")
if isinstance(response, str) and response.strip():
return response.strip()
if past_steps:
return past_steps[-1]["result"]
return "未能生成最终回答。"
def run(self, query: str, verbose: bool = True) -> str:
plan = self._plan(query)
past_steps: list[dict[str, str]] = []
if verbose:
print("=== 计划阶段 / Multi-step Plan ===")
for idx, step in enumerate(plan, start=1):
print(f"{idx}. {step}")
for round_id in range(1, self.max_rounds + 1):
if not plan:
break
current_step = plan[0]
if verbose:
print(f"\n=== 执行阶段 Round {round_id} ===")
print(f"执行步骤: {current_step}")
result = self._execute_step(query, plan, current_step, past_steps)
past_steps.append({"step": current_step, "result": result})
if verbose:
print("步骤结果:")
print(result)
done, response, new_plan = self._replan(query, plan[1:], past_steps)
if done:
if response:
return response
return self._final_response(query, past_steps)
plan = new_plan
if verbose and plan:
print("更新后剩余计划:")
for idx, step in enumerate(plan, start=1):
print(f"{idx}. {step}")
return self._final_response(query, past_steps)
from agent import PlanModeAgent
def main() -> None:
default_query = "写一篇关于量子计算的短文"
query = input(f"请输入问题(直接回车使用默认问题):\n[{default_query}]\n> ").strip()
if not query:
query = default_query
agent = PlanModeAgent(max_rounds=8)
final_answer = agent.run(query=query, verbose=True)
print("\n=== Final Answer ===")
print(final_answer)
if __name__ == "__main__":
main()
工程启示
Plan Mode 本质上是:用“结构化控制流”约束模型的自由生成。
这是 Agent 稳定性的关键来源之一。
五、反思模式(Reflection Mode):智能体的“自我进化”
Reflection 是 Agent 体系的质量跃迁。
其核心结构:
执行 -> 评价(Critic) -> 修正(Refine) -> 再执行
Reflection 原理图:
典型框架
- Reflexion
- Self-Refine
Reflection 示例代码
DRAFT_PROMPT = """
你是 ReflectionMode 中的“初稿生成器(Draft Generator)”。
请根据用户目标先给出一个可读的初稿。
要求:
1. 内容紧扣用户目标,不要偏题。
2. 结构清晰,语言自然。
3. 直接输出正文,不要输出解释。
"""
REFLECTION_PROMPT = """
你是 ReflectionMode 中的“反思器(Self-Critique)”。
请对当前草稿做严格评审,并判断是否还需要优化。
评审维度:
1. 是否准确回应用户目标
2. 信息是否完整、有逻辑
3. 表达是否清晰、易读
4. 是否存在明显事实或表述问题
输出要求(必须是 JSON,且只能输出 JSON):
{
"status": "good_enough 或 needs_improvement",
"issues": ["问题1", "问题2"],
"suggestions": ["建议1", "建议2"]
}
当草稿已经足够好时:
1. status 必须为 good_enough
2. issues 与 suggestions 允许为空数组
"""
REFINEMENT_PROMPT = """
你是 ReflectionMode 中的“改写器(Refiner)”。
请根据反思建议改写草稿,输出更好的版本。
要求:
1. 保留原文有效信息,修正问题点。
2. 优先执行 suggestions 中的高价值建议。
3. 若建议与用户目标冲突,以用户目标优先。
4. 直接输出改写后的正文,不要输出解释。
"""
import json
import re
from typing import Any, Optional
from llm import get_client_and_model
from prompt import DRAFT_PROMPT, REFINEMENT_PROMPT, REFLECTION_PROMPT
class ReflectionModeAgent:
def __init__(self, max_rounds: int = 3) -> None:
self.client, self.model = get_client_and_model()
self.max_rounds = max_rounds
def _send_messages(self, messages: list[dict[str, str]], temperature: float = 0.3) -> str:
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
temperature=temperature,
)
content = response.choices[0].message.content
return content or ""
def _extract_json_object(self, text: str) -> Optional[dict[str, Any]]:
text = text.strip()
for candidate in (text, self._extract_fenced_json(text), self._extract_brace_json(text)):
if not candidate:
continue
try:
parsed = json.loads(candidate)
except json.JSONDecodeError:
continue
if isinstance(parsed, dict):
return parsed
return None
def _extract_fenced_json(self, text: str) -> str:
match = re.search(r"```json\s*(.*?)```", text, re.DOTALL | re.IGNORECASE)
return match.group(1).strip() if match else ""
def _extract_brace_json(self, text: str) -> str:
start = text.find("{")
end = text.rfind("}")
if start == -1 or end == -1 or end <= start:
return ""
return text[start : end + 1]
def _build_draft(self, query: str) -> str:
messages = [
{"role": "system", "content": DRAFT_PROMPT},
{"role": "user", "content": f"用户目标:{query}"},
]
draft = self._send_messages(messages, temperature=0.6).strip()
return draft or f"(未生成内容)用户目标:{query}"
def _reflect(self, query: str, draft: str) -> tuple[bool, str]:
messages = [
{"role": "system", "content": REFLECTION_PROMPT},
{
"role": "user",
"content": f"用户目标:{query}\n\n当前草稿:\n{draft}",
},
]
output = self._send_messages(messages, temperature=0.1).strip()
data = self._extract_json_object(output) or {}
status = data.get("status")
issues = data.get("issues", [])
suggestions = data.get("suggestions", [])
if not isinstance(issues, list):
issues = []
if not isinstance(suggestions, list):
suggestions = []
if status == "good_enough":
return True, "草稿质量已达标,无需继续优化。"
clean_issues = [item.strip() for item in issues if isinstance(item, str) and item.strip()]
clean_suggestions = [item.strip() for item in suggestions if isinstance(item, str) and item.strip()]
if not clean_issues and not clean_suggestions:
if output:
return False, f"模型反思输出(原文):{output}"
return False, "反思器未给出有效建议,请增强准确性、结构和可读性。"
feedback_parts = []
if clean_issues:
feedback_parts.append("问题:" + ";".join(clean_issues))
if clean_suggestions:
feedback_parts.append("建议:" + ";".join(clean_suggestions))
return False, "\n".join(feedback_parts)
def _refine(self, query: str, draft: str, feedback: str) -> str:
messages = [
{"role": "system", "content": REFINEMENT_PROMPT},
{
"role": "user",
"content": (
f"用户目标:{query}\n\n"
f"当前草稿:\n{draft}\n\n"
f"反思建议:\n{feedback}"
),
},
]
refined = self._send_messages(messages, temperature=0.4).strip()
return refined or draft
def run(self, query: str, verbose: bool = True) -> str:
draft = self._build_draft(query)
if verbose:
print("=== Draft 1 ===")
print(draft)
for round_id in range(1, self.max_rounds + 1):
done, feedback = self._reflect(query, draft)
if verbose:
print(f"\n=== Reflection {round_id} ===")
print(feedback)
if done:
return draft
draft = self._refine(query, draft, feedback)
if verbose:
print(f"\n=== Refinement {round_id} ===")
print(draft)
return draft
from agent import ReflectionModeAgent
def main() -> None:
default_query = "写一段关于“ReflectionMode 反思模式”的技术博客内容,包含概念、流程和一个应用场景。"
query = input(f"请输入问题(直接回车使用默认问题):\n[{default_query}]\n> ").strip()
if not query:
query = default_query
agent = ReflectionModeAgent(max_rounds=3)
final_answer = agent.run(query=query, verbose=True)
print("\n=== Final Answer ===")
print(final_answer)
if __name__ == "__main__":
main()
为什么它有效?
大模型第一次输出往往:
- 方向对
- 但细节有问题
加入 Critic 角色后:
- 能指出错误
- 给出改进建议
- 再次生成更优版本
这在:
- 代码生成
- 文案创作
- 数学推理
场景中效果显著。
工程代价
- Token 成本增加
- 推理时间增加
- 系统复杂度增加
但换来的收益是:成功率的显著提升。
Reflection 本质是:用“多次思考”换“单次成功”。
六、多智能体模式(Multi-Agent):从个人英雄主义到团队协作
当单 Agent 复杂度达到上限时,多 Agent 架构出现了。
核心思想:
让不同角色各司其职。
两种常见组织形式
1.层级架构
Boss Agent
↓
Worker Agent
↓
Executor
类似公司组织。
2.协作架构
- 程序员 Agent 写代码
- 测试员 Agent 找 Bug
- 架构师 Agent 设计系统
Multi-Agent 原理图:
代表框架:
- MetaGPT
- AutoGen
优势
- 模块化
- 可替换
- 易扩展
- 适合企业级系统
Multi-Agent 示例代码
RESEARCHER_PROMPT = """
你是 Multi-Agent 系统中的研究员(Researcher)。
你的任务是把工具提供的原始资料整理成可执行情报。
要求:
1. 聚焦用户目标,提炼关键信息,不写空话。
2. 给出 5-8 条高密度研究要点。
3. 输出必须是 JSON,且只能输出 JSON。
输出格式:
{
"research_notes": "结构化研究结论,建议用 1. 2. 3. 编号",
"risks": ["潜在风险1", "潜在风险2"]
}
"""
PLANNER_PROMPT = """
你是 Multi-Agent 系统中的规划师(Planner)。
你要基于研究结论设计最小可执行计划。
要求:
1. 步骤按执行顺序排列。
2. 每一步可执行、可验证。
3. 总步数控制在 4-6 步。
4. 输出必须是 JSON,且只能输出 JSON。
输出格式:
{
"plan_steps": ["步骤1", "步骤2", "步骤3"],
"acceptance_criteria": ["验收标准1", "验收标准2"]
}
"""
EXECUTOR_PROMPT = """
你是 Multi-Agent 系统中的执行员(Executor)。
你要根据计划产出可直接用于技术博客的正文内容。
要求:
1. 内容聚焦“Agent 的 5 种模式”中的 Multi-Agent 模式。
2. 结构清晰,优先给出架构、流程、价值和适用场景。
3. 使用中文,避免营销化语气。
4. 输出必须是 JSON,且只能输出 JSON。
输出格式:
{
"draft": "可直接发布的博客正文"
}
"""
EVALUATOR_PROMPT = """
你是 Multi-Agent 系统中的评估员(Evaluator)。
你要判断执行结果是否达到可发布标准。
要求:
1. 仅基于用户目标与验收标准进行评估。
2. 给出明确通过/不通过结论。
3. 若不通过,反馈必须是可执行修改建议。
4. 输出必须是 JSON,且只能输出 JSON。
输出格式:
{
"pass": true,
"score": 90,
"feedback": "评估结论与修改建议"
}
"""
FINAL_RESPONSE_PROMPT = """
你是 Multi-Agent 系统的最终汇总器。
请输出最终给用户的回答。
要求:
1. 如果草稿已通过评估,直接给出最终版本。
2. 如果未通过但达到返工上限,给出当前最佳版本并明确剩余风险。
3. 输出必须是 JSON,且只能输出 JSON。
输出格式:
{
"final_answer": "最终回答"
}
"""
from typing import Any
TOOL_SPECS = [
{
"name": "search_knowledge",
"description": "检索 Multi-Agent 相关基础知识",
"parameters": {
"type": "object",
"properties": {
"topic": {"type": "string", "description": "检索主题"},
},
"required": ["topic"],
},
},
{
"name": "fetch_case_snippets",
"description": "返回博客可复用的案例片段",
"parameters": {
"type": "object",
"properties": {
"topic": {"type": "string", "description": "案例主题"},
},
"required": ["topic"],
},
},
{
"name": "quality_rubric",
"description": "返回技术博客验收标准",
"parameters": {
"type": "object",
"properties": {},
},
},
]
def search_knowledge(topic: str) -> str:
return (
f"主题:{topic}\n"
"多智能体模式核心:\n"
"1. 系统管理器负责任务拆分、角色路由、状态跟踪。\n"
"2. 研究员负责信息收集与证据整理,降低后续幻觉风险。\n"
"3. 规划师将目标转成步骤和验收标准,提升执行可控性。\n"
"4. 执行员按计划产出结果,不直接跳步。\n"
"5. 评估员做独立质量门禁,触发返工闭环。\n"
"6. 多智能体优势在于角色分工、并行扩展、质量可追踪。"
)
def fetch_case_snippets(topic: str) -> str:
return (
f"案例主题:{topic}\n"
"可复用片段:\n"
"- 场景A:复杂研究任务(资料检索、事实交叉验证、结构化输出)。\n"
"- 场景B:工程交付任务(计划生成、代码执行、质量评审)。\n"
"- 场景C:内容生产任务(选题研究、提纲规划、正文写作、终审把关)。"
)
def quality_rubric() -> str:
return (
"验收标准:\n"
"1. 是否准确说明多智能体角色分工与信息流。\n"
"2. 是否体现‘管理器-执行-评估-返工’闭环。\n"
"3. 是否给出至少 2 个适用场景与边界。\n"
"4. 是否可直接用于技术博客发布(结构完整、语言清晰)。"
)
TOOL_REGISTRY = {
"search_knowledge": search_knowledge,
"fetch_case_snippets": fetch_case_snippets,
"quality_rubric": quality_rubric,
}
def run_tool(tool_name: str, tool_input: dict[str, Any]) -> str:
tool_fn = TOOL_REGISTRY.get(tool_name)
if not tool_fn:
return f"工具不存在: {tool_name}"
try:
return tool_fn(**tool_input)
except TypeError as exc:
return f"工具参数错误: {exc}"
import json
import re
from dataclasses import dataclass, field
from typing import Any, Optional
from llm import get_client_and_model
from prompt import (
EVALUATOR_PROMPT,
EXECUTOR_PROMPT,
FINAL_RESPONSE_PROMPT,
PLANNER_PROMPT,
RESEARCHER_PROMPT,
)
from tools import TOOL_SPECS, run_tool
@dataclass
class MultiAgentState:
query: str
research_notes: str = ""
risks: list[str] = field(default_factory=list)
plan_steps: list[str] = field(default_factory=list)
acceptance_criteria: list[str] = field(default_factory=list)
draft: str = ""
passed: bool = False
score: int = 0
feedback: str = ""
rework_count: int = 0
phase: str = "research"
trace: list[str] = field(default_factory=list)
class MultiAgentBlogAgent:
def __init__(self, max_rounds: int = 10, max_reworks: int = 2) -> None:
self.client, self.model = get_client_and_model()
self.max_rounds = max_rounds
self.max_reworks = max_reworks
def _send_messages(self, messages: list[dict[str, str]]) -> str:
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
temperature=0.2,
)
content = response.choices[0].message.content
return content or ""
def _extract_json_object(self, text: str) -> Optional[dict[str, Any]]:
text = text.strip()
for candidate in (text, self._extract_fenced_json(text), self._extract_brace_json(text)):
if not candidate:
continue
try:
parsed = json.loads(candidate)
except json.JSONDecodeError:
continue
if isinstance(parsed, dict):
return parsed
return None
def _extract_fenced_json(self, text: str) -> str:
match = re.search(r"```json\s*(.*?)```", text, re.DOTALL | re.IGNORECASE)
return match.group(1).strip() if match else ""
def _extract_brace_json(self, text: str) -> str:
start = text.find("{")
end = text.rfind("}")
if start == -1 or end == -1 or end <= start:
return ""
return text[start : end + 1]
def _manager_route(self, state: MultiAgentState) -> tuple[str, str]:
if state.phase == "research":
return "researcher", "先收集事实和样例,降低后续输出偏差。"
if state.phase == "plan":
if state.rework_count > 0:
return "planner", "评估未通过,规划师根据反馈重排步骤。"
return "planner", "将研究结论转成可执行计划。"
if state.phase == "execute":
return "executor", "按计划产出博客正文。"
if state.phase == "evaluate":
return "evaluator", "独立评估质量并决定是否返工。"
return "done", "流程完成。"
def _run_researcher(self, state: MultiAgentState) -> None:
tool_outputs = {
"tool_specs": TOOL_SPECS,
"search_knowledge": run_tool("search_knowledge", {"topic": state.query}),
"fetch_case_snippets": run_tool("fetch_case_snippets", {"topic": "Multi-Agent 多智能体模式"}),
"quality_rubric": run_tool("quality_rubric", {}),
}
messages = [
{"role": "system", "content": RESEARCHER_PROMPT},
{
"role": "user",
"content": (
f"用户目标:{state.query}\n\n"
f"工具返回资料:\n{json.dumps(tool_outputs, ensure_ascii=False, indent=2)}"
),
},
]
output = self._send_messages(messages)
data = self._extract_json_object(output) or {}
notes = data.get("research_notes")
risks = data.get("risks")
if not isinstance(notes, str) or not notes.strip():
notes = (
f"1. {tool_outputs['search_knowledge']}\n"
f"2. {tool_outputs['fetch_case_snippets']}\n"
f"3. {tool_outputs['quality_rubric']}"
)
if not isinstance(risks, list):
risks = ["缺少真实工具/数据源时,研究结论会偏向示例化。"]
state.research_notes = notes.strip()
state.risks = [item.strip() for item in risks if isinstance(item, str) and item.strip()]
state.trace.append("researcher")
def _default_plan(self, query: str) -> list[str]:
return [
f"明确 {query} 的目标读者与输出范围",
"定义系统管理器与四个角色的职责边界",
"按信息流描述任务拆分、执行与评估闭环",
"补充适用场景、边界与落地建议",
"整理为可直接发布的博客正文",
]
def _run_planner(self, state: MultiAgentState) -> None:
messages = [
{"role": "system", "content": PLANNER_PROMPT},
{
"role": "user",
"content": (
f"用户目标:{state.query}\n\n"
f"研究结论:{state.research_notes}\n\n"
f"风险点:{json.dumps(state.risks, ensure_ascii=False)}\n\n"
f"评估反馈(若为空表示首轮):{state.feedback}"
),
},
]
output = self._send_messages(messages)
data = self._extract_json_object(output) or {}
plan_steps = data.get("plan_steps")
criteria = data.get("acceptance_criteria")
if not isinstance(plan_steps, list):
plan_steps = self._default_plan(state.query)
clean_steps = [step.strip() for step in plan_steps if isinstance(step, str) and step.strip()]
if not clean_steps:
clean_steps = self._default_plan(state.query)
if not isinstance(criteria, list):
criteria = [
"角色分工与任务边界清晰",
"包含管理器路由与返工闭环",
"包含至少 2 个适用场景",
"内容可直接用于技术博客",
]
clean_criteria = [item.strip() for item in criteria if isinstance(item, str) and item.strip()]
state.plan_steps = clean_steps
state.acceptance_criteria = clean_criteria
state.trace.append("planner")
def _run_executor(self, state: MultiAgentState) -> None:
messages = [
{"role": "system", "content": EXECUTOR_PROMPT},
{
"role": "user",
"content": (
f"用户目标:{state.query}\n\n"
f"执行计划:{json.dumps(state.plan_steps, ensure_ascii=False)}\n\n"
f"验收标准:{json.dumps(state.acceptance_criteria, ensure_ascii=False)}\n\n"
f"研究结论:{state.research_notes}\n\n"
f"上轮评估反馈(若为空表示首轮):{state.feedback}"
),
},
]
output = self._send_messages(messages)
data = self._extract_json_object(output) or {}
draft = data.get("draft")
if not isinstance(draft, str) or not draft.strip():
draft = output
state.draft = draft.strip()
state.trace.append("executor")
def _run_evaluator(self, state: MultiAgentState) -> None:
messages = [
{"role": "system", "content": EVALUATOR_PROMPT},
{
"role": "user",
"content": (
f"用户目标:{state.query}\n\n"
f"验收标准:{json.dumps(state.acceptance_criteria, ensure_ascii=False)}\n\n"
f"当前草稿:\n{state.draft}"
),
},
]
output = self._send_messages(messages)
data = self._extract_json_object(output) or {}
passed = data.get("pass")
score = data.get("score")
feedback = data.get("feedback")
if not isinstance(passed, bool):
passed = False
if not isinstance(score, int):
score = 60 if passed else 45
if not isinstance(feedback, str) or not feedback.strip():
feedback = "评估输出缺失,建议补充更明确的架构流程、场景和边界。"
state.passed = passed
state.score = max(0, min(100, score))
state.feedback = feedback.strip()
state.trace.append("evaluator")
def _finalize(self, state: MultiAgentState) -> str:
messages = [
{"role": "system", "content": FINAL_RESPONSE_PROMPT},
{
"role": "user",
"content": (
f"用户目标:{state.query}\n\n"
f"是否通过评估:{state.passed}\n"
f"评估分数:{state.score}\n"
f"评估反馈:{state.feedback}\n\n"
f"当前草稿:\n{state.draft}"
),
},
]
output = self._send_messages(messages)
data = self._extract_json_object(output) or {}
final_answer = data.get("final_answer")
if isinstance(final_answer, str) and final_answer.strip():
return final_answer.strip()
if state.passed:
return state.draft
return (
"[未达到通过标准,返回当前最佳版本]\n"
f"评估反馈:{state.feedback}\n\n"
f"{state.draft}"
)
def run(self, query: str, verbose: bool = True) -> dict[str, Any]:
state = MultiAgentState(query=query)
for round_id in range(1, self.max_rounds + 1):
role, reason = self._manager_route(state)
if verbose:
print(f"\n=== Round {round_id} ===")
print(f"[Manager] 路由到: {role}")
print(f"[Manager] 决策原因: {reason}")
if role == "done":
break
if role == "researcher":
self._run_researcher(state)
state.phase = "plan"
if verbose:
print("[Researcher] 研究结论已产出。")
continue
if role == "planner":
self._run_planner(state)
state.phase = "execute"
if verbose:
print("[Planner] 执行计划已生成。")
for idx, step in enumerate(state.plan_steps, start=1):
print(f" {idx}. {step}")
continue
if role == "executor":
self._run_executor(state)
state.phase = "evaluate"
if verbose:
print("[Executor] 草稿已生成。")
continue
if role == "evaluator":
self._run_evaluator(state)
if verbose:
print(f"[Evaluator] pass={state.passed}, score={state.score}")
print(f"[Evaluator] feedback={state.feedback}")
if state.passed:
state.phase = "done"
elif state.rework_count >= self.max_reworks:
state.phase = "done"
else:
state.rework_count += 1
state.phase = "plan"
if verbose:
print(f"[Manager] 进入返工流程,第 {state.rework_count} 次返工。")
continue
final_answer = self._finalize(state)
return {
"final_answer": final_answer,
"passed": state.passed,
"score": state.score,
"rework_count": state.rework_count,
"trace": state.trace,
}
from agent import MultiAgentBlogAgent
def main() -> None:
default_query = "写一段技术博客内容,解释 Agent 的 5 种模式里 Multi-Agent 多智能体模式的工作机制与价值。"
query = input(f"请输入问题(直接回车使用默认问题):\n[{default_query}]\n> ").strip()
if not query:
query = default_query
agent = MultiAgentBlogAgent(max_rounds=10, max_reworks=2)
result = agent.run(query=query, verbose=True)
print("\n=== Final Answer ===")
print(result["final_answer"])
print("\n=== Meta ===")
print(
f"passed={result['passed']} | score={result['score']} | "
f"rework_count={result['rework_count']}"
)
print("trace=", " -> ".join(result["trace"]))
if __name__ == "__main__":
main()
挑战
- 通信协议设计
- 角色边界定义
- 状态同步问题
- Token 成本爆炸
Multi-Agent 更像一个“AI 组织系统”,而不是单个模型。
七、总结对比:如何选择?
| 模式 | 核心特点 | 复杂度 | 推荐场景 |
|---|---|---|---|
| ReAct | 实时推理、单次任务 | 低 | 简单 API 调用、搜索助手 |
| CodeAct | 编程驱动、高精度 | 中 | 数据处理、环境控制 |
| Plan Mode | 结构化拆解、长路径 | 中 | 项目规划、报告生成 |
| Reflection | 自我迭代、高质量 | 中 | 代码优化、内容精修 |
| Multi-Agent | 分工协作、高吞吐 | 高 | 企业级复杂工作流 |
选型建议
- 如果你刚入门:从 ReAct 开始。
- 如果涉及计算或环境操作:用 CodeAct。
- 如果任务路径长:加入 Plan。
- 如果质量要求高:加入 Reflection。
- 如果系统极其复杂:考虑 Multi-Agent。
现实工程中,往往是多种模式混合使用。
例如:
Plan + CodeAct + Reflection
这已经成为主流工程实践。
八、结语:迈向 AGI 的工程阶梯
一个重要认知:Agent 模式不是在提升模型智力,而是在提升系统可靠性。
我们正在通过“工程结构”弥补模型的:
- 不稳定性
- 非确定性
- 幻觉问题
Agent 是从“模型驱动”走向“系统驱动”的关键一步。
真正的 AGI,也许不只是一个超级模型,而是:一个由多个智能模块构成的协作系统。
如果你已经在实践 Agent 架构,欢迎在掘金分享你的工程心得。
未来 3 年,将是 Agent 架构快速进化的时代。
而现在,正是入场的最好时机。