嗨,我是辉哥,一个致力于使用 AI 技术搞副业的超级个体
决定智能体能力上限的不是底层大模型的参数量,而是它如何组织"思考"与"行动"的过程。ReAct、Plan-and-Solve 和 Reflection 三种范式分别代表了边想边做、先规划后执行、自我反思迭代三种问题解决策略,覆盖了当前智能体开发中最主流的工作流模式。
为了让你直观感受三种范式的差异,本章所有示例都围绕日常生活中的常见任务展开:查询外部信息、计算多步问题、优化解决方案。你会看到不同场景下,各类任务如何被不同的范式最有效地解决。
在深入细节之前,建议你先带着一个核心问题阅读:你的任务更适合哪种范式? 本章回答的核心问题:
- ReAct 如何让智能体在推理与行动之间交替循环,动态适应未知环境
- Plan-and-Solve 如何将复杂任务解耦为规划与执行两个阶段,保持目标一致性
- Reflection 如何通过执行-反思-优化的迭代闭环,将合格方案提升为优秀方案
- 三种范式之间的演进关系:为什么一种不够,需要引入下一种
目录
3.1 ReAct 范式
ReAct 将推理与行动融合为 Thought -> Action -> Observation 的交替循环,使智能体能够感知环境、实时调整策略。它是处理探索性任务的首选范式。
3.1.1 为什么需要 ReAct
理解一个范式,最好的方式是回到它被提出之前。在 ReAct 出现之前,大语言模型的主流用法分为两条路线。
一条是纯推理路线,典型代表是思维链(Chain-of-Thought)。模型被引导逐步展开推理过程,擅长逻辑推导和数学计算。但它有一个致命缺陷:完全依赖训练时学到的知识,无法与外部世界交互。面对训练数据截止之后的问题,或者需要实时信息的场景,模型只能猜测或编造答案,这就是所谓的事实幻觉。
另一条是纯行动路线。模型直接输出要执行的动作,比如调用搜索 API 或查询数据库。但它缺少推理环节,无法分析当前情况、评估行动结果、调整下一步策略。一旦工具返回的结果出乎预期,模型没有纠错能力。
ReAct 的核心洞察是:思考与行动不是对立的,而是相辅相成。ReAct 要解决的根本问题是:如何让模型在"想"的同时也能"做",用"做"的结果来校正"想"的方向? 类比:人在陌生城市找路——你不会在出发前就在脑子里规划好每一步(纯思考),也不会漫无目的地乱走(纯行动),而是走几步看看路牌,发现方向不对就调整。思考为行动提供方向和依据,行动的结果反过来修正和验证思考。二者交替进行,形成一个闭环。理解了这个核心思想后,我们来看看它是如何在代码层面具体运作的。
3.1.2 工作流:Thought -> Action -> Observation
ReAct 将每一步输出规范为三个固定环节:
- Thought(思考):智能体的推理过程。分析当前情况,判断还需要什么信息,规划下一步做什么
- Action(行动):智能体决定的具体动作,通常以特定格式调用外部工具
- Observation(观察):工具执行后返回的结果
循环过程如下:
初始问题
|
v
[Thought 1] -> 我需要查询用户提到的 wttr.in 天气 API 的信息
|
v
[Action 1] -> Search["wttr.in API 文档 使用方法"]
|
v
[Observation 1] -> wttr.in 是一个免费的天气 API,支持通过 URL 参数控制输出格式...
|
v
[Thought 2] -> API 文档显示了参数说明,我需要进一步了解认证和限流情况
|
v
[Action 2] -> Search["wttr.in rate limit free usage"]
|
v
[Observation 2] -> wttr.in 免费版每小时最多 200 次请求,无需注册 API Key...
|
v
[Thought 3] -> 已经收集了足够的信息,可以回答用户了
|
v
[Action 3] -> Finish[wttr.in 是一个免费的天气 API,无需 API Key,通过 URL 参数即可使用...]
|
v
最终答案
形式化表达如下。用数学语言来说,在时间步 t,模型根据原始问题 x 和之前的行动-观察历史,生成当前思考 o_t 和行动 a_t:
(o_t, a_t) = Model(x, {(a_1, o_1), ..., (a_{t-1}, o_{t-1})})
工具执行行动 a_t 后返回新的观察结果:
o_t = Tool(a_t)
每次循环都将新的 (Action, Observation) 追加到历史上下文中,作为下一轮 Thought 的输入。这个循环持续到 Thought 判断任务已完成。
上面的形式化表达看起来有些抽象,但核心就一句话:每一步都带着之前的记忆继续前进。重点记住这个循环机制即可,不必纠结于数学符号的细节。
3.1.3 工具定义
ReAct 必须依赖外部工具才能与真实世界交互。一个良好定义的工具包含三个要素:
- 名称(Name):简洁唯一的标识符,供智能体在 Action 中调用
- 描述(Description):自然语言描述工具的用途和使用场景。这是关键部分,模型依赖它来判断何时使用哪个工具
- 执行逻辑(Execution Logic):实际执行任务的函数
以下是一个搜索工具的简化实现:
import os
from serpapi import SerpApiClient
def search(query: str) -> str:
"""网页搜索工具,用于获取时事、事实等外部信息。"""
api_key = os.getenv("SERPAPI_API_KEY", "")
client = SerpApiClient({"q": query, "api_key": api_key})
result = client.search()
# 优先返回知识图谱摘要
if "knowledge_graph" in result:
return str(result["knowledge_graph"].get("description", ""))
# fallback:返回前几条搜索结果摘要
snippets = [r.get("snippet", "") for r in result.get("organic_results", [])[:3]]
return "\n".join(s for s in snippets if s)
管理多个工具需要一个统一的执行器:
import re
class ToolExecutor:
def __init__(self):
self.tools = {}
def register(self, name: str, func, description: str):
self.tools[name] = {"func": func, "description": description}
def execute(self, action_text: str) -> str:
"""解析 'ToolName[input]' 格式并执行对应工具。"""
match = re.match(r"(\w+)\[(.*)\]", action_text, re.DOTALL)
if not match:
return "错误:无法解析行动指令"
tool_name, tool_input = match.group(1), match.group(2)
if tool_name not in self.tools:
return f"错误:未找到工具 '{tool_name}'"
return self.tools[tool_name]["func"](tool_input)
def get_available_tools(self) -> str:
return "\n".join(
f"- {name}: {info['description']}" for name, info in self.tools.items()
)
wttr.in 适合教学和轻量演示,但免费版没有 SLA 保证,高峰期可能响应缓慢或不可用;数据精度有限,不支持历史天气查询和长期预报。在生产环境中应使用 OpenWeatherMap、AccuWeather 等商业 API。
工具描述的质量直接影响智能体的表现。描述需要明确说明工具的用途、适用场景,甚至不适用的场景,帮助模型做出正确的工具选择。
3.1.4 完整实现
到这里,我们已经掌握了 ReAct 范式的三大核心要素:交替循环的工作流、形式化表达、工具定义。接下来把它们组合起来,看看一个完整的 ReAct 智能体是如何运转的。
ReAct 智能体的核心是一个循环:格式化提示词 -> 调用 LLM -> 解析输出 -> 执行动作 -> 整合结果。
提示词模板定义了智能体的行为规范,是驱动整个循环的"大脑指令":
REACT_PROMPT_TEMPLATE = """
你是一个有能力调用外部工具的智能助手。
可用工具如下:
{tools}
请严格按照以下格式进行回应:
Thought: 你的思考过程,用于分析问题、拆解任务和规划下一步行动。
Action: 你决定采取的行动,格式为 ToolName[input]。
当收集到足够信息时,使用 Finish[最终答案] 输出答案。
现在,请开始解决以下问题:
Question: {question}
History: {history}
"""
完整的 ReActAgent 类:
class ReActAgent:
def __init__(self, llm_client, tool_executor, max_steps=5):
self.llm_client = llm_client
self.tool_executor = tool_executor
self.max_steps = max_steps
self.history = []
def run(self, question: str):
self.history = []
for step in range(1, self.max_steps + 1):
print(f"--- 第 {step} 步 ---")
prompt = REACT_PROMPT_TEMPLATE.format(
tools=self.tool_executor.get_available_tools(),
question=question,
history="\n".join(self.history),
)
response = self.llm_client.think([{"role": "user", "content": prompt}])
if not response:
print("错误:LLM 未能返回有效响应")
break
thought, action = self._parse_output(response)
print(f"Thought: {thought}")
print(f"Action: {action}")
if "Finish" in action:
answer = re.search(r"Finish\[(.*)\]", action, re.DOTALL)
final = answer.group(1).strip() if answer else action
print(f"\n最终答案: {final}")
return final
observation = self.tool_executor.execute(action)
print(f"Observation: {observation}")
self.history.append(
f"Thought: {thought}\nAction: {action}\nObservation: {observation}"
)
print("\n已达到最大步数限制,任务可能未完成。")
return None
def _parse_output(self, text: str):
thought_match = re.search(r"Thought:\s*(.*?)(?=\nAction:|$)", text, re.DOTALL)
action_match = re.search(r"Action:\s*(.*?)$", text, re.DOTALL)
thought = thought_match.group(1).strip() if thought_match else None
action = action_match.group(1).strip() if action_match else None
return thought, action
运行示例:
将上面的代码片段串联起来,就可以让智能体开始工作了:
executor = ToolExecutor()
executor.register(
"Search", search, "一个网页搜索引擎。用于获取时事、事实等外部信息。"
)
# llm_client 需预先配置好 API Key
# llm_client = YourLLM(model="YOUR-MODEL", apiKey="YOUR-API-KEY", baseUrl="YOUR-URL")
agent = ReActAgent(llm_client, executor, max_steps=5)
agent.run("我想在项目中使用 wttr.in 天气 API,它需要认证吗?有调用限制吗?")
典型运行输出:
--- 第 1 步 ---
Thought: 这个问题需要查询 wttr.in API 的使用文档...
Action: Search[wttr.in API 文档 使用方法]
Observation: wttr.in 是一个免费的天气 API,支持通过 URL 参数控制输出格式...
--- 第 2 步 ---
Thought: API 文档没有明确说明认证要求,我需要进一步确认调用限制...
Action: Search[wttr.in rate limit free usage]
Observation: wttr.in 免费版每小时最多 200 次请求,无需注册 API Key...
--- 第 3 步 ---
Thought: 已经收集了足够的信息来回答这个问题。
Action: Finish[wttr.in 是一个免费的天气 API,无需 API Key...]
最终答案: wttr.in 是一个免费的天气 API...
3.1.5 特点与局限
优势:
- 高可解释性:Thought 链完整记录了智能体的决策依据,每一步为什么这么做都清晰可见,便于调试和审计
- 动态纠错:走一步看一步,根据 Observation 实时调整策略。搜索结果不理想时可以修正搜索词重试
- 工具协同:LLM 负责规划推理,工具负责执行具体任务,突破了单一 LLM 在知识时效性和计算准确性上的局限
固有局限:
- 步进决策缺乏全局视野:每一步只基于当前信息做局部最优选择,没有整体规划。面对需要多步协作的复杂任务,可能走弯路
- 对 LLM 能力依赖强:模型逻辑推理或指令遵循能力不足时,容易生成错误规划或不符合格式的 Action,导致流程中断
- 执行效率低:每一步都需要串行调用 LLM,伴随网络延迟和 API 成本。任务越复杂,累积延迟越明显
- 提示词脆弱:机制的稳定运行依赖精心设计的提示词模板,微小变动都可能影响模型行为
调试建议:
- 调用 LLM 前打印完整提示词,追溯决策源头
- 输出解析失败时打印原始文本,判断是模型格式问题还是解析逻辑有误
- 模型频繁出错时,在提示词中加入完整的 Thought-Action-Observation 示例(Few-shot Prompting)
- 将 temperature 设为 0 保证输出确定性
ReAct 解决了纯思考和纯行动各自孤立的问题,但它"走一步看一步"的策略在处理需要长远规划的复杂任务时容易迷失方向。比如你需要整理一份月度开支报告,涉及分类统计、趋势分析、总结建议等多个环节——这种任务有明确的结构和步骤,不需要逐步探索,而是需要整体规划。
当任务需要明确的全局规划能力时,Plan-and-Solve 范式给出了更好的答案。
3.2 Plan-and-Solve 范式
Plan-and-Solve 将任务处理解耦为规划和执行两个独立阶段,先制定完整计划再逐步实施。它适合结构清晰、可被分解为独立子步骤的复杂推理任务。
3.2.1 为什么需要 Plan-and-Solve
理解一个范式的设计动机,往往要从它的"前一个范式的短板"说起。ReAct 将思考和行动融合在每一步中,这种反应式策略在探索性任务中表现出色。但它的弱点也很明显:缺乏全局规划。
考虑一个场景:需要整理一份家庭月度开支报告,涉及分类统计、趋势分析、节省建议等多个环节。如果用 ReAct,模型会在每一步根据当前信息决定下一步做什么。这种局部决策容易在中间步骤中迷失方向,忘记最终目标是什么,或者遗漏关键环节。ReAct 的问题在于:每一步都做了当前看起来最合理的选择,但局部最优不等于全局最优。
人类面对复杂任务时的做法是先制定计划再执行。先想清楚整体要做什么、分几个步骤、每个步骤的目标是什么,然后按部就班地完成。Plan-and-Solve 的核心动机就是将这种"先谋后动"的策略引入智能体——把"做什么"和"怎么做"解耦,让规划器拥有全局视野,执行器只需专注完成当前步骤。
3.2.2 工作流:Plan -> Solve
Plan-and-Solve 将整个流程解耦为两个独立阶段:
- 规划阶段(Planning Phase):接收完整问题,分解并制定清晰的分步行动计划
- 执行阶段(Solving Phase):严格按计划中的步骤逐一执行,每一步的输出作为下一步的上下文
完整问题
|
v
[Planning Phase]
输入: 原始问题
输出: [步骤1, 步骤2, ..., 步骤n]
|
v
[Solving Phase]
步骤1: 执行,得到结果 r_1
步骤2: 在 r_1 基础上执行,得到结果 r_2
...
步骤n: 在所有历史结果基础上执行,得到最终答案 r_n
|
v
最终答案
形式化表达如下。规划器根据原始问题 x 生成包含 n 个步骤的计划 P:
P = [p_1, p_2, ..., p_n] = Planner(x)
执行器逐一完成计划。第 i 步的结果 r_i 依赖于原始问题 x、完整计划 P、以及之前所有步骤的结果:
r_i = Solver(x, P, {r_1, r_2, ..., r_{i-1}}, p_i)
简单来说:规划器负责"把大问题拆成小问题",执行器负责"按顺序解决每个小问题,并记住之前的结果"。最终答案就是最后一步的结果 r_n。
到这里,Plan-and-Solve 的核心机制已经很清晰了——它和 ReAct 最大的区别在于,规划阶段拥有一次性看到全局的能力。接下来我们分别看看规划器和执行器的具体实现。
3.2.3 规划器
规划器的任务是接收原始问题,输出结构化的行动计划。关键是强制模型输出可解析的格式:
import ast
class Planner:
def __init__(self, llm_client):
self.llm_client = llm_client
def plan(self, question: str) -> list[str]:
# 规划提示词:要求模型输出 Python 列表格式的计划
prompt = f"""
你是一个顶级的AI规划专家。你的任务是将用户提出的复杂问题分解成一个由多个简单步骤组成的行动计划。
请确保计划中的每个步骤都是一个独立的、可执行的子任务,并且严格按照逻辑顺序排列。
你的输出必须是一个Python列表,其中每个元素都是一个描述子任务的字符串。
问题: {question}
请严格按照以下格式输出你的计划:
[\"步骤1\", \"步骤2\", \"步骤3\", ...]
"""
response = self.llm_client.think([{"role": "user", "content": prompt}]) or ""
try:
plan = ast.literal_eval(response.strip())
return plan if isinstance(plan, list) else []
except (ValueError, SyntaxError) as e:
print(f"解析计划时出错: {e}")
return []
规划器输出的质量直接决定整个系统的成败。如果计划遗漏了关键步骤或步骤间逻辑关系混乱,执行阶段只会按错误计划推进,不会主动修正。
3.2.4 执行器与状态管理
规划器生成好计划后,执行器需要按部就班地落实每一步。与规划器不同,执行器的核心任务是维护状态、确保信息在步骤间正确传递:
EXECUTOR_PROMPT_TEMPLATE = """
你是一位顶级的AI执行专家。你的任务是严格按照给定的计划,一步步地解决问题。
# 原始问题:
{question}
# 完整计划:
{plan}
# 历史步骤与结果:
{history}
# 当前步骤:
{current_step}
请仅输出针对"当前步骤"的回答,不要输出任何额外的解释:
"""
class Executor:
def __init__(self, llm_client):
self.llm_client = llm_client
def execute(self, question: str, plan: list[str]) -> str:
history = ""
for i, step in enumerate(plan):
print(f"-> 执行步骤 {i+1}/{len(plan)}: {step}")
prompt = EXECUTOR_PROMPT_TEMPLATE.format(
question=question,
plan=plan,
history=history if history else "无",
current_step=step,
)
response = self.llm_client.think([{"role": "user", "content": prompt}]) or ""
history += f"步骤 {i+1}: {step}\n结果: {response}\n\n"
print(f" 结果: {response}")
return response
3.2.5 整合与运行
将规划器和执行器组合为完整的智能体:
class PlanAndSolveAgent:
def __init__(self, llm_client):
self.planner = Planner(llm_client)
self.executor = Executor(llm_client)
def run(self, question: str):
print(f"问题: {question}")
plan = self.planner.plan(question)
if not plan:
print("无法生成有效的行动计划。")
return None
final_answer = self.executor.execute(question, plan)
print(f"\n最终答案: {final_answer}")
return final_answer
运行示例:
agent = PlanAndSolveAgent(llm_client)
agent.run("一个团队第一周修复了8个 bug,第二周修复的数量是第一周的 1.5 倍,第三周比第二周多修复了 4 个。请问这三周总共修复了多少个 bug?平均每周修复多少个?")
典型输出:
问题: 一个团队第一周修复了8个 bug,第二周修复的数量是第一周的 1.5 倍,第三周比第二周多修复了 4 个。请问这三周总共修复了多少个 bug?平均每周修复多少个?
--- 正在生成计划 ---
["确认第一周的 bug 修复数量: 8个", "计算第二周的 bug 修复数量: 8 x 1.5 = 12个", "计算第三周的 bug 修复数量: 12 + 4 = 16个", "计算三周总修复数量: 8 + 12 + 16 = 36个", "计算平均每周修复数量: 36 / 3 = 12个"]
--- 正在执行计划 ---
-> 执行步骤 1/5: 确认第一周的 bug 修复数量: 8个
结果: 8
-> 执行步骤 2/5: 计算第二周的 bug 修复数量: 8 x 1.5 = 12个
结果: 12
-> 执行步骤 3/5: 计算第三周的 bug 修复数量: 12 + 4 = 16个
结果: 16
-> 执行步骤 4/5: 计算三周总修复数量: 8 + 12 + 16 = 36个
结果: 36
-> 执行步骤 5/5: 计算平均每周修复数量: 36 / 3 = 12个
结果: 12
最终答案: 三周共修复了 36 个 bug,平均每周修复 12 个。
Plan-and-Solve 适用于结构性强的复杂任务:多步数学应用题、需要整合多个信息源的报告撰写、代码生成任务等。
3.2.6 特点与优势
优势:
- 全局规划能力:规划器一次性看到完整问题,生成结构化行动计划,避免 ReAct 的局部最优陷阱
- 任务解耦:规划与执行职责分离,Planner 专注"做什么",Executor 专注"怎么做",便于分别优化
- 可预测性强:计划一旦生成,步骤数量和执行顺序确定,便于预估时间和资源消耗
- 不需要外部工具:纯推理任务无需工具即可运行,成本低于 ReAct
3.2.7 局限
Plan-and-Solve 引入了全局规划,但有一个明显的缺陷:计划一旦生成就是静态的,执行过程中无法自我审视和优化结果。如果初稿存在逻辑漏洞或质量问题,执行器只会按部就班地推进,不会主动发现并修正。
这就好比你写好一份文档或方案,功能完整但表达不够精炼——如果不检查修改直接提交,质量就会打折扣。人类完成工作后总会反复检查、优化、改进。
如何让智能体在执行后主动审视和优化自己的输出?Reflection 范式解决了这个问题。
3.3 Reflection 范式
Reflection 通过执行-反思-优化的迭代循环,使智能体能够自我审查和改进输出。它将一次性任务执行转变为持续优化过程,显著提升复杂任务的成功率和答案质量。
3.3.1 为什么需要 Reflection
前两种范式各有专长,但它们共享一个共同特点:一旦完成任务,工作流程就结束了。输出什么就是什么,没有内部机制来审视和修正这个结果。
但在实际场景中,初稿很少是最终版本。写代码后需要 code review,写报告后需要校对,做数学题后需要验算。人类通过这种自我反思的能力持续提升工作质量。Reflection 要解决的根本问题是:如何让智能体像人一样,在完成初稿后主动检查、发现并修正自己的错误? 类比:写完文章后自己读一遍、圈出需要修改的地方、然后重写——这个循环重复几次后,文章质量会明显提升。Reflection 的核心思想就是为智能体引入这种事后自我校正的能力,提供一个内部纠错回路,不再完全依赖外部工具反馈,而是能够主动发现并修正更高层次的逻辑和策略错误。
3.3.2 工作流:Execute -> Reflect -> Refine
Reflection 的工作流程是一个三步循环:
- 执行(Execution):智能体尝试完成任务,生成初步解决方案(初稿)
- 反思(Reflection):智能体调用带有特殊提示词的模型实例,扮演评审员角色,审视初稿并指出问题:事实性错误、逻辑漏洞、效率问题、遗漏信息等
- 优化(Refinement):智能体将初稿和评审反馈作为新上下文,再次调用模型生成修订稿
任务描述
|
v
[Execution] -> 生成初稿 y_0
|
v
[Reflect] -> 评审初稿,生成反馈 f_0
| "代码时间复杂度过高,建议..."
v
[Refine] -> 结合初稿和反馈,生成修订稿 y_1
|
v
[Reflect] -> 评审 y_1,生成反馈 f_1
|
v
[Refine] -> 结合 y_1 和 f_1,生成修订稿 y_2
|
v
... 循环直到反馈不再提出新问题或达到迭代次数上限
|
v
最终输出
形式化表达如下。y_i 是第 i 次迭代的输出(y_0 为初始输出),反思模型(Reflector)针对当前输出 y_i 生成反馈 f_i:
f_i = Reflector(task, y_i)
优化模型(Refiner)结合任务描述、上一版输出和反馈意见,生成新版输出:
y_{i+1} = Refiner(task, y_i, f_i)
简单来说:每次迭代就是"写稿 -> 审稿 -> 改稿"的循环。Reflector 扮演审稿人角色,指出问题;Refiner 扮演作者角色,根据意见修改。循环直到审稿人不再提出新问题。
到这里,Reflection 的工作机制已经清晰了。接下来我们看看它是如何通过记忆模块来支撑这个迭代循环的。
3.3.3 记忆模块
Reflection 的核心是迭代,迭代的前提是记住之前的尝试和反馈。需要一个记忆模块来存储每次循环的完整轨迹:
from typing import List, Dict, Any, Optional
class Memory:
def __init__(self):
self.records: List[Dict[str, Any]] = []
def add_record(self, record_type: str, content: str):
self.records.append({"type": record_type, "content": content})
def get_trajectory(self) -> str:
"""将所有记录格式化为连贯的提示文本。"""
parts = []
for r in self.records:
if r["type"] == "execution":
parts.append(f"--- 上一轮尝试 (代码) ---\n{r['content']}")
elif r["type"] == "reflection":
parts.append(f"--- 评审员反馈 ---\n{r['content']}")
return "\n\n".join(parts)
def get_last_execution(self) -> Optional[str]:
for r in reversed(self.records):
if r["type"] == "execution":
return r["content"]
return None
3.3.4 Reflection 智能体实现
Reflection 需要三个不同角色的提示词协同工作:
INITIAL_PROMPT = """
你是一位资深的Python程序员。请根据以下要求编写一个Python函数。
代码必须包含完整的函数签名、文档字符串,并遵循PEP 8编码规范。
要求: {task}
请直接输出代码。
"""
REFLECT_PROMPT = """
你是一位极其严格的代码评审专家,对代码性能有极致要求。
请审查以下代码,专注于找出算法效率上的主要瓶颈。
# 原始任务:
{task}
# 待审查的代码:
{code}
请分析该代码的时间复杂度,并提出具体的、可行的改进建议。
如果代码在算法层面已经达到最优,请回答"无需改进"。
"""
REFINE_PROMPT = """
你是一位资深的Python程序员,正在根据评审专家的反馈来优化代码。
# 原始任务:
{task}
# 上一轮的代码:
{last_code_attempt}
# 评审员的反馈:
{feedback}
请根据反馈生成优化后的新版本代码。代码须包含完整的函数签名和文档字符串。
"""
完整的 ReflectionAgent 类:
三个角色的提示词准备就绪后,我们来把它们整合到完整的智能体类中:
class ReflectionAgent:
def __init__(self, llm_client, max_iterations=3):
self.llm_client = llm_client
self.memory = Memory()
self.max_iterations = max_iterations
def run(self, task: str):
print(f"任务: {task}")
# 初始执行
initial_prompt = INITIAL_PROMPT.format(task=task)
initial_code = self._call_llm(initial_prompt)
self.memory.add_record("execution", initial_code)
# 迭代循环:反思与优化
for i in range(1, self.max_iterations + 1):
print(f"\n--- 第 {i}/{self.max_iterations} 轮迭代 ---")
last_code = self.memory.get_last_execution()
reflect_prompt = REFLECT_PROMPT.format(task=task, code=last_code)
feedback = self._call_llm(reflect_prompt)
self.memory.add_record("reflection", feedback)
if "无需改进" in feedback:
print("代码已无需改进,任务完成。")
break
refine_prompt = REFINE_PROMPT.format(
task=task,
last_code_attempt=last_code,
feedback=feedback,
)
refined_code = self._call_llm(refine_prompt)
self.memory.add_record("execution", refined_code)
final_code = self.memory.get_last_execution()
print(f"\n最终代码:\n{final_code}")
return final_code
def _call_llm(self, prompt: str) -> str:
return self.llm_client.think([{"role": "user", "content": prompt}]) or ""
3.3.5 运行实例
以"编写一个 Python 函数,对一个整数列表去重并返回排序后的结果"为例:
任务: 编写一个Python函数,对输入的整数列表去重并返回升序排序的结果
--- 初始尝试 ---
生成了先排序再去重的初版代码(使用了 sorted() + list 遍历,存在冗余操作)
--- 第 1/3 轮迭代 ---
反思: 指出代码存在冗余操作——先排序再去重的顺序不合理,应该先去重再排序,减少排序的数据量。同时建议使用 set 代替遍历去重
优化: 成功实现了 set 去重 + sorted() 的简洁版本
--- 第 2/3 轮迭代 ---
反思: 当前实现已经是最优方案(时间复杂度 O(n log n),空间复杂度 O(n)),无需改进
代码已无需改进,任务完成
最终代码:
def deduplicate_and_sort(numbers: list[int]) -> list[int]:
"""对整数列表去重并返回升序排序结果。"""
return sorted(set(numbers))
这个案例展示了 Reflection 的核心价值:初版代码功能正确但实现冗余,通过反思发现效率瓶颈,通过优化实现代码精简。迭代式改进将合格方案提升为优秀方案。
3.3.6 成本收益分析
Reflection 能够显著提升输出质量,但这种能力提升并非没有代价。在决定是否使用 Reflection 之前,我们需要先理清它的成本与收益。
成本:
- 模型调用开销增加:每轮迭代至少额外调用两次 LLM(一次反思、一次优化),多轮迭代使 API 成本成倍增加
- 任务延迟显著:Reflection 是串行过程,每轮优化必须等待上一轮反思完成,不适合对实时性要求高的场景
- 提示工程复杂度上升:需要为执行、反思、优化不同阶段分别设计和调试有效的提示词
收益:
- 解决方案质量的跃迁:将功能正确的初始方案迭代优化为性能高效的最终方案
- 鲁棒性与可靠性增强:通过自我纠错,发现并修复初始方案中的逻辑漏洞、事实性错误或边界情况处理不当
Reflection 适合对结果质量和可靠性要求极高、且对实时性要求相对宽松的场景:关键业务代码生成、科学研究中的复杂推演、深度决策支持系统。如果需要快速响应或大致正确即可,更轻量的 ReAct 或 Plan-and-Solve 是更具性价比的选择。
三种范式各有利弊,理解它们之间的演进关系,能帮助我们在实际开发中做出更合适的选择。
3.4 范式对比与选型指南
3.4.1 演进关系
三种范式之间存在清晰的演进因果链——每一层演进,都是在弥补前一层的核心短板:
信息交互层:纯思考(思维链)和纯行动各自孤立 → 无法与外部交互、容易事实幻觉 → 引出 ReAct(将推理与行动融合为交替循环)
全局规划层:ReAct 步进决策缺乏全局规划 → 复杂任务易迷失方向 → 引出 Plan-and-Solve(先规划后执行,一次性生成完整计划)
自我纠错层:ReAct 和 Plan-and-Solve 一次性输出 → 无法自我纠错和改进 → 引出 Reflection(执行-反思-优化迭代闭环)
这条演进链背后的规律很简单:每一种新范式,都是在弥补前一种范式中暴露出的关键短板。但每解决一个短板,又会暴露新的短板——ReAct 缺全局规划,Plan-and-Solve 缺自我纠错,Reflection 的代价是更高的计算成本。没有"完美范式",只有"最适合当前任务的范式"。
3.4.2 对比分析
| 维度 | ReAct | Plan-and-Solve | Reflection |
|---|---|---|---|
| 核心策略 | 边想边做,动态调整 | 先规划后执行,按部就班 | 先生成后反思,迭代改进 |
| 工作流 | Thought-Action-Observation 循环 | Plan -> Solve 两阶段 | Execute -> Reflect -> Refine 循环 |
| LLM 调用次数 | 每步一次(不确定步数) | 规划 1 次 + 执行 N 次 | 初始 1 次 + 迭代 2K 次 |
| 是否需要外部工具 | 是 | 否(可配合使用) | 否(可配合使用) |
| 全局规划 | 无,局部决策 | 有,一次性生成完整计划 | 无,通过迭代收敛 |
| 纠错机制 | 实时纠错(基于 Observation) | 计划内顺序执行,无自动纠错 | 事后纠错(基于自我反思) |
| 适用任务 | 探索性、需外部信息 | 结构性强、可分解 | 质量要求高、需迭代优化 |
| 劣势/风险 | 步进决策缺乏全局视野,复杂任务易走弯路 | 计划静态无法修正,初稿错误会传导 | 调用成本高,延迟大,不适合实时场景 |
3.4.3 选型指南
选择 ReAct 的场景:
- 需要查询实时信息(天气、新闻、股价)
- 需要与外部 API 交互(操作数据库、调用服务接口)
- 任务目标不明确,需要探索性搜索
- 需要精确计算,应交给计算器工具而非依赖 LLM
选择 Plan-and-Solve 的场景:
- 多步数学应用题或逻辑推理题
- 需要整合多个信息源的报告撰写
- 代码生成任务,需先构思整体架构再逐一实现
- 任务的逻辑路径确定,适合预先分解
选择 Reflection 的场景:
- 生成关键的业务代码或技术文档
- 科学研究中的复杂逻辑推演
- 需要深度分析和规划的决策支持
- 对最终结果的准确性、可靠性和质量有极高要求
3.4.4 混合使用
三种范式并非互斥,可以组合使用:
- ReAct + Reflection:先用 ReAct 探索外部信息生成初步方案,再用 Reflection 自我审查和优化。适合需要外部信息输入且对输出质量要求极高的场景
- Plan-and-Solve + Reflection:Plan-and-Solve 生成结构化结果后,通过 Reflection 多轮改进。适合代码生成、报告撰写等对质量要求严格的场景
- Plan-and-Solve + ReAct:在执行阶段,某个步骤如果需要外部信息,嵌套调用 ReAct 来完成。适合部分步骤需要探索性搜索的复杂任务
选择哪一种范式或组合,取决于四个核心维度:是否需要外部工具交互、任务是否可以预先分解、对结果质量的要求有多高、对执行延迟的容忍度如何。
本章小结
回顾一下本章的核心内容:
- ReAct 通过 Thought-Action-Observation 循环赋予智能体环境感知和动态纠错能力,解决了纯推理和纯行动各自孤立的问题
- Plan-and-Solve 将复杂任务分解为规划与执行两阶段,以全局规划克服了 ReAct 步进决策容易迷失方向的缺陷
- Reflection 引入执行-反思-优化的迭代闭环,将一次性输出转变为持续优化过程,弥补了前两种范式无法自我改进的不足
- 三种范式形成清晰的演进因果链:信息交互能力 -> 全局规划能力 -> 自我纠错能力,层层递进,每一层都在前一层的局限上引入新机制
- 实际开发中应根据任务的探索性、结构性和质量要求选择单一范式或组合使用
下一章将探索低代码平台构建智能体的方案,了解如何通过图形化和模块化的方式快速搭建、调试和部署智能体应用。从代码编写到可视化组装,我们会看到智能体开发效率如何被进一步提升。