3.2 语义缓存:把推理算力砍掉一两成的杠杆 1.3 节的反模式三讲过一个案例:一个团队只做精确缓存,命中率长期低于 1%,推理成本始终降不下来。我接手后做的第一件事就是上线语义缓存,两周内命中率爬到 22%,单请求成本降了将近 20%。团队的反应是"这么有效,为什么我们之前没做"——答案是他们被"用户问题千差万别"这个直觉误导了,以为缓存没用。但真实数据是:在 FAQ、客服、通用知识这些场景里,意图相同的请求比例远超想象,只是问法不同而已。 这一节讲透语义缓存的原理、命中率优化、失效机制,以及它的收益模型。对百万级平台来说,这是每年能省下数百万甚至数千万 GPU 成本的杠杆,值得认真对待。 语义缓存的原理 先说清楚为什么精确缓存几乎没用。
1.3 节的反模式三讲过一个案例:一个团队只做精确缓存,命中率长期低于 1%,推理成本始终降不下来。我接手后做的第一件事就是上线语义缓存,两周内命中率爬到 22%,单请求成本降了将近 20%。团队的反应是"这么有效,为什么我们之前没做"——答案是他们被"用户问题千差万别"这个直觉误导了,以为缓存没用。但真实数据是:在 FAQ、客服、通用知识这些场景里,意图相同的请求比例远超想象,只是问法不同而已。
这一节讲透语义缓存的原理、命中率优化、失效机制,以及它的收益模型。对百万级平台来说,这是每年能省下数百万甚至数千万 GPU 成本的杠杆,值得认真对待。
先说清楚为什么精确缓存几乎没用。精确缓存要求请求字符串完全一致才命中,但真实用户的提问千变万化。"今天天气怎么样""今天天气如何""今天天气好不好",字面完全不同,精确缓存全部 miss。哪怕同一意图有一万种问法,精确缓存也只能命中其中一种。
语义缓存的思路是:把每个请求用 embedding 模型编码成向量,新请求来时与历史请求的向量做相似度检索,相似度高于阈值则认定"语义等价",直接返回历史响应。这样"今天天气怎么样"和"今天天气如何"虽然字面不同,但 embedding 向量很接近,相似度超过阈值,就命中了同一个缓存响应。
整个流程是:新请求进来先 embedding 编码,然后到向量库里做相似度检索,命中(相似度高于阈值)就直接返回历史响应,不调用推理;未命中才走正常推理,推理完把响应连同 embedding 一起存入缓存供下次复用。
关键变量有两个:embedding 模型的质量(决定"语义等价"判断得准不准),以及相似度阈值(决定"敢不敢认定等价")。这两个参数直接决定命中率和误命中率,是语义缓存调优的核心。
命中率是语义缓存的生命线。影响它的因素有几个,逐一说怎么优化。
阈值低,命中的请求多,但误命中风险高——两个其实不等价的请求被判为相同,返回了"似是而非"的响应,这比不命中还糟(不命中至少走正常推理,误命中是直接返回错误答案)。阈值高,命中少,但准确。
我的建议是起步用较高阈值(比如 cosine 相似度 0.92 以上),宁可少命中也不要误命中。等系统跑稳了、有了足够的命中/误命中数据,再逐步下调阈值寻找最优平衡点。千万不要一上来就追求高命中率把阈值调低——误命中伤的是用户体验和信任,远比少省点成本严重。
通用 embedding 模型对垂直领域的语义捕捉可能不准。比如医疗场景里"心梗"和"心肌梗死"是同一个意思,但通用模型可能觉得它们的语义距离不近。垂直场景要考虑用领域数据微调 embedding 模型,或者用更大的模型。embedding 模型的成本(每次编码要一次推理)也要权衡,太重的 embedding 模型本身会吃掉一部分省下来的成本。
不同业务场景的命中率差异巨大。FAQ 类、通用知识类,重复意图多,命中率可达 30% 甚至更高。个性化、时效性强的场景("我的订单状态""现在几点"),命中率极低,缓存意义不大。
策略上要分级:高命中场景激进缓存(高阈值、长 TTL),低命中场景不缓存或短 TTL。强行对低命中场景上语义缓存,省下的成本可能还不够 embedding 编码的开销,得不偿失。
缓存不是"命中就万事大吉",还要保证不返回过期或错误的响应。这是语义缓存最容易翻车的地方,很多团队上线后出过"返回旧版本答案"的事故,根因都是失效机制没做好。
模型升级失效。换新模型版本时,旧响应缓存要整体失效,否则用户问一个问题,拿到的是上一个版本模型的回答,质量特征都不对。实现上给每条缓存打上 model_version 标签,请求来时只查匹配当前版本的缓存。
prompt 模板变更失效。系统提示词改了(比如调整了模型的人设),旧缓存也要失效,因为同样的用户问题,在不同系统提示词下答案应该不同。给缓存打上 prompt_hash 标签,prompt 变了 hash 就变,自动失效。
时效性失效。涉及实时信息的请求("今天天气""最新新闻"),短 TTL 或干脆不缓存——这类请求的答案随时间变化,缓存命中反而是错的。给这类请求标记 no_cache 或设很短的 TTL(比如 5 分钟)。
失效机制要自动化,不能靠人工记得"今天换了模型要去清缓存"。把失效条件编码进缓存键(model_version + prompt_hash + TTL),任一变化就自然失效,是最稳的做法。
缓存命中率与成本节省的关系,可以用一个公式量化:
单请求成本节省 ≈ 命中率 × (1 - 缓存响应成本/推理成本)
缓存命中的请求,其服务成本只有一次向量检索加返回(相比推理可以忽略,约为推理成本的 1% 到 2%)。所以:
这就是"命中率每升 10 个百分点,成本下降约 8% 到 10%"的由来。对一个年 GPU 成本 5000 万的百万级平台,把命中率从 5% 做到 25%,每年能省下近 1000 万。这是任何其它优化都难以匹敌的杠杆——不需要换模型、不需要换硬件、不需要改业务,只是把已经算过的响应复用起来。
最后说几个上线后容易踩的坑。
误命中伤体验。返回了"似是而非"的响应,用户会觉得"这个 AI 怎么答非所问"。对策是较高阈值加命中后校验——对高敏感场景(如医疗、法律),命中后再做一次轻量校验(比如关键词匹配),校验通过才返回缓存。embedding 成本。每个请求要一次 embedding,虽然比推理便宜得多,但海量请求下也是成本,要监控 embedding 服务的负载。隐私合规。缓存用户请求的 embedding 和响应,要注意 PII(个人身份信息)合规——脱敏后再缓存,或对敏感请求不缓存,避免用户隐私数据沉淀在缓存库里。
冷启动问题。新上线的语义缓存,库里还没积累足够的历史请求,命中率会有一段"冷启动期"很低。这是正常的,跑几天数据积累后命中率会自然爬升。不要因为头几天命中率低就否定它。
语义缓存用 embedding 相似度匹配,把"问法不同但语义等价"的请求导向历史响应,命中率 15% 到 30%,是高并发平台打平成本的关键杠杆。调优的核心是相似度阈值(宁高勿低)、embedding 质量(垂直场景要微调)、按场景分级缓存。必须配套失效机制(model 版本、prompt 版本、TTL),否则会返回过期答案。
至此第三章完成。上下文压缩(3.1)和语义缓存(3.2)构成了降本的两条腿——前者降低单次请求的 prefill 算力,后者减少需要推理的请求数。两者结合,能让百万级平台的推理成本结构发生质变。
下一章讲模型路由与编排——如何把请求智能分发到不同模型,这是在缓存之外的另一层降本与体验优化手段。