LLM-based Planning:从后端视角理解 Agent 规划层

结合 chao-go 项目实战,讲解 Agent 如何动态生成执行计划


一、核心定义

一句话理解

Planning = 业务流程编排层 ,以前程序员写死 if-else,现在 LLM 动态生成执行计划。

传统后端 vs Agent Planning

复制代码
传统后端(写死流程):
┌─────────────────────────────────────────────────────┐
│  submitOrder() {                                     │
│      checkStock();        // Step 1                 │
│      createOrder();       // Step 2                 │
│      deductInventory();   // Step 3                 │
│      sendMessage();       // Step 4                 │
│  }                                                   │
│                                                      │
│  问题:流程固定,无法根据实际情况调整                  │
└─────────────────────────────────────────────────────┘

Agent Planning(动态流程):
┌─────────────────────────────────────────────────────┐
│  用户:"帮我查订单 ORD001 能否改签"                   │
│                                                      │
│  LLM 动态生成计划:                                   │
│    Plan:                                             │
│      1. order_query    → 查订单状态                  │
│      2. change_check   → 判断改签条件                │
│      3. recommend      → 推荐方案                    │
│                                                      │
│  根据实际结果动态调整:                               │
│    - 订单不存在 → 提示用户确认,不继续                │
│    - 已起飞 → 直接返回不可改签                        │
│    - 正常 → 完整执行三步                              │
└─────────────────────────────────────────────────────┘

二、常见 Planning 模式

2.1 ReAct(Reasoning + Acting)

最经典的 Planning 模式,chao-go 项目采用此模式。

复制代码
循环流程:
┌─────────────────────────────────────────────────────┐
│                                                      │
│   Thought(思考)← LLM 决定下一步做什么              │
│      ↓                                               │
│   Action(行动)← 执行工具                           │
│      ↓                                               │
│   Observation(观察)← 记录结果                      │
│      ↓                                               │
│   Thought(下一轮思考)← 基于观察继续决策            │
│      ↓                                               │
│   ... 循环直到完成 ...                               │
│                                                      │
└─────────────────────────────────────────────────────┘

特点:逐步决策,每步都能根据上一步结果调整。

2.2 Plan-and-Execute

先规划完整计划,再逐步执行。

复制代码
Phase 1: Plan(规划)
┌─────────────────────────────────────────────────────┐
│  LLM 生成完整计划:                                   │
│    1. order_query                                   │
│    2. change_check                                  │
│    3. recommend                                     │
└─────────────────────────────────────────────────────┘
                    ↓
Phase 2: Execute(执行)
┌─────────────────────────────────────────────────────┐
│  逐步执行计划中的每一步                               │
│  (执行过程中不再询问 LLM)                           │
└─────────────────────────────────────────────────────┘

特点:稳定、成本低,适合企业级 Agent。

2.3 Tree of Thoughts(ToT)

树状规划,评估后选择最优路径。

复制代码
                根节点
               /    \
            方案A   方案B
            /  \     /  \
          A1   A2  B1   B2
          
评估每个分支,选择最优路径

特点:适合复杂推理、多方案决策。

2.4 Graph Planning

DAG 图规划,多工具协作。

复制代码
    order_query
       /    \
      ↓      ↓
  user_info  flight_info
       \      /
        ↓    ↓
     recommend

特点:适合 LangGraph、工作流引擎。


三、chao-go 项目实现:ReAct 模式

3.1 核心数据结构

复制代码
// internal/react/agent.go

// Thought 思考阶段 - LLM 决定下一步做什么
type Thought struct {
    Reasoning   string                 `json:"reasoning"`    // 推理过程
    ToolName    string                 `json:"tool_name"`    // 决定调用的工具
    Arguments   map[string]interface{} `json:"arguments"`    // 工具参数
    IsFinished  bool                   `json:"is_finished"`  // 是否完成
    FinalAnswer string                 `json:"final_answer"` // 最终答案
}

// Observation 观察阶段 - 工具执行结果
type Observation struct {
    Content string `json:"content"`  // 结果内容
    IsError bool   `json:"is_error"` // 是否错误
}

// Step 单次循环步骤
type Step struct {
    Thought     *Thought     `json:"thought"`
    Observation *Observation `json:"observation"`
}

// AgentState Agent 状态
type AgentState struct {
    UserInput string  `json:"user_input"`  // 用户原始输入
    History   []Step  `json:"history"`     // 执行历史
    StartTime int64   `json:"start_time"`  // 开始时间
}

