RAG缓存问答结果,相同用户问题直接返回缓存降低消耗

很多RAG问答服务上线后,都会遇到同一个问题:用户反复问相似甚至完全一样的问题,系统每次都去向量库检索、拼Prompt、再调用LLM生成答案。
这个过程既慢又烧Token。
其实对重复问题完全可以把第一次生成的答案缓存起来,下次直接返回缓存结果,消耗能降一大截。
本文用Redis做缓存,给你一套可直接照做的实现方案。

先判断你的场景适不适合加缓存

RAG缓存不是万能药,它最适用于高频重复提问的场景。
比如企业内部知识库问答、客服FAQ、产品使用手册问答,用户问来问去就那么几十个问题。
如果你的问题是长尾、几乎没有重复的开放性提问,缓存命中率会很低,加缓存的意义不大。

判断标准很简单:在日志里统计相同问题的重复次数,如果重复率超过20%,就值得加缓存。缓存不仅省Token,还能让响应时间从几秒降到几十毫秒,体验提升明显。

开始前需要准备什么

操作前先确认环境里已有这些组件:

  • 一个可用的RAG问答服务,不管你自己写的还是用的LangChain之类的框架。
  • Redis数据库,如果还没有,可以直接用宝塔面板装一个,或者跑Docker命令:
  docker run -d --name redis-rag -p 6379:6379 redis:7
  • Python环境,准备安装redishashlib库(后者是标准库)。

核心思路是:用问题内容的哈希值作为缓存键,如果两次问的问题完全相同,哈希值一样,就能直接命中缓存。

用Redis给RAG问答加缓存的具体步骤

下面以Python为例,改造你的RAG问答函数。
改造前,你的代码大概长这样:

# 原来的问答函数
def ask_question(question):
    context = search_vector_db(question)  # 从向量库检索
    answer = call_llm(question, context)  # 调用LLM生成答案
    return answer

改造后,加上缓存逻辑:

import redis
import hashlib

r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)

def get_cache_key(question):
    # 把问题文本转成MD5,保证同样的问题得到同样的键
    return f"rag:q:{hashlib.md5(question.encode('utf-8')).hexdigest()}"

def ask_question_with_cache(question, ttl=86400):
    cache_key = get_cache_key(question)

    # 1. 先查缓存
    cached = r.get(cache_key)
    if cached:
        print("[缓存命中]")
        return cached

    # 2. 没命中才走原来的RAG流程
    context = search_vector_db(question)
    answer = call_llm(question, context)

    # 3. 把答案存入缓存,设置24小时过期
    r.setex(cache_key, ttl, answer)
    print("[缓存未命中,已写入]")
    return answer

这里的关键点是:问题文本必须严格一致才能命中缓存
如果你希望连“怎么重置密码”和“如何重置密码”这种相似问题也能命中,那就得升级成语义缓存,用Embedding把问题向量化后存进向量数据库,计算相似度来匹配。
不过语义缓存复杂度高,建议先跑通精确缓存,再看实际重复率决定要不要做。

避坑指南:这几件事一定要注意

缓存键不要用原始问题文本直接当Key。问题文本可能很长,直接当Key浪费Redis内存,而且容易超出Key长度限制。
用MD5或SHA-1哈希后当Key更稳妥。

答案里如果包含时间、用户昵称等动态信息,不适合缓存。比如“今天天气”这种答案每天都在变,TLL要设短一点,甚至不缓存。

要在日志里加命中标记,方便后续统计。就像上面代码里打印的[缓存命中],生产环境建议记录到结构化日志里,方便用Grafana或者Kibana看命中率。

缓存雪崩问题也需要注意。如果你把缓存过期时间都设为固定的24小时,到了第二天会一起失效,瞬间全部请求打到LLM上。
建议给ttl加一个随机值,比如86400 + random.randint(0, 3600),打散过期时间。

怎么验证缓存真的生效了

跑完改造,别急着部署,先用最简单的方式验证:

  1. 启动服务,连续两次调用ask_question_with_cache,传完全一样的问题。
  2. 观察控制台或日志,第一次会打印[缓存未命中,已写入],第二次打印[缓存命中]
  3. 对比两次返回内容是否一致。如果答案里没有动态参数,两次应该完全相同。

更进一步,可以统计50个真实用户问题里的缓存命中率:

# 假设日志文件为 app.log
grep "缓存命中" app.log | wc -l
grep "缓存未命中" app.log | wc -l

命中数量除以总数就是当前命中率。
命中率越高,省下的Token越多。
你能直观感受到的是,响应时间从原来的好几秒变成几十毫秒,LLM调用次数明显减少,账单自然就降下来了。

如果你正在给RAG服务降本,建议先按本文把精确缓存跑通,再根据重复率决定是否上语义缓存。
实际效果会在高频问答场景里非常明显,而且这套改造对原有逻辑影响很小,随时可以回退。
如果你的问题场景有特殊性,比如答案需要实时更新,记得单独调整缓存过期策略。

分享到:
上一篇
Embedding批量处理,大量文档向量化任务多进程加速脚本
下一篇
Agent禁止递归自我调用,循环任务检测机制
1
系统公告

机房迁移升级通知

尊敬的用户: IP 段 103.23.148.x、156.224.29.x 原香港一区线路波动、攻击频繁,平台定于 7 月 5 日凌晨分批迁移至香港 GIA 机房,硬件升级 AMD 铂金机型。 迁移均在凌晨操作,最大程度降低业务影响,迁移期间服务器临时关机; 升级后配置不降低、费用不涨价,数据默认同步迁移; 迁移后 IP 全部更换,请及时修改域名解析、防火墙白名单; 建议提前备份重要数据,有问题可联系在线客服。 感谢理解与支持! 泽御云科技 2026.06.30
服务中心
客服
在线客服
24小时为您服务
咨询
联系我们
联系我们,为您的业务提供专属服务。
24/7 技术支持
如果您遇到寻求进一步的帮助,请过工单与我们进行联系。
24/7 即时支持
泽御云
售前客服
泽御云
泽御云
售后客服
泽御云
技术支持
评价
您对当前页面的整体感受是否满意?
😞
非常不满意
😕
不满意
😐
一般
🙂
满意
😊
非常满意