计划模式上下文压缩蒸馏:oh-my-pi "批准并压缩上下文"流程的设计与实现
导读
在 oh-my-pi(⌥ Coding agent with the IDE wired in)的计划模式(Plan Mode)中,当用户批准计划后可以选择"批准并压缩上下文(Approve and compact context)":系统将长达数十轮的规划讨论蒸馏为一份精炼摘要,再以全新上下文继续执行。本文以 plan-mode-compact-instructions.md 为核心,讲解这条蒸馏指令的完整取舍契约——哪些信息必须保留、哪些必须丢弃、计划文件为何是唯一权威来源,以及它如何通过 internalGuidance 通道到达原生摘要器而不泄露给公共扩展钩子。读完本文,你将理解 oh-my-pi 计划模式的完整生命周期,并能据此为自己的 Agent 产品设计类似的"讨论 → 批准 → 压缩 → 执行"上下文策略。
一、指令文件的定位:一次压缩调用的"蒸馏宪法"
plan-mode-compact-instructions.md 是 oh-my-pi 计划模式专属的系统提示模板,全文围绕一个核心命令展开:
Prepare to execute approved plan.(准备执行已批准的计划。) MUST distill plan-mode discussion.(必须蒸馏计划模式的讨论。)
它不负责描述计划本身,而是规定在压缩发生时,摘要器如何从计划讨论中筛选信息。它属于 oh-my-pi 计划模式提示族中的一员,同族文件还包括:
- plan-mode-active.md:计划模式下系统提示的完整内容,规定工作树只读、通过
xd://propose写入计划标题以请求批准、计划文件必须命名为local://<slug>-plan.md等; - plan-mode-approved.md:批准后执行阶段的系统提示,把完整计划内联进提示并保留
local://持久副本; - plan-filename.md:为计划文件命名的小型提示(1~3 个词的主题)。
在 plan-mode-active.md 的批准选项中,"Approve and compact context"(批准并压缩上下文)被描述为 "discussion distilled, then executes here"(讨论被蒸馏,然后在当前会话执行)。而本次讨论的这条指令,正是驱动这次蒸馏的提示文本。
二、指令的取舍契约:Preserve 与 Drop
指令正文将蒸馏行为明确划分为"必须保留"与"必须丢弃"两组,这是整个提示的灵魂,也是执行阶段(executor)能否脱离对话上下文独立工作的关键。
必须保留(Preserve)
- 计划理由(Plan rationale)与明确被否决的备选方案(explicitly rejected alternatives):执行者需要知道"为什么选择这条路线",以及"为什么没有走另一条路线"。被否决的方案往往记录了对约束条件的理解,丢弃它们会导致执行者面对类似岔路时重复做设计决策。
- 关键决策与驱动约束(Key decisions; driving constraints):决策本身以及促成决策的约束条件必须成对保留。只有结论没有原因,执行者无法判断约束变化后是否仍然适用。
- 执行者需要的已发现文件、符号与代码路径(Discovered files, symbols, code paths executor needs):规划阶段通过探索得到的文件位置、符号签名、关键代码路径,是执行阶段最昂贵的信息——它们无法从计划正文重新推导,一旦丢失执行者只能重新探索。
- 规划期间用户表达过的偏好(User preferences expressed during planning):用户在规划对话中提出的明确偏好,代表了不可从代码推导的需求,属于 plan-mode-active.md 中定义的"Preferences/tradeoffs"一类信息,必须原样进入摘要。
必须丢弃(Drop)
- 工具调用噪音(Tool-call noise):文件读取、搜索等工具调用的原始记录,如果其结果已经被计划正文或计划讨论所吸收,就不再需要保留。这是蒸馏中最主要的体积削减来源——规划阶段可能产生几十轮
read/grep调用,但真正有价值的是它们"发现了什么",而不是"调用了几次"。 - 被取代的计划草稿(Superseded plan drafts):规划是迭代的,早期草稿会被后续版本覆盖。保留旧草稿只会稀释执行者对最终版本的信赖。
- 计划文件中已重申的上下文(Context restated in plan file):凡是在计划文件中已经出现的上下文,摘要中不得重复——这既避免了冗余,也保证了"计划文件是唯一权威"这一原则不被破坏。
这三条"丢弃"规则共同指向一个目标:摘要中不出现任何一条可以从计划文件本身获得的信息,从而把压缩后残留的上下文体积压缩到最小。
三、计划文件:权威真相的唯一来源
指令中的条件模板块(Handlebars 语法)揭示了整条蒸馏逻辑的锚点:
{{#if planFilePath}}
Approved plan file: `{{planFilePath}}`; authoritative source of truth.
MUST preserve this durable path; the plan body is re-inlined for the
executor after compaction, so NEVER restate it in the summary.
{{/if}}
当渲染时提供了 planFilePath(即"批准并压缩上下文"场景,必然存在已批准的计划文件),摘要器必须遵守三条规则:
- 计划文件是权威真相(authoritative source of truth):一切以
local://<slug>-plan.md的内容为准; - 必须保留持久路径:
planFilePath这个路径本身必须被保留进摘要,因为它是指向完整计划的唯一句柄; - 绝不重申计划正文:压缩完成后,计划正文会由执行阶段系统提示重新内联(re-inlined)给执行者,因此摘要中永远不要重复计划正文。
第三条规则与 plan-mode-approved.md 的执行阶段设计精确对应:批准后提示会携带 <plan path="{{planFilePath}}">{{planContent}}</plan> 的内联副本,并明确 "NEVER re-read {{planFilePath}} while the inline plan is intact; the path is for subagent handoff and recovery only"(内联计划完好时绝不重读计划文件,该路径仅供子代理交接与恢复使用)。也就是说:压缩阶段负责保留路径、丢弃正文;执行阶段负责内联正文、仅在副本失效时才回读文件。两者互补,构成完整的上下文管理闭环。
四、实现链路:指令如何在代码中被渲染与传递
这条提示并非停留在文档层面,它被真实地接入 oh-my-pi 的会话流程。在 interactive-mode.ts 中可以看到完整调用链:
const compactionPrompt = prompt.render(planModeCompactInstructionsPrompt, {
planFilePath: options.planFilePath,
});
渲染时唯一注入的变量正是 planFilePath——即上一节条件块的输入。随后该提示通过 handleCompactCommand(...) 的第四个参数被传入压缩流程,源码注释明确说明了设计意图:
Ride the plan-mode distillation prompt through as
internalGuidanceso it reaches native summarization without leaking into the publiccustomInstructionschannel onsession_before_compact— extensions there treat that field as user focus and would query-bias the summary toward the plan boilerplate (issue #4359).
即:这条内部蒸馏指令走 internalGuidance 专用通道,只到达原生摘要器(native summarizer),绝不进入公共的 customInstructions 通道。
关于压缩管道的整体背景,可参考 docs/compaction.md:它详细记录了 CompactionEntry 会话条目模型、prepareCompaction() 的边界与切点逻辑、软/远程/快照(snapcompact)等多种压缩方法,以及摘要生成后如何重建 LLM 上下文。其中明确写道:
The hook's
customInstructionscarries only the public user focus. Internal summarizer guidance — currently the plan-mode "Approve and compact context" distillation prompt — travels a separateinternalGuidancechannel onCompactOptionsthat reaches only native summarization, never this hook orsession.compacting; when both are set the summarizer usesinternalGuidancewhile hooks still see the publiccustomInstructions(issue #4359).
五、钩子隔离契约:为什么不能走 customInstructions
plan-mode-compact-instructions.md 虽然只有短短 17 行,却牵动了一个真实的生产问题(issue #4359),并有专门的回归测试守护。
问题背景:早期的实现曾把这条蒸馏指令通过公共的 customInstructions 参数传给 AgentSession.compact(...),结果它顺着 session_before_compact 扩展钩子流到了扩展侧。而扩展(例如基于查询聚焦摘要的扩展)会把该字段当作"用户关注点"来使用,于是计划模式的样板文本(plan-mode boilerplate)会取代操作者的真实意图,导致摘要被错误地引导(query-biased)。
针对该问题的回归测试位于 agent-session-plan-compact-hook-instructions.test.ts,测试契约明确列出三条:
- 计划模式压缩必须调用
AgentSession.compact时传customInstructions: undefined,指令通过CompactOptions.internalGuidance传递; session_before_compact钩子事件对内部指令压缩必须看到customInstructions: undefined;- 原生摘要器(经
@oh-my-pi/pi-agent-core/compaction调用)仍必须收到该指令以引导摘要方向。
测试用例逐一验证:
- 内部指令不进公共钩子:
session.compact(undefined, { internalGuidance: planGuidance })后,session_before_compact事件中的customInstructions为undefined,而摘要器收到的指令正是planGuidance; - 用户焦点原样进公共通道:
session.compact("focus on the auth refactor")时,钩子与摘要器都能看到用户焦点字符串,因为它是公共信息; - 两者同时存在时内部指令优先:同时传用户焦点与
internalGuidance时,摘要器使用internalGuidance,钩子仍只见用户焦点——防止调用方误把计划提示当作用户焦点泄漏出去。
六、从指令看设计哲学:面向未知执行者的自包含计划
综合 plan-mode-active.md 与本次指令,可以提炼出 oh-my-pi 计划模式压缩机制背后的一贯原则:
- 计划是执行规格,不是设计文档:一份合格的计划必须让"对对话一无所知的称职工程师"能够自上而下零设计决策地执行。压缩蒸馏正是为了让批准后的执行从干净的上下文开始,同时不丢失任何决策信息。
- 细节服务于"消除执行者决策",而非凑篇幅:Preserve 列表中的"已否决方案"和"驱动约束"看似多余,实则是消除执行者面临岔路时再做判断的必要信息;Drop 列表中的"计划草稿"和"已重申上下文"看似无害,实则是让执行者重新做设计的温床。
- 单一权威来源:计划文件
local://<slug>-plan.md是唯一真相;摘要只携带它的路径,正文由执行阶段重新内联。两个阶段各司其职,避免同一份信息在多个地方以不一致的形态出现。 - 内部机制与公共扩展面严格隔离:
internalGuidance通道的存在,让内部提示可以引导摘要方向,同时保持扩展钩子的语义纯净——钩子看到的customInstructions永远只代表用户意图。
七、实践启示
对于使用或扩展 oh-my-pi 的开发者,以下几点值得留意:
- 使用计划模式的长任务:"批准并压缩上下文"适合规划讨论很长、但执行路径明确的场景。规划阶段的探索越多,压缩收益越大——前提是计划文件本身足够决策完整(decision-complete),否则压缩后的执行者将无据可依。
- 编写扩展时:如果你的扩展消费
session_before_compact事件并把customInstructions解释为用户意图,那么 oh-my-pi 当前的实现已经保证你不会看到计划模式的内部样板文本;若你仍观测到意外内容,可对照 agent-session-plan-compact-hook-instructions.test.ts 中定义的契约排查。 - 深入阅读:完整的压缩管道(触发条件、边界切点、方法链、快照压缩等)见 docs/compaction.md;计划模式的完整行为约束见 plan-mode-active.md 与 plan-mode-approved.md。
小结
plan-mode-compact-instructions.md 虽然是一份 17 行的提示模板,却是 oh-my-pi 计划模式压缩机制中承上启下的关键节点:它以"Preserve/Drop"二元清单定义了蒸馏的信息边界,以计划文件路径锚定了唯一权威来源,并通过 internalGuidance 通道在"引导摘要"与"隔离公共扩展面"之间取得平衡。理解这份指令,就等于理解了 oh-my-pi 如何让一次长达数十轮的计划讨论,以最小的信息损失、最干净的上下文,无缝过渡到执行阶段。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0632
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00