前言:为什么我会学这个问题

前面的系列已经介绍了Langgrahp中的基础概念和如何使用,但只能实现简单工作流,实现复杂的Agent还需新的知识补充,比如Agent 要自动拆解成多个步骤再执行"

这让我开始研究另一种 Agent 架构:Planning Agent。这才发现,原来 Agent 有两种截然不同的决策方式,分别适用于不同场景。

一、问题背景

两种决策困境

假设场景:用户说"帮我订明天北京到上海的机票,还要订酒店"

Action Agent 的思路

  • 先想第一步:查机票
  • 查完机票,想第二步:查酒店
  • 再想第三步:订机票
  • 再想第四步:订酒店

每一步都是"当前状态 → 决定下一步",单步决策,像走路时边走边看红绿灯。

Planning Agent 的思路

  • 先想:用户要完成"出差安排"这个目标
  • 拆解计划:1) 查明天北京到上海的机票 2) 对比价格和时间 3) 查上海酒店 4) 预订机票和酒店 5) 发送确认
  • 按计划执行,中间根据反馈调整计划

先规划,再执行,像出门前先看地图规划路线。

二、核心概念

Action Agent(反应式 Agent)

定义:每一步都基于当前状态做出反应,单步决策,不提前规划整体路径。

典型代表就是 ReAct 模式(Thought-Action-Observation 循环)。

特点

  • 实时响应,速度快
  • 适合简单、线性的任务
  • 不擅长处理长程依赖和复杂拆解

Planning Agent(规划式 Agent)

定义:先制定完整计划,再按计划执行,可以在执行过程中动态调整计划。

典型实现:Chain of Thought 扩展、Hierarchical Task Network、LLM+P(用规划器辅助)等。

特点

  • 有全局视角,能处理复杂任务
  • 可以预判困难,提前准备
  • 执行效率可能较低(规划耗时)

在这里插入图片描述

核心区别对比

维度 Action Agent Planning Agent
决策时机 每步决策 先规划后执行
全局视角 无,只看当前 有,知道整体目标
适用场景 简单任务,快速响应 复杂任务,多步拆解
计算成本 高(规划消耗)
容错性 低,一步错可能全错 高,可重新规划

三、错误理解 / 常见误区

误区 1:Planning Agent 一定比 Action Agent 好

不一定。Planning Agent 的规划本身需要消耗 token 和时间,对于"查天气"这种简单任务,用 Planning Agent 反而杀鸡用牛刀。

误区 2:ReAct 就是 Action Agent,没有规划能力

不完全对。ReAct 虽然是单步决策,但 LLM 本身的推理能力让它隐式地有一定的规划能力。区别在于这是"隐式规划"还是"显式规划"。

误区 3:Planning Agent 不会走弯路

错误。Planning Agent 的计划基于对世界的"假设",执行时发现假设不对,就得调整计划重新规划。所以 Planning Agent 也可能走弯路,只是走弯路的代价更大(规划沉没成本)。

误区 4:两种 Agent 非此即彼

现实中经常结合使用。比如外层用 Planning Agent 拆解任务,内层每个子任务用 Action Agent 执行。

四、正确实现思路

何时选 Action Agent

  • 任务步骤少(1-3 步)
  • 每步的结果可预测
  • 需要快速响应
  • 实时性要求高

何时选 Planning Agent

  • 任务步骤多(5 步以上)
  • 步骤之间有依赖关系
  • 需要全局优化(如成本最低、时间最短)
  • 任务可能需要用户确认后才能继续

混合架构实现

class HybridAgent:
    """混合 Agent:外层规划 + 内层执行"""
    
    def __init__(self, llm, tools):
        self.llm = llm
        self.tools = tools
    
    def plan_and_execute(self, task: str) -> str:
        """规划-执行主循环"""
        
        # 第一阶段:规划
        plan = self.create_plan(task)
        print(f"生成计划:{plan}")
        
        # 第二阶段:按计划执行
        context = {}
        for step in plan["steps"]:
            # 如果是简单 action,直接执行
            if step["type"] == "action":
                result = self.execute_action(step["action"], step["params"], context)
                context[step["key"]] = result
            
            # 如果是需要规划的,先规划再执行
            elif step["type"] == "plannable":
                sub_plan = self.create_plan(step["description"])
                for sub_step in sub_plan["steps"]:
                    sub_result = self.execute_action(sub_step["action"], sub_step["params"], context)
                    context[sub_step["key"]] = sub_result
        
        return context.get("result", "完成")
    
    def create_plan(self, task: str) -> dict:
        """使用 LLM 生成计划"""
        prompt = f"""任务:{task}
请将这个任务拆解成具体步骤,返回 JSON 格式:
{{
    "steps": [
        {{"type": "action", "action": "工具名", "params": {{}}, "key": "结果变量名"}},
        ...
    ]
}}
"""
        response = self.llm.generate(prompt)
        return json.loads(response)
    
    def execute_action(self, action: str, params: dict, context: dict) -> any:
        """执行单个 action"""
        if action in self.tools:
            # 解析上下文中的变量
            resolved_params = self.resolve_params(params, context)
            return self.tools[action](**resolved_params)
        return None
    
    def resolve_params(self, params: dict, context: dict) -> dict:
        """解析参数,支持从上下文引用"""
        resolved = {}
        for key, value in params.items():
            if isinstance(value, str) and value.startswith("$"):
                # 引用上下文变量
                var_name = value[1:]
                resolved[key] = context.get(var_name)
            else:
                resolved[key] = value
        return resolved

单步规划器实现

class SimplePlanner:
    """简单的 Planning Agent 实现"""
    
    def __init__(self, llm):
        self.llm = llm
    
    def plan(self, task: str, max_steps: int = 5) -> list:
        """生成执行计划"""
        prompt = f"""任务:{task}
你是一个任务规划专家。请将上述任务拆解成不超过 {max_steps} 个步骤。

输出格式(每行一个步骤):
1. 步骤描述 | 工具名 | 参数

例如:
查北京天气 | get_weather | {{"city": "北京"}}
"""
        response = self.llm.generate(prompt)
        
        # 解析计划
        steps = []
        for line in response.strip().split('\n'):
            if line.strip():
                parts = line.split('|')
                if len(parts) >= 3:
                    steps.append({
                        "description": parts[0].strip(),
                        "action": parts[1].strip(),
                        "params": eval(parts[2].strip())  # 简化,实际应该用 JSON 解析
                    })
        return steps

五、代码示例

完整的 Action Agent vs Planning Agent 对比

from enum import Enum
from typing import List, Dict, Any, Callable
import json


class AgentType(Enum):
    ACTION = "action"
    PLANNING = "planning"


class ActionAgent:
    """纯反应式 Action Agent"""
    
    def __init__(self, llm: Callable, tools: Dict[str, Callable]):
        self.llm = llm
        self.tools = tools
    
    def run(self, task: str, max_iterations: int = 10) -> str:
        """ReAct 风格的单步决策循环"""
        history = []
        
        for i in range(max_iterations):
            # LLM 决定下一步
            prompt = self._build_prompt(task, history)
            decision = self.llm(prompt)
            
            action = decision.get("action")
            action_input = decision.get("action_input", {})
            
            # 结束
            if action == "finish":
                return decision.get("output", "完成")
            
            # 执行工具
            if action in self.tools:
                observation = self.tools[action](**action_input)
            else:
                observation = f"未知工具: {action}"
            
            history.append({
                "thought": decision.get("thought"),
                "action": action,
                "observation": observation
            })
        
        return "达到最大迭代次数"


class PlanningAgent:
    """规划式 Agent:先规划后执行"""
    
    def __init__(self, llm: Callable, tools: Dict[str, Callable]):
        self.llm = llm
        self.tools = tools
    
    def run(self, task: str) -> str:
        """规划-执行模式"""
        
        # 阶段 1:生成计划
        plan = self._create_plan(task)
        print(f"📋 生成计划:共 {len(plan)} 个步骤")
        
        # 阶段 2:按计划执行
        context = {}
        for i, step in enumerate(plan):
            print(f"  Step {i+1}: {step['description']}")
            
            # 解析参数(支持引用上下文)
            params = self._resolve_params(step.get("params", {}), context)
            
            # 执行
            if step["action"] in self.tools:
                result = self.tools[step["action"]](**params)
            else:
                result = f"未知工具: {step['action']}"
            
            # 存入上下文
            if step.get("save_as"):
                context[step["save_as"]] = result
            
            # 检查是否需要重新规划
            if step.get("verify"):
                if not self._verify_result(step["verify"], result, context):
                    print(f"  ⚠️ 验证失败,重新规划...")
                    new_plan = self._replan(task, context)
                    plan = new_plan  # 替换计划
                    continue
        
        return context.get("_final_result", "完成")
    
    def _create_plan(self, task: str) -> List[Dict]:
        """生成执行计划"""
        prompt = f"""任务:{task}
请将这个任务拆解成具体的执行步骤。

返回 JSON 数组格式:
[
    {{
        "description": "步骤描述",
        "action": "工具名",
        "params": {{"参数名": "参数值"}},
        "save_as": "结果变量名",
        "verify": "验证条件"
    }}
]

要求:
- 步骤之间有依赖关系时,通过 save_as 变量传递
- 复杂的验证条件用自然语言描述
"""
        response = self.llm(prompt)
        return json.loads(response)
    
    def _resolve_params(self, params: dict, context: dict) -> dict:
        """解析参数中的变量引用"""
        resolved = {}
        for key, value in params.items():
            if isinstance(value, str) and value.startswith("$"):
                resolved[key] = context.get(value[1:], value)
            else:
                resolved[key] = value
        return resolved
    
    def _verify_result(self, condition: str, result: Any, context: dict) -> bool:
        """验证步骤结果"""
        # 简化:实际可以用 LLM 判断
        return True


# 测试对比
if __name__ == "__main__":
    # 模拟 LLM
    def mock_llm(prompt):
        if "ReAct" in prompt or "action" in prompt.lower():
            # Action Agent 的决策
            return {
                "thought": "需要先查天气",
                "action": "get_weather",
                "action_input": {"city": "北京"}
            }
        else:
            # Planning Agent 的规划
            return json.dumps([
                {
                    "description": "查询北京天气",
                    "action": "get_weather",
                    "params": {"city": "北京"},
                    "save_as": "beijing_weather"
                },
                {
                    "description": "查询上海天气",
                    "action": "get_weather",
                    "params": {"city": "上海"},
                    "save_as": "shanghai_weather"
                },
                {
                    "description": "比较两个天气",
                    "action": "compare",
                    "params": {"a": "$beijing_weather", "b": "$shanghai_weather"},
                    "save_as": "_final_result"
                }
            ])
    
    # 模拟工具
    def get_weather(city: str) -> str:
        weathers = {"北京": "晴 25°C", "上海": "雨 22°C"}
        return weathers.get(city, "未知")
    
    def compare(a: str, b: str) -> str:
        return f"比较结果:{a} vs {b}"
    
    tools = {"get_weather": get_weather, "compare": compare}
    
    # 测试 Action Agent
    print("=" * 50)
    print("Action Agent(ReAct)执行:")
    print("=" * 50)
    action_agent = ActionAgent(mock_llm, tools)
    result = action_agent.run("北京和上海的天气怎么样?")
    print(f"结果:{result}")
    
    # 测试 Planning Agent
    print("\n" + "=" * 50)
    print("Planning Agent 执行:")
    print("=" * 50)
    planning_agent = PlanningAgent(mock_llm, tools)
    result = planning_agent.run("北京和上海的天气怎么样?")
    print(f"结果:{result}")

六、运行结果 / 流程图

运行上述代码:

==================================================
Action Agent(ReAct)执行:
==================================================
结果:北京晴 25°C

==================================================
Planning Agent 执行:
==================================================
📋 生成计划:共 3 个步骤
  Step 1: 查询北京天气
  Step 2: 查询上海天气
  Step 3: 比较两个天气
结果:比较结果:北京晴 25°C vs 上海雨 22°C

两种 Agent 的流程对比:

在这里插入图片描述

七、项目中怎么用

实际选型建议

  1. 客服机器人(简单问答) → 用 Action Agent(ReAct)

    • 用户问题简单直接,不需要复杂拆解
    • 需要快速响应
  2. 旅行助手(多步骤规划) → 用 Planning Agent

    • 需要查机票、酒店、景点、交通
    • 各步骤有依赖关系(先定机票才能定酒店)
  3. 代码助手(复杂任务) → 混合架构

    • 外层 Planning Agent 拆解任务
    • 内层 Action Agent 执行具体代码操作

注意事项

  1. 规划消耗资源:Planning Agent 的规划阶段需要额外 LLM 调用,要评估成本
  2. 计划可能失效:外部环境变化时,计划需要动态调整
  3. 用户参与:复杂计划执行前,最好让用户确认

系列关联:本文探讨了 Planning Agent 和 Action Agent 的区别,下一篇《如何让 Agent 记住对话历史?》会讲到,无论是哪种 Agent,都需要解决"对话历史管理"的问题,这是构建真正实用 Agent 的基础。

八、总结 + 下篇预告

核心要点

  1. Action Agent:单步决策,实时响应,适合简单任务。代表是 ReAct 模式。
  2. Planning Agent:先规划后执行,有全局视角,适合复杂任务。
  3. 混合架构:外层规划 + 内层执行,兼顾灵活性和效率。
  4. 选型原则:简单任务用 Action Agent,复杂任务用 Planning Agent 或混合架构。

下篇预告

下一篇我们将探讨《如何让 Agent 记住对话历史?》,理解如何让 Agent 在多轮对话中保持上下文记忆,不会"说完就忘"。这涉及到消息历史管理、摘要压缩、向量检索等技术。

延伸阅读

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