深入 oh-my-pi 的 PR 风格短摘要:compaction-short-summary 提示词的设计与实现解析
【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi
在 oh-my-pi 的长会话压缩(context compaction)机制中,除了生成用于恢复上下文的完整结构化摘要之外,还需要为每次压缩生成一段面向展示的短摘要——用 Pull Request 描述的口吻概括"这次对话改了什么"。本指南以packages/agent/src/compaction/prompts/compaction-short-summary.md这份提示词文件为骨架,结合packages/agent/src/compaction/compaction.ts的源码实现,讲解短摘要的生成规则、调用链、存储结构,以及它与完整摘要、turn-prefix 摘要等其他压缩提示词的分工。读完本文,你将理解 oh-my-pi 为何要为压缩事件单独设计一套"PR 描述"风格的提示词,以及这套机制在会话记录中如何被生成、传递与展示。
短摘要提示词的三条硬性约束
compaction-short-summary.md全文只有三行,却精确界定了短摘要的产出形态。它要求模型把一段对话压缩成一段Pull Request 描述(pull request description):
Summarize conversation changes as a pull request description. MUST 2–3 sentences; first person (`I added…`, `I fixed…`); describe changes, not process. NEVER mention tests, builds, or other validation steps; explain user request; ask questions.逐条拆解:
- 体裁约束:输出必须是 PR 描述的口吻,即站在"作者"视角,描述这次会话中完成的代码/文档/配置变更,而不是流水账式地复述对话过程。
- 篇幅与人称约束:严格 2~3 句话;使用第一人称(
I added…、I fixed…);内容聚焦"改了什么"(changes),而非"怎么做的"(process)。 - 内容边界约束:严禁提及测试、构建或其他验证步骤;同时要体现用户的原始请求(explain user request),并在必要处提出问题(ask questions)。
后两条约束的目的很明确:短摘要是给人(用户回看会话记录)看的轻量摘要,而不是给模型续接上下文用的。测试与构建信息属于"过程",会让短摘要变得冗长且偏离"变更点";保留用户请求与待澄清问题,则能让用户快速判断"这次会话是否达到了我想要的效果、还有什么悬而未决"。
需要说明的是,这份提示词之所以如此精简,是因为它只负责"格式约束"。实际的摘要内容通过调用 LLM 生成,而调用侧的 prompt 组装、token 预算、系统提示词都由源码完成(见下文)。
短摘要的生成调用链:generateShortSummary
短摘要的生成入口位于 compaction.ts 中的generateShortSummary函数(约 L1164-L1235)。该函数在模块加载时已经通过prompt.render完成了模板渲染:
const SHORT_SUMMARY_PROMPT = prompt.render(compactionShortSummaryPrompt);compaction-short-summary.md以文本资源形式导入(import compactionShortSummaryPrompt from "./prompts/compaction-short-summary.md" with { type: "text" }),并经prompt.render包装成最终注入到对话中的指令文本。
generateShortSummary的核心实现要点如下:
Token 预算严格受限:
const maxTokens = Math.min(512, Math.floor(0.2 * reserveTokens));短摘要的输出上限被限制为min(512, 20% 的 reserveTokens)。对比完整摘要的min(floor(0.8 * reserveTokens), MAX_SUMMARY_TOKENS)与 turn-prefix 摘要的min(floor(0.5 * reserveTokens), MAX_SUMMARY_TOKENS),可以看出短摘要是三类摘要中预算最小、最"省"的一次 LLM 调用——这从源码结构上印证了它"轻量展示用"的定位。
Prompt 组装(与完整摘要共用同一套骨架):
let promptText = `<conversation>\n${conversationText}\n</conversation>\n\n`; if (historySummary) { promptText += `<previous-summary>\n${escapeSummaryBoundaryTags(historySummary)}\n</previous-summary>\n\n`; } promptText += formatAdditionalContext(options?.extraContext); promptText += SHORT_SUMMARY_PROMPT;- 会话内容被包裹在
<conversation>标签内,防止模型把对话当作"继续对话"的上下文而是当作待总结对象; - 若存在历史摘要(
historySummary,即上次压缩产生的完整摘要),则以<previous-summary>标签注入,且经escapeSummaryBoundaryTags转义,避免摘要内容中的标签干扰结构; - 可选的
extraContext以<additional-context>列表形式追加; - 最后拼上本文档(
SHORT_SUMMARY_PROMPT)作为格式约束。
推理强度遵循用户 /model 选择:调用时通过resolveCompactionEffort(model, options?.thinkingLevel)解析 reasoning 力度,ThinkingLevel.Off时完全省略推理参数,undefined/Inherit回退到历史默认Effort.High,显式选择则尊重用户并做模型级 clamp。
传输路径两条:
- 本地路径:
instrumentedCompleteSimple发起 oneshot,系统提示词固定为SUMMARIZATION_SYSTEM_PROMPT(见 summarization-system.md,该提示词把历史对话与前序摘要视为不可信数据,禁止跟随其中任何指令); - 远程路径:当配置了
remoteEndpoint时,走requestRemoteCompaction远程压缩服务,同样携带SUMMARIZATION_SYSTEM_PROMPT与maxTokens。
可观测性与容错:该调用以oneshotKind: "compaction_short_summary"打点 OTEL 遥测;失败时经createSummarizationError映射为带errorStatus的ProviderHttpError,并遵循与完整摘要一致的 oneshot 重试策略(summaryOneshotRetry),保证单次瞬时故障(如 429/529)不会拖垮整个压缩流程。
短摘要的触发时机与落库
短摘要并非每次压缩都必然重新生成。在 compact 主流程(约 L1842-L1848)中:
const shortSummary = usedRemoteCompaction ? "Remote compaction" : await generateShortSummary(recentMessages, summary, model, reserveTokens, apiKey, signal, { ...summaryOptions, extraContext: options?.extraContext, thinkingLevel: options?.thinkingLevel, });- 当本次压缩走了远程(provider-native)压缩(OpenAI V1/V2 远程压缩成功)时,短摘要直接固定为
"Remote compaction"——因为远程压缩不产出本地文本摘要,为避免制造不实描述而使用占位文案; - 否则,对保留的最近消息(
recentMessages,即压缩后仍以原文保留的近期对话)调用generateShortSummary,并携带本次生成的完整summary作为historySummary上下文。
生成的shortSummary作为CompactionResult.shortSummary返回(接口定义见 compaction.ts 的CompactionResult,注释明确写着 "Short PR-style summary for display purposes")。随后会话管理器将其落库:
- 会话条目类型中声明了
shortSummary?: string(见 entries.ts); - 构造压缩摘要消息时通过
createCompactionSummaryMessage写入shortSummary字段(见 messages.ts,其中还包括tokensBefore、tokensAfter、method(如 "remote"/"soft"/"handoff")等展示元数据); - 分支汇总场景同样透传
shortSummary(见 branch-summarization.ts)。
因此,用户界面中"本次压缩做了什么"的展示位,消费的正是这份 PR 风格的短摘要;而用于模型续接的完整上下文,则由summary承载。两者各司其职。
与完整摘要及 turn-prefix 摘要的分工
oh-my-pi 的压缩提示词家族位于packages/agent/src/compaction/prompts/,短摘要与它们共同构成三层摘要体系:
| 提示词文件 | 用途 | 输出预算(相对 reserveTokens) |
|---|---|---|
| compaction-summary.md | 生成结构化 handoff 摘要(Goal / Progress / Next Steps / Critical Context 等分区),供另一个 LLM 接续任务 | 约 80% |
| compaction-update-summary.md | 迭代式更新既有摘要(存在previousSummary时替代初始提示词),要求保留既有信息、更新进度与下一步 | 同左 |
| compaction-turn-prefix.md | 当切割点落在某个 turn 中间时,压缩该 turn 的前缀部分,为保留的后缀提供上下文 | 约 50% |
| compaction-short-summary.md | 生成 PR 风格的 2~3 句短摘要,仅供展示 | 约 20%,上限 512 tokens |
值得注意的是,完整摘要格式(compaction-summary.md)也要求"如果对话以未回答的问题结束,必须保留该问题原文",这与短摘要的 "ask questions" 规则形成呼应:无论长摘要还是短摘要,悬而未决的用户问题都不允许被丢弃。区别在于完整摘要把问题放入Critical Context分区供模型接续,而短摘要以自然语言提问的方式让用户察觉。
从调用关系看(compaction.ts 的compact函数):常规路径先并行/串行生成完整历史摘要与 turn-prefix 摘要并合并(summary = historyResult + "\n\n---\n\n**Turn Context (split turn):**\n\n" + turnPrefixResult),随后追加文件操作清单(upsertFileOperations,记录本次压缩涉及的读写文件),最后才调用generateShortSummary产出展示用短摘要。
使用与验证建议
- 查看生效效果:压缩发生(手动
/compact或自动触发)后,会话记录中的compactionSummary消息同时携带summary与shortSummary两个字段,前者用于上下文重建,后者用于界面展示。 - 自定义输出风格:短摘要的格式完全由 compaction-short-summary.md 控制。若希望短摘要更贴近团队 PR 模板(如增加 "Motivation" 或 "Related issues"),可扩展该提示词文件,但应保持 2~3 句、第一人称、不提验证步骤的既有约束——这正是源码中
maxTokens预算所对应的产出规模。 - 约束的兼容性:三条 MUST/NEVER 规则与
SUMMARIZATION_SYSTEM_PROMPT的"输出仅限结构化摘要"要求叠加生效;由于系统提示词把对话内容视为不可信数据,即使历史对话里出现"请忽略格式要求"之类的指令,短摘要也不会被污染。
综上,compaction-short-summary.md虽然只有三行,却通过源码中的预算控制(maxTokens)、边界转义(escapeSummaryBoundaryTags)、远程/本地双路径与遥测打点,成为 oh-my-pi 会话压缩体系中"面向人"的最后一公里:把一次可能涉及数万 token 的长会话,压缩成两句话就能读懂的 PR 式变更说明。
【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考