简介:本文从成本视角拆解大模型推理中的缓存命中机制:解释KV缓存与前缀复用的技术原理,说明缓存输入、未缓存输入与输出token的计费差异,分析前缀一致性、缓存时效、显存调度等影响因素,并给出命中率监控口径、prompt结构设计、成本归因等可落地的评估方法与优化清单,帮助多轮对话与Agent场景有效控制token支出。
在使用大模型API或自建推理服务时,token费用通常是最主要、增长最快的成本项。本文聚焦其中一个直接决定账单水平的机制,缓存命中:它是什么、为什么能把一部分输入token的计费降到普通输入的若干分之一、哪些因素决定你能拿到多少折扣,以及如何围绕它建立成本评估与优化体系。
需要先明确两点判断框架。第一,缓存命中优化的不是“单价”,而是“重复前缀的有效价格系数”——同样的输入token,命中缓存的部分按折扣价计费,未命中的部分按全价计费,因此成本分析的第一步是搞清楚账单里两类输入的占比结构。第二,评估这项成本不能只比单价贵不贵,要结合对话轮次深度、会话并发量、Agent任务的上下文规模、输出长度占比和业务增长预期综合判断;同样的命中率提升,在长上下文Agent场景中带来的节省,可能与短对话场景相差一个数量级。
这些场景的共同特征是:输入中存在“逐token完全一致的长前缀”。反之,一次性短问答场景命中空间有限,成本重点应放在输出长度控制上。
自回归模型每生成一个token前,都要对整个输入做一次前向计算,这一步称为prefill。prefill会在模型的每一层为输入的每个位置生成两份张量,Key与Value,合称KV缓存。后续token的Query直接检索这些已算好的K和V,历史部分的中间结果不必重算。KV缓存的物理本质,就是这段前缀已经算出的中间状态,它是一种“算力换显存”的资产:第一次计算的钱以张量形式存下来,后续请求直接取用。
KV缓存能跨请求复用,依赖因果注意力的一条性质:第i个位置的K和V只由它之前的token决定。由此得到缓存匹配的硬规则:只有从第一个token起逐个相同的前缀才能复用;序列中任何一处不同,其后的所有KV全部作废。语义上再接近的两个prompt,只要token序列不一致,就完全不命中。这条规则是全部缓存成本优化的物理基础,所有优化动作最终都收敛为一件事:让重复发送的内容在token序列上严格前置一致。
第一,客户端每轮重发完整历史,并不意味着服务端“多收了钱”,重发历史中与缓存一致的部分按折扣价计费;上一轮的回复要到下一轮作为输入重发时才进入可缓存范围;本轮正在生成的输出始终按输出价计费。第二,市面上另有一类按语义相似度直接返回旧答案的“语义缓存”,那属于检索系统,答错风险取决于检索质量,与这里的精确前缀复用是两套机制。从成本角度看:前缀缓存省的是重复输入的计费;语义缓存可能连输出费用一起省,但引入了正确性风险,需要单独评估质量成本。
调用商业API场景的账单可拆为三块:
| 成本项 | 计费逻辑 | 缓存的影响 | 成本特征 |
|---|---|---|---|
| 未命中输入token | 按输入全价计费 | 无 | 随前缀不一致程度线性增加 |
| 命中输入token | 按折扣价计费(部分主流服务约为普通输入价的十分之一量级) | 复用已计算前缀 | 优化主战场,随命中率提升线性下降 |
| 输出token | 按输出价计费,通常不低于输入价 | 不受影响 | 由生成长度决定,缓存无法降低 |
基于前述实测形态可以得出定性结论:当历史前缀约119K、新增约875 token、前缀绝大部分命中时,输入侧的有效均价被大幅拉低;若前缀全部未命中,同样的请求输入费用可高出数倍。这说明同样的token量,账单可能因命中率不同而差出一个量级,这也是为什么“只比单价”会严重误判成本。
自部署推理服务时,成本结构换了一层皮,但逻辑相同:
| 成本项 | 与缓存的关系 |
|---|---|
| GPU计算成本(实例费用、折旧、电力) | 命中减少重复prefill计算量,直接缩短单请求处理时间 |
| 显存成本 | KV缓存驻留显存换取复用,命中率的代价是显存占用 |
| 运维人力成本 | 缓存管理、逐出策略、显存规划带来的复杂度 |
主流自部署推理引擎普遍采用分页式KV缓存管理:把KV显存切成固定大小的块(常见块大小为16 token量级),请求按块申请,通过逻辑块表映射到不连续的物理块,从而缓解显存碎片。这层设计的成本意义在于:显存利用率决定了缓存能驻留多少前缀,进而决定并发场景下的实际命中率。
第一步,建立用量口径。 每轮请求的token用量应拆成三项:未命中输入、命中输入、输出。缓存命中率定义为命中输入token占总输入token的比例。不要凭感觉估算,主流大模型服务的用量返回接口中普遍提供缓存命中输入token的计数字段(不同厂商字段命名不同),评估一律以该字段实测值为准,因为命中与否取决于服务端缓存状态,客户端无法事先断定。
第二步,建立成本模型。 单次请求成本 ≈ 未命中输入 × 输入单价 + 命中输入 × 输入单价 × 折扣系数 + 输出 × 输出单价。用它做两件事:复盘过去账单(用实测字段回填),以及预测未来成本(用命中率50%、80%、归零等多种假设做敏感性分析)。
第三步,区分固定与弹性成本。 system prompt、工具定义等稳定前缀属于“可缓存固定成本”,其有效价格取决于命中率;新增用户消息和输出属于弹性成本,随业务量增长。
第四步,设置监控与预算。 核心指标包括:命中率随会话轮次的变化曲线、单会话累计成本、单任务成本(Agent场景)。为会话或任务设置成本上限和告警线;命中率异常下跌往往是prompt结构被无意改动的信号,应触发排查。
第五步,成本归因。 按业务线、应用、场景维度归集token费用,定位哪个场景的前缀浪费最严重,把优化力气花在占比最大的地方。
原则:稳定内容前置,动态内容后置。
messages = [{"role": "system", "content": SYSTEM_PROMPT}, # 固定:职责、规则、工具定义*history, # 追加式:历史消息生成后不再改写{"role": "user", "content": current_user_input}, # 动态:本轮新内容放最后]
反模式:在system prompt开头插入当前时间、随机会话ID,或对检索结果做不稳定排序,这些“看似无害”的字段会把后面十几万token全部打回全价。
滑动窗口截断和摘要压缩可以缩短历史,但要清楚代价:截断点之后的历史一旦改写,前缀一致性被破坏,缓存优势部分或全部失效。“截断省token”与“保持前缀稳定省折扣”之间存在权衡,应基于实测字段做对比测算后再定策略,而不是默认压缩一定省钱。
在缓存有效期内发起后续轮次;重试请求保持完全相同的输入序列;批量任务把共享模板放在最前面、变量统一放尾部,使集群内多个请求共享同一段前缀缓存。
按并发规模规划显存,避免缓存因显存不足被频繁逐出;利用分页式KV缓存管理降低碎片;对多轮会话采用“输出KV续接在同一序列、下一轮前缀与上一轮完全对齐”的续接策略,使命中范围延伸到上一轮回复的末尾,引擎只对新增尾部做prefill。
缓存管不了输出,输出成本要单独治理:约束生成长度、要求结构化输出、为输出token设置单独预算。
缓存命中不只是省钱:prefill计算量减少直接缩短首token延迟,同一份优化同时改善成本和响应速度,这是它优于多数降本手段的地方。但要注意三个平衡点:其一,为追求命中把大量低相关内容堆进稳定前缀,会让折扣价的输入基数膨胀——折扣价仍是价格不是免费,前缀应稳定且精简;其二,压缩历史换取短输入可能击穿缓存,需测算净效果;其三,语义缓存看似省钱更多,但返回错误答案的质量成本(返工、用户流失)可能远超token节省,只在容错较高的场景才值得启用。
token成本的本质是“结构 × 价格 × 用量”三者相乘,缓存命中改写的是其中的结构项:它让重复前缀的有效价格系数显著低于1。围绕它的成本治理可以归纳为五条原则:把稳定内容前置、让历史追加式生长;用实测的命中计数字段建立口径,而不是靠假设;用敏感性分析做预算,给命中率留最坏情况余量;对输出成本单独设防;把每次优化建立在回归验证之上。对多轮对话与Agent这类长上下文业务,这套治理往往决定了成本曲线是随规模线性恶化,还是被长期稳定压平。