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 的产生率。


相关推荐
青梅煮酒与君饮6 小时前
浅谈大模型、Agent、Function Calling、MCP、Skill、Subagent、Langchain、Workflow
人工智能·python·语言模型·langchain·llama
liu****6 小时前
LangChain-AI应用开发框架(一)
c++·python·langchain·本地部署大模型
飞Link7 小时前
深入挖掘 LangChain Community 核心组件,从数据接入到企业级 RAG 实战
开发语言·python·langchain
m0_747124538 小时前
LangChain 嵌入向量详解
python·ai·langchain
wggmrlee8 小时前
RAG基于LangChain实现
linux·langchain
老王熬夜敲代码9 小时前
持久化记忆redis
langchain
wuhen_n9 小时前
LangChain.js 初探:从手写代码到框架思维
langchain·ai编程·编程语言
June bug9 小时前
【AI赋能测试】基于 langchain+DeepSeek 的 AI 智能体
经验分享·功能测试·测试工具·职场和发展·langchain·自动化·学习方法
云雾J视界9 小时前
2026年AI Agent框架选型指南:OpenClaw vs LangChain vs AutoGen 深度对比
大数据·人工智能·langchain·agent·open claw