当前位置: 首页 > article >正文

【灶台导航】 RAG系统的容错设计:从向量搜索到关键词降级,一个都不能少

当三个外部依赖都可能随时挂掉时如何保证用户永远有响应问题完美主义害死人做RAG系统时我们很容易陷入一种思维定势向量检索要准、LLM要强、整个链路要丝滑。但现实是——任何一个外部服务挂了用户就得不到响应。在微信小程序这种C端场景可用性比准确性重要得多。用户不在乎你用的是Qdrant还是Pinecone他只知道点进来白屏关掉再也不会用。我们系统依赖三个外部服务SiliconFlow Embedding API将用户问题转成向量Qdrant向量数据库存储和检索菜谱向量DeepSeek LLM基于检索结果生成回复任何一个挂了如果按传统思路直接报错用户就流失了。本文分享我们如何在云函数环境中用四级降级策略保证系统永远有响应——宁可推荐不够精准也不能白屏无响应。容错架构四层兜底层层递进整体架构如下用户消息 │ ▼ ┌─────────────────────┐ │ 第1级: Qdrant向量搜索 │ ──超时/失败──┐ └─────────┬───────────┘ │ │ ▼ 有高分结果? ┌─────────────────────┐ │ │ │ 第2级: TF-IDF关键词检索 │ ──超时/失败──┐ │ └────── 成功 ──────→ │ (纯JS无外部依赖) │ │ │ └─────────┬───────────┘ │ │ │ ▼ │ 有高分结果? ┌──────────────┐ │ │ │ │ 第3级: 无RAG │ │ │ └── 成功 ──→ 用TF-IDF结果 │ LLM直接回答 │ │ └──── 失败 ──→ └──────┬───────┘ │ │ └────────────────────── 统一送入DeepSeek ──────────────────────┘ │ DeepSeek也挂了? │ ▼ ┌──────────────────────┐ │ 第4级: fallbackResponse │ │ 本地关键词数据库查询 │ └──────────────────────┘核心设计原则每一级都不依赖上一级每一级都有自己的超时控制失败后静默降级。第1级→第2级向量检索失败时的TF-IDF降级向量检索失败可能的原因Embedding API 超时5秒无响应Qdrant 查询超时3秒无响应网络错误ETIMEDOUT、ECONNREFUSEDQdrant 服务完全不可用降级逻辑的核心代码// chat/index.js - retrieveContext()asyncfunctionretrieveContext(message,openid){letsystemRagRecipes[]letcontextText// ═══ 尝试向量搜索 ═══try{constvectorResultsawaitwithTimeout(qdrant.vectorSearch(message,3),TIMEOUT_CONFIG.embeddingTIMEOUT_CONFIG.qdrant,// 8秒总超时向量检索超时)constrelevantvectorResults.filter(rr.similarity0.3)if(relevant.length0){systemRagRecipesrelevant.map(rr.recipe)contextTextformatContext(relevant)}}catch(e){// Qdrant挂了或超时静默降级 — 用户完全无感知console.warn([RAG-Vector] 向量检索失败降级为 TF-IDF:,e.message)}// ═══ 仅在向量搜索无结果时走TF-IDF ═══if(systemRagRecipes.length0){const{data:recipes}awaitdb.collection(recipes).where({isPrivate:_.neq(true)}).limit(50).get()constresultssearch(message,recipes,3)// TF-IDF检索constrelevantresults.filter(rr.similarity0.05)// 阈值更宽松if(relevant.length0){systemRagRecipesrelevant.map(rr.recipe)contextTextformatContext(relevant)}}return{contextText,ragRecipes:systemRagRecipes}}关键设计细节1. 静默降级catch块中只打日志不抛异常用户永远看不到系统繁忙之类的错误提示。2. 阈值差异TF-IDF用0.05向量用0.3。因为两者的分数分布不同TF-IDF的相似度天然更低阈值需要调低。3. 只查系统菜谱降级时过滤isPrivate避免查到其他用户的私人菜谱造成隐私问题。TF-IDF的纯JS实现零依赖才能真兜底为什么TF-IDF能成为可靠的第二级因为它是纯JavaScript实现不依赖任何外部服务、不依赖C扩展、不依赖网络。云函数环境中很多npm包需要编译如jieba分词部署困难。我们实现了一个轻量级的中文TF-IDF中文分词二元组停用词// tfidf.jsconstSTOP_WORDSnewSet([的,了,是,在,和,也,都,不,就,有])functiontokenize(text){constchineseChunkstext.match(/[\u4e00-\u9fa5]/g)||[]consttokens[]for(constchunkofchineseChunks){// 双字组合匹配菜名、食材名for(leti0;ichunk.length-1;i){constbigramchunk[i]chunk[i1]if(!STOP_WORDS.has(bigram))tokens.push(bigram)}}returntokens}为什么用bigram而不是jieba因为云函数装不了C扩展而bigram对短文本菜名、食材名效果足够好。像宫保鸡丁会被切分为[“宫保”,“保鸡”,“鸡丁”]足够匹配用户查询。增强归一化TFfunctioncomputeTFIDF(tokens,idf){consttf{}for(consttoftokens)tf[t](tf[t]||0)1constmaxTfMath.max(...Object.values(tf))consttfidf{}for(const[term,freq]ofObject.entries(tf)){// 增强归一化避免长文档的单字频率被过度归一化constnormalizedTf0.50.5*(freq/maxTf)tfidf[term]normalizedTf*(idf[term]||1)}returntfidf}0.5 0.5 * (tf/maxTf)比简单的tf/maxTf更柔和避免长文档中重复词失去权重。IDF平滑// 预处理计算IDFfunctionprecomputeIdf(recipes){constNrecipes.lengthconstdf{}for(constrecipeofrecipes){consttokenstokenize(recipe.name (recipe.ingredients||[]).join( ))constuniquenewSet(tokens)for(consttermofunique){df[term](df[term]||0)1}}constidf{}for(const[term,freq]ofObject.entries(df)){// 1平滑防止除零再1偏移保证IDF不为0idf[term]Math.log((N1)/(freq1))1}returnidf}超时控制每个环节都有底线没有超时控制的降级是假降级——一个请求卡住3分钟用户早走了。constTIMEOUT_CONFIG{embedding:5000,// Embedding API: 5秒qdrant:3000,// Qdrant查询: 3秒ragQuery:3000,// TF-IDF查询: 3秒deepseekApi:30000,// DeepSeek: 30秒cloudFunction:34000// 云函数总超时: 34秒留1秒缓冲}// 用Promise.race实现functionwithTimeout(promise,ms,errorMsg){returnPromise.race([promise,newPromise((_,reject)setTimeout(()reject(newError(errorMsg)),ms))])}关键原则每个外部调用独立超时Embedding超时不影响后续降级总超时兜底云函数34秒超时保证不会无限等待超时即降级超时被当作失败进入下一级第3级LLM降级向量检索和TF-IDF都失败了怎么办走纯LLM生成无RAG上下文// chat/index.js - callDeepSeekAPI()try{constresponseawaitgot.post(deepseekUrl,{timeout:{request:TIMEOUT_CONFIG.deepseekApi}})// 解析响应...}catch(err){console.warn([DeepSeek] API调用失败走本地兜底:,err.message)returnawaitfallbackResponse(userMessage)}此时依赖就只剩下DeepSeek API了。但如果DeepSeek也挂了呢第4级本地关键词兜底最后一道防线不依赖任何外部API只依赖云数据库。asyncfunctionfallbackResponse(userMessage){constmsguserMessage.toLowerCase()// 1. 问候语直接回复不需要查库constgreetings[你好,hello,hi,嗨]if(greetings.some(gmsg.includes(g))){return{reply:你好我是灶台导航助手可以帮你推荐菜谱~,action:ask}}// 2. 关键词→分类映射constcategoryMap{牛:beef,猪:pork,鸡:chicken,鱼:fish,素:vegetarian,甜:dessert}letmatchedCategorynullfor(const[keyword,category]ofObject.entries(categoryMap)){if(msg.includes(keyword)){matchedCategorycategory;break}}// 3. 从数据库按分类或关键词查菜谱constdbcloud.database()const_db.commandletquerydb.collection(recipes)if(matchedCategory){queryquery.where({category:matchedCategory})}else{// 模糊匹配菜名constkeywordArrmsg.split(/[\s,、]/).filter(kk.length0)constorConditionskeywordArr.map(k({name:db.RegExp({regexp:k,options:i})}))queryquery.where(_.or(orConditions))}const{data:recipes}awaitquery.limit(3).orderBy(views,desc).get()if(recipes.length0){constreply为您推荐以下${matchedCategory?matchedCategory:相关}菜谱\nrecipes.map(r${r.name}${r.difficulty||中等}).join(\n)return{reply,recommendations:recipes,action:recommend}}// 连数据库都查不到——兜底的兜底return{reply:暂时没找到相关菜谱可以说说你想吃什么食材吗,action:ask}}这个兜底层有三大特点零外部依赖只调用云数据库即便Embedding/Qdrant/DeepSeek全挂也能工作规则驱动关键词映射不依赖AI100%确定渐进式降级问候语直接回复 → 分类匹配 → 模糊匹配 → 通用提示降级触发条件速查表降级级别触发条件用户感知向量→TF-IDF① Embedding API 超时(5s)② Qdrant 查询超时(3s)③ 网络错误④ 所有结果相似度 0.3无感知推荐精度略降TF-IDF→纯生成① 数据库查询超时(3s)② 所有结果相似度 0.05③ 数据库为空无感知回答无菜谱引用纯生成→兜底DeepSeek API 超时(30s) 或报错回复变简短可能答非所问兜底内部降级关键词/分类匹配失败返回通用提示继续对话为什么不合并多路结果有人会问为什么不做向量检索和TF-IDF的并行召回融合排序当前设计选择串行降级而非并行融合原因如下数据规模小菜谱数量在百级单路召回已经足够覆盖分数不可比向量相似度(01)和TF-IDF分数(0几十)尺度不同简单线性融合会引入噪声延迟更低向量检索成功时不需要等待TF-IDF执行串行反而更快代码简单逻辑清晰维护成本低如果后续扩展到万级数据可以考虑升级为多路并行召回Cross-Encoder重排序。完整的请求生命周期以一次完整对话为例追踪数据路径1. 用户输入家里有鸡肉和土豆想做点下饭的 2. 小程序调用云函数 wx.cloud.callFunction({ name: chat, data: { message: ... } }) 3. 云函数入口 exports.main() ├─ qdrant.setApiKey(sk-xxx) └─ processChat(event, openid) 4. retrieveContext() ├─ 尝试向量搜索 │ ├─ getEmbedding() → SiliconFlow API │ └─ searchQdrant() → 返回宫保鸡丁(0.72)、土豆烧鸡(0.68)、辣子鸡丁(0.51) ├─ 过滤相似度 0.3 → 3条通过 └─ 返回 RAG 上下文 5. callDeepSeekAPI() ├─ System Prompt 基础词 RAG上下文 └─ DeepSeek返回 → {reply:推荐宫保鸡丁...,action:recommend} 6. resolveRecipeIds() └─ 宫保鸡丁 → 匹配 ragRecipes 中的真实 _id 7. 保存会话到数据库 8. 返回小程序如果第4步向量检索失败会自动走TF-IDF降级后续流程完全一样用户无感知。降级效果实测场景1正常情况所有服务正常用户“想吃点下饭的”Qdrant返回宫保鸡丁(0.72)、鱼香肉丝(0.68)阈值0.3通过直接使用向量结果总耗时~2秒场景2Qdrant临时宕机Embedding API超时(5s)→抛异常自动降级TF-IDF按下饭关键词匹配找到标签含下饭的菜谱用户无感知耗时增加约1秒降级开销场景3用户说你好Qdrant返回低分结果(最高0.15)TF-IDF也无有意义匹配进入纯生成模式DeepSeek直接回复用户得到正常问候无报错场景4DeepSeek也挂了进入fallbackResponse关键词匹配回复您好请说食材名称…虽然不够智能但用户不会看到白屏关于Qdrant的安全提醒目前Qdrant直接暴露在公网且无认证存在风险风险任何人都可以查询/修改/删除你的向量数据数据泄露或被恶意清空两种加固方案方案一API Key认证推荐# docker-compose.ymlservices:qdrant:image:qdrant/qdrantenvironment:-QDRANT__SERVICE__API_KEYyour-secure-key-here方案二防火墙白名单更安全# 云厂商控制台设置入站规则# 只允许云函数所在网段访问 Qdrant 端口(6333,6334)后续扩展方向当前架构满足小规模场景后续可以迭代的方向查询改写先调LLM将模糊提问“晚上吃啥”改写为精确query“家常菜 简单 快手 30分钟”提高召回率多路召回重排序向量检索、TF-IDF、元数据过滤三路并行用Cross-Encoder重排序增量同步菜谱新增/修改后自动触发Qdrant更新而非全量重新同步缓存层对高频查询的Embedding结果做缓存减少SiliconFlow API调用混合检索利用Qdrant的filtering能力先按分类/难度过滤再做向量匹配监控告警增加各级降级的metrics上报及时发现问题小结容错不是锦上添花而是RAG系统上线的必备条件。核心思路分级降级向量搜索→关键词搜索→LLM直答→本地兜底每一级都不依赖上一级独立超时每个外部调用有自己的超时不互相阻塞超时即降级静默失败用户永远看不到技术错误只是推荐精度逐步降低零依赖兜底最后一道防线只依赖云数据库保证最低可用性在C端场景用户要的不是完美的推荐而是一个永远能用的产品。项目地址Gitee/ZaoTaiNavigation团队名称倒灶了队更新时间2026年5月

相关文章:

【灶台导航】 RAG系统的容错设计:从向量搜索到关键词降级,一个都不能少

当三个外部依赖都可能随时挂掉时,如何保证用户永远有响应?问题:完美主义害死人 做RAG系统时,我们很容易陷入一种思维定势:向量检索要准、LLM要强、整个链路要丝滑。但现实是——任何一个外部服务挂了,用户就…...

号卡系统后台一键生图换图添加随心ai密钥教程

号卡产品全新上线随心ai一键生图、智能换图功能,操作极简,秒出优质素材,告别手动作图。 1.登录号卡系统后台首页先更新版本2.到号卡系统设置——系统系统设置——号卡设置——下滑就可以看到随心AI密钥入口需要填写密钥3.随心ai密钥申请入口h…...

AI增强自动化工作流:从规则驱动到意图驱动的智能决策实践

1. 项目概述:当AI遇见自动化工作流最近在GitHub上看到一个挺有意思的项目,叫“NitroRCr/AIaW”。光看名字,可能有点摸不着头脑,但点进去研究一下,你会发现它其实是一个将人工智能(AI)与自动化工…...

RE正则提取数字

RE正则提取数字import resddfff1234567890aasdfff s1s[::-1] print(fs:{s};s1:{s1}) option_str re.sub("\D", "", s) print(option_str )...

AI 术语通俗词典:贝叶斯估计

贝叶斯估计是统计学、机器学习、概率推断和人工智能中非常重要的一个术语。它用来描述一种在已有认识的基础上,根据新数据更新参数判断的方法。换句话说,贝叶斯估计是在回答:我们原来对参数有一个初步判断,现在看到了一批数据&…...

AI应用配置管理实战:从环境变量到多租户架构的工程化解决方案

1. 项目概述:AI配置管理的“瑞士军刀”最近在折腾AI应用开发,特别是那些需要调用不同模型、处理复杂提示词的项目时,配置管理简直是个噩梦。每个模型API的密钥格式不一样,提示词模板散落在各个脚本里,环境变量多得记不…...

免费好用的去水印工具推荐:哪个效果最好?免费去水印工具对比 2026 实测

免费好用的去水印工具推荐:哪个效果最好?免费去水印工具对比 2026 实测 去水印这件事,真的是越来越高频了。自媒体剪素材、收藏喜欢的短视频、整理图片资料……一旦碰到带水印的内容,找个顺手的工具就成了刚需。网上工具多&#x…...

小米Agent岗二面:你们 RAG 知识库上线之后,文档更新了怎么办?

👔面试官:你们 RAG 知识库上线之后,文档更新了怎么办?总不能每次改个文档就把整个知识库重建一遍吧。 🙋‍♂️我:可以直接找到变了的那个 chunk,更新它的向量就行了。 👔面试官&a…...

免费一键去图片水印的App有哪些?免费去图片水印软件推荐,2026实测好用工具盘点

免费一键去图片水印的App有哪些?免费去图片水印软件推荐,2026实测好用工具盘点 在日常用图的过程中,水印几乎是绕不开的麻烦——从网络下载的素材到平台截图,从拍摄叠加的文字标注到品牌Logo,各种形式的水印让图片用起…...

DOM Node:深入解析与高效使用

DOM Node:深入解析与高效使用 引言 DOM(Document Object Model)是现代网页开发的核心技术之一,它允许开发者以程序化的方式操作HTML文档。DOM Node是DOM的核心概念之一,理解并熟练使用DOM Node对于提高网页开发效率至关重要。本文将深入解析DOM Node的概念、类型、属性和…...

从新手到老手:四类Ozon卖家选品工具选择指南

选品工具没有“最好”,只有“最匹配你当前阶段”。四类卖家,四种方案。市面上的Ozon选品工具,功能各有侧重。有的擅长给数据,有的擅长给结论,有的擅长管店铺。不同阶段的卖家,痛点不同,适合的工…...

02数据模型与单词仓库-鸿蒙PC端Electron开发

欢迎加入开源鸿蒙PC社区 https://harmonypc.csdn.net/ 源码仓库 https://atomgit.com/qq_33247427/englishProject.git 效果截图 第2篇:数据模型与单词仓库 系列教程导航 篇号 标题 状态 01 环境搭建与项目创建 ✅ 已完成 02 数据模型与单词仓库 本篇 …...

Serverless平台为何总让人“又爱又恨”?揭秘Lovable设计的3层情感化架构(开发者体验×运维韧性×业务敏捷)

更多请点击: https://intelliparadigm.com 第一章:Serverless平台为何总让人“又爱又恨”? Serverless 架构在现代云原生开发中已成为主流选择,它承诺“无需管理服务器”,让开发者专注业务逻辑。然而,在真…...

面试记录 (2026/5/12)

问题一:java并发包下的AQS,了解多少? 这个真是没看过源码,就不班门弄斧了 直接学习下 大佬的经验 https://blog.csdn.net/qq_45772447/article/details/149126295?fromshareblogdetail&sharetypeblogdetail&sharerId149126295&…...

Sora 2国内可用性深度测评(2024Q2最新版):API调用失败率<0.8%的私有化部署方案首次公开

更多请点击: https://intelliparadigm.com 第一章:ChatGPT Sora 2视频生成怎么用 Sora 2 并非 OpenAI 官方发布的模型——截至目前(2024年中),OpenAI 仅公开了 Sora(初代)的演示能力&#xff0…...

Deep Lake:统一多模态AI数据存储与向量检索的实践指南

1. 项目概述:Deep Lake,一个为AI而生的数据湖 如果你正在构建一个需要处理图像、文本、音频、PDF,甚至医学影像DICOM文件的大模型应用,或者你在训练一个需要高效加载海量数据的深度学习模型,那么你很可能正被数据管理…...

OpenClaw智能体引导基准测试:本地LLM多步骤任务执行能力评估

1. 项目概述:一个专为LLM智能体设计的“开箱即用”能力基准测试 如果你最近在关注本地大语言模型(LLM)和智能体(Agent)的进展,可能会发现一个现象:很多模型在标准问答或代码生成任务上表现不错…...

【Google全家桶AI功能2026终极前瞻】:20位谷歌AI Lab核心工程师闭门透露的7大颠覆性升级路径

更多请点击: https://intelliparadigm.com 第一章:Google全家桶AI功能2026升级全景图谱 2026年,Google正式将Gemini 3.5 Ultra深度集成至全系生产力产品中,实现跨端、实时、上下文感知的AI协同。核心升级聚焦于“意图理解前置化”…...

Claude API开发实战:从模型选型到工具调用,一站式资源与代码详解

1. 项目概述与核心价值最近在折腾AI应用开发的朋友,估计没少为Claude API的调用和管理头疼。官方文档虽然详尽,但当你需要快速查找某个特定端点、对比不同模型参数,或者只是想找个现成的代码片段时,那种在多个页面间跳转、反复搜索…...

智慧工地起重机吊钩检测数据集VOC+YOLO格式1138张1类别

数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件)图片数量(jpg文件个数):1138标注数量(xml文件个数):1138标注数量(txt文件个数):1138标注类别…...

LLM与图数据库融合:自然语言驱动知识图谱查询实战

1. 项目概述:当LLM遇见图数据库,知识推理的新范式最近在探索如何让大语言模型(LLM)更好地处理复杂、结构化的知识时,我遇到了一个非常有意思的项目:dylanhogg/llmgraph。这个项目本质上是一个桥梁&#xff…...

IV测试仪选购避坑指南,这几点一定要提前了解

在光伏产业链中,IV测试仪应用广泛,覆盖组件分选、实验室检定、电站验收、运维排查等场景。市面上仪器品类繁杂,包含台式实验室款、生产线分选款、户外检测款,价格差距悬殊。不少采购人员不懂场景适配,盲目比价、堆砌参…...

只做中外合作办学,并且把它做深、做精

在中外合作办学领域,信息的透明与路径的可靠始终是学生与家长最核心的诉求。当越来越多项目涌现,如何甄别真正具备专业沉淀与行业敬畏心的服务者,成为选择前的第一道课题。这就是简申品牌存在的意义,而它背后的力量,来…...

如何快速集成Draw.io Mermaid插件:提升图表绘制效率的终极指南

如何快速集成Draw.io Mermaid插件:提升图表绘制效率的终极指南 【免费下载链接】drawio_mermaid_plugin Mermaid plugin for drawio desktop 项目地址: https://gitcode.com/gh_mirrors/dr/drawio_mermaid_plugin 还在为绘制复杂的流程图、时序图而烦恼吗&am…...

从RNN的“失忆症”到LSTM的“记忆宫殿”:图解三个门控单元如何拯救梯度消失

从RNN的"失忆症"到LSTM的"记忆宫殿":图解三个门控单元如何拯救梯度消失 想象一下,你正在阅读一本精彩的小说,但每翻过一页就会忘记前一页的大部分内容——这就是标准RNN神经网络面临的困境。在自然语言处理和时间序列分析…...

OpenAI面向欧洲部分用户开放网络安全专用模型GPT-5.5-Cyber,应对AI网络威胁

OpenAI推出欧洲专属网络安全模型 5月12日消息,据eWeek报道,OpenAI正式面向欧洲地区的部分用户开放了网络安全专用模型GPT-5.5-Cyber。该模型基于GPT-5.5架构开发,专为经过OpenAI验证的网络安全防御人员打造。 满足网络安全关键任务需求 GPT-5…...

Arm Forge工具链在HPC中的调试与性能优化实践

1. Arm Forge工具链概述高性能计算(HPC)领域的开发者经常面临并行程序调试和性能优化的挑战。Arm Forge作为一套集成化工具平台,包含了三个核心组件:DDT并行调试器、MAP性能分析器和Performance Reports报告生成工具。这套工具链特别适合处理MPI、OpenMP…...

NodeMCU固件烧录终极指南:告别命令行,5分钟搞定ESP8266刷机!

NodeMCU固件烧录终极指南:告别命令行,5分钟搞定ESP8266刷机! 【免费下载链接】nodemcu-pyflasher Self-contained NodeMCU flasher with GUI based on esptool.py and wxPython. 项目地址: https://gitcode.com/gh_mirrors/no/nodemcu-pyfl…...

Docker镜像标准化机器人开发环境:OpenClaw项目协作实践

1. 项目概述:一个面向协作开发的OpenClaw项目镜像最近在开源社区里,一个名为laolin5564/openclaw-collab-dev的Docker镜像引起了我的注意。这个镜像的名字本身就很有意思,它明确指向了“OpenClaw”和“协作开发”这两个核心概念。对于从事机器…...

基于 DWT 的盲数字水印实现(嵌入与提取)

一、原理 盲数字水印(Blind Watermarking)指提取水印时无需原始载体图像,仅依靠含水印图像和密钥即可完成。 DWT(离散小波变换) 将图像分解为: LL:低频近似分量(能量集中&#xff0c…...