让 Agent 学会"先想后做"——Plan 模式的故事

以前是"干了再说",现在是"想好了再干"


一、一个让我抓狂的瞬间

前几天我在 REPL 里跟 Agent 对话。

我:“分析一下 上海.xlsx,看看有多少列,顺便统计一下每个区的 POI 数量。”

Agent 说"好的",然后直接开始干活了。

profile_dataset → 获取文件概览 → 然后试图用 run_script 写 Python 脚本统计每个区的 POI 数量。

然后……它卡住了。因为统计每个区的 POI 数量需要先知道"区"这个列叫什么名字,而它还没看数据就开始写脚本了。脚本写错了,跑了半天,失败了。

我心想:你为什么不先看一下数据长什么样,再决定怎么写脚本呢?

更让我郁闷的是,如果它先告诉我"我要做以下几步:1. 先预览数据看列名 2. 写脚本按区域分组统计 3. 输出结果",我可能会说"等一下,那个文件有 10 万行,你写 Python 脚本可能会很慢,不如用数据库查询"。

但我没有机会说,因为它直接开始干了。

这让我意识到一个问题:Agent 太"冲动"了。

它接到任务就直接动手,既不先说明自己的计划,也不给用户"踩刹车"或"调整方向"的机会。

就像一个下属接到任务后一声不吭就去做了,结果做了一半你发现方向偏了,想叫停已经来不及了。

今天这个更新,就是解决这个问题的。


二、先解释一个概念:什么是 “Plan 模式”?

概念解释:Plan 模式就是让 Agent 在动手之前,先出一份设计方案给用户看。用户确认了,它再执行。

这很像装修房子:

  • 旧模式:装修队直接砸墙开工。你发现砸错了,已经晚了。
  • Plan 模式:装修队先给你看"施工图纸"——“我们打算这么砸、这么改、这么布线”。你说"行",他们才开始干活。

对于数据任务,Plan 模式也是这样:

  • 旧模式:Agent 直接调工具,干了再说。
  • Plan 模式:Agent 先输出一份方案卡片——

任务分析方案

我计划分三步完成:

  1. profile_dataset 预览 上海_poi.xlsx,查看列名和行数
  2. 根据"区"列,用 filter_rows 或 Python 脚本分组统计每个区的 POI 数量
  3. 将统计结果导出为 CSV

风险提示:该文件有 10 万行,Python 脚本可能需要约 1 分钟,建议先用 SQL 查询或数据库方式。

请确认:回复"按这个做"执行,或"改方案"调整,或"只要方案"只看不干。

用户看了方案,可以根据自己的判断做决策:

  • “按这个做” → 确认执行
  • “改方案” → 调整后再来
  • “直接执行” → 跳过确认,立刻开干
  • “只要方案” → 只要规划,不执行

用户重新获得了"控制权"。


三、什么时候触发 Plan 模式?

不是所有任务都需要出方案。

如果你问"今天天气怎么样",Agent 直接搜索回答就行了,不需要先出个"天气查询方案"。

那什么时候触发呢?由 PLAN_MODE 环境变量控制:

效果
auto(默认) 数据配方场景或"需要工具且 ≥2 个子任务"时自动出方案
always 所有工具类任务都先出方案
off 关闭 Plan 模式,和以前一样直接执行

默认 auto 的设计思路是:简单的单步任务(比如"读取 README.md")不需要方案,多步的复杂任务(比如"分析 Excel 并导出结果")才需要方案。

这样既不会让简单任务变得啰嗦,也能在复杂任务上给用户足够的控制权。


四、怎么实现?

4.1 PlanComposer —— 方案生成器

PlanComposer 负责生成方案卡片,它的信息来源有三个:

  1. DataRecipe(数据配方):如果用户输入匹配了某个配方(如 export-file-listprofile-dataset),直接按配方的标准步骤生成方案。
  2. IntentPlanner(意图规划):如果匹配不到配方,就调用 LLM 分析意图,生成子任务和推荐工具。
  3. 启发式规则:如果都不匹配,就根据关键词硬编码兜底。

优先级:Recipe > IntentPlanner > 启发式。

4.2 PlanMode —— 意图分类器

PlanMode 负责判断用户当前的说法属于哪种意图:

用户说的话 意图 行为
“分析 xxx.xlsx” 需要方案 出方案,等确认
“按这个做” / “确认” 确认执行 执行挂起的方案
“直接执行:完整需求” 跳过确认 不确认直接执行
“只要方案” 只要方案 只规划,不执行
“改方案” 重新规划 重新出方案

核心逻辑:用户说的每一句话,系统都会先判断它是"新任务的描述"还是"对已有方案的操作指令"。

如果是"按这个做",系统会检查 Session 里有没有挂起的方案。有就执行,没有就提示"当前没有待确认的方案"。

4.3 Session 存储挂起方案

方案会暂存在 SessionRecord.pendingPlan 中。

这样设计的妙处在于:即使你在 REPL 里没有立刻确认,关了 REPL 再打开,用 --continue/continue 加载 Session,方案还在。

你可以这样操作:

  1. npm run repl → “分析 示例数据.xlsx”
  2. 看到方案卡片,没确认,直接 /exit 退出
  3. 过一会儿,npm run repl -- --continue
  4. 输入"按这个做" → 方案还在,继续执行

方案不会因为你退出程序就丢失。

没有 Session 也能出方案:如果当前是单次执行模式(无 Session),但用户要求出方案,系统会自动创建一个临时 Session 来存储 pendingPlan。用户确认后,方案被消费并清空。


五、一个完整的例子

场景:用户想分析 Excel 文件

用户:分析示例数据.xlsx 有多少列,空值情况

Agent(Plan 模式,auto 触发):

任务分析方案

我计划分两步完成:

  1. profile_dataset 预览 示例数据.xlsx,查看列数、列名、空值数量
  2. 根据预览结果,汇总空值情况

注意事项:如果文件大于 10MB,预览可能只扫描前 5000 行,空值统计仅为抽样结果。

请确认:回复"按这个做"执行,或"改方案"调整,或"只要方案"只看不干。

用户:按这个做

Agent:调用 profile_dataset("示例数据.xlsx") → 返回预览结果 → 汇总回答:“该文件有 6 列,其中 XX 列有 X 个空值……”

如果用户不满意方案

用户:改方案,把预览行数加到 50 行

Agent:重新出方案:“计划用 profile_dataset("示例数据.xlsx", sample_size=50) 预览……”

如果用户想跳过确认

用户:直接执行:分析 示例数据.xlsx 有多少列,空值情况

Agent:直接调用 profile_dataset,执行并回答,不出方案卡片


六、以前 vs 现在

场景:复杂数据分析任务

之前(无 Plan 模式)

用户:分析 上海.xlsx 有多少列,每个区的 POI 数量
Agent:直接开始干活 → 调工具 → 写脚本 → 失败 → 重试 → 勉强完成
用户:全程看着,无法干预,也不知道 Agent 在干啥

现在(Plan 模式)

用户:分析 上海.xlsx 有多少列,每个区的 POI 数量
Agent:先出方案 → "我计划三步完成……"
用户:看了一下,发现文件有 10 万行 → "等一下,你先做数据采样,不要全量统计"
Agent:调整方案 → 执行 → 完成
用户:全程掌控,方向可控

本质变化:用户从"旁观者"变成了"指挥者"。


七、和之前的功能怎么配合?

功能 和 Plan 模式的关系
DataRecipes 方案生成的"标准答案"来源
IntentPlanner 方案生成的"智能理解"来源
TaskStateTracker 方案确认后,子任务完成度检查依然有效
ObservedFacts 方案确认后,执行过程中的事实检查依然有效
Session 挂起方案存在 Session 里,支持跨轮次确认

Plan 模式不是替代这些功能,而是在它们之前加了一层"规划层":

用户输入
  → PlanMode(判断要不要出方案)
  → PlanComposer(生成方案)
  → 用户确认
  → 然后进入原有的执行流程
    → IntentPlanner → DataRecipes → TaskStateTracker → ObservedFacts

Plan 模式是"前哨站",原有功能是"执行层"。


八、关于"控制权"

这次更新的最大价值,不是"Agent 更聪明了",而是**“用户更有掌控感了”**。

以前用户把任务交给 Agent,就像把钥匙交给一个陌生人开车——他往哪开你完全不知道,只能等结果。

现在用户先看"路线图",再决定"走不走、怎么走"。

这才是人机协作该有的样子。

Agent 提供专业方案,用户做最终决策。Agent 负责"怎么干",用户负责"该不该干"。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