Plan Mode
先想后做——计划模式是登录在案的合作状态,不是模型的心情。
模式开关 + 策略提示词 + 退出工具。
问题
有时候你希望 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/plan 与 docs/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 里是一枚登录在案的会话事件。软指导与硬执行分离,是它最重要的设计取舍。