@@ -19,7 +19,7 @@ Status: implemented
遵循[能力 seam Agent Note( agent 决策记录) ](../architecture/2026-06-13-capability-seams.md ), 压缩以独立包( package) 发布, 使契约、算法和( 后续的) 消费方 surface 各自独立演进:
1. **接口 ** — `@deepseek-ai/dsh-compact` :抽象 `CompactService` ,拥有 `ctx.compact` 键、`CompactionResult` 词汇、`compact/*` 会话事件、手动失败分类体系以及规范的检查点消息来源。它将 `compactIfNeeded()` 、`compactNow()` 和 `compactRegion()` 声明为**抽象方法**——契约说明压缩*做什么*,而非*怎么做*。
2. **实现 ** — `@deepseek-ai/dsh-compact-basic` :具体的 `BasicCompactService` ,消费 `ctx.tokenMeter` ,并拥有尾→头保留遍历、通过 `ctx.llm.stream()` 生成摘要、surface 替换、锁、步骤后 压力处理和规范的上下文溢出恢复。`summarize()` 是其唯一的子类钩子;计价与回放仍归 meter 所有。
2. **实现 ** — `@deepseek-ai/dsh-compact-basic` :具体的 `BasicCompactService` ,消费 `ctx.tokenMeter` ,并拥有尾→头保留遍历、通过 `ctx.llm.stream()` 生成摘要、surface 替换、锁、步骤前 压力处理和规范的上下文溢出恢复。`summarize()` 是其唯一的子类钩子;计价与回放仍归 meter 所有。
3. **无模型配套服务 ** — `@deepseek-ai/dsh-compact-tool-result-prune` :一个具体的可选服务,在后端选择摘要范围之前,重写当前过大的 `tool/result` 节点。它不是第二种压缩实现,也不实现 `CompactService` 。
4. **面向用户的消费方 ** — `@deepseek-ai/dsh-command-compact` 通过 `ctx.commands` 注册无参数 `/compact` ,并调用后端无关的 `compactNow()` 操作。它是供用户直接控制的命令,不是面向模型的工具。
@@ -33,7 +33,7 @@ Status: implemented
早期草案将完整算法( 保留遍历、token 求和、文本提取)作为接口上的具体方法。这会将契约重新耦合到一种策略:想要不同保留策略或事件排序的后端必须与继承来的具体代码对抗。将三个操作都设为抽象,把所有*怎么做*的决策放在后端,并让接口保持为*做什么*的声明。token 测量根本不是压缩钩子;单例服务使多个消费方能够共享逐会话的回放折叠。
`compactIfNeeded(agent, trigger, signal)` 接受显式的 `'pressure' | 'context-overflow'` 触发原因与取消信号。它只读取最新的持久化已路由请求;没有 header 就不执行工作,任何已路由的提供方/模型目标都使用单例估算器。`compactNow(agent, signal)` 会预留空闲轮次接纳 ,即使未达到压力也进行一次有效的平衡缩减;不存在这种范围时返回 `null` ,且不写入任何内容。`compactRegion(start, end, agent, signal?)` 将 `agent.session` 作为唯一会话身份,并为显式调用方保留可选 signal。默认摘要器依次从显式配置、最新记录的已路由目标和 agent 选项解析目标,并在任何 `llm/stream` 路由后记录提供方/模型对。它回放已路由请求的前缀,并将压缩指令追加为尾部 user 消息,从而复用提供方的热 KV cache; 见[摘要前缀缓存 Agent Note ](../bug-fix/2026-07-21-compaction-summary-prefix-cache-reuse.md )。该调用将提供方无关的 `GenerateOptions.purpose` 设为 `compaction` ; 适配器可以将此用途映射为对模型隐藏的传输元数据, DeepSeek 适配器会发送 `x-deepseek-harness-compact: 1` 。
`compactIfNeeded(agent, trigger, signal)` 接受显式的 `'pressure' | 'context-overflow'` 触发原因与取消信号。它只读取最新的持久化已路由请求;没有 header 就不执行工作,任何已路由的提供方/模型目标都使用单例估算器。`compactNow(agent, signal)` 要求 agent 处于 idle ,即使未达到压力也进行一次有效的平衡缩减;不存在这种范围时返回 `null` ,且不写入任何内容。`compactRegion(start, end, agent, signal?)` 将 `agent.session` 作为唯一会话身份,并为显式调用方保留可选 signal。默认摘要器依次从显式配置、最新记录的已路由目标和 agent 选项解析目标,并在任何 `llm/stream` 路由后记录提供方/模型对。它回放已路由请求的前缀,并将压缩指令追加为尾部 user 消息,从而复用提供方的热 KV cache; 见[摘要前缀缓存 Agent Note ](../bug-fix/2026-07-21-compaction-summary-prefix-cache-reuse.md )。该调用将提供方无关的 `GenerateOptions.purpose` 设为 `compaction` ; 适配器可以将此用途映射为对模型隐藏的传输元数据, DeepSeek 适配器会发送 `x-deepseek-harness-compact: 1` 。
### 成功的持久步骤工作完成后运行自动压力检查
@@ -42,9 +42,9 @@ Status: implemented
规范的提供方上下文溢出走另一条路径。失败步骤先关闭,`agent/request-error` 接收原始请求错误。compact-basic 自行持有按 agent 计的溢出次数,在强制执行一次有效且平衡的缩减前先修剪,且仅当 `session.surface.replaceGeneration` 增加时才返回 `{ kind: 'retry' }` ;这包括没有摘要范围时仅修剪取得的进展。随后循环关闭失败轮次,开启新的编号重试轮次,并从持久日志重建请求。没有替换、任何替换前的恢复失败、取消、耗尽的上限或无关错误都会保留原始提供方失败。如果修剪已经推进 generation, 而后续摘要工作失败, 恢复会从该持久的已修剪 surface 重试,除非取消或资源释放胜出。完整生命周期决策见[调用后恢复 Agent Note ](../architecture/2026-07-10-after-call-compaction-pressure-and-overflow-recovery.md )。
```
assistant/message → tool/result/context/steering
await waterfall agent/pre-step ⟵ pressure compaction before the next request
step/end
assistant/message → tool/result/context/steering → step/end
claim the next batch → await waterfall agent/pre-step ⟵ pressure compaction before the next request
enter → next step/start
provider overflow → step/end
await waterfall agent/request-error ⟵ forced compaction between attempts
@@ -53,7 +53,7 @@ retry → next numbered step/start ⟵ derives from the replacement surface
### 保留是轮次无关的;工具配对平衡是唯一的结构守卫
自动压缩在**每个成功的**步骤之后检查,而非每轮一次。这对失控轮次存活至关重要:工具密集型的 ReAct 轮次每步追加一个 `assistant/message` + 一个 `tool/result` ,因此 surface 会在一轮之内增长。步骤后检查可以在后续步骤开启 前压缩早期已关闭的工具对;如果请求率先越过限制,由提供方确认的溢出仍是兜底机制。
自动压缩在**每个成功的**步骤之后检查,而非每轮一次。这对失控轮次存活至关重要:工具密集型的 ReAct 轮次每步追加一个 `assistant/message` + 一个 `tool/result` ,因此 surface 会在一轮之内增长。下一个 pre-step 检查可以在继续执行打开另一步骤之 前压缩早期已关闭的工具对;如果请求率先越过限制,由提供方确认的溢出仍是兜底机制。
`compactIfNeeded` 保留估算大小达到解析后保留 token 预算的最小完整 surface 单元尾部,压缩更早的节点。一个单元是一个完整的已关闭步骤或一条无步骤消息。如果 token 截断点落在步骤内部,保留范围会扩展直到切割点满足工具配对平衡。平衡按 surface 顺序检查,而非日志序号,因为替换摘要在旧的 surface 位置拥有新的序号。`dsh-compact` 导出前后边缘辅助函数;只要 `replaceGeneration` 不变,其逐会话缓存就只折叠新增的 surface 尾部节点,面对仅日志增长时不读取事件,并在替换后重建当前成员关系与平衡。`compactRegion` 拒绝将工具调用与其结果拆分的边界。进行中的轮次不享受特殊保留。
@@ -96,7 +96,7 @@ compact/end → log-only. Releases the lock (carries `error` on a recoverab
1. **可检测的崩溃孤儿 + 来源追溯 ** (首要)。摘要生成是一次慢速模型调用,持久化在 `compact/start` * 之后 * 。摘要生成中途崩溃会留下一个没有匹配 `compact/end` 的 `compact/start` ——一个可检测的孤儿。最后释放锁(而非最先)将崩溃窗口从*静默损坏*转变为可检测的孤儿。
2. **防止并发压缩。 ** 每个自动、手动和显式范围入口点都会拒绝活动的未匹配 `compact/start` 。该标记对就是唯一的锁;没有进程本地 mutex 重复承担同一职责。
该锁只排除另一项压缩,不排除无关事实。其标记是时间点,而不是排他的容器,因此空闲注入的上下文 可以出现在独立手动 start 与 end 之间。自动工作要求其轮次内的整个 surface 保持稳定。手动工作只重新验证所选位置 span, 使其外部的仅追加上下文在替换后保持可见。
该锁只排除另一项压缩,不排除无关事实。其标记是时间点,而不是排他的容器,因此持久 inbox splice 可以出现在独立手动 start 与 end 之间。自动工作要求其轮次内的整个 surface 保持稳定。手动工作只重新验证所选位置 span, 使其外部的仅追加上下文在替换后保持可见。
生命周期边界使崩溃状态含义明确:
@@ -111,7 +111,7 @@ compact/end → log-only. Releases the lock (carries `error` on a recoverab
## 曾考虑的替代方案
- **完整算法作为接口的具体方法**——否决,因为它将契约重新耦合到一种保留策略。三个操作都是抽象的;可复用测量属于单独的 LLM 系列服务,`summarize()` 是 basic 唯一的钩子。
- **在 `agent/request` 或临时 `agent/pre-step` 输入上执行压缩**——否决,因为两者都无法证明最终的持久请求,而且都会将通用生命周期耦合到压缩专属的信封数据。步骤后回放与 规范溢出恢复同时 覆盖成功和被拒绝的调用。
- **在 `agent/request` 或压缩专属的 loop 回调上执行压缩**——否决,因为前者观察的是临时请求,后者会将通用生命周期耦合到压缩策略。对先前持久请求进行 pre-step 回放,再加上 规范溢出恢复,即可 覆盖成功和被拒绝的调用。
- **`compact` 布尔值或无类型的请求元数据 map**——否决,因为多个辅助调用种类会变成互斥标志,而开放 map 会丢弃由编译器检查的词汇。一个类型化的 `purpose` 判别字段可以扩展其他调用种类,而无需再为 `GenerateOptions` 添加字段。
- **单独的 `compact/error` 事件**——否决:`compact/end` 保留 `error?` 字段,与 `tool/result` 的自包含错误一致——一个事件即可区分成功与失败,无需关联兄弟事件。
- **教导核心轮次修复识别 `compact/*` **——否决:通用 end-seed 边界已经能够区分先前生命周期的历史;为每个未来的 `xxx/start … xxx/end` 插件对修补核心模块,恰好是能力 seam 架构存在的意义所要避免的耦合。
@@ -128,7 +128,7 @@ compact/end → log-only. Releases the lock (carries `error` on a recoverab
## 测试
- **单元测试:** 使用真实 Loader 和 invariant 插件覆盖完整单元保留、修剪配置与回放、富块顺序、元数据保留、收敛、`compact/end` 的两种结果、开放尾部拒绝、仅修剪与带摘要的溢出恢复、generation 证明、上限和原始错误保留。
- **循环测试:** 测试固定步骤后处理发生在持久工具结果之后、`step/end ` 之前,使用实际 `agent/request` 路由,关闭失败步骤,分配新的重试编号,并覆盖完整的抛出/带内溢出 → 压缩 → 重建重试组合。
- **手动测试:** 无需模型密钥即可固定接纳 、标记顺序、注入保留、活动/ 陈旧未匹配标记分类、取消、闭合/ flush 失败、命令映射以及排队 TUI 流程。
- **循环测试:** 测试固定 pre-step 发生在前一个 `step/end` 之后、下一个 `step/start ` 之前,使用实际 `agent/request` 路由,关闭失败步骤,分配新的重试编号,并覆盖完整的抛出/带内溢出 → 压缩 → 重建重试组合。
- **手动测试:** 无需模型密钥即可固定 maintenance 串行化 、标记顺序、注入保留、活动/ 陈旧未匹配标记分类、取消、闭合/ flush 失败、命令映射以及排队 TUI 流程。
- **带密钥 e2e: ** 真实模型和 bash 会话在降低的限制下触发压缩,记录完整的 `compact/start…end` 对,缩小 surface, 并完成任务。
- **快照缺口:** 失控轮次压缩尚无法回放,因为摘要调用未记录 `assistant/chunk` 事件或 `sessionId` ;交错摘要调用的回放仍是后续工作。