Skip to content

什么是计划模式

计划模式(plan mode)是一种被记录的每-agent 协作状态,它改变模型收到的语言指引并节制一个特定的退出工具——它是软指引,不是强制机制。唯一包是 packages/plan/plan-mode 下的 @deepseek-ai/dsh-plan-mode,UI 半壁在 packages/client/ui-plan。沙箱模式与审批策略独立地执行限制,且读写计划状态。

计划模式给模型一段部署撰写的系统提示词段,告诉它先探索与设计,并注册 exit_plan_mode,从而在真正干活前把完整计划呈现给人类评审。它限制工具,本身也要求每步审批。

激活:一个按会话持久化的设置

计划模式存储为 plan/mode ——一个仅日志、全值替换的 SessionEventMap 成员,承载 { active: boolean }。因为被记录,foldPlanMode(events) 直接从容器的会话日志恢复计划状态,因此 resume、fork 与压缩都会自动保留它。UI 通过 session/event 观察已提交的翻转。

服务表面(ctx.planMode):

成员语义
set(agent, active)空闲时立即追加独立的 plan/mode 事件(下个提示词前没有轮中 pre-step);运行时则持有待决选择备下次接受的轮中 pre-step。返回 committed/queuedcancelled 反转或 noop
get(agent)返回 { active, pending? },把被记录状态(用于组装当前步骤)与用户的轮中选择分开。

当最后一次被记录的请求头描述了另一状态时,改变的用户选择会贡献一条插件来源的 user/message 通知。Fork 的 agent 继承被记录的计划状态;新产生的 agent 初始为 inactive。

与普通模式的行为差异

  • 激活时,插件把配置的 section 渲染为名为 plan:policy、提示顺序 50 的系统提示词段。inactive 模式不贡献文本,因此没有 token 成本,且除非发生转换否则不会使其 KV 失效。
  • exit_plan_mode 始终注册(两种状态),保持工具模式跨转换稳定。其执行路径只接受激活的计划模式,并且只有经 ctx.userQuestions 获得精确用户审批后才离开计划模式。
  • 进入或离开会从顺序 50 起改变系统提示词,从而移动 KV 缓存的可复用边界。

这与基于工具与守卫的强制形成鲜明对比:计划模式从不禁用任何工具。需要强制限制的部署必须独立配置沙箱与审批控制。

评审 / 退出流程

exit_plan_mode 是经评审的退出。当模型提交其计划时,工具发起一个问题,携带 plan-review 呈现意图:

ts
const REVIEW_ID = 'plan-review'
intent: { kind: 'plan-review', approve: APPROVE_LABEL }

有能力 UI(Web 客户端)把计划呈现为决策而非通用问题,命名 approve 标签;不识别该标签的 UI 则渲染通用选项列表。两种方式工具都读到相同的答案。获批的评审返回规范值 { approved: true };被拒的评审是携带评审反馈的失败调用;被忽略的评审(用户关闭请求去说话)会如此报告给模型,告诉它留在计划模式并等待消息。

EXIT_PLAN_MODE = 'exit_plan_mode'packages/plan/plan-mode/src/index.ts 导出;其 schema 钉在 docs/tool-catalog.md@deepseek-ai/dsh-plan-mode 条目。

/plan 命令与退出条件

当组合了 ctx.commands 时,该包注册 /plan 并保留精确参数 off 用于直接退出:

输入行为
/plan选择计划模式(激活)
/plan <消息>先选择计划模式,再通过 agent.steer() 提交 <消息>,使之在计划指引下成为下一步骤的普通已记录用户消息
/plan off不发模型输入地选择 inactive;也取消待决的进入

退出条件因此是两个:/plan off(人类直接退出),或模型提交计划后经获批的 exit_plan_mode 评审。不存在基于计时器或工具计数的退出。

退出工具 schema 与评审交换

exit_plan_mode两种状态下都保持可用,从而让工具 schema 跨转换保持稳定;无论哪种方式,调用方都按 ToolRuntime 模式支付 schema 成本。执行行为随状态而异:

状态exit_plan_mode 行为
激活计划模式打开 plan-review 用户问题;获批 → 返回 { approved: true } 并离开计划模式;被拒 → 带评审反馈的失败调用;被忽略 → 命名用户接管权的失败调用
inactive调用失败(该工具并无可退出的东西)

评审问题声明 plan-review 呈现意图,命名 Approve 为批准标签。因为意图只改呈现,不识别该标签的 UI 会渲染通用选项列表却返回相同标签,所以工具任一种方式都读到相同的 { approved: boolean } 值。plan 参数、评审结果与叙述式活跃退出都会正常扩展会话历史,且不改工具目录。

会话投影单元

当组合挂载 ctx.sessionProjections@deepseek-ai/dsh-session-projection)时,此包注册 plan 投影单元。它折叠两类事件:一条名为 plan、带已记录 argscommand/run 记录设定想要目标(off → inactive,其他 → active),而 plan/mode 提交被记录状态并清除它。view 派生 { active, pending },其中 pending 是纯重放量,可单独从日志恢复。它通过 ./types 服务宿主消费者、通过 ./client 服务客户端聚合。未组合注册表的配置不受影响。

持久存储语义

计划状态是全值替换事件,而非增量:每次 plan/mode 写入都记录整个 { active } 值,因此 foldPlanMode(events) 无需累积即可恢复当前状态——最后一次被记录的值(或 false)胜出。这带来具体后果:

  • Resume 与 fork 纯粹从日志重建计划模式;没有需要重新发明的内存标志。
  • 压缩会连同其他一切遮蔽较早的 plan/mode 事件;因为只有最后的值有意义,遮蔽中间的翻转不改变任何东西。
  • agent 空闲时的 set() 会立即追加,因为下个提示词前没有轮中 pre-step。运行时的 set() 则把选择排队给下一次接受的轮中 pre-step;初始与续作 pre-step 应用待决选择。同一步骤的请求恢复重试会复用其冻结的组装,并让选择保持待决以备下次 pre-step。

active(被记录、持久)与 pending(尚未提交的轮中选择)之间的区分,正是 get(agent) 返回 { active, pending? } 的原因。

模型体验:针对性摘要

  • 激活时plan:policy 段出现在提示顺序 50;inactive 不贡献任何内容。进入或离开会从顺序 50 起改变系统提示词并移动 KV 缓存的可复用边界——这是计划模式唯一的缓存效果。
  • 可选的 /plan <消息> 后缀经 agent.steer() 变成一个修剪过的用户文本块,其历史 token 成本与单独提交该文本相同;裸 /plan/plan off 不增加任何 token。
  • 获批的 exit_plan_mode 评审在 plan/review 交换之后离开计划模式;被忽略的评审告诉模型留在计划模式并等待用户的消息。

源码布局与公共表面

packages/plan/plan-mode/src/ 很紧凑——四个文件:

文件关注点
index.ts插件 apply():注册 plan:policy 段、/plan 命令、exit_plan_modeplan/mode 事件与 ctx.planMode;导出 EXIT_PLAN_MODESectionConfig 类型
types.tsplan/mode 事件载荷与可合并扩展的 SessionEventMap 声明
client.tsWeb 会话尾部使用的客户端安全 plan 投影值与 SessionProjectionMap
invariant.ts为重放不变量伴生件校验 plan/mode 事件与投影折叠

该插件本身就是整个 @deepseek-ai/dsh-plan-mode 包——不像压缩家族那样有独立的 Service Definition / Provider 切分。计划模式是一个产品插件,通过三个接缝组合:系统提示词 section 注册表(plan:policy)、ctx.commands/plan)与 ctx.userQuestionsexit_plan_mode 评审)。当部署省略 ctx.commands 时,/plan 命令便不被注册;服务与 exit_plan_mode 仍然可用。

配置

yaml
- id: plan-mode
  name: '@deepseek-ai/dsh-plan-mode'
  config:
    section: |
      You are in plan mode. Explore and design before presenting the complete
      plan through exit_plan_mode.

section 必填且非空;未知键在加载时失败。该包刻意不接受任意命名模式、工具过滤、沙箱设置或审批策略——这是记录在计划专属协作状态 Agent Note 中的有界显式设计。

UI 侧(简要)

packages/client/ui-plan 是 Web 渲染器。它消费插件拥有的 /plan 命令,并提供专用 plan-review 渲染器,把 exit_plan_modedetail 呈现为带 approve 动作的计划评审。只有 Web UI 才有这个专用渲染器;其他交互 provider 通过其通用选项流呈现同一个问题。UI 驱动 ctx.planMode.set() 并通过 session/event 观察 plan/mode 翻转。

值得记录的一个注意点:若进程在另一次接受的轮中 pre-step 之前退出,在轮的最后一次已接受 pre-step 之后做出的选择会丢失,因此 UI 必须重新应用它。

计划模式与沙箱、审批的不同

一个常见误解是期望计划模式去限制行为。它并不——它改语言指引并节制一个退出工具。三者独立组合:

机制它约束什么
计划模式dsh-plan-mode软指引(系统提示词段)+ 经评审的 exit_plan_mode;无工具限制
沙箱模式dsh-sandbox / native工具执行的文件系统与操作系统隔离
审批策略dsh-user-approval工具执行前的一次性 ask/never 门禁

想要既先计划协作强制安全的部署需三者都配置;计划模式既不读也不写沙箱或审批旋钮的状态。这种分离是刻意的,便于每个维度独立演进。

已知限制与设计边界

上游 README 明确说明了计划模式不是什么:

  • 指引,而非强制。 需要强制限制的部署必须独立配置沙箱与审批控制。
  • 没有创建期计划选项。 Fork 的 agent 继承被记录的计划状态,而新产生的 agent 初始为 inactive。
  • 没有工具限制、计时器或审批策略属于此包——这是有界、刻意的设计。
  • 活跃子节点评审边界。 被另一 agent 拥有的活跃子节点无法打开 exit_plan_mode 评审;失败的调用会告诉子节点把未决决策纳入其最终结果。单是持久 fork 血统并不会阻止被当作运行时根恢复的会话打开评审。
  • 单一专用渲染器。 只有 Web UI 有 plan-review 渲染器;另一个交互 provider 通过其通用选项流呈现相同请求。

进一步阅读

  • 交互:命令、提问与审批/planexit_plan_mode 所依赖的问题接缝与命令平面。
  • 系统提示词组装plan:policy 段如何在顺序 50 处注册。
  • 权限与审批 — 为何计划模式只是指引而强制是分开的。
  • packages/plan/plan-mode/README.md — 完整行为契约(持久化状态、投影、模型体验)。
  • .agents/notes/implemented/simplification/2026-07-22-plan-specific-collaboration-state.md — 设计决策记录。