Planner

我们将把你的智能体,从一个"单打独斗的码农",升级为一个包含"架构师 (Planner) + 程序员 (Coder) + 测试员 (Tester)"的微服务开发团队


🧠 阶段五核心思考:为什么我们需要 Planner(规划师)?

在写代码之前,我们先用架构设计的思维来推演一下:

  • 大模型的局限性(Context Window 的迷失): 对于简单的需求(如"写个回文判断"),让 Claude 直接生成代码(Zero-shot)是很轻松的。但如果用户的需求极其复杂,例如: "写一个脚本,先读取本地的 CSV 配置文件,然后并发请求 3 个不同的天气 API 取平均值,最后把结果存入 SQLite 数据库。"
  • "面条代码"灾难: 如果直接把这个复杂需求扔给 Coder,它大概率会把所有逻辑揉在一个巨大的函数里。这会导致两个致命问题:一是极容易写出 Bug,二是 Tester 一旦报错,满屏的 Traceback 会让 Coder 在修复时彻底"晕头转向",最终在重试 3 次后惨淡收场(触发熔断)。
  • 引入架构师(Planner 节点):

在真实的软件开发中,拿到复杂需求的第一步是系统设计与任务拆解 。我们需要在 Graph 的最前端加入一个 planner_node。它的任务绝对不是写代码,而是输出一份**"施工图纸(步骤列表)"**。


🐍 代码实现:升级 State 与加入 Planner 节点

为了实现这个蓝图,我们需要在全局的 State 中新增一个字段来存放这份"施工图纸",并为 Planner 专门定义一个结构化输出。

Step 1: 升级全局状态 (State) 与定义 Planner 契约
复制代码
from typing import TypedDict, Annotated, List, Optional
import operator
from pydantic import BaseModel, Field

# ==========================================
# 1. 升级全局 State
# ==========================================
class SkillsCreatorState(TypedDict):
    user_requirement: str

    # 【新增】:存放 Planner 拆解出的详细步骤列表
    # 这里用覆盖逻辑即可,因为通常只在最开始规划一次
    plan: List[str]  

    current_code: str
    current_test_code: str
    execution_logs: Annotated[List[str], operator.add]
    iteration_count: int
    error_message: Optional[str]

# ==========================================
# 2. 定义 Planner 的输出结构 (Schema)
# ==========================================
class PlannerOutputSchema(BaseModel):
    """规划师节点输出的结构"""
    steps: List[str] = Field(
        description="""将用户的复杂需求拆解为 3-5 个具体的实现步骤。
每个步骤需要清晰说明要做什么,比如'步骤1:定义数据类','步骤2:实现API请求'等。"""
    )
Step 2: 编写 Planner 节点

这个节点就像一个经验丰富的架构师,只动口(做规划),不动手(写代码)。

复制代码
from langchain_core.prompts import ChatPromptTemplate

# 假设 structured_llm 依然是绑定了 Claude 的实例
# 针对 Planner,我们绑定 Planner 的 Schema
planner_llm = llm.with_structured_output(PlannerOutputSchema)

def planner_node(state: SkillsCreatorState) -> dict:
    """负责将复杂需求拆解为具体步骤的节点"""
    print(">>> [节点执行]: 进入 Planner 节点,正在进行系统架构设计...")

    user_req = state.get("user_requirement")

    prompt = ChatPromptTemplate.from_messages([
        ("system", "你是一个资深的 Python 架构师。你的任务是将用户的复杂需求,拆解为清晰、解耦的开发步骤。不要写代码,只输出步骤。"),
        ("human", f"用户的需求是:{user_req}")
    ])

    # 调用大模型生成规划
    response: PlannerOutputSchema = planner_llm.invoke(prompt)

    print(f"    [Planner] 拆解完成,共 {len(response.steps)} 个步骤。")
    for i, step in enumerate(response.steps, 1):
        print(f"      - 步骤 {i}: {step}")

    # 将生成的步骤列表作为补丁更新到 State 的 'plan' 字段中
    return {
        "plan": response.steps,
        "execution_logs": [f"[Log] Planner 生成了包含 {len(response.steps)} 步的开发计划。"]
    }
Step 3: 修改 Coder 节点的 Prompt (让程序员看图纸)

现在,工单(State)流转到 Coder 手里时,里面多了一份 plan。我们需要把这份图纸喂给 Claude,让它照着写。

复制代码
# 在 real_coder_node 内部,我们需要做如下修改:

def real_coder_node(state: SkillsCreatorState) -> dict:
    # ... (前面的获取状态代码保持不变) ...
    plan = state.get("plan", []) # 获取施工图纸

    # 将列表拼接成易读的字符串
    plan_str = "\n".join([f"步骤 {i+1}: {step}" for i, step in enumerate(plan)])

    # 在首次生成代码时,把计划加入 Prompt
    if not error_message:
        human_prompt = f"""用户的需求是:{user_req}
        
【架构师提供的开发计划】:
请你严格按照以下步骤来实现代码:
{plan_str}
"""
    # ... (其余逻辑与阶段四完全相同) ...
Step 4: 调整图的拓扑结构 (Graph Topology)

最后,我们需要改变流水线的连接方式。起点不再是 Coder,而是 Planner。

复制代码
from langgraph.graph import StateGraph, START, END

builder = StateGraph(SkillsCreatorState)

builder.add_node("planner", planner_node)
builder.add_node("coder", real_coder_node)
builder.add_node("tester", tester_node)

# ==========================================
# 全新的有向图边连接 (Edges)
# ==========================================
# 1. 启动 -> planner (架构师先上)
builder.add_edge(START, "planner")

# 2. planner -> coder (架构师画完图,交给程序员)
builder.add_edge("planner", "coder")

# 3. coder -> tester (程序员写完,交给 QA)
builder.add_edge("coder", "tester")

# 4. tester -> 【条件分支】 (QA 测试完,决定是结束还是打回给程序员)
builder.add_conditional_edges(
    "tester", 
    route_after_test,
    {
        "success": END,
        "max_retries_reached": END,
        "retry": "coder"  # 注意:如果出错,是打回给 coder 修 Bug,而不是打回给 planner 重新规划!
    }
)

💡 逻辑深度解析

  • 关注"退回逻辑(Retry Path)"的设计

add_conditional_edges 中,当代码测试失败时,我们选择路由回 coder,而不是 planner

为什么这么设计? 这符合真实的软件工程逻辑。需求拆解和架构(Planner)通常是没问题的,跑不通大概率是因为代码(Coder)的具体实现写错了。如果每次报错都让架构师重新推翻重来,不仅浪费 API Token,还会导致整个系统的上下文极其不稳定。

  • 分治法(Divide and Conquer)的体现

通过把大问题变成有序号的小任务(plan_str),Claude 在编写代码时会更有逻辑性。它会在代码中写出结构更清晰的函数,甚至主动把不同的步骤封装成不同的子函数,极大降低了 Bug 的产生率。


相关推荐
大模型真好玩18 小时前
LangChain DeepAgents 速通指南(七)—— DeepAgents使用Agent Skill
人工智能·langchain·deepseek
王飞飞不会飞1 天前
服务器LLama Factory Lora 微调模型过程记录
langchain·aigc
Csvn1 天前
🌟 LangChain 30 天保姆级教程 · Day 27|RAG 安全加固实战!防注入、防泄露、权限控制,守护企业知识资产!
langchain
王莎莎-MinerU1 天前
MinerU + LangChain 实战:从 PDF 解析到 AI 问答全流程
人工智能·langchain·pdf·开源·产品运营·团队开发·个人开发
老王熬夜敲代码1 天前
LangSmith监控与可视化
langchain
老王熬夜敲代码1 天前
多智能体协作(Multi-Agent Supervisor)
langchain
ok_hahaha1 天前
AI从头开始-黑马LongGraph-简单学习
人工智能·学习·langchain·lang graph
倦王1 天前
langchain下载以及相关的一些包的下载
langchain
羑悻的小杀马特1 天前
LangChain 检索器与 RAG 系统的深度解析与应用
langchain·检索器·rag
尘埃落定wf1 天前
LangChain Tools工具模块完全指南:@tool装饰器+StructuredTool+Pydantic校验+实战案例
python·ai·langchain