3.2 ReAct 循环实现

复制代码
// Agent ReAct Agent
type Agent struct {
    llm    LLMClient        // LLM 客户端
    tools  ToolRegistry     // 工具注册表
    prompt *PromptTemplate  // Prompt 模板
    config *Config          // 配置
}

// Run 执行 ReAct 循环
func (a *Agent) Run(userInput string) (*Result, error) {
    // 初始化状态
    state := &AgentState{
        UserInput: userInput,
        History:   make([]Step, 0),
    }

    // ========== Planning 循环 ==========
    for i := 0; i < a.config.MaxIterations; i++ {

        // ---------- Phase 1: Thought(思考)----------
        // 生成 Prompt,包含:可用工具、历史记录、用户输入
        promptData := PromptData{
            Tools:     a.tools.ListTools(),  // 告诉 LLM 有哪些工具可用
            History:   state.History,        // 告诉 LLM 之前做了什么
            UserInput: userInput,            // 用户原始需求
        }
        prompt := a.prompt.Render(promptData)

        // 调用 LLM 生成思考
        thought := a.llm.GenerateThought(prompt)

        // 检查是否完成
        if thought.IsFinished {
            return &Result{FinalAnswer: thought.FinalAnswer}, nil
        }

        // ---------- Phase 2: Action(行动)----------
        // 获取工具处理器
        handler, ok := a.tools.GetHandler(thought.ToolName)
        if !ok {
            // 工具不存在,记录错误,继续循环
            observation := &Observation{
                Content: fmt.Sprintf("工具不存在: %s", thought.ToolName),
                IsError: true,
            }
            state.History = append(state.History, Step{Thought: thought, Observation: observation})
            continue
        }

        // 执行工具
        content, err := handler(thought.Arguments)

        // ---------- Phase 3: Observation(观察)----------
        observation := &Observation{
            Content: content,
            IsError: err != nil,
        }

        // 记录历史,下一轮 LLM 会看到这个结果
        step := Step{Thought: thought, Observation: observation}
        state.History = append(state.History, step)
    }

    // 达到最大迭代次数
    return &Result{Error: "达到最大迭代次数"}, fmt.Errorf("max iterations")
}

3.3 Prompt 模板

复制代码
// NewPromptTemplate 创建 Prompt 模板
func NewPromptTemplate() *PromptTemplate {
    tmpl := "你是一个 ReAct Agent,可以使用以下工具解决问题。\n\n" +
        "## 可用工具\n\n" +
        "{{range .Tools}}" +
        "\n### {{.Name}}\n{{.Description}}\n" +
        "参数:\n{{range $k, $v := .Parameters}}" +
        "  - {{$k}} ({{$v.Type}}): {{$v.Description}}\n{{end}}" +
        "{{end}}\n\n" +
        "## 输出格式\n\n" +
        "每次回复必须是纯 JSON(不要 markdown 代码块):\n\n" +
        "{\n" +
        "  \"reasoning\": \"你的推理过程\",\n" +
        "  \"tool_name\": \"工具名称(完成时填 finish)\",\n" +
        "  \"arguments\": {\"参数名\": \"参数值\"},\n" +
        "  \"is_finished\": false,\n" +
        "  \"final_answer\": \"最终答案(仅完成时填写)\"\n" +
        "}\n\n" +
        "## 历史记录\n\n" +
        "{{if .History}}\n" +
        "{{range $i, $step := .History}}" +
        "\n### 第 {{add $i 1}} 轮\n" +
        "- **思考**: {{.Thought.Reasoning}}\n" +
        "- **行动**: {{.Thought.ToolName}}({{json .Thought.Arguments}})\n" +
        "- **观察**: {{if .Observation.IsError}}错误: {{else}}成功: {{end}}{{.Observation.Content}}\n" +
        "{{end}}\n" +
        "{{else}}\n(无历史记录)\n{{end}}\n\n" +
        "## 用户输入\n\n" +
        "{{.UserInput}}\n\n"

    return &PromptTemplate{template: t}
}

四、Planning 的关键:上下文传递

Planning 之所以能"动态",是因为每一步都能看到之前的结果:

4.1 执行流程示例

复制代码
用户输入: "查订单 ORD001 能否改签"

