发布时间:2026/7/12 17:04:14
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% 真正需要深度推理的问题。

相关新闻

2026/7/12 17:04:14

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

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

2026/7/12 17:04:14

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

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

2026/7/12 19:44:22

4D-RGPT-8B性能基准测试:A100 GPU上的推理速度与精度分析

4D-RGPT-8B性能基准测试:A100 GPU上的推理速度与精度分析 【免费下载链接】4D-RGPT-8B 项目地址: https://ai.gitcode.com/hf_mirrors/nvidia/4D-RGPT-8B 4D-RGPT-8B是NVIDIA开发的专业多模态大型语言模型,作为NVILA视觉语言模型家族的一部分&am…

2026/7/12 19:44:22

独立开发技术栈怎么选

独立开发者选技术栈时,很容易被两个方向拉扯。 一边是“用最熟悉的技术,先做出来”;另一边是“用最先进的技术,未来更强”。听起来都对,但真正的问题不是哪个技术更高级,而是哪个技术能让你用最少的维护成…

2026/7/12 19:44:22

windows网络适配器驱动开发-网环元素管理

网环元素管理 按照本主题中的指南在网络数据传输期间管理 NET_RING 结构及其元素。本主题中的规则描述了在不同的数据路径方案下,客户端驱动程序可以修改哪些 net ring 元素的成员以及何时可以修改,并提供了客户端驱动程序在使用这些结构时应牢记的一般信…

2026/7/12 19:39:22

社区贡献指南:如何参与AMD MiniMax-M2.1-MXFP4项目开发

社区贡献指南:如何参与AMD MiniMax-M2.1-MXFP4项目开发 【免费下载链接】MiniMax-M2.1-MXFP4 项目地址: https://ai.gitcode.com/hf_mirrors/amd/MiniMax-M2.1-MXFP4 AMD MiniMax-M2.1-MXFP4是一个基于AMD硬件优化的高性能大语言模型项目,专为AM…

2026/7/12 0:01:29

3步解锁音乐自由:ncmdumpGUI终极NCM文件解密转换指南

3步解锁音乐自由:ncmdumpGUI终极NCM文件解密转换指南 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换,Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 你是否曾在网易云音乐下载了心爱的歌曲&#…

2026/7/12 0:01:29

CANoe 19 SP3 配置 GB/T 27930-2023 A类系统:3步搭建BMS仿真测试环境

CANoe 19 SP3 配置 GB/T 27930-2023 A类系统:3步搭建BMS仿真测试环境随着新能源汽车行业的快速发展,充电通信协议的标准化和测试验证变得尤为重要。GB/T 27930-2023作为中国智能充电协议的最新版本,对充电机与电动汽车之间的通信提出了更严格…

2026/7/12 0:01:29

3步搞定RTL8852BE驱动:从零开始配置Wi-Fi 6网卡

3步搞定RTL8852BE驱动:从零开始配置Wi-Fi 6网卡 【免费下载链接】rtl8852be Realtek Linux WLAN Driver for RTL8852BE 项目地址: https://gitcode.com/gh_mirrors/rt/rtl8852be 还在为Linux系统无法识别RTL8852BE Wi-Fi 6网卡而烦恼吗?&#x1f…

2026/7/12 0:01:29

3步解锁音乐自由:ncmdumpGUI终极NCM文件解密转换指南

3步解锁音乐自由:ncmdumpGUI终极NCM文件解密转换指南 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换,Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 你是否曾在网易云音乐下载了心爱的歌曲&#…

2026/7/12 0:01:29

CANoe 19 SP3 配置 GB/T 27930-2023 A类系统:3步搭建BMS仿真测试环境

CANoe 19 SP3 配置 GB/T 27930-2023 A类系统:3步搭建BMS仿真测试环境随着新能源汽车行业的快速发展,充电通信协议的标准化和测试验证变得尤为重要。GB/T 27930-2023作为中国智能充电协议的最新版本,对充电机与电动汽车之间的通信提出了更严格…

2026/7/12 0:01:29

3步搞定RTL8852BE驱动:从零开始配置Wi-Fi 6网卡

3步搞定RTL8852BE驱动:从零开始配置Wi-Fi 6网卡 【免费下载链接】rtl8852be Realtek Linux WLAN Driver for RTL8852BE 项目地址: https://gitcode.com/gh_mirrors/rt/rtl8852be 还在为Linux系统无法识别RTL8852BE Wi-Fi 6网卡而烦恼吗?&#x1f…

2026/7/12 11:21:32

3个高效策略:快速掌握Axure中文界面配置

3个高效策略:快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…