一、引言在人工智能应用领域如何让大语言模型LLM处理复杂问题是关键课题。Plan-And-Solve计划与执行模式是一种经典的多步骤推理框架它将复杂问题分解为多个简单步骤逐一解决最终得到正确答案。本文以Plan_and_solve.py为例深入剖析该模式的设计理念与实现细节。二、什么是 Plan-And-Solve2.1 核心思想Plan-And-Solve 的核心理念“先规划后执行”。┌──────────────┐ ┌──────────────┐ │ Planner │────▶│ Executor │ │ (规划器) │ │ (执行器) │ └──────────────┘ └──────────────┘ │ │ ▼ ▼ 生成计划 执行计划 分解问题 逐步解决2.2 解决的问题问题场景传统方式Plan-And-Solve复杂数学题LLM 可能算错分步骤计算每步可追溯多步骤任务容易遗忘或重复按计划执行不遗漏需要中间结果最终答案错误难排查每步有结果可追踪三、代码结构解析3.1 整体架构# 两大核心组件PLANNER_PROMPT_TEMPLATE# 规划器提示词模板EXECUTOR_PROMPT_TEMPLATE# 执行器提示词模板# 三大核心类Planner# 负责分解问题Executor# 负责执行计划PlanAndSolveAgent# 整合两者协调工作3.2 工作流程图用户提问 │ ▼ ┌─────────────────────────┐ │ PlanAndSolveAgent │ │ .run() │ └─────────────────────────┘ │ ▼ ┌─────────────────────────┐ │ Planner │ │ .plan() │ │ │ │ 调用 1 次 LLM │ │ 返回步骤列表 │ └─────────────────────────┘ │ ▼ ┌─────────────────────────┐ │ Executor │ │ .execute() │ │ │ │ 循环 N 次每步骤1次 │ │ 返回最终答案 │ └─────────────────────────┘ │ ▼ 输出最终答案LLM 调用总次数 1 N其中 N 为计划步骤数四、核心组件详解4.1 规划器 (Planner)4.1.1 提示词模板设计PLANNER_PROMPT_TEMPLATE 你是一个顶级的AI规划专家。你的任务是将用户提出的复杂问题分解成一个由多个简单步骤组成的行动计划。 请确保计划中的每个步骤都是一个独立的、可执行的子任务并且严格按照逻辑顺序排列。 你的输出必须是一个Python列表其中每个元素都是一个描述子任务的字符串。 问题:{question}请严格按照以下格式输出你的计划python与作为前后缀是必要的:python[步骤1,步骤2,步骤3,...]“”**设计要点** - 明确要求 LLM 输出 Python 列表格式 - 指定 python 作为格式标记便于后续解析 #### 4.1.2 Planner 类实现 python class Planner: def __init__(self, llm_client: HelloAgentsLLM): self.llm_client llm_client def plan(self, question: str) - list[str]: prompt PLANNER_PROMPT_TEMPLATE.format(questionquestion) messages [{role: user, content: prompt}] response_text self.llm_client.think(messagesmessages) or # 解析 LLM 返回的内容 plan_str response_text.split(python)[1].split()[0].strip() plan ast.literal_eval(plan_str) return plan if isinstance(plan, list) else []关键步骤解析步骤代码作用1.split(python)[1]提取 python 后的内容2.split()[0]提取结束标记前的内容3.strip()去除首尾空白4ast.literal_eval()安全地将字符串转为列表4.1.3 LLM 返回示例输入问题: 一个水果店周一卖出了15个苹果。周二卖出的苹果数量是周一的两倍。周三卖出的数量比周二少了5个。请问这三天总共卖出了多少个苹果LLM 返回好的我来帮你分析这个问题。 python [计算周二卖出的苹果数量15×230, 计算周三卖出的苹果数量30-525, 计算三天卖出的苹果总数15302570]这是你的解决计划。**解析后得到** python [计算周二卖出的苹果数量15×230, 计算周三卖出的苹果数量30-525, 计算三天卖出的苹果总数15302570]4.2 执行器 (Executor)4.2.1 提示词模板设计EXECUTOR_PROMPT_TEMPLATE 你是一位顶级的AI执行专家。你的任务是严格按照给定的计划一步步地解决问题。 你将收到原始问题、完整的计划、以及到目前为止已经完成的步骤和结果。 请你专注于解决当前步骤并仅输出该步骤的最终答案不要输出任何额外的解释或对话。 # 原始问题: {question} # 完整计划: {plan} # 历史步骤与结果: {history} # 当前步骤: {current_step} 请仅输出针对当前步骤的回答: 设计要点传递原始问题保持上下文传递完整计划让 LLM 知道全局传递历史步骤与结果关键创新点明确当前步骤明确任务4.2.2 Executor 类实现classExecutor:def__init__(self,llm_client:HelloAgentsLLM):self.llm_clientllm_clientdefexecute(self,question:str,plan:list[str])-str:history# 历史记录final_answer# 最终答案fori,stepinenumerate(plan,1):# 构建提示词promptEXECUTOR_PROMPT_TEMPLATE.format(questionquestion,planplan,historyhistoryifhistoryelse无,current_stepstep)messages[{role:user,content:prompt}]# 调用 LLMresponse_textself.llm_client.think(messagesmessages)or# 更新历史记录historyf步骤{i}:{step}\n结果:{response_text}\n\nfinal_answerresponse_textreturnfinal_answer执行流程示例┌─────────────────────────────────────────────────┐ │ 第1次循环计算周二卖出的苹果数量 │ │ history 无 │ │ LLM 返回: 周二卖出30个苹果 │ │ history 更新为: │ │ 步骤 1: 计算周二卖出...结果: 周二卖出30个苹果 │ └─────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────┐ │ 第2次循环计算周三卖出的苹果数量 │ │ history 步骤1...周二卖出30个苹果 │ │ LLM 返回: 周三卖出25个苹果 │ │ history 更新为: │ │ 步骤1...周二卖出30个苹果\n步骤2...周三卖出25个 │ └─────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────┐ │ 第3次循环计算三天卖出的苹果总数 │ │ history 步骤1...周二卖出30个苹果\n步骤2...周三卖出25个 │ │ LLM 返回: 三天总共卖出70个苹果 │ │ final_answer 三天总共卖出70个苹果 │ └─────────────────────────────────────────────────┘五、关键知识点5.1enumerate(plan, 1)的妙用fori,stepinenumerate(plan,1):作用同时获取索引和值且索引从 1 开始。参数含义示例plan要遍历的列表[A, B, C]1起始值可灵活调整从 1 开始输出对比enumerate(plan, 1) → 1, A / 2, B / 3, C enumerate(plan, 0) → 0, A / 1, B / 2, C (默认值)应用场景人性化展示1/5比0/5更直观字母编号enumerate(plan, ord(A))可得到A, B, C...5.2 字符串解析split()的链式调用plan_strresponse_text.split(python)[1].split()[0].strip()解析过程原始文本: 我来帮你分析...\npython\n[步骤1, 步骤2]\n\n这是计划。 步骤1: split(python) [我来帮你...\n, [步骤1, 步骤2]\n\n这是计划。] 步骤2: [1] [步骤1, 步骤2]\n\n这是计划。 步骤3: split() [[步骤1, 步骤2]\n, \n这是计划。] 步骤4: [0] [步骤1, 步骤2]\n 步骤5: strip() [步骤1, 步骤2]5.3ast.literal_eval()vseval()# 推荐安全的方式planast.literal_eval([步骤1, 步骤2])# 返回: [步骤1, 步骤2]# 不推荐存在安全风险planeval([步骤1, 步骤2])# 返回: [步骤1, 步骤2]区别方法安全性用途ast.literal_eval()✅ 安全仅解析字面量列表、字典、数字、字符串等eval()❌ 危险执行任意 Python 代码5.4history的设计哲学historyf步骤{i}:{step}\n结果:{response_text}\n\n为什么需要 history上下文传递让 LLM 记住前序步骤的结果避免遗忘LLM 在长对话中可能忘记早期信息结果可追溯每一步都有记录便于调试简化写法historyhistoryifhistoryelse无# 等价于historyhistoryor无六、模式优势与局限6.1 优势优势说明✅ 可追溯每一步都有结果便于调试✅ 逻辑清晰问题分解后更容易解决✅ 错误隔离单步错误不会影响整体✅ 易于维护Planner 和 Executor 职责分离6.2 局限局限说明❌ LLM 调用多每个步骤调用一次成本较高❌ 步骤依赖如果规划不合理可能出错❌ 错误累积前一步错误可能影响后续七、总结Plan-And-Solve 模式是一种简单但强大的多步骤推理框架Planner一次性调用 LLM将复杂问题分解为步骤计划Executor遍历每个步骤携带历史记录调用 LLM 执行History通过传递历史记录解决 LLM 上下文遗忘问题最终答案返回最后一步的结果这种模式特别适合需要多步骤推理、结果可追溯、逻辑严密的场景如数学计算、复杂分析、任务规划等。