═════════════════════════════════════════════════════════════
【第 1 轮循环】
═════════════════════════════════════════════════════════════

LLM 看到:
┌─────────────────────────────────────────────────────┐
│ - 用户输入:"查订单 ORD001 能否改签"                 │
│ - 可用工具:[order_query, change_check, recommend]  │
│ - 历史:(空)                                       │
└─────────────────────────────────────────────────────┘

LLM 决策:
┌─────────────────────────────────────────────────────┐
│ reasoning: "需要先查订单状态"                        │
│ tool_name: "order_query"                            │
│ arguments: {"order_id": "ORD001"}                   │
└─────────────────────────────────────────────────────┘

执行结果:
┌─────────────────────────────────────────────────────┐
│ {"status": "已出票", "flight": "CA1234"}            │
└─────────────────────────────────────────────────────┘

═════════════════════════════════════════════════════════════
【第 2 轮循环】
═════════════════════════════════════════════════════════════

LLM 看到:
┌─────────────────────────────────────────────────────┐
│ - 用户输入:"查订单 ORD001 能否改签"                 │
│ - 可用工具:[order_query, change_check, recommend]  │
│ - 历史:                                            │
│   第 1 轮:                                         │
│     思考: 需要先查订单状态                          │
│     行动: order_query({"order_id": "ORD001"})       │
│     观察: {"status": "已出票", "flight": "CA1234"}  │
└─────────────────────────────────────────────────────┘

LLM 决策(基于第 1 轮结果):
┌─────────────────────────────────────────────────────┐
│ reasoning: "订单已出票,需要检查改签条件"            │
│ tool_name: "change_check"                           │
│ arguments: {"order_id": "ORD001"}                   │
└─────────────────────────────────────────────────────┘

执行结果:
┌─────────────────────────────────────────────────────┐
│ {"can_change": true, "fee": 200}                    │
└─────────────────────────────────────────────────────┘

═════════════════════════════════════════════════════════════
【第 3 轮循环】
═════════════════════════════════════════════════════════════

LLM 看到:
┌─────────────────────────────────────────────────────┐
│ - 历史:                                            │
│   第 1 轮:order_query → {"status": "已出票"}       │
│   第 2 轮:change_check → {"can_change": true}      │
└─────────────────────────────────────────────────────┘

LLM 决策:
┌─────────────────────────────────────────────────────┐
│ reasoning: "可以改签,生成最终答案"                  │
│ is_finished: true                                   │
│ final_answer: "订单 ORD001 可以改签,手续费 200 元" │
└─────────────────────────────────────────────────────┘

4.2 关键代码:历史传递

复制代码
// 每轮循环,历史都会追加
state.History = append(state.History, Step{
    Thought:     thought,     // LLM 的思考
    Observation: observation,  // 工具执行结果
})

// 下一轮生成 Prompt 时,历史会被包含
promptData := PromptData{
    History: state.History,  // ← 关键:传递历史
}

五、后端类比

5.1 架构映射

|------------|--------------------|
| 传统后端 | Agent Planning |
| Controller | Prompt(接收请求) |
| Service 编排 | Planning(流程控制) |
| Service | Skills(业务能力) |
| DAO | Tool(原子操作) |

5.2 代码对比

传统后端(写死流程):

复制代码
public Result handleOrder(String orderId) {
    // Step 1: 查订单
    Order order = orderDao.query(orderId);
    
    // 预判所有分支
    if (order == null) {
        return Result.error("订单不存在");
    }
    
    if (!order.getStatus().equals("已出票")) {
        return Result.error("订单状态不允许改签");
    }
    
    // Step 2: 检查改签条件
    ChangeResult change = changeService.check(orderId);
    
    if (!change.isCanChange()) {
        return Result.error(change.getReason());
    }
    
    // Step 3: 推荐方案
    return recommendService.suggest(order, change);
}

Agent Planning(动态流程):

复制代码
func (a *Agent) Run(userInput string) (*Result, error) {
    for i := 0; i < maxIterations; i++ {
        // LLM 根据历史动态决策
        thought := a.llm.GenerateThought(prompt)

        if thought.IsFinished {
            return thought.FinalAnswer
        }

        // 执行工具
        result := execute(thought.ToolName, thought.Arguments)

        // 记录历史,下一轮会看到
        history.Append(result)
    }
}

5.3 核心差异

|--------|---------------|--------------------|
| 维度 | 传统后端 | Agent Planning |
| 流程定义 | 程序员写死 | LLM 动态生成 |
| 分支处理 | 预判所有 if-else | LLM 根据上下文决策 |
| 异常处理 | try-catch 预定义 | LLM 自动调整路径 |
| 扩展性 | 需要改代码 | 只需加工具定义 |


六、运行示例

复制代码
# 进入项目目录
cd chao-go

# Mock 模式运行(不需要 API Key)
go run examples/react-demo/main.go

# 真实 LLM 模式
go run examples/react-demo/main.go -real -input "格式化 JSON 并计算 MD5"

输出示例

复制代码
╔════════════════════════════════════════════════════════════╗
║           ReAct Agent 演示                                 ║
║  Reasoning + Acting = 思考 + 行动                           ║
╚════════════════════════════════════════════════════════════╝

📡 使用阿里百炼 qwen-plus

════════════════════════════════════════════════════════════
🎯 用户输入: 格式化 JSON 并计算 MD5
════════════════════════════════════════════════════════════

【第 1 轮循环】
────────────────────────────────────────────────────────

🤔 [Phase 1: Thought - 思考]
   推理: 用户需要格式化 JSON 并计算 MD5。我先调用 json_format 工具格式化 JSON。
   决定调用: json_format
   参数: map[input:{"name":"test","value":123}]

👉 [Phase 2: Action - 行动]
   执行工具: json_format

👀 [Phase 3: Observation - 观察]
   ✅ 结果: {
     "name": "test",
     "value": 123
   }

【第 2 轮循环】
────────────────────────────────────────────────────────

🤔 [Phase 1: Thought - 思考]
   推理: JSON 已格式化成功。现在调用 crypto_hash 计算 MD5 哈希值。
   决定调用: crypto_hash
   参数: map[input:{"name":"test","value":123} algorithm:md5]

👉 [Phase 2: Action - 行动]
   执行工具: crypto_hash

👀 [Phase 3: Observation - 观察]
   ✅ 结果: md5({"name":"test","value":123}) = simulated_hash_28

【第 3 轮循环】
────────────────────────────────────────────────────────

🤔 [Phase 1: Thought - 思考]
   ✅ 任务完成!
   最终答案: 已完成!
     1. JSON 格式化结果:{ "name": "test", "value": 123 }
     2. MD5 哈希值:md5(...) = simulated_hash_28

════════════════════════════════════════════════════════════
📊 执行结果
════════════════════════════════════════════════════════════
✅ 成功: true
📝 步骤数: 2
🎯 最终答案: 已完成!...

七、总结

核心观点

|----------|------------------------------------------|
| 维度 | 说明 |
| 本质 | 业务流程编排层,动态生成执行计划 |
| 实现 | ReAct 循环(Thought → Action → Observation) |
| 关键 | 上下文传递,每步决策都能看到历史结果 |
| 价值 | 流程动态调整,无需写死所有分支 |
| 后端类比 | Service 编排层,但由 LLM 动态决策 |

Planning 在五层架构中的位置

复制代码
Prompt(规则层)
      ↓
Planning(规划层)← 动态生成执行计划
      ↓
MCP(连接层)
      ↓
Skills(能力层)
      ↓
Harness(质量层)

一句话总结

Planning 让 Agent 从"执行固定流程"进化为"动态规划路径",是 Agent 自主性的核心来源。


参考资料

相关推荐
覆东流1 小时前
Java开发环境搭建
java·开发语言·后端
yspwf1 小时前
用 NestJS 构建一个同时支持用户与订单的 gRPC 微服务
后端
wuhen_n1 小时前
LangChain Function Call 实战:让 AI 调用自定义工具
前端·langchain·ai编程
用户3499904939191 小时前
我用 AI 做了一个 PR Review 工作流
ai编程
DyLatte1 小时前
很多人把坚持,误以为成长
前端·后端·程序员
canonical_entropy1 小时前
自进化的两个尺度:RMSP Agent 与 AGE 方法论的深层结构对应
aigc·agent·ai编程
小马爱打代码1 小时前
SpringBoot + 延迟消息 + 时间轮:订单超时、优惠券过期等场景的高效实现方案
java·spring boot·后端
长大19881 小时前
MySQL 索引失效常见场景:开发优化必记要点
后端
达达尼昂1 小时前
AI Native 工程实践 : agent 自动化测试
前端·后端·架构