dsh-agent-context-pruner

August 17, 2026 · View on GitHub

上下文剪枝插件(爱丽丝判断为核心)——开发会话中大部分上下文是一次性消费的残留,剪掉它们省 token 且保护注意力。

安装

cd <你的 self-plugins>
git clone https://github.com/jonah791/dsh-agent-context-pruner.git
cd dsh-agent-context-pruner
pnpm install
pnpm build
``$

## 设计定调(2026-08-16 主人)

- **「是否无用由爱丽丝自己判断」**:插件只提供 候选检测(含缓存代价)+ 执行原语 + 统计;剪不剪、剪哪些、何时剪——决策归爱丽丝(自主性铁律:机制给原语不给剧本)
- **「不能破坏缓存命中」**:候选按位置标注剪后缓存代价(其后内容重新 \text{prefill} 的一次性成本)。\text{tail}(最后 3 节点内)= 零缓存破坏;\text{near}-\text{tail} 小代价;\text{middle} 大代价——\text{middle} 仅在「节省  \times  剩余轮数 > 缓存代价」时剪
- **\text{replay}-\text{safe}**:头+标记+尾替换(官方 \text{dsh}-\text{compaction}-\text{tool}-\text{result}-\text{pruner} 同款 \text{surfaceOp} \text{replace}),仅追加事件日志保留完整原始事件(可回放恢复);替换前写 \text{compaction}/\text{prune} 定价事件(\text{tokenMeter} 定价)
- **白名单**:只剪 \text{tool}/\text{result} 节点;系统注入/主人消息/当前任务轮永不入候选
- **注意力保护(爱丽丝侧判断纪律)**:结论已落盘 + 可低成本重取 + 不在当前任务链,三条全满足才剪;剪前自问「主人下一句就问这个,我能答上来吗?」

## 工具

| 工具 | 说明 |
|------|------|
| $prune_candidates` | 候选检测(只读):扫描当前会话表层 tool/result,列出 seq/轮次/大小/估算 token/超预算/剪后缓存代价/位置提示(tail/near-tail/middle) |
| `prune_apply` | 执行剪枝(可写):按 seq 剪指定节点(头+标记+尾),幂等(已剪/预算内自动跳过),返回替换明细与累计统计 |
| `prune_stats` | 统计(只读):本会话累计剪枝次数/节省字符/节省 token/最近剪枝时间(进程内累计,重启清零) |

## 配置

| 配置键 | 缺省 | 含义 |
|--------|------|------|
| thresholdChars | 8192 | 合并文本超过此码点数才算超预算(缺省阈值) |
| headChars | 4096 | 保留开头码点数 |
| tailChars | 1024 | 保留末尾码点数 |

## 用法

1. 上下文压力高时(或主动巡检):`prune_candidates` 查看候选
2. 按判断纪律挑选:tail 放心剪;middle 权衡缓存代价
3. `prune_apply({ seqs: [...] })` 执行
4. `prune_stats` 查看节省

## 每轮缓存命中率(v0.1.2+)

`prune_stats` 输出每轮每次模型请求的 provider 实测命中率(usage 透传):
`hitRate = cacheRead / (input + cacheRead + cacheWrite)`(DeepSeek adapter 从 prompt_tokens 中减出缓存计数)。

实证(2026-08-16 turn 102,剪 middle 节点 730739 前后):
- 剪前 step 1-4:命中率 99.27% / 99.96% / 99.88% / 99.91%(cacheRead ~18.9 万)
- 剪后 step 5(第一个请求):**20.89%**——input 从 ~200 暴涨到 149,826(剪枝点后全部重新 prefill,一次性代价)
- step 6-7:99.65% / 99.96% 完全恢复(新前缀被缓存)

结论:middle 剪枝 = 一次性重新 prefill 代价(实测 ~15 万 token)+ 长期每轮省下被剪内容;恢复后命中率回满。tail 剪枝零破坏(剪后序列是原前缀)。数据与候选标注的 cacheCostTokens 模型吻合。

## 入口守卫(v0.2.0+,核心:主人北极星「不破坏缓存命中 + 上下文内容有效」)

大工具结果在产生时(append 后 setImmediate)立即折叠为「头 1024 + 尾 512 + 标记」——模型首次看到的就是折叠版,**原文从未进过上下文**(无「先 prefill 再剪」的浪费);确定性折叠 → 缓存前缀稳定 → 命中率不破坏。事件日志原文保留(replay-safe)。

- `expand(callId)`:按 callId 恢复全量(surfaceOp replace 回原文;豁免集防再折叠)
- `prune_guard`:查看/切换守卫(mode=on/off;阈值配置 guardThresholdChars 4096 / head 1024 / tail 512)

验证(2026-08-16 18:47):返回 9k/12k 字符大结果 → 模型看到头+尾+标记(含 expand callId)→ foldedCount+1 → expand 恢复全量(替换节点落地)→ 命中率保持 99.5%+。

注意:run_code 嵌套工具(read 等)的结果不产生会话 tool/result(worker 内执行);只有 run_code 自身返回进上下文——守卫对会话级工具结果生效。

## 验证记录(2026-08-16)

- 候选扫描:真实会话 66 个 tool/result,超预算 3 个(最大 49k 字符/12k token),位置/缓存代价标注正确
- 执行剪枝:seq 745172(9637→5159 字符,省 2422 token),替换节点落地(surfaceOp replace),compaction/prune 定价事件写入
- 幂等:已剪节点不再出现在候选
- 修复记录:`session.append` 解绑导致 `reading 'log'` 崩溃 → `.bind(session)` 保持 this

## 相关

- [我的数字生命爱丽丝 — 插件生态中心(架构总览)](https://github.com/jonah791/alice-digital-life)

## License

MIT