让 Agent 学会“先想后做“——Plan 模式的故事
让 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 先输出一份方案卡片——
任务分析方案
我计划分三步完成:
- 用
profile_dataset预览上海_poi.xlsx,查看列名和行数- 根据"区"列,用
filter_rows或 Python 脚本分组统计每个区的 POI 数量- 将统计结果导出为 CSV
风险提示:该文件有 10 万行,Python 脚本可能需要约 1 分钟,建议先用 SQL 查询或数据库方式。
请确认:回复"按这个做"执行,或"改方案"调整,或"只要方案"只看不干。
用户看了方案,可以根据自己的判断做决策:
- “按这个做” → 确认执行
- “改方案” → 调整后再来
- “直接执行” → 跳过确认,立刻开干
- “只要方案” → 只要规划,不执行
用户重新获得了"控制权"。
三、什么时候触发 Plan 模式?
不是所有任务都需要出方案。
如果你问"今天天气怎么样",Agent 直接搜索回答就行了,不需要先出个"天气查询方案"。
那什么时候触发呢?由 PLAN_MODE 环境变量控制:
| 值 | 效果 |
|---|---|
auto(默认) |
数据配方场景或"需要工具且 ≥2 个子任务"时自动出方案 |
always |
所有工具类任务都先出方案 |
off |
关闭 Plan 模式,和以前一样直接执行 |
默认 auto 的设计思路是:简单的单步任务(比如"读取 README.md")不需要方案,多步的复杂任务(比如"分析 Excel 并导出结果")才需要方案。
这样既不会让简单任务变得啰嗦,也能在复杂任务上给用户足够的控制权。
四、怎么实现?
4.1 PlanComposer —— 方案生成器
PlanComposer 负责生成方案卡片,它的信息来源有三个:
- DataRecipe(数据配方):如果用户输入匹配了某个配方(如
export-file-list、profile-dataset),直接按配方的标准步骤生成方案。 - IntentPlanner(意图规划):如果匹配不到配方,就调用 LLM 分析意图,生成子任务和推荐工具。
- 启发式规则:如果都不匹配,就根据关键词硬编码兜底。
优先级:Recipe > IntentPlanner > 启发式。
4.2 PlanMode —— 意图分类器
PlanMode 负责判断用户当前的说法属于哪种意图:
| 用户说的话 | 意图 | 行为 |
|---|---|---|
| “分析 xxx.xlsx” | 需要方案 | 出方案,等确认 |
| “按这个做” / “确认” | 确认执行 | 执行挂起的方案 |
| “直接执行:完整需求” | 跳过确认 | 不确认直接执行 |
| “只要方案” | 只要方案 | 只规划,不执行 |
| “改方案” | 重新规划 | 重新出方案 |
核心逻辑:用户说的每一句话,系统都会先判断它是"新任务的描述"还是"对已有方案的操作指令"。
如果是"按这个做",系统会检查 Session 里有没有挂起的方案。有就执行,没有就提示"当前没有待确认的方案"。
4.3 Session 存储挂起方案
方案会暂存在 SessionRecord.pendingPlan 中。
这样设计的妙处在于:即使你在 REPL 里没有立刻确认,关了 REPL 再打开,用 --continue 或 /continue 加载 Session,方案还在。
你可以这样操作:
npm run repl→ “分析 示例数据.xlsx”- 看到方案卡片,没确认,直接
/exit退出 - 过一会儿,
npm run repl -- --continue - 输入"按这个做" → 方案还在,继续执行
方案不会因为你退出程序就丢失。
没有 Session 也能出方案:如果当前是单次执行模式(无 Session),但用户要求出方案,系统会自动创建一个临时 Session 来存储 pendingPlan。用户确认后,方案被消费并清空。
五、一个完整的例子
场景:用户想分析 Excel 文件
用户:分析示例数据.xlsx 有多少列,空值情况
Agent(Plan 模式,auto 触发):
任务分析方案
我计划分两步完成:
- 用
profile_dataset预览示例数据.xlsx,查看列数、列名、空值数量- 根据预览结果,汇总空值情况
注意事项:如果文件大于 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 负责"怎么干",用户负责"该不该干"。
更多推荐



所有评论(0)