Prompt 缓存,一次讲明白:TaoToken 统一 Key 下的配置骨架与验证
1. 为什么你的 Agent 每轮都在交“重复税”先说一个我观察到的现象很多人用 Cline 写代码前几轮响应很快聊到二三十轮之后明显感觉变慢账单也涨得离谱。打开用量明细一看输入 token 是输出 token 的几十倍。这不是模型变笨了而是每一轮请求都在把同一段内容重新算一遍。这段被反复计算的内容就是 prompt 里的静态前缀。系统提示词、工具定义、项目上下文、行为规范这些在同一个会话里基本不变但每次请求都会完整地送进模型模型也要完整地做一遍 prefill。一个两万 token 的系统提示词跑五十轮就是一百万 token 的重复计算。你为它付了钱它没有产出任何新东西。Prompt 缓存要解决的就是这件事。它把静态前缀对应的 Key-Value 张量存下来下一轮请求如果前缀哈希一致直接读取缓存跳过重复的 prefill 计算。计费上缓存读取通常只有基础输入价格的一个零头写入会略贵一点但只要命中率够高整体成本能压下来一大截。这篇面向的是用 Cline、CC Switch 这类工具做日常开发的读者。我会先讲清楚缓存命中的判定逻辑再给出 TaoToken 统一 Key 下的 settings.json 和 config.toml 配置骨架最后用一次真实请求验证缓存到底有没有生效。你跟着配一遍就能看懂自己项目里哪些设计在偷偷破坏缓存。2. 缓存命中的底层逻辑前缀哈希决定一切要配好缓存先得接受一个反直觉的规则内容一样但顺序不同缓存就是 miss。因为缓存匹配靠的是前缀文本的哈希顺序一变哈希就变整段前缀重新计算。一次 LLM 推理分两个阶段。Prefill 阶段处理完整输入对每个 token 做矩阵计算建立内部表示这一步是计算密集的也是最贵的。Decode 阶段逐个生成输出 token更偏内存读取。缓存优化的对象就是 prefill。Transformer 在 prefill 时会给每个 token 生成 Query、Key、Value 三个向量。关键在于Key 和 Value 只依赖它之前的 token。也就是说只要某段前缀不变它对应的 KV 张量就不需要重算。没有缓存时请求结束这些张量就被丢弃下次请求从头再来。有缓存时基础设施按前缀哈希索引这些张量命中就直接取回。由此推出三条必须遵守的纪律。第一不要在会话中途增删工具工具定义属于缓存前缀改了后面基本全废。第二不要中途切换模型缓存和模型绑定换模型等于重建。第三不要通过改系统前缀来传递状态正确做法是把状态提醒追加到最新的用户消息里让前缀保持稳定。判断缓存是否生效看响应里的三个字段cache_creation_input_tokens是写入缓存的量cache_read_input_tokens是从缓存读取的量input_tokens是正常处理的输入量。缓存效率就看读取量和写入量的比例这个指标应该像监控在线率一样持续盯着。3. TaoToken 前置统一 Key 与接入地址TaoToken 在这里扮演的角色是统一入口。你不需要为每个工具单独维护一套密钥和地址用同一个 Key 走同一条 API 通道Cline、CC Switch 以及你自己写的脚本都能接进来。这样缓存策略、用量统计、模型切换都在一个地方管排查缓存问题时不用在多个平台之间来回对照。需要提前准备的东西不多一个 TaoToken 账号一个 API Key以及你要接入的工具。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基础地址是 https://taotoken.net/api 注意这个地址不带查询参数。API Key 在控制台生成地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。生成之后先复制保存页面刷新后不一定还能看到完整值。如果你还没决定用哪个模型可以先去模型对话页面试一下 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 确认模型可用再写进配置。有一点要提醒缓存是模型侧的能力TaoToken 负责把请求正确转发过去但前缀稳不稳定取决于你怎么组织 prompt。配置只是通道纪律才是缓存命中的关键。4. 可复制配置骨架settings.json 与 config.toml下面给两份骨架一份给 Cline 这类走 JSON 配置的工具一份给走 TOML 的工具。字段名以你实际使用的版本为准重点是结构把稳定内容放前面把易变内容放后面并且不要在会话中途动前缀。先看settings.json{ apiProvider: openai-compatible, apiKey: sk-你的TaoToken密钥, baseUrl: https://taotoken.net/api, model: claude-sonnet-4-5, systemPrompt: 你是一个严谨的编码助手。以下是项目约定\n- 使用 TypeScript 严格模式\n- 提交信息遵循 Conventional Commits\n- 不要引入新的运行时依赖, tools: [ read_file, write_file, run_command, search_code ], enablePromptCache: true, cacheTtl: 1h }这里systemPrompt和tools都属于静态前缀写进去之后整个会话不要改。enablePromptCache打开缓存cacheTtl设成一小时适合一次连续编码会话。如果你用的是扩展缓存价格会更高命中率不够时反而不划算先按默认来。再看config.toml[provider] name taotoken base_url https://taotoken.net/api api_key sk-你的TaoToken密钥 model claude-sonnet-4-5 [cache] enabled true ttl 1h auto_breakpoint true [prompt] system 你是一个严谨的编码助手。 项目使用 TypeScript 严格模式。 提交信息遵循 Conventional Commits。 tools [read_file, write_file, run_command, search_code]auto_breakpoint true让缓存断点随对话推进自动前移省去手动计算 token 边界。手动设边界很容易错边界错了就吃不到缓存所以能用自动就用自动。两份配置的共同点是系统提示词和工具列表固定对话历史和工具输出放在动态尾部。你组织自己的 Agent 时也照这个顺序最上面系统指令接着工具定义然后检索到的上下文最底部才是对话历史和工具输出。5. 验证请求看三个字段确认缓存命中配置写完不算完得用一次真实请求确认缓存真的生效了。最直接的办法是连续发两次相同前缀的请求对比响应里的缓存字段。先发第一次请求建立缓存curl https://taotoken.net/api/v1/messages \ -H Content-Type: application/json \ -H x-api-key: sk-你的TaoToken密钥 \ -H anthropic-version: 2023-06-01 \ -d { model: claude-sonnet-4-5, max_tokens: 256, system: [ { type: text, text: 你是一个严谨的编码助手。项目使用 TypeScript 严格模式。, cache_control: {type: ephemeral} } ], messages: [ {role: user, content: 用一句话说明这个项目的编码规范。} ] }第一次响应里cache_creation_input_tokens应该是一个大于零的值说明前缀被写入了缓存。cache_read_input_tokens此时通常是零。紧接着发第二次请求前缀完全一样只改用户消息curl https://taotoken.net/api/v1/messages \ -H Content-Type: application/json \ -H x-api-key: sk-你的TaoToken密钥 \ -H anthropic-version: 2023-06-01 \ -d { model: claude-sonnet-4-5, max_tokens: 256, system: [ { type: text, text: 你是一个严谨的编码助手。项目使用 TypeScript 严格模式。, cache_control: {type: ephemeral} } ], messages: [ {role: user, content: 再补充一条关于测试的要求。} ] }第二次响应里cache_read_input_tokens应该明显大于零而cache_creation_input_tokens接近零。这就说明前缀命中了缓存只有新增的用户消息在正常计费。如果第二次的读取量还是零说明前缀没对上回去检查系统提示词和工具列表有没有被改动。实测下来把系统提示词和工具定义固定住之后连续对话的缓存读取比例能稳定在很高的水平输入成本下降非常明显。这个验证动作建议每换一次配置就做一遍别等账单出来才发现缓存一直没命中。6. 本篇常见错排查缓存不命中绝大多数不是通道问题而是前缀被破坏了。下面几个是我踩过的坑按出现频率排。第一个会话中途改了工具列表。Cline 里有时候会自动加载新工具或者你手动勾选了一个之前没选的工具工具定义一变前缀哈希就变后面全部 miss。解决办法是会话开始前把要用的工具一次性选好中途不要动。第二个系统提示词里塞了动态内容。比如把当前时间、当前文件路径、随机 ID 写进系统提示词每次请求都不一样缓存永远建不起来。这类信息应该放到用户消息里不要污染前缀。第三个中途切换模型。缓存和模型绑定从 Sonnet 切到别的模型整段缓存作废。如果你确实要换模型接受这一次重建的成本别指望还能命中。第四个手动设缓存断点设错了位置。断点应该落在稳定前缀的末尾落在动态内容中间等于白设。能用auto_breakpoint就别手动算。第五个为了压缩上下文直接改系统提示词。正确做法是保持系统提示词、工具、对话历史不变把压缩指令作为一条新消息追加进去这样前缀还能复用只有压缩指令本身按新 token 计费。排查顺序建议这样先看响应里的cache_read_input_tokens是不是零是零就检查前缀有没有变前缀没变再看模型有没有换都没问题再检查断点位置。按这个顺序走基本能定位到原因。7. 把缓存纪律固化进你的工作流配置和验证都跑通之后剩下的是习惯问题。我自己的做法是把系统提示词和工具列表当成项目资产来管理写进版本控制改动要走 review避免随手加一句话就把缓存搞崩。Cline 的会话尽量一次开长一点频繁开关会话等于频繁重建缓存省下来的钱又还回去了。如果你要长期跑编码任务或者 Agent 工作流可以考虑用 Coding Plan 把用量和缓存策略统一管起来入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。接入细节和字段说明看文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite API Key 在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 管理。用 Claude Code 的话Anthropic 相关配置参考 https://taotoken.net/claude-code?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_codeutm_campaignrewrite 。缓存不是打开开关就完事的功能它是一种架构纪律。前缀稳定、工具稳定、结构稳定命中率自然就上去了。你省下的每一分重复计算都是实打实的成本。