Python 实现推理与规划
本章节介绍 AI Agent 的核心智能能力,即推理与规划能力。
不同于传统的指令执行模式,具备推理能力的 Agent 能够进行复杂的思考过程。
它能够将复杂任务分解为可执行的步骤,并基于上下文做出决策。
ReAct 框架
ReAct(Reasoning + Acting)是一种将推理与行动相结合的框架。
在 ReAct 模式中,Agent 会交替进行推理和行动,形成一个闭环。
推理指导行动,行动结果又反馈给推理过程,如此循环往复。
ReAct 模拟了人类的问题解决过程:在采取行动前先思考,行动后观察结果,然后基于新信息继续推理。
工作原理
ReAct 的核心思想可以概括为三个阶段的循环:
第一步,推理阶段。Agent 基于当前上下文生成下一步的行动思考。
第二步,行动阶段。Agent 执行选定的工具或动作。
第三步,观察阶段。Agent 接收行动结果,更新上下文,返回第一步继续推理。
代码实现
下面是一个 ReAct Agent 的简化实现:
ReAct Agent 基本实现
"""
ReAct 框架的 Agent 实现
核心机制:推理 -> 行动 -> 观察 -> 推理(循环)
"""
def __init__(self, llm, tools, max_iterations=5):
# LLM 实例,用于推理和生成
self.llm = llm
# 可用工具列表
self.tools = tools
# 最大迭代次数,防止无限循环
self.max_iterations = max_iterations
def run(self, task):
"""
执行 ReAct 循环
:param task: 用户任务描述
:return: 执行结果
"""
# 初始化上下文,包含任务和历史信息
context = {
"task": task,
"steps": [], # 已执行步骤的记录
"observations": [] # 观察结果的记录
}
for i in range(self.max_iterations):
# 阶段一:推理 - 基于当前上下文生成下一步行动
reasoning = self.llm.reason(context)
# 判断是否需要执行工具,还是直接返回答案
if reasoning.needs_action:
# 选择要使用的工具
action = reasoning.select_tool(self.tools)
# 执行工具并获取观察结果
observation = action.execute(reasoning.tool_input)
# 将观察结果添加到上下文
context["observations"].append(observation)
else:
# 不需要行动,直接返回推理得出的答案
return reasoning.answer
# 达到最大迭代次数仍未完成
return "达到最大迭代次数限制"
典型应用场景
ReAct 模式特别适合以下场景:
需要在环境中探索、收集信息后才能完成的任务。
智能搜索场景:Agent 需要先搜索信息,再基于搜索结果进行推理和总结。
对话式问答:Agent 需要通过多轮对话澄清需求并获取信息。
复杂问题求解:问题不能一步解决,需要多个中间步骤。
注意:ReAct 模式的优势在于它的灵活性,能够根据每一步的执行结果动态调整后续行动。但这也意味着执行路径可能不稳定,适合需要探索的任务。
思维链(Chain of Thought, CoT)
思维链是一种促使模型展示逐步推理过程的技术。
CoT 不是让模型直接给出答案,而是引导它先展示推理步骤,再得出最终结论。
为什么需要思维链
直接让模型输出答案存在几个问题:
复杂推理的中间步骤容易被忽略或跳过。
难以定位错误发生在哪里。
用户无法理解模型的思考过程。
思维链通过要求模型展示推理过程,解决了这些问题。
Zero-shot CoT
Zero-shot CoT 是一种无需示例即可激发逐步推理能力的方法。
只需要在提示词末尾添加 "让我们一步一步地思考" 这样的触发语句。
Zero-shot CoT 示例
prompt = """
问题:小明有 5 个苹果,小红给了他 3 个,小明吃掉了 2 个,还剩多少个?
让我们一步一步地思考:
"""
# 调用语言模型
response = llm.generate(prompt)
# 模型输出示例:
# 第一步:计算小明收到苹果后的总数
# 小明原来有 5 个苹果,小红给了他 3 个
# 5 + 3 = 8 个
#
# 第二步:计算吃掉后的数量
# 小明吃掉了 2 个
# 8 - 2 = 6 个
#
# 结论:还剩 6 个苹果
Few-shot CoT
Few-shot CoT 通过提供包含详细推理过程的示例,帮助模型学习特定的推理模式。
当 Zero-shot CoT 效果不佳时,可以尝试 Few-shot CoT。
Few-shot CoT 示例
prompt = """
示例 1:
问题:小张有 10 元钱,买了 3 本书,每本 2 元,还剩多少?
让我们一步一步地思考:
- 小张原来有 10 元
- 每本书 2 元,买了 3 本,花费 3 × 2 = 6 元
- 10 - 6 = 4 元
答案:还剩 4 元
示例 2:
问题:一只猫每小时抓 2 只老鼠,8 小时抓了多少只?
让我们一步一步地思考:
- 每小时抓 2 只老鼠
- 8 小时抓了 8 × 2 = 16 只
答案:抓了 16 只
问题:{user_question}
让我们一步一步地思考:
"""
response = llm.generate(prompt)
思维链的优势
思维链技术带来三个核心价值:
可追溯性:将复杂推理分解为可追踪的中间步骤,便于理解推理过程。
可解释性:增强模型的可解释性,让用户知道答案从何而来。
准确性提升:显著提升复杂推理任务的准确率,尤其在数学和逻辑任务上效果明显。
Tree of Thoughts(ToT)
思维树是思维链的扩展,它不再局限于线性推理。
ToT 在每个推理节点探索多条可能的路径,形成树状结构。
这使得 Agent 能够进行多路径探索、回溯和全局评估。
与思维链的区别
CoT 采用线性推理,每一步依赖前一步的结论,适合有明确路径的问题。
ToT 采用空间推理,同时考虑多个可能的分支,适合需要探索和规划的问题。
代码实现
ToT Agent 基本实现
"""
Tree of Thoughts Agent 实现
核心机制:在每个节点生成多个候选分支,评估后选择最优继续
"""
def __init__(self, llm, max_depth=4, beam_size=3):
# LLM 实例
self.llm = llm
# 最大搜索深度
self.max_depth = max_depth
# 每层保留的候选节点数量(beam width)
self.beam_size = beam_size
def solve(self, problem):
"""
使用 ToT 框架解决问题
:param problem: 问题描述
:return: 最优解
"""
# 创建根节点,包含问题作为初始状态
root = ThoughtNode(problem, depth=0)
# 初始化前沿节点列表(待扩展的节点)
frontier = [root]
# 逐层扩展
for depth in range(self.max_depth):
# 存储所有候选节点
all_candidates = []
# 遍历所有前沿节点
for node in frontier:
# 为当前节点生成多个候选下一步
candidates = self.llm.generate_thoughts(
node.content, # 当前节点内容
n=self.beam_size # 生成数量
)
# 创建新节点并加入候选列表
for cand in candidates:
all_candidates.append(
ThoughtNode(cand, depth + 1, parent=node)
)
# 评估所有候选节点
evaluated = self.evaluator.rank(all_candidates)
# 选择最优的 beam_size 个节点作为下一轮前沿
frontier = evaluated[:self.beam_size]
# 检查是否找到解决方案
if self.is_solution(frontier):
break
# 回溯找到最优解
return self.backtrack_best(frontier)
应用场景
ToT 特别适合需要做选择或规划的场景:
创意写作:生成多个故事发展方向,评估后选择最优。
战略规划:评估多个行动方案的潜在结果。
复杂决策:需要考虑多种可能性的决策问题。
任务规划与 MCTS
蒙特卡洛树搜索(Monte Carlo Tree Search, MCTS)是一种用于复杂决策问题的启发式搜索算法。
MCTS 在 Agent 规划中有广泛应用,尤其适合游戏 AI 和复杂任务规划。
核心思想
MCTS 通过模拟随机游戏来评估每个决策节点的潜在价值。
它不需要评估所有可能的路径,而是通过抽样和统计来指导搜索方向。
MCTS 的四个步骤
第一步,选择(Selection):从根节点开始,递归选择最优子节点直到达到叶节点。
选择时使用 UCB(Upper Confidence Bound)公式平衡探索与利用。
第二步,扩展(Expansion):在叶节点添加一个或多个子节点。
第三步,模拟(Simulation):从新节点开始,随机模拟游戏直到结束。
第四步,反向传播(Backpropagation):更新模拟路径上所有节点的统计信息。
代码实现
MCTS 规划器实现
class MCTSNode:
"""
MCTS 树节点
存储节点状态、统计数据和子节点关系
"""
def __init__(self, state, parent=None, action=None):
# 当前状态
self.state = state
# 父节点引用
self.parent = parent
# 从父节点到达此节点的动作
self.action = action
# 子节点列表
self.children = []
# 此节点的访问次数
self.visit_count = 0
# 此节点的累计奖励值
self.reward = 0.0
def is_fully_expanded(self):
"""检查是否已扩展所有可能的子节点"""
return len(self.children) > 0
def is_terminal(self):
"""检查是否为终止节点(游戏结束或目标达成)"""
return self.state.is_terminal()
def uct_child(self):
"""
使用 UCT 公式选择最优子节点
UCT = reward/visits + C * sqrt(ln(parent_visits)/visits)
C 是探索常数,通常设为 sqrt(2)
"""
# 探索常数,平衡探索与利用
C = math.sqrt(2)
return max(
self.children,
key=lambda c: c.reward / c.visit_count +
C * math.sqrt(math.log(self.visit_count) / c.visit_count)
)
class MCTSPlanner:
"""
MCTS 规划器
使用蒙特卡洛树搜索为 Agent 生成行动规划
"""
def __init__(self, simulation_limit=1000, exploration_constant=1.41):
# 最大模拟次数
self.simulation_limit = simulation_limit
# 探索常数
self.exploration_constant = exploration_constant
def plan(self, initial_state):
"""
从初始状态开始规划
:param initial_state: 初始状态
:return: 最优动作
"""
# 创建根节点
root = MCTSNode(initial_state)
# 执行多次模拟
for _ in range(self.simulation_limit):
# 1. 选择:从根节点向下选择最优子节点直到叶节点
node = self._selection(root)
# 2. 扩展:如果不是终止节点,扩展一个新节点
if not node.is_terminal():
node = self._expansion(node)
# 3. 模拟:从新节点开始随机模拟到终止
reward = self._simulation(node)
# 4. 反向传播:更新路径上所有节点的统计
self._backpropagation(node, reward)
# 返回根节点最优子节点对应的动作
return root.best_child().action
def _selection(self, node):
"""选择阶段:选择最优子节点"""
while node.is_fully_expanded() and not node.is_terminal():
node = node.uct_child()
return node
def _expansion(self, node):
"""扩展阶段:添加新子节点"""
# 生成所有可能的动作
possible_actions = node.state.get_possible_actions()
# 为每个动作创建一个子节点
for action in possible_actions:
new_state = node.state.apply_action(action)
child = MCTSNode(new_state, parent=node, action=action)
node.children.append(child)
# 返回随机一个子节点(也可使用确定性策略)
return node.children[0]
def _simulation(self, node):
"""模拟阶段:随机模拟到游戏结束"""
state = node.state
while not state.is_terminal():
# 随机选择动作
actions = state.get_possible_actions()
action = random.choice(actions)
state = state.apply_action(action)
# 返回最终奖励
return state.get_reward()
def _backpropagation(self, node, reward):
"""反向传播:更新统计信息"""
while node is not None:
node.visit_count += 1
node.reward += reward
node = node.parent
注意:MCTS 的计算成本较高,适合需要深度规划但有明确终止条件的场景。对于实时性要求高的任务,可能需要限制模拟次数或使用其他方法。
Reflexion(自我反思)
Reflexion 是一种让 Agent 具有自我反思能力的技术。
通过为 Agent 添加反思机制,它能够在失败后分析错误原因,调整策略并重试。
核心思想
Agent 不仅执行动作,还要观察结果并反思:我为什么失败?下次应该如何改进?
这种能力对于持续学习和自我改进至关重要。
代码实现
Reflexion Agent 实现
"""
具有自我反思能力的 Agent
核心机制:执行 -> 评审 -> 反思 -> 重试
"""
def __init__(self, actor, reviewer, max_retries=3):
# 执行器:负责执行具体任务
self.actor = actor
# 评审器:负责评估执行结果
self.reviewer = reviewer
# 最大重试次数
self.max_retries = max_retries
def run(self, task):
"""
执行带自我反思的任务
:param task: 任务描述
:return: 执行结果
"""
# 维护执行历史
history = []
for attempt in range(self.max_retries):
# 阶段一:尝试执行任务
result = self.actor.execute(task, history)
# 记录本次尝试
history.append({
"attempt": attempt,
"result": result
})
# 阶段二:评审结果
feedback = self.reviewer.evaluate(task, result)
# 阶段三:检查是否成功
if feedback.is_success:
return result
# 阶段四:反思失败原因
# 生成新的策略提示,指导下一次尝试
reflection = self.reviewer.reflect(
task, # 原始任务
result, # 失败结果
feedback # 评审反馈
)
# 将反思结果添加到历史,供下次尝试参考
history.append({
"type": "reflection",
"content": reflection
})
# 所有重试都失败,返回历史记录
return history
class Reviewer:
"""评审器:评估执行结果并生成反思"""
def evaluate(self, task, result):
"""
评估执行结果
:return: 包含是否成功及详细反馈的对象
"""
# 检查结果是否满足任务要求
is_success = self.check_success(task, result)
if is_success:
return EvaluationResult(is_success=True)
else:
# 生成失败原因分析
failure_reasons = self.analyze_failures(task, result)
return EvaluationResult(
is_success=False,
reasons=failure_reasons
)
def reflect(self, task, result, feedback):
"""
生成反思内容
帮助 Actor 理解失败原因并改进策略
"""
prompt = f"""
任务:{task}
执行结果:{result}
失败原因:{feedback.reasons}
请分析失败原因,并给出改进建议。
重点说明:
1. 哪里出了问题?
2. 下次应该如何避免?
3. 需要改变什么策略?
"""
return self.llm.generate(prompt)
应用场景
Reflexion 特别适合以下场景:
需要持续改进的任务,如对话系统、代码生成等。
错误代价高但重试成本低的场景。
需要从失败中学习的情况。
任务分解策略
复杂任务通常需要分解为可管理的子任务。
有效的任务分解是规划能力的基础。
递归任务分解
将任务递归地分解为更小的子任务,直到子任务可以直接执行。
递归任务分解
"""
递归分解任务
:param task: 要分解的任务
:param is_executable_fn: 判断任务是否可直接执行的函数
:param decompose_fn: 分解任务的函数
:return: 可执行任务列表
"""
# 如果任务可以直接执行,直接返回
if is_executable_fn(task):
return [task]
# 分解任务为子任务
subtasks = decompose_fn(task)
# 递归分解每个子任务
result = []
for subtask in subtasks:
# 对子任务递归调用分解函数
result.extend(
decompose(subtask, is_executable_fn, decompose_fn)
)
return result
# 示例:判断任务是否可执行
def is_code_complete(task):
"""检查任务是否可以直接执行代码"""
return task.get("type") == "code" and len(task.get("dependencies", [])) == 0
# 示例:分解复杂任务
def decompose_programming_task(task):
"""分解编程任务"""
if "write_function" in task["type"]:
return [
{"type": "understand_requirements", "task": task["spec"]},
{"type": "write_code", "spec": task["spec"]},
{"type": "write_tests", "function": task["function_name"]},
{"type": "verify_tests", "function": task["function_name"]}
]
return [task]
平行任务分解
识别可以并行执行的独立子任务,提高执行效率。
这是加速任务执行的关键策略。
层次任务分解
将任务分为不同抽象层次,高层任务调用低层任务,形成任务层次树。
适合需要多层抽象的复杂系统。
Plan-and-Execute 模式
Plan-and-Execute 是一种将规划与执行分离的架构模式。
Agent 首先完整地规划整个任务流程,然后按计划执行。
与 ReAct 的区别
ReAct 是边推理边执行,更灵活但路径可能不稳定。
Plan-and-Execute 是先规划后执行,更稳定但缺乏动态调整能力。
代码实现
Plan-and-Execute Agent 实现
"""
Plan-and-Execute 架构的 Agent
核心机制:先完整规划,再按计划执行
"""
def __init__(self, planner, executor):
# 规划器:负责制定执行计划
self.planner = planner
# 执行器:负责执行具体步骤
self.executor = executor
def run(self, task):
"""
执行任务
分为规划阶段和执行阶段
"""
# ==================== 规划阶段 ====================
# 一次性生成完整的执行计划
plan = self.planner.create_plan(task)
# ==================== 执行阶段 ====================
results = []
# 按计划顺序执行
for step in plan.steps:
# 执行当前步骤
result = self.executor.execute(step)
results.append(result)
# 检查是否需要重新规划
# 例如:执行结果与预期不符,或出现意外情况
if self.needs_replan(results):
# 基于当前结果重新规划
plan = self.planner.replan(task, results)
# 整合所有结果
return self.summarize(results)
def needs_replan(self, results):
"""
判断是否需要重新规划
检查执行结果是否符合预期
"""
# 获取最后一个结果
last_result = results[-1]
# 如果结果明显不符合预期,需要重规划
if last_result.is_unexpected():
return True
# 如果结果导致后续步骤无法执行,需要重规划
if last_result.blocks_future():
return True
return False
class Planner:
"""规划器:生成任务执行计划"""
def create_plan(self, task):
"""创建初始执行计划"""
prompt = f"""
任务:{task}
请制定详细的执行计划,包括:
1. 需要执行的步骤顺序
2. 每个步骤的具体操作
3. 步骤之间的依赖关系
输出格式:
步骤1: [操作描述]
步骤2: [操作描述]
...
"""
plan_text = self.llm.generate(prompt)
return Plan.parse(plan_text)
def replan(self, task, results):
"""基于执行结果重新规划"""
# 分析已完成的结果
completed = [r for r in results if r.is_success]
failed = [r for r in results if not r.is_success]
# 生成调整后的计划
prompt = f"""
原始任务:{task}
已完成步骤:{completed}
失败步骤:{failed}
请制定调整后的执行计划:
"""
plan_text = self.llm.generate(prompt)
return Plan.parse(plan_text)
class Executor:
"""执行器:执行具体的计划步骤"""
def execute(self, step):
"""执行单个步骤"""
# 根据步骤类型选择执行方式
if step.type == "tool_call":
return self.execute_tool_call(step)
elif step.type == "code":
return self.execute_code(step)
elif step.type == "query":
return self.execute_query(step)
return Result(success=False, error="Unknown step type")
注意:Plan-and-Execute 模式适合任务结构相对稳定、可以提前规划的场景。对于需要灵活应变的环境,ReAct 模式可能更合适。
章节小结
本章节介绍了 AI Agent 的推理与规划核心能力。
ReAct 框架通过推理与行动的循环,让 Agent 能够边执行边调整策略。
思维链(CoT) 通过展示逐步推理过程,提升复杂任务的执行准确性。
思维树(ToT) 扩展了思维链,支持多路径探索和回溯。
MCTS 是一种启发式搜索算法,适合复杂决策和规划问题。
Reflexion 赋予 Agent 自我反思能力,从失败中学习和改进。
任务分解策略 是规划能力的基础,包括递归分解、平行分解和层次分解。
Plan-and-Execute 采用先规划后执行的模式,适合结构稳定的任务。
这些技术可以单独使用,也可以组合使用,构建更强大的 Agent 系统。
点我分享笔记