Mini Prompt Cache¶
从 CC 真实的 services/api 缓存逻辑(散落在多个文件、~800 行)提炼到 ~150 行 的 prefix-matching + TTL + 持久化 cache。 6 个测试场景:miss+write / full hit / prefix hit / 不同 prefix / TTL 过期 / 持久化。
文件¶
mini-prompt-cache/
├── src/
│ ├── cache.ts PromptCache + hashMessages + findPrefixHash(~150 行)
│ └── cli.ts 6 测试场景
└── README.md
跑¶
真实代码对照¶
| Demo | 真实代码 | 简化 |
|---|---|---|
hashMessages |
claude.ts:300 cacheKey |
SHA256 前 16 chars |
findPrefixHash |
claude.ts:400 lookup (~100 行) |
8 行 |
get() |
claude.ts:500 get |
prefix loop + TTL check |
| 4 级缓存 | cache.ts:1200 (~600 行) |
1 级内存 + 1 持久化 |
核心 4 件套¶
1️⃣ Hash = SHA256(stable stringify)¶
const stable = messages.map((m) => `${m.role}:${m.content}`).join("\n");
return sha256(stable).slice(0, 16);
2️⃣ Prefix matching = "拿 [0..i] 一段段试"¶
for (let i = messages.length; i >= 1; i--) {
const prefix = messages.slice(0, i);
const key = hashMessages(prefix);
if (cache.has(key)) return key; // 找到最长匹配
}
3️⃣ TTL = 懒清理¶
懒清理:不在后台跑 timer,而是在 get 时检查过期。简单 + 0 资源占用。4️⃣ LRU 简化 = "满了删第一个"¶
if (this.memory.size >= this.maxEntries) {
const oldestKey = this.memory.keys().next().value; // Map 保持 insertion order
this.memory.delete(oldestKey);
}
6 个场景输出¶
📌 Test 1: miss + write
📌 Test 2: full hit (same messages)
hit prefix=2/2 → "4"
📌 Test 3: prefix hit (extend conversation)
hit prefix=2/3 → "4" (reused 2+2 answer)
📌 Test 4: different prefix → miss
miss? ✅
📌 Test 5: TTL expiration (1ms TTL)
expired? ✅
📌 Test 6: persist to disk + reload
saved to /tmp/mini-prompt-cache.json
loaded 2 entries from disk
reload hit? value="answer6"
与 mini-query-engine 组合¶
把 cache 加到 engine 之前:
// engine.ts
async *run(messages) {
const cached = this.cache.get(messages);
if (cached) {
yield { type: "text", text: cached.value };
return; // 完全跳过 API!
}
// ... 调 API
this.cache.set(messages, response);
}
省 token 成本 60-80%(实测 CC 命中率)。
进阶练习¶
- 加 LRU 真实版:用 doubly-linked-list + Map
- 加 4 级缓存:内存 / 磁盘 / Redis / 远程 registry
- 加 cache stampede 防护:单飞(singleflight)防止并发击穿
- 加 negative caching:失败响应也缓存(短期)
- 加 cache key compression:用 LZ4 压缩大 messages
- 加 invalidation by tag:删除某个 tag 的所有 cache
相关阅读¶
- topics/prompt-cache.md —— 跨阶段专题
- topics/cache-strategies.md —— 缓存策略
- topics/deep-dive-claude-api.md —— 3419 行 API 拆解
- mini-query-engine/ —— 可与本 demo 组合
- Anthropic Prompt Caching 文档