ReAct是一种反应式的、步进决策的智能体,而Plan-and-Solve则是在动工之前则完成所有的规划,它的整个流程可以解耦成两个阶段:

  1. 规划阶段(Planning Phase): 智能体接收用户的完整问题,它的第一个任务是将问题分解为分步骤的行动计划,这个计划本身就是一次大模型调用产物。
  2. 执行阶段(Solving Phase):获取完整的计划后,智能体严格按照计划中的步骤逐一执行,每一步执行都可能是独立的LLM调用或者对上一步的加工处理,直到所有步骤完成,最终得出答案。

可以将这两个阶段形式化表达为:

  1. 规划模型$\pi_{\text{plan}}$根据原始问题$q$生成一个包含$n$个步骤的计划$P=(p_1, p_2, \dots, p_n)$
    $P = \pi_{\text{plan}}(q)$

  2. 随后,在执行阶段,执行模型$\pi_{\text{solve}}$会逐一完成计划中的步骤。对于第$i$个步骤,其解决方案$s_i$的生成会
    同时依赖于原始问题$q$、完整计划$P$以及之前所有步骤的执行结果$(s_1, \dots, s_{i-1})$:
    $s_i = \pi_{\text{solve}}(q, P, (s_1, \dots, s_{i-1}))$

image-20260803200725819

Plan-and-Solve 适用于可以被清晰分解的复杂任务,包括多步数学应用题,需要整个多个信息源的报告、代码生成等。

Plan-and-Solve实现

假设,目标问题是:“周一卖出15个水果,周二卖出的苹果数量是周一两倍,周三卖出的数量比周二少五个”。

则两个阶段的任务为:

  1. 规划阶段:将问题分级为三个步骤:计算周二销量、计算周三销量、计算总销量
  2. 执行阶段:按照计划一步步完成计算,并将每步结果作为下一步输入。

规划阶段

规划阶段的产出为:["步骤1","步骤2","步骤3", ...]

Prompt 设计

PLANNER_PROMPT_TEMPLATE = """
你是一个顶级的AI规划专家。你的任务是将用户提出的复杂问题分解成一个由多个简单步骤组成的行动计划。
请确保计划中的每个步骤都是一个独立的、可执行的子任务,并且严格按照逻辑顺序排列。
你的输出必须是一个Python列表,其中每个元素都是一个描述子任务的字符串。
问题:{question}
请严格按照以下格式输出你的计划,```python与```作为前后缀是必要的:
```python
["步骤1","步骤2","步骤3", ...]
```
"""

这个提示词具备以下特点:

  • 角色设定
  • 任务描述
  • 格式规约

代码实现

class Planner:
    def __init__(self, llm_client):
        self.llm_client = llm_client
        
    def plan(self, question: str) -> list[str]:
        prompt = PLANNER_PROMPT_TEMPLATE.format(question = question)
        
        messages = [{"role": "user", "content": prompt}]
        
        response_text = self.llm_client.think(messages = messages) or ""
        
        try:
            plan_str = response_text.split("```python")[1].split("```")[0].strip()
            plan = ast.literal_eval(plan_str)
            return plan if isinstance(plan, list) else []
        except Exception as e:
            return []

执行器与状态管理

在规划器(Planner)生成了清晰的行动蓝图后,需要一个执行器(Executor)来完成计算中的任务。执行器在已有上下文的基础上,专注解决具体某个步骤的问题,因此,执行器的prompt需要具备以下特点:

  • 原始问题
  • 完整计划
  • 历史步骤结果
  • 当前步骤
EXECUTOR_PROMPT_TEMPLATE = """
你是一位顶级的AI执行专家。你的任务是严格按照给定的计划,一步步地解决问题。
你将收到原始问题、完整的计划、以及到目前为止已经完成的步骤和结果。
请你专注于解决"当前步骤",并仅输出该步骤的最终答案,不要输出任何额外的解释或对话。

# 原始问题:
{question}

# 完整计划:
{plan}

# 历史步骤与结果:
{history}

# 当前步骤:
{current_step}

请仅输出针对"当前步骤"的回答:
"""

代码实现

class Exceptor:
    def __init__(self, llm_client):
        self.llm_client = llm_client

    def execute(self, question: str, plan: list[str]) -> str:
        response_text = ""
        history = ""

        for i, step in enumerate(plan):
            prompt = EXECUTOR_PROMPT_TEMPLATE.format(
                question = question,
                plan = plan,
                history = history if history else "无",
                current_step = step
            )

            messages = [{"role": "user", "content": prompt}]

            response_text = self.llm_client.think(messages = messages) or ""

            history += f"步骤 {i + 1}: {step} \n 结果:{response_text}\n\n"

        final_answer = response_text
        return final_answer

整体实现

class PlanAndSolveAgent:
    def __init__(self, llm_client):
        self.llm_client = llm_client
        self.planner = Planner(self.llm_client)
        self.executor = Exceptor(self.llm_client)
        
    def run(self, question: str):
        plan = self.planner.plan(question)
        if not plan:
            return
        
        final_answer = self.executor.execute(question, plan)