跳转至

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

npm install && npm start

真实代码对照

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);
为什么 stable:不同 insertion order 的相同 messages 必须 hash 相同。slice(0, 16):节省内存(碰撞概率 ~10⁻¹⁹ 仍然安全)。

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;  // 找到最长匹配
}
为什么:用户加新消息前,对话前缀已 cache。重命中率可 60-80%

3️⃣ TTL = 懒清理

if (Date.now() > entry.expiresAt) {
  cache.delete(key);
  this.metrics.expirations++;
  continue;
}
懒清理:不在后台跑 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);
}
简化版 LRU:真实用 doubly-linked-list 实现,本 demo 借 Map 的 insertion order。

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 命中率)。

进阶练习

  1. 加 LRU 真实版:用 doubly-linked-list + Map
  2. 加 4 级缓存:内存 / 磁盘 / Redis / 远程 registry
  3. 加 cache stampede 防护:单飞(singleflight)防止并发击穿
  4. 加 negative caching:失败响应也缓存(短期)
  5. 加 cache key compression:用 LZ4 压缩大 messages
  6. 加 invalidation by tag:删除某个 tag 的所有 cache

相关阅读