缓存机制,同样的问题不要让大模型回答两次

发布时间:2026/8/7 11:53:53
缓存机制,同样的问题不要让大模型回答两次 缓存机制同样的问题不要让大模型回答两次上一篇聊异步把执行速度提上去了。速度提上去账单也跟着涨。每个请求都打到大模型哪怕问的是一模一样的问题模型还是老老实实算一遍token照烧。这篇就聊缓存把算过的结果存下来下次再问直接取能省一截是一截。为什么Agent需要缓存Agent跑起来重复调用比你想的频繁。客服场景里用户问怎么退货今天问明天问十个人里八个问的差不多。每次都让模型重新组织一遍答案答案大同小异token花得冤枉。Agent内部也有重复。一个任务跑下来中间可能调好几次模型有些中间步骤的输入高度相似。比如每次都让模型先判断意图输入就是用户那句话翻来覆去就那些。这些重复调用全压在模型上成本和延迟都白搭。我自己踩过这个坑。做一个文档问答Agent测试阶段同一个问题反复调跑一下午账单几十刀。当时没接缓存每个问题都实打实打模型。后来加了缓存同样的测试再跑一遍账单掉到几刀以内。那会儿才体会到缓存这东西在AI应用里算省钱的命门。原理也简单。把prompt和模型参数算个哈希当key结果当value存起来。下次同样的输入进来先查缓存命中直接返回没命中再打模型。省的是模型那一段的计算和等待。InMemoryCache最简单的缓存是InMemoryCache存进程内存里进程一停就没了。fromlangchain_core.globalsimportset_llm_cachefromlangchain_core.cachesimportInMemoryCachefromlangchain_openaiimportChatOpenAI set_llm_cache(InMemoryCache())modelChatOpenAI(modelgpt-4o-mini)print(model.invoke(什么是向量数据库))print(model.invoke(什么是向量数据库))两次invoke同一句话第二次几乎瞬间返回因为命中缓存没打模型。set_llm_cache是全局设置设一次所有模型实例都走这个缓存。InMemoryCache图快零配置调试和原型阶段最合适。坏处也明显进程重启缓存就没了多进程或者多台机器之间不共享。上了gunicorn起四个worker每个worker各存各的命中率惨不忍睹。SQLiteCache想跨重启保留缓存SQLiteCache把结果写到本地sqlite文件里。fromlangchain_core.globalsimportset_llm_cachefromlangchain_community.cacheimportSQLiteCache set_llm_cache(SQLiteCache(database_path.langchain.db))设完之后所有缓存结果写进.langchain.db这个文件。进程重启缓存还在下次同样的输入照样命中。适合个人项目或者单机部署的小服务。SQLiteCache有个坑我得提一句。它默认建表没怎么管并发多进程同时写同一个db文件偶尔会锁库报错。跑批处理脚本一个进程没事上了多worker的Web服务就可能撞。碰到这种情况要么换Redis要么把缓存挪到独立的单进程里。RedisCache真上量多机器多进程缓存得放共享存储里Redis是标配。RedisCache把缓存放到Redis所有进程所有机器读同一份命中率高还自带过期机制。fromlangchain_core.globalsimportset_llm_cachefromlangchain_community.cacheimportRedisCachefromredisimportRedis set_llm_cache(RedisCache(redis_Redis(hostlocalhost,port6379)))RedisCache构造函数传一个redis客户端实例进去LangChain拿它读写缓存。服务重启缓存还在加机器命中率照样高生产环境基本都走这套。有个细节得注意。Redis里缓存的value是序列化之后的完整响应占内存。问题量大的时候留意Redis的内存上限该设过期时间就设别让缓存把Redis撑爆。RedisCache支持传ttl参数控制过期我的习惯是给个几小时到一天看问题更新频率。语义缓存相似问题也命中前面几种都是精确匹配输入一字不差才命中。但用户问怎么退货和退货流程是什么两句话字面不同意思一样精确匹配抓不到还得打模型。语义缓存干的就是这个。它把每个问题算个embedding向量存下来新问题来了先算向量去库里找最相似的相似度超过阈值就认为命中直接返回那条答案。RedisSemanticCache是LangChain现成的实现。fromlangchain_core.globalsimportset_llm_cachefromlangchain_community.cacheimportRedisSemanticCachefromlangchain_openaiimportOpenAIEmbeddingsfromredisimportRedis set_llm_cache(RedisSemanticCache(redis_Redis(hostlocalhost,port6379),embeddingOpenAIEmbeddings(),distance_threshold0.2,))distance_threshold控制相似度门槛越小越严格。设得太大不相关的问题也命中答非所问。设得太小跟精确匹配差不多优势没了。这个值得花时间调拿一批真实问题挨个试看哪些该命中没命中、哪些不该命中却命中了。语义缓存我踩过一个坑。threshold调得太松用户问怎么退货命中了怎么退款的缓存退货和退款在电商里是两套流程答案给错了用户直接投诉。语义缓存对问题相近但答案必须区分的场景要格外小心客服、医疗、法律这类宁可严一点别图省事。缓存策略怎么选几种缓存怎么挑看部署形态和问题特征。本地开发、原型验证InMemoryCache一行代码搞定图省事。单机部署的小服务、脚本批处理SQLiteCache结果落盘不怕重启。多进程或多机器的生产服务RedisCache共享缓存命中率高。问题表述发散、重复语义多的场景再叠加语义缓存。几个通用建议。缓存命中得可观测打日志记录命中没命中不然哪天缓存没生效你都不知道钱照花。模型升级或者提示词改了旧缓存全失效得有办法清。语义缓存上线前拿真实问题集做一轮评测threshold调到误命中率能接受再放出去。还有一点工具调用的结果别一股脑进缓存。Agent里模型决定调哪个工具这个决策过程缓存了工具本身的数据变了缓存还是老结果容易出脏数据。我一般只缓存纯文本问答那一段涉及工具和检索的步骤留着实时跑。小结这篇把LangChain的缓存机制过了一遍。为什么需要缓存重复调用白烧token。InMemoryCache存内存图快SQLiteCache落盘抗重启RedisCache共享适合多机语义缓存连相似问题都能命中。每种各有各的适用场景选对了省token省延迟选错了要么没用要么答错。缓存是省钱省时间的一招光有缓存还不够。代码写得乱、链搭得歪、Prompt拼得随意这些毛病缓存救不了。下一篇就聊LangChain最佳实践看看代码组织和工程上有哪些该守的规矩。