prompt-cache-linter:定位 LLM 提示词缓存断点并估算漏计成本
AlexRyan92/prompt-cache-linter
prompt-cache-linter 是一个零依赖工具,用于找出 LLM 提示词缓存失效的确切字节位置、失效原因以及每次未命中带来的月度成本。它支持单条 payload 扫描秒级时间戳、UUID、epoch 时间戳等易变模式,也支持对比两条连续 payload 做字节级比较,定位分歧点并统计断点下方被浪费的稳定内容。
Find the exact byte where your LLM prompt cache breaks, what broke it, and what the miss costs per month. Zero dependencies.
Prompt caches (Anthropic, OpenAI, Gemini) match exact byte prefixes. One timestamp at the top of a system prompt silently re-bills your entire prefix on every request, at full price, forever. Teams discover this months later, in the invoice, if they discover it at all.
Two modes:
- Single payload: scan for volatile patterns (second-level timestamps, UUIDs, epoch stamps, trace IDs, wall-clock times), each with its position, severity, and the fix.
- Two consecutive payloads (the honest test): byte-level comparison finds the exact divergence point, then a re-sync pass measures the stable content stranded below the break, the span that would cache if the volatile bytes above it moved. Serialization drift (key order, escaping) that no pattern scanner can see shows up here.
There is a hosted version that runs entirely in the browser, and the economics are worked through in Prompt Caching Is Free Money.
Install
npm install prompt-cache-linter # library npx cache-lint --help # CLI, no install
CLI
Capture two consecutive request bodies from the same session (an HTTP proxy, your SDK's debug logging, or your gateway's request log), then:
$ npx cache-lint request-a.json request-b.json --rpd 5000 --price 4 mode: dual cache ceiling: 22 of 222 tokens (9.9%) breaks at: character 89 stranded: ~181 stable tokens below the break cost of miss: $97.74/month at 5,000 req/day findings: [break] second-level timestamp at 58: "2026-10-11T04:22:17Z" Changes every request. Drop it, coarsen to a date, or move it below the stable prefix.
Exit code is 2 when a break-severity finding exists, so it slots into CI:
- run: npx cache-lint fixtures/request-a.json fixtures/request-b.json
Library
import { lint, scan, commonPrefixLength, stableBelow } from 'prompt-cache-linter'; const report = lint(requestA, requestB, { requestsPerDay: 5000, inputPricePerM: 4 }); report.cacheCeilingTokens; // tokens that can ever hit the cache report.divergenceIndex; // exact character where the prefix breaks report.strandedStableTokens; // identical content below the break report.monthlyCostOfMiss; // USD/month left on the table report.findings; // [{ name, severity, index, match, advice }]
Assumptions, stated plainly
Tokens are estimated at ~4 characters each. The cost model uses Anthropic-style economics (cache reads at 10% of input price, the default cacheDiscount: 0.9); OpenAI caches automatically at a smaller discount, so treat dollar figures as an upper bound there. The location of the break is provider-independent. Pattern scanning is necessary but not sufficient: your SDK's serialization order only shows up in the two-payload comparison, which is why that mode exists.
License
MIT © Alexander Ryan
来源:GitHub · github.com