这个坑值得记一笔。自建的语义缓存用 text-embedding-3-small 做向量,余弦阈值定 0.92,用户问「退款要几天」时直接返回了「退货要几天」的缓存答案,客诉翻日志才发现两条查询相似度是 0.931。

刚开始盯着「缓存键没带业务线和商品类目」,折腾了半个多小时,排掉了。

真正麻烦的地方在于,几个原因的现象几乎一样。短句的向量区分度低,0.92 对八字以内的问句太松;缓存前没做意图分类,退款和退货落在同一簇;向量库里混着上一版提示词生成的答案。

第一件事是把两条查询的相似度和各自命中的 key 打出来

python -c "import numpy as np;a=np.array([1,0]);b=np.array([0.93,0.37]);print(round(float(a@b/np.linalg.norm(b)),3))"

随后按业务线给缓存键加前缀再统计命中率

redis-cli --scan --pattern 'sem:*' | Measure-Object

再往下把阈值提到 0.97 观察两周的命中率变化

python -c "print('threshold 0.97')"

末了对高风险意图直接跳过缓存

python -c "risk={'退款','赔付','投诉'};print('bypass',len(risk))"

改完先别撤,确认这几项。「退款要几天」不再命中退货的答案,缓存命中率从 41% 降到 33%,误命中样本清零,人工抽检 200 条没有新的错答。

补一句。语义缓存的阈值不是越高越安全,长句可以放宽短句必须收紧,涉及金额和时效的意图最好直接绕过缓存。线上能稳定复现的问题都不算难,难的是只在大流量下才出现的那种。

作者 9823189