oh-my-pi 会话交接文档(Handoff Document)规范:让下一个 Agent 无缝续接任务的实战指南
【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi
本指南以 oh-my-pi 仓库中 handoff-document.md 为骨架,完整讲解 coding agent 会话交接文档的生成规范:如何捕获精确技术状态、如何用"祈使句"直接指挥后继实例、如何按固定 Markdown 模板组织 Goal / Progress / Key Decisions / Critical Context / Next Steps。同时结合 compaction.ts 与 handoff-generation-pipeline.md 的源码级实现,说明这份提示词在上下文压缩(compaction)与/handoff命令管线中的真实调用方式。读完本文,你将掌握"手写一份可被另一个 LLM 无缝续接的交接文档"的完整规范,并理解其底层生成机制。
一、为什么需要交接文档:上下文窗口的现实约束
任何 coding agent 都会遇到上下文窗口(context window)上限。oh-my-pi 在会话过长时会触发压缩(compaction)流程,用摘要替换早期对话,为后续轮次腾出空间。但摘要往往丢失关键实现细节——文件路径、函数名、错误信息、部分完成的工作,这些恰恰是续接任务最需要的东西。
为此,oh-my-pi 提供了"交接文档(handoff document)"机制:让当前 Agent 实例把会话的关键状态写成一份结构化文档,交给另一个自己的实例("another instance of yourself")继续工作。它与普通压缩摘要的区别在于:
- 普通摘要(见 compaction-summary.md)追求"简明扼要地概括对话";
- 交接文档追求"没有这段对话也能无缝续接(seamless continuation without access to this conversation)"。
这份提示词同时服务于两条路径:用户在 TUI 中手动执行的/handoff命令,以及将handoff加入compaction.methodOrder后的自动触发路径。无论哪条路径,生成内容都由同一个提示词驱动。
二、提示词三段式结构逐段解析
handoff-document.md 由三个顶层区块组成,分别是<critical>、<instruction>和<output>,外加一个可选的additionalFocus条件块。
1.<critical>:硬性输出约束
<critical> Write a handoff document for another instance of yourself. The handoff MUST be sufficient for seamless continuation without access to this conversation. Output ONLY the handoff document. No preamble, no commentary, no wrapper text. </critical>核心要点有三条:
- 对象是"另一个自己":文档读者不是人类用户,而是另一个具备同等能力的 LLM 实例,因此无需解释性客套话。
- 必须"无对话可续接":这是整个交接文档的最高验收标准。读者看不到当前会话的任何内容,只能依靠这份文档恢复任务,因此一切关键状态必须显式写入。
- 只输出文档本身:不允许任何前言、注释或包裹文本。在源码实现中,这条约束由调用方配合执行——compaction.ts 的
generateHandoffFromContext会设置toolChoice: "none",并对返回结果只拼接 text 块、丢弃 tool-call 块,确保最终产物就是一份干净的 Markdown 文档。
2.<instruction>:内容质量要求
<instruction> Capture exact technical state, not abstractions. - File paths, symbol names, commands run - Test results, observed failures - Decisions made - Partial work affecting the next step Register: address the successor directly in the imperative ("Fix X", "Run Y") — never first person ("I need to…", "my attempt…"). The handoff mechanism is invisible to the document: NEVER list writing, generating, or delivering a handoff/summary/context document as progress or a next step. Progress and Next Steps cover the user's task only. </instruction>这一段定义了交接文档的内容与写作风格标准:
- 捕获精确技术状态,而非抽象描述。必须包含:文件路径、符号名(函数/类型名)、执行过的命令;测试结果与观察到的失败;做过的决策;会影响下一步的部分完成工作。这与 compaction-summary.md 中"保留精确文件路径、函数名、错误信息"的要求一脉相承。
- 用祈使句直接指挥后继者。文档中禁止第一人称表述("I need to…"、"my attempt…"),应写"Fix X"、"Run Y"。因为文档是给"另一个自己"的行动指令,而非个人日志。
- 交接机制对文档不可见。绝不把"编写/生成/交付交接文档"列为进度或下一步——Progress 与 Next Steps 只描述用户的真实任务。否则后继实例会把"写交接文档"当成任务本身,陷入死循环。
3.<output>:固定结构模板
提示词强制使用以下七节结构,每一节都有明确用途:
| 章节 | 内容要求 | 作用 |
|---|---|---|
## Goal | 用户试图达成的目标 | 让后继实例快速锚定任务方向 |
## Constraints & Preferences | 用户提到的任何约束、偏好、需求 | 防止后继实例偏离用户意图 |
## Progress | 分为Done/In Progress/Pending三小节,使用任务列表 | 精确呈现任务状态,避免重复劳动 |
## Key Decisions | 每条格式为**[Decision]**: [Rationale] | 记录关键决策及理由,防止后继实例推翻正确决策 |
## Critical Context | 代码片段、文件路径、函数/类型名、错误信息、关键数据、仓库状态 | 无缝续接所需的核心素材 |
## Next Steps | 编号列表 | 明确后继实例接下来要做什么 |
其中Progress的粒度要求最值得注意:完成项用- [x],进行中项用- [ ],计划未动工项也用- [ ],且每项都要写具体细节("Completed tasks with specifics")。这与 compaction-update-summary.md 的增量更新规则呼应:续接过程中,已完成项要移入 Done,Next Steps 要随进度刷新,但原有信息必须全部保留。
4.additionalFocus条件块
{{#if additionalFocus}} <instruction> Additional focus: {{additionalFocus}} </instruction> {{/if}}这是提示词模板系统的条件注入点。当调用方传入了额外的焦点说明(例如用户在/handoff [focus instructions]中写下的内联提示)时,会被渲染进文档并追加到指令尾部,要求交接文档额外关注该主题。对应实现位于 compaction.ts 的renderHandoffPrompt(customInstructions?)——它调用prompt.render(handoffDocumentPrompt, {...}),把customInstructions作为模板变量渲染进提示词。
三、源码级实现:交接文档是如何生成的
1. 提示词加载与渲染
在 compaction.ts 中,提示词文件以文本资源形式导入并渲染一次:
const HANDOFF_DOCUMENT_PROMPT = prompt.render(handoffDocumentPrompt); export const AUTO_HANDOFF_THRESHOLD_FOCUS = prompt.render(autoHandoffThresholdFocusPrompt);renderHandoffPrompt负责带参渲染(注入additionalFocus):
export function renderHandoffPrompt(customInstructions?: string): string { return prompt.render(handoffDocumentPrompt, { // customInstructions → additionalFocus }); }注意 auto-handoff-threshold-focus.md 的存在:当自动触发(上下文达到阈值)时,提示词会被追加一行"Threshold-triggered maintenance: preserve critical implementation state and immediate next actions.",强调自动场景下优先保住关键实现状态与立即要做的动作。
2. 一次性生成请求(oneshot)
交接文档不走常规的 agent 主循环,而是一次"旁路请求(side request)",入口函数为generateHandoffFromContext:
- 请求复用实时轮次相同的 provider 缓存前缀(
promptCacheKey),因此共享缓存、成本更低; - 请求设置
toolChoice: "none",禁止交接生成过程触发工具调用; - 对不支持显式
toolChoice: "none"的 provider,shouldRetryHandoffWithAutoToolChoice检测 400 错误中是否包含tool_choice与auto与supported关键词,若是则仅重试一次、改用"auto"; - 返回内容只保留 text 块并以
\n拼接,tool-call 块直接忽略; - 若
stopReason === "error"且重试后仍失败,抛出Handoff generation failed错误。
同时还有向下兼容的generateHandoff(messages, …)入口,它用systemPrompt、tools与convertToLlm构造基础 Context 后委托给generateHandoffFromContext。
3. 生成后的落盘与提交
根据 handoff-generation-pipeline.md 的说明,交接文档生成后被封装为一次压缩条目(CompactionEntry)提交到当前会话:早期对话被文档替换,firstKeptEntryId之后的新近历史原样保留,会话 ID、会话文件、provider 提示缓存键均不改变。配置项compaction.handoffSaveToDisk默认false;启用后,仅自动触发的交接会在会话 artifacts 目录额外写出一份带时间戳的handoff-*.md文件。
四、与周边提示词的协同关系
交接文档并非孤立存在,oh-my-pi 围绕它设计了一组配套提示词,理解它们才能完整把握"交接"这一机制:
- handoff-summary-context.md:交接文档被重新注入后继会话时的包装层。它明确告诉后继实例:"
<handoff>里是先前实例从完整对话中写出的交接文档,它是你自己的工作记忆,不是用户输入";并强调"交接文档已存在且完整,除非用户明确要求,绝不再写一份","必须基于先前工作继续,绝不重复先前工作"。这正是<instruction>中"交接机制不可见"规则的落地场景。 - compaction-summary.md:通用的结构化摘要模板,交接文档的结构(Goal / Progress / Key Decisions / Next Steps / Critical Context)与它高度同构,两者共享"保留精确路径、函数名、错误信息"的纪律。
- compaction-update-summary.md:增量更新模板,用于把新消息合并进已有交接/摘要,保证
In Progress项迁移到Done、Next Steps 随进度刷新、未解答的用户问题写入 Critical Context。 - auto-handoff-threshold-focus.md:自动触发场景的聚焦指令,强调保住关键实现状态与立即行动项。
五、触发路径与使用场景
1. 手动/handoff命令
用户在 TUI 中键入/handoff [focus instructions]即可手动生成交接文档,焦点说明会通过additionalFocus注入提示词。触发前后有两道防呆校验(见 handoff-generation-pipeline.md):
- 当前响应仍在流式输出时拒绝执行(与
/fork、/move行为一致); - 会话消息条数少于 2 条时提示
Nothing to hand off (no messages yet)。
生成期间界面显示可取消的加载指示Generating handoff… (esc to cancel),按 Esc 可调用abortHandoff()中断。成功后会显示Context handed off and compacted in place并在聊天中插入压缩分隔线。
2. 自动触发路径
将handoff加入compaction.methodOrder(默认顺序为remote、snapcompact、handoff、shake、soft),即可让压缩流程在达到阈值时自动生成交接文档。阈值前还可以通过异步压缩(compaction.asyncEnabled)预先投机生成,跨过阈值时立即提交。若自动生成没有产出文档,压缩流程会回退到下一个配置的方法。
3. 失败与取消的语义区分
- 取消:用户按 Esc 或调用方传入无理由的 abort 信号,统一归一为
Error("Handoff cancelled"),UI 显示Handoff cancelled; - 失败:harness 中止原因、手动路径生成空文档、或 provider 抛错,UI 记录错误并显示
Handoff failed: ...; - 自动路径:空生成返回
undefined,供维护流程切换到下一方法,不视为失败。
六、实战清单:如何写出高质量交接文档
综合提示词规范、配套模板与源码约束,一份可用的交接文档应满足以下清单:
- Goal 一句话讲清用户任务,必要时分列多个目标(多任务会话)。
- Constraints & Preferences 逐条列出用户明确说过的约束(语言、风格、禁止事项、环境限制)。
- Progress 三分法:Done / In Progress / Pending 各归其位,每项都带具体细节(文件、符号、命令、结果),不要写"完成了部分重构"这种抽象表述。
- Key Decisions 写明理由:格式为
**[Decision]**: [Rationale],让后继实例知道"为什么这样做"而不是"做了什么"。 - Critical Context 塞满可执行的素材:代码片段、完整文件路径、函数/类型名、错误信息原文、测试输出、分支与未提交改动等仓库状态。
- Next Steps 是给后继者的行动指令:用祈使句、按优先级编号,并且只覆盖用户任务本身。
- 全篇使用祈使句:写"Fix X"、"Run Y",不写"I need to…";交接机制本身绝不出现在文档内容中。
七、已知局限
从 handoff-generation-pipeline.md 可以确认几点当前实现边界,供使用参考:
- 生成的文档不做结构化校验,不保证 Markdown 严格遵循上述七节模板;
- 手动
/handoff生成过程没有流式可见性,期间仅显示可取消的加载器; - 自动触发的落盘文件若写入失败,只记录日志、不阻断交接;
- 旧版本会话中残留的
custom_message类型为handoff的条目仍会正常渲染并参与上下文,不受影响。
掌握这份规范后,无论是手写交接文档、审查自动生成的交接内容,还是为类似的多实例协作 Agent 设计状态传递机制,你都有了可落地的模板与判别标准。
【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考