RAG 缓存策略:相同查询别重复检索,热门问题预加载
RAG 缓存策略相同查询别重复检索热门问题预加载一、每问必查的性能黑洞用户问退货政策是什么Agent 去向量库检索、拼接上下文、调用模型推理耗时 2.3 秒。一分钟后同一个用户又问了一遍你们的退货政策。Agent 重新走完整条链路检索→拼接→推理又花了 2.1 秒。这就是典型的知识问答场景20% 的热门问题贡献了 80% 的检索和推理负载。如果你的 RAG 系统每次查询都从头检索一遍相当于每处理一个问题都在重新发明轮子。缓存不是可选优化而是 RAG 系统从 Demo 到生产的关键一步。二、RAG 的多级缓存架构RAG 的缓存可以在三个层次上生效语义缓存Semantic Cache对用户问题做语义相似度匹配命中后直接返回缓存的答案。适合高频重复问题。检索缓存Retrieval Cache缓存向量检索的结果Top-K 文档片段跳过向量库查询。适合相似但不完全相同的问题。Embedding 缓存Embedding Cache缓存文本的向量表示避免对相同文本重复计算 Embedding。适合知识库文档不变但频繁查询的场景。flowchart TD Q[用户问题] -- S1{语义缓存} S1 --|相似度 0.95| AC[返回缓存答案] S1 --|未命中| E1[计算 Embedding] E1 -- S2{Embedding 缓存} S2 --|命中| EC[复用已有向量] S2 --|未命中| CEM[调用 Embedding 模型] CEM -- EC EC -- S3{检索缓存} S3 --|命中| RC[复用检索结果] S3 --|未命中| VS[向量库检索] VS -- RC RC -- S4{是否热门问题} S4 --|是| PR[预加载到语义缓存] S4 --|否| LLM[模型推理] PR -- LLM LLM -- R[返回结果] R -- SC[写入语义缓存]多级缓存的核心是让每个请求从最合适的缓存层命中逐级向后穿透只有最冷门的全新问题才会走到最慢的完整链路。三、Go 实现带 TTL 和 LRU 的多级 RAG 缓存package ragcache import ( container/list context crypto/sha256 encoding/hex fmt math sync time ) // 三层数据结构 // CacheEntry 通用缓存条目 type CacheEntry struct { Key string Value interface{} ExpiresAt time.Time } func (e *CacheEntry) IsExpired() bool { return time.Now().After(e.ExpiresAt) } // LRUCache 带 TTL 的 LRU 缓存 type LRUCache struct { capacity int items map[string]*list.Element lruList *list.List mu sync.RWMutex } type lruItem struct { key string entry *CacheEntry } func NewLRUCache(capacity int) *LRUCache { return LRUCache{ capacity: capacity, items: make(map[string]*list.Element), lruList: list.New(), } } func (c *LRUCache) Get(key string) (interface{}, bool) { c.mu.Lock() defer c.mu.Unlock() elem, ok : c.items[key] if !ok { return nil, false } item : elem.Value.(*lruItem) if item.entry.IsExpired() { c.removeElement(elem) return nil, false } // 移到链表头部最近使用 c.lruList.MoveToFront(elem) return item.entry.Value, true } func (c *LRUCache) Set(key string, value interface{}, ttl time.Duration) { c.mu.Lock() defer c.mu.Unlock() // 已存在则更新 if elem, ok : c.items[key]; ok { c.lruList.MoveToFront(elem) item : elem.Value.(*lruItem) item.entry.Value value item.entry.ExpiresAt time.Now().Add(ttl) return } // LRU 淘汰 if c.lruList.Len() c.capacity { oldest : c.lruList.Back() if oldest ! nil { c.removeElement(oldest) } } entry : CacheEntry{ Key: key, Value: value, ExpiresAt: time.Now().Add(ttl), } elem : c.lruList.PushFront(lruItem{key: key, entry: entry}) c.items[key] elem } func (c *LRUCache) removeElement(elem *list.Element) { c.lruList.Remove(elem) item : elem.Value.(*lruItem) delete(c.items, item.key) } // 多级 RAG 缓存管理器 // MultiLevelCache 多级 RAG 缓存 type MultiLevelCache struct { // 第一层语义缓存问题 → 最终答案 semanticCache *LRUCache // 第二层检索结果缓存问题哈希 → Top-K 文档片段 retrievalCache *LRUCache // 第三层Embedding 缓存文本哈希 → 向量 embeddingCache *LRUCache // 热门问题预加载列表 hotQuestions map[string]string // 问题 → 答案 hotMu sync.RWMutex // 统计信息 stats CacheStats } // CacheStats 缓存统计 type CacheStats struct { SemanticHits int64 RetrievalHits int64 EmbeddingHits int64 TotalRequests int64 mu sync.Mutex } func NewMultiLevelCache() *MultiLevelCache { return MultiLevelCache{ semanticCache: NewLRUCache(1000), // 缓存 1000 个问答对 retrievalCache: NewLRUCache(500), // 缓存 500 个检索结果 embeddingCache: NewLRUCache(10000), // 缓存 10000 个向量 hotQuestions: make(map[string]string), } } // GetAnswer 多级缓存查询入口 func (m *MultiLevelCache) GetAnswer(ctx context.Context, question string) (string, bool) { m.stats.mu.Lock() m.stats.TotalRequests m.stats.mu.Unlock() // 第一级语义缓存 // 实际项目中用余弦相似度匹配 if answer, ok : m.semanticCache.Get(question); ok { m.stats.mu.Lock() m.stats.SemanticHits m.stats.mu.Unlock() return answer.(string), true } return , false } // GetRetrieval 检索缓存查询 func (m *MultiLevelCache) GetRetrieval(ctx context.Context, question string) ([]Document, bool) { key : m.hashKey(retrieval: question) if docs, ok : m.retrievalCache.Get(key); ok { m.stats.mu.Lock() m.stats.RetrievalHits m.stats.mu.Unlock() return docs.([]Document), true } return nil, false } // GetEmbedding 向量缓存查询 func (m *MultiLevelCache) GetEmbedding(ctx context.Context, text string) ([]float64, bool) { key : m.hashKey(text) if vec, ok : m.embeddingCache.Get(key); ok { m.stats.mu.Lock() m.stats.EmbeddingHits m.stats.mu.Unlock() return vec.([]float64), true } return nil, false } // StoreSemantic 存储语义缓存 func (m *MultiLevelCache) StoreSemantic(question, answer string, ttl time.Duration) { m.semanticCache.Set(question, answer, ttl) } // StoreRetrieval 存储检索结果缓存 func (m *MultiLevelCache) StoreRetrieval(question string, docs []Document, ttl time.Duration) { key : m.hashKey(retrieval: question) m.retrievalCache.Set(key, docs, ttl) } // StoreEmbedding 存储向量缓存 func (m *MultiLevelCache) StoreEmbedding(text string, vec []float64, ttl time.Duration) { key : m.hashKey(text) m.embeddingCache.Set(key, vec, ttl) } // PreloadHotQuestions 预加载热门问题 func (m *MultiLevelCache) PreloadHotQuestions() { m.hotMu.RLock() defer m.hotMu.RUnlock() for question, answer : range m.hotQuestions { // 热门问题用较长的 TTL30 分钟 m.semanticCache.Set(question, answer, 30*time.Minute) } } // UpdateHotQuestions 更新热门问题列表可从监控数据自动生成 func (m *MultiLevelCache) UpdateHotQuestions(questions map[string]string) { m.hotMu.Lock() defer m.hotMu.Unlock() m.hotQuestions questions } // 余弦相似度计算简化版 func cosineSimilarity(a, b []float64) float64 { if len(a) ! len(b) || len(a) 0 { return 0 } var dotProduct, normA, normB float64 for i : range a { dotProduct a[i] * b[i] normA a[i] * a[i] normB b[i] * b[i] } if normA 0 || normB 0 { return 0 } return dotProduct / (math.Sqrt(normA) * math.Sqrt(normB)) } func (m *MultiLevelCache) hashKey(text string) string { h : sha256.Sum256([]byte(text)) return hex.EncodeToString(h[:])[:16] } // 集成到 RAG 查询流程 type Document struct { Content string Score float64 } func QueryWithCache(ctx context.Context, question string, cache *MultiLevelCache) (string, error) { // 1. 检查语义缓存 if answer, ok : cache.GetAnswer(ctx, question); ok { return answer, nil } // 2. 检查检索缓存 var docs []Document if cachedDocs, ok : cache.GetRetrieval(ctx, question); ok { docs cachedDocs } else { // 实际向量检索 docs []Document{} // 实际实现中从向量库检索 cache.StoreRetrieval(question, docs, 5*time.Minute) } // 3. 调用模型推理 context_str : for _, d : range docs { context_str d.Content \n } answer : fmt.Sprintf(基于 %d 条文档生成的回答, len(docs)) // 4. 存储语义缓存 cache.StoreSemantic(question, answer, 10*time.Minute) return answer, nil }四、缓存策略的边界与权衡语义相似度阈值是核心参数。阈值太低会导致错误命中用户问怎么退货、缓存的答案是怎么下单阈值太高则命中率低。建议从 0.90 开始根据实际数据调整。也可以分场景客服场景可以低一些0.85医疗场景必须严格0.95。缓存失效策略要与知识库更新联动。知识库更新后相关的检索缓存和语义缓存都应该失效。否则会出现知识库已更新但 Agent 还在回答旧信息的情况。最简单的做法是按知识库的更新时间戳来标记缓存版本。Embedding 缓存的收益取决于知识库稳定性。如果知识库天天变Embedding 缓存的命中率会很低。对于静态知识库如产品手册、API 文档Embedding 缓存可以省掉 90% 以上的 Embedding 调用。热门问题预加载需要数据支撑。盲猜哪些问题是热门的很容易猜错。建议从生产日志中提取 Top-100 高频查询自动化生成预加载列表每日更新。五、总结RAG 的多级缓存策略不是一上来就全上而是按收益从高到低逐级实现语义缓存最高命中率→ 检索缓存中等收益→ Embedding 缓存适合静态知识库。每一级缓存的 TTL 要根据数据变化频率独立设置。核心目标让 80% 的常见查询在缓存中命中把宝贵的 GPU 资源留给那 20% 真正需要深度推理的问题。

相关新闻

Python 日志结构化:JSON 格式 + TraceID + 业务字段的三层规范

Python 日志结构化:JSON 格式 + TraceID + 业务字段的三层规范

Python 日志结构化:JSON 格式 TraceID 业务字段的三层规范 一、"日志里有,但查不到"的排障困境 线上用户投诉"订单支付成功但状态没更新"。查日志时发现同一笔交易的日志分散在 4 个不同的服务里:网关写了一条 INFO: p…

2026/7/12 17:04:18阅读更多 →
RapidOCR多引擎架构深度解析:从算法优化到跨平台部署的全面突破

RapidOCR多引擎架构深度解析:从算法优化到跨平台部署的全面突破

RapidOCR多引擎架构深度解析:从算法优化到跨平台部署的全面突破 【免费下载链接】RapidOCR 📄 Awesome OCR multiple programing languages toolkits based on ONNX Runtime, OpenVINO, MNN, PaddlePaddle, TensorRT and PyTorch. 项目地址: https://g…

2026/7/12 17:04:18阅读更多 →
Go context 传递实战:超时、取消和元数据的最佳实践

Go context 传递实战:超时、取消和元数据的最佳实践

Go context 传递实战:超时、取消和元数据的最佳实践 一、一个 Goroutine 泄漏引发的凌晨排查 凌晨两点收到告警:某服务 Goroutine 数量从平时的 200 飙升到 15000,内存占用 8GB,OOM 被 kill 了三次。排查发现源头是上游服务调用超…

2026/7/12 17:04:18阅读更多 →
Jackhammer企业级安全解决方案:如何应对大规模代码库的挑战

Jackhammer企业级安全解决方案:如何应对大规模代码库的挑战

Jackhammer企业级安全解决方案:如何应对大规模代码库的挑战 【免费下载链接】jackhammer Jackhammer - One Security vulnerability assessment/management tool to solve all the security team problems. 项目地址: https://gitcode.com/gh_mirrors/ja/jackhamm…

2026/7/12 22:14:48阅读更多 →
Linux进程管理核心知识点与AI面试题讲解

Linux进程管理核心知识点与AI面试题讲解

在Linux操作系统中,进程管理是内核的核心功能之一,也是后端开发、运维及内核相关岗位面试的高频考点。本文将从虚拟地址空间与写时复制、进程调度机制、进程终止与等待三大核心模块拆解底层原理,并结合经典面试题给出详细解答,帮助…

2026/7/12 22:14:48阅读更多 →
Godot 2D游戏AI系统完全指南:从基础巡逻到高级群体行为

Godot 2D游戏AI系统完全指南:从基础巡逻到高级群体行为

Godot 2D游戏AI系统完全指南:从基础巡逻到高级群体行为 【免费下载链接】godot-platformer-2d 2d Metroidvania-inspired game for the 2019 GDquest Godot Kickstarter course project. 项目地址: https://gitcode.com/gh_mirrors/go/godot-platformer-2d G…

2026/7/12 22:14:48阅读更多 →
Qt C++实战:Modbus RTU协议详解及主站实现

Qt C++实战:Modbus RTU协议详解及主站实现

文章目录 一 Modbus RTU 协议 1.1 Modbus 协议分类及通信过程 1.2 Modbus-RTU 协议数据帧格式 二 Modbus RTU 主站上位机 2.1 界面设计 2.2 编码实现 2.2.1 串口操作 2.2.2 从站操作 2.2.3 信息显示 2.3 测试 其他 Modbus 总线协议属于应用层协议,是一种常见的工业系统通讯协议…

2026/7/12 22:14:48阅读更多 →
Web Audio Samples:Chrome官方团队打造的Web音频开发终极指南

Web Audio Samples:Chrome官方团队打造的Web音频开发终极指南

Web Audio Samples:Chrome官方团队打造的Web音频开发终极指南 【免费下载链接】web-audio-samples Web Audio API samples by Chrome Web Audio Team 项目地址: https://gitcode.com/gh_mirrors/we/web-audio-samples 想要快速掌握Web Audio API开发吗&#…

2026/7/12 22:14:48阅读更多 →
SmartPack-Kernel-Manager快速设置模板:一键应用最优配置

SmartPack-Kernel-Manager快速设置模板:一键应用最优配置

SmartPack-Kernel-Manager快速设置模板:一键应用最优配置 【免费下载链接】SmartPack-Kernel-Manager Source code of SmartPack-Kernel Manager, the Ultimate Tool to Manage your Kernel 项目地址: https://gitcode.com/gh_mirrors/smar/SmartPack-Kernel-Mana…

2026/7/12 22:09:47阅读更多 →
VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异当你在VSCode中启动一个新的TypeScript项目时,第一个技术决策往往从安装方式开始。这个看似简单的选择——全局安装还是项目本地安装——实际上会深刻影响你的开发流程、团队协作和…

2026/7/12 0:02:11阅读更多 →
智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手 【免费下载链接】zhihuishu 智慧树刷课插件,自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 智慧树刷课插件是一款专为智慧树在线教育平台设计的Chrome浏…

2026/7/12 0:02:11阅读更多 →
Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 你是否在GOG或Epic Games Store购买了心仪的游戏…

2026/7/12 0:02:11阅读更多 →
VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异当你在VSCode中启动一个新的TypeScript项目时,第一个技术决策往往从安装方式开始。这个看似简单的选择——全局安装还是项目本地安装——实际上会深刻影响你的开发流程、团队协作和…

2026/7/12 0:02:11阅读更多 →
智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手 【免费下载链接】zhihuishu 智慧树刷课插件,自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 智慧树刷课插件是一款专为智慧树在线教育平台设计的Chrome浏…

2026/7/12 0:02:11阅读更多 →
Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 你是否在GOG或Epic Games Store购买了心仪的游戏…

2026/7/12 0:02:11阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/11 16:20:28阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/11 23:15:38阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/12 21:43:43阅读更多 →