Plan Mode

先想后做——计划模式是登录在案的合作状态,不是模型的心情。

模式开关 + 策略提示词 + 退出工具。

60 行1 个工具

问题

有时候你希望 Agent 只出方案、不动手:"先给我一个重构计划,我批准了你再改代码。"

s05 的 todo 是"怎么干活"的计划,但缺一个开关:现在是规划阶段还是执行阶段? 没有这个开关,Agent 收到"给个计划"会礼貌地列几行,然后接着动手——因为它不知道"给个计划"意味着"现在不许碰文件"。

解决方案

计划模式 = 一个会话级状态开关 + 状态相关的系统提示词 + 一个退出工具:

  • plan_mode(on) 工具:打开/关闭计划模式;
  • 计划模式开启时,系统提示词追加"当前处于计划模式,只输出方案,不得调用写工具";
  • exit_plan_mode 工具:模型完成任务后主动退出。
let planMode = false;

define("plan_mode", {
  type: "object", properties: { active: { type: "boolean" } }, required: ["active"],
}, ({ active }) => {
  planMode = active;
  return `计划模式:${active ? "开启" : "关闭"}`;
});

define("exit_plan_mode", { type: "object", properties: {}, required: [] },
  () => { planMode = false; return "已退出计划模式"; });

工作原理

第 1 步:计划模式是一个区块贡献(s10 的机制在这里派上用场)——状态变化自动反映到下一轮的系统提示词:

section("plan:policy", 5, planMode
  ? "当前处于计划模式:只输出方案与步骤,禁止调用任何写工具(write_file、bash 写命令)。方案完成后调用 exit_plan_mode。"
  : "");

第 2 步:执行闸门——计划模式下,写工具在分发前被拦截。策略是双保险:提示词指导模型,闸门约束事实:

async function gate(name, args) {
  if (planMode && WRITE_TOOLS.has(name)) return "DENIED: 计划模式禁止写操作,先调用 exit_plan_mode";
  return "allow";
}

第 3 步:退出后,模型回到执行模式继续干活。开关是显式状态——不是靠"模型自觉",而是靠提示词 + 闸门双约束。

为什么不用"让模型自己判断"?因为判断不可靠、不可见、不可恢复。显式状态可以被用户看见(UI 显示"计划模式")、被工具检查、被日志记录。

试一下

运行 s12:

  • 开启计划模式,让它"规划一个项目的目录结构"——它应该只给方案
  • 让它"顺便创建目录"——观察闸门拒绝
  • 调用 exit_plan_mode 后再问同样的问题——观察行为差异

观察重点:状态开关 + 提示词 + 闸门三者缺一不可。如果只有提示词没有闸门,模型偶尔会"手滑";如果只有闸门没有提示词,模型不知道为什么被拒。

以下内容基于 packages/plandocs/subsystems/plan.md 的核查。DSH 的计划模式(dsh-plan-mode)是"登录的合作状态":

一、状态在会话日志里

  • plan/mode纯日志事件{ active: boolean }):折叠(fold)日志得到当前状态。resume、fork、压缩后状态自动恢复,不需要内存镜像。
  • 状态切换在 step 边界生效:选择先挂起,下一个被接受的 agent/pre-step 把它追加进日志。

二、三个构成

部件 说明
plan:policy prompt 区块 部署拥有的计划模式指导文本(你的提示词文案,由部署配置)
exit_plan_mode 工具 模型可见的退出通道
/plan 命令 人类命令(commands 通道,不经模型轮次)

三、它是"软指导"

关键设计:计划模式本身不执行——plan:policy 只是指导区块。真正禁止写操作的是独立的沙箱模式与审批策略(s03 的两个系统),它们不读计划状态、各管各的。软状态负责引导,硬策略负责执行

一句话:你的 planMode 布尔值,在 DSH 里是一枚登录在案的会话事件。软指导与硬执行分离,是它最重要的设计取舍。