pi-deepseek-cache0.5.0
DeepSeek prefix-cache extension for pi — hit-rate telemetry, prefix guard, and cache-friendly compaction.
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
🚀 pi-deepseek-cache
充分发挥 Pi 编码代理中 DeepSeek 上下文缓存的能力。
稳定的提示前缀 · 更高的缓存命中率 · 实时缓存统计 — 让长会话的大部分输入按缓存命中费率结算。
✨ 为什么需要这个
DeepSeek API 内置了磁盘上下文缓存:任何提示前缀与已持久化的 cache prefix unit 完全匹配的请求,都按更便宜的缓存命中费率计费。但前提是——提示前缀必须跨轮次稳定,逐 token 一致。
在长时间的代理会话中,这出乎意料地困难:
- 系统提示或工具列表在各轮之间微妙变化
- 对话历史不断增长和偏移
- 大型 / 非确定性的工具输出破坏前缀
- 重复的元数据块未对齐
pi-deepseek-cache 让你的 DeepSeek 提示保持缓存友好,并精确展示效果 —— 无论你走的是官方 API,还是任何把 DeepSeek 放在 OpenAI 兼容端点后面的第三方部署。
🎯 功能特性
- 前缀守卫 — 从上下文中剥离
volatile-scratch消息,保持提示前缀跨轮次稳定 - 稳定工具排序 — 在
before_provider_request中按字典序排序tools(对齐 HarnessorderTools),工具顺序抖动不再击穿缓存 - 缓存破坏诊断 — 对
system+tools+messages三段做稳定 JSON + SHA-256 指纹,仅在既有前缀被改写时告警,正常追加不打扰;回落到任意仍存活的早期前缀视为有效命中(官方 unit 可并存);空/未知 payload 静默跳过 - 命中率遥测 — 从每次响应中按模型累计
cacheRead/input/cacheWrite/turns并持久化到磁盘;切模型不会把两个相差几十倍的单价混成一个数字 - 实时状态栏 — 每条消息后在 Pi 底栏显示当前模型的累计命中率与轮次
- ASCII 趋势图 — 使用
/cache-graph可视化全部模型累计口径的命中率趋势(平坦率特判 + 定点标签) - 成本节省估算 — 按模型目录(
models.json/ pi 内置目录)里当前模型的cost计价 —— 这才是官方与第三方部署都成立的真源。只有 provider 是官方deepseek时才额外套用官方 Models & Pricing 价目表与 peak/off-peak 时段;目录里零价的模型显示—而不是编一个数字。/cache-stats同时展示累计/本轮命中率、所用模型、单价来源、单价,以及按模型的明细 - 缓存友好的 compaction — 存在官方
deepseek-flash(legacy 名deepseek-v4-flash回退)时优先用它;否则当前模型本身已是flash档时直接用当前模型;否则用当前 provider 的flash档模型;再否则用当前模型。统一 temperature 0;结果按 SHA-256 缓存并跨会话复用(LRU 上限 64)。注意:这是独立冷调用,并非对路由请求的 verbatim 回放 —— 详见 §7.4 - 原子持久化 —
tmp + rename原子写入 + 防抖异步落盘 +session_start/session_shutdown生命周期保障,无撕裂 JSON /cache-reset— 一条命令清空所有统计、历史和摘要缓存
📦 安装
需要 Pi 和 Node.js ≥ 18。
pi install npm:pi-deepseek-cache
或从 Git 安装:
pi install git:github.com/ruanbw/pi-deepseek-cache
🚦 快速开始
- 确保已配置 DeepSeek provider(设置
DEEPSEEK_API_KEY)。 - 选择 DeepSeek 模型,如
deepseek/deepseek-v4-flash。 - 开始编码——扩展会自动激活并在底栏显示缓存统计。
export DEEPSEEK_API_KEY=sk-...
pi --model deepseek/deepseek-v4-flash
用的是第三方部署(OpenRouter / 自建端点 / 本地 router)?不需要 DeepSeek API Key,也不需要 deepseek provider —— 见下一节。
🔌 第三方部署(OpenAI 兼容)
所有钩子都与 provider 无关:遥测读的是端点自己上报的 usage.cacheRead / usage.input,前缀守卫与破坏诊断作用于即将发出的 wire payload。因此自建 vLLM/SGLang、托管 router、本地网关服务的 DeepSeek 模型同样适用 —— 扩展不关心你前面挂的是什么 provider id。
与官方 API 的差别:
| 层 | 官方 deepseek provider |
第三方部署 |
|---|---|---|
| 命中率遥测 | prompt_cache_hit_tokens / prompt_cache_miss_tokens |
端点只要上报命中 token(如 prompt_tokens_details.cached_tokens)就能测;完全不上报时每轮都看起来是 miss |
| 计价 | 官方价目表 + peak/off-peak 折半 | 取模型目录里当前模型的 cost,flat 价 —— 峰谷是 DeepSeek 的计费策略,不是宿主的。目录无价 → — 而非猜测 |
| 前缀守卫 · 工具字典序 · 破坏诊断 | 相同 | 相同 —— 字节稳定的前缀对任何前缀缓存实现都有益 |
| compaction 摘要器 | deepseek-flash(legacy deepseek-v4-flash 回退) |
当前模型已是 flash 档则直接用当前模型;否则取当前 provider 里第一个 flash 档模型(优先名字含 deepseek);再否则用当前模型 |
两条第三方宿主特有的注意事项:
- 实例亲和(instance affinity):自建引擎的 prefix cache 通常是按实例的,因此一次 miss 可能是“router 把这一轮发到了另一个副本”,而不是“前缀被改写”。破坏诊断分不出这两种情况。如果你的 router 能把会话固定到同一后端(magpie 通过
compat.sendSessionAffinityHeaders做到),保持开启。 - 计费跟随宿主,不跟随 DeepSeek:各宿主对缓存输入的折扣方式不同(打折 / 免费 / 完全不打折)。节省额按你在目录里配置的单价估算,因此请把
models.json里的cost.input/cost.cacheRead填准 —— 对非官方 provider,这是扩展唯一信任的计价依据。
// ~/.pi/agent/models.json —— 一个第三方 DeepSeek 部署
{
"providers": {
"my-router": {
"api": "openai-completions",
"baseUrl": "https://my-router.example/v1",
"apiKey": "...",
"models": [
{
"id": "deepseek-v4.1-flash",
"name": "DeepSeek V4.1 Flash (my-router)",
"contextWindow": 1000000,
"maxTokens": 384000,
"cost": { "input": 0.15, "output": 0.28, "cacheRead": 0.003, "cacheWrite": 0 },
},
],
},
},
}
pi --model my-router/deepseek-v4.1-flash
🧩 命令
| 命令 | 说明 |
|---|---|
/cache-stats |
弹窗显示命中率、缓存命中/未命中 token、轮次、预估节省、单价来源与按模型明细 |
/cache-graph |
弹窗显示全部模型累计口径的缓存命中率 ASCII 趋势图 |
/cache-reset |
重置所有统计、历史和摘要缓存(同时清除内存和磁盘) |
🔍 工作原理
| 层 | 说明 | Harness 谱系 |
|---|---|---|
| P1 — 遥测 | 在 message_end 事件中按模型(provider + id)累计 cacheRead / input / cacheWrite / turns,原子持久化到 ~/.pi/agent/extensions/deepseek-cache/stats.json(可用 PI_DEEPSEEK_CACHE_DIR 改目录);节省额按模型目录计价,仅官方 deepseek provider 额外套用官方价目表 + 峰谷时段 |
TokenUsage DISJOINT(translate.ts:mapUsage)—— input = prompt_tokens - cacheRead |
| P2 — 前缀守卫 | 在 context 中过滤 volatile-scratch;在 before_provider_request 中按字典序排序 tools;对 system + tools + messages 三段做稳定 JSON 指纹,与有界的已知前缀集合做包含检测 |
orderTools / sameSchema / canonicalHeader / headerEquals(packages/core/system-prompt、packages/core/session) |
| P3 — Compaction | 在 session_before_compact 时将历史序列化为单条 user 消息,temperature 0 做摘要:优先官方 deepseek-flash(legacy 名回退),否则当前 provider 的 flash 档,再否则当前模型;SHA-256 缓存(LRU 64)并原子持久化 |
无 parity —— Harness compaction-basic/summarizer.ts 会回放 system+tools+shadowed messages + 尾部指令以复用 warm 前缀;本路径是独立冷调用,取舍理由见 §7.4 |
📖 深入原理:前缀缓存原理 —— 官方 Context Caching on Disk(基于 SWA 的独立 cache prefix unit、3 种落盘时机、best-effort)+ Harness 五重强制 + Pi 映射
🛠️ 故障排查
- 缓存命中率低 → 通常是静态前缀在变化。避免在提示开头注入时间戳、随机 ID 或易变的工具输出。注意:增删扩展会改变
tools段,其破坏力与改写历史等同。 - "Cache prefix change" 警告 → 对话历史、工具集合或某个工具的 schema 被修改。检查是否有工具或扩展在变更过去的消息,或在轮次之间注册/注销工具。在第三方宿主上,它也可能只是 router 把你换到了另一个副本(见实例亲和)。
- 底栏无显示 → 确认已选择服务 DeepSeek 的模型,且其 provider 凭据可用。
- 第三方端点上命中率恒为 0% → 该端点不在
usage里上报命中 token,扩展无从测量。去查原始响应,不要怀疑扩展。 - 预估节省显示
—→ 模型在目录里没有可用的cost.input(免费 / 未标价)。给models.json里对应模型补上cost才会有数字。
🧪 测试
npm test # 80 个测试(70 单元 + 10 集成)
🤝 贡献
欢迎提交 Issue 和 PR!提交前请运行:
npm run lint # ESLint + Prettier 检查
npm test # 单元 + 集成测试
📄 许可证
MIT © ruanbw