Skip to content

Python 实现推理与规划

本章节介绍 AI Agent 的核心智能能力,即推理与规划能力。

不同于传统的指令执行模式,具备推理能力的 Agent 能够进行复杂的思考过程。

它能够将复杂任务分解为可执行的步骤,并基于上下文做出决策。


ReAct 框架

ReAct(Reasoning + Acting)是一种将推理与行动相结合的框架。

在 ReAct 模式中,Agent 会交替进行推理和行动,形成一个闭环。

推理指导行动,行动结果又反馈给推理过程,如此循环往复。

ReAct 模拟了人类的问题解决过程:在采取行动前先思考,行动后观察结果,然后基于新信息继续推理。

工作原理

ReAct 的核心思想可以概括为三个阶段的循环:

第一步,推理阶段。Agent 基于当前上下文生成下一步的行动思考。

第二步,行动阶段。Agent 执行选定的工具或动作。

第三步,观察阶段。Agent 接收行动结果,更新上下文,返回第一步继续推理。

代码实现

下面是一个 ReAct Agent 的简化实现:

ReAct Agent 基本实现

python

class ReActAgent:

    """

    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 示例

python

# 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 示例

python

# 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 基本实现

python

class ToTAgent:

    """

    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 规划器实现

python

import math

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 实现

python

class ReflexionAgent:

    """

    具有自我反思能力的 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 特别适合以下场景:

需要持续改进的任务,如对话系统、代码生成等。

错误代价高但重试成本低的场景。

需要从失败中学习的情况。


任务分解策略

复杂任务通常需要分解为可管理的子任务。

有效的任务分解是规划能力的基础。

递归任务分解

将任务递归地分解为更小的子任务,直到子任务可以直接执行。

递归任务分解

python

def decompose(task, is_executable_fn, decompose_fn):

    """

    递归分解任务

    :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 实现

python

class PlanAndExecuteAgent:

    """

    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 系统。

AI 思考中...

RAG 与知识检索

Python 实现 RAG 与知识检索

基于 VitePress 构建,部署于 GitHub Pages