尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
LangChain Corrective RAG:检索结果不好时,怎么补救
个人主页 for_ever_love__ 欢迎各位大佬莅临其他栏目: 大模型开发从0到1 其他栏目: iOS项目总结大全 其他栏目: 我想学python了 其他栏目: iOS UI 文章目录LangChain Corrective RAG检索结果不好时怎么补救一、核心思路二、第一步给文档打分三、第二步按分数分流3.1 路径 Agenerate直接用3.2 路径 Brefine精炼3.3 路径 Cfallback换检索源四、用 LangGraph 串起来五、成本评估六、效果验证七、什么时候用八、和相似方案的区别九、小结LangChain Corrective RAG检索结果不好时怎么补救检索回来的文档质量参差有的完全相关有的沾点边有的纯粹是噪声。直接全喂给模型噪声会干扰答案。Corrective RAGCRAG的思路是先给检索结果打个分按分数走不同的补救路径。一、核心思路检索 → 逐条打分相关 / 模糊 / 不相关 ↓ 按质量分流 ├─ 全是相关的 → 直接用 ├─ 有模糊的 → 精炼一下再用 └─ 全不相关 → 换检索源比如转网页搜索关键在分流——不同质量走不同处理而不是一刀切。对比一下普通 RAG 的做法# 普通 RAG不管质量全喂docsretriever.invoke(q)answerchain.invoke({context:format_docs(docs),question:q})问题在于如果 top-5 里有 3 条是噪声模型很可能被带偏。宁滥勿缺的召回策略必须有对应的质量控制环节。二、第一步给文档打分fromlangchain_core.promptsimportChatPromptTemplatefromlangchain_openaiimportChatOpenAIfromlangchain_core.output_parsersimportStrOutputParser GRADE_PROMPTChatPromptTemplate.from_template( 判断下面的文档片段能否回答用户的问题。 只输出一个词 - relevant文档中包含能回答问题的信息 - ambiguous文档中有一部分相关信息但不足以完整回答 - irrelevant文档与问题无关 不要解释只输出这三个词之一。 【问题】 {question} 【文档片段】 {doc} )grade_chain(GRADE_PROMPT|ChatOpenAI(modelgpt-4o-mini,temperature0,max_tokens10)|StrOutputParser())defgrade_documents(question:str,docs:list)-dict:verdictsgrade_chain.batch([{question:question,doc:d.page_content}fordindocs],config{max_concurrency:5},)buckets{relevant:[],ambiguous:[],irrelevant:[]}fordoc,vinzip(docs,verdicts):keyv.strip().lower()buckets[keyifkeyinbucketselseambiguous].append(doc)returnbuckets用batch并发打分5 条文档大约 1 秒内完成。max_tokens10省成本。三、第二步按分数分流fromtypingimportLiteraldefdecide(buckets:dict)-Literal[generate,refine,fallback]:relevantbuckets[relevant]ambiguousbuckets[ambiguous]iflen(relevant)2:returngenerate# 有足够的相关文档直接生成ifrelevantorambiguous:returnrefine# 有点相关但不够精炼一下returnfallback# 全不相关换检索源3.1 路径 Agenerate直接用defgenerate(state):ctxformat_docs(state[relevant])ans(prompt|llm|StrOutputParser()).invoke({context:ctx,question:state[question]})return{answer:ans}3.2 路径 Brefine精炼模糊的文档通常很长只有一两句有用。抽出来丢掉其余部分。REFINE_PROMPTChatPromptTemplate.from_template( 从下面的文档片段中摘出与问题相关的原句。 只摘录原文中出现的内容不要改写、不要推断、不要补充。 如果片段中确实没有相关信息输出无。 【问题】 {question} 【文档片段】 {doc} )refine_chainREFINE_PROMPT|llm|StrOutputParser()defrefine(state):docsstate[relevant]state[ambiguous]snippetsrefine_chain.batch([{question:state[question],doc:d.page_content}fordindocs],config{max_concurrency:5},)ctx\n\n.join(sforsinsnippetsifs.strip()ands.strip()!无)ifnotctx:return{answer:没有找到能够回答这个问题的信息。}ans(prompt|llm|StrOutputParser()).invoke({context:ctx,question:state[question]})return{answer:ans}这一步等价于前面讲过的上下文压缩但在 CRAG 里它是按质量触发的而不是每次都做——更省。3.3 路径 Cfallback换检索源知识库里没有不代表答案不存在。tooldefweb_search(query:str)-str:当知识库中没有答案时用互联网搜索补充。returnsearch_api(query)deffallback(state):# 1. 先改写查询可能是表述问题new_qrewrite_chain.invoke({question:state[question]})# 2. 换源再检索一次docsweb_retriever.invoke(new_q)ifhas_webelse[]ifnotdocs:return{answer:没有找到相关信息。}# 3. 对外部来源要更谨慎ans(strict_prompt|llm|StrOutputParser()).invoke({context:format_docs(docs),question:state[question]})return{answer:ans,source:web}⚠️fallback 到网页搜索要慎重外部内容的可靠性无法保证企业场景可能涉及数据合规。要么不开要么明确标注答案来自互联网仅供参考。四、用 LangGraph 串起来fromtypingimportTypedDict,Annotated,Literalfromlanggraph.graphimportStateGraph,START,ENDclassState(TypedDict):question:strdocuments:listrelevant:listambiguous:listanswer:strsource:strattempts:intdefretrieve(state:State):docsretriever.invoke(state[question])return{documents:docs,attempts:state.get(attempts,0)1}defgrade(state:State):bucketsgrade_documents(state[question],state[documents])return{relevant:buckets[relevant],ambiguous:buckets[ambiguous]}defroute(state:State)-Literal[generate,refine,fallback,give_up]:ifstate[attempts]2:# 已经重试过一次不再折腾returngenerateifstate[relevant]elsegive_upiflen(state[relevant])2:returngenerateifstate[relevant]orstate[ambiguous]:returnrefinereturnfallbackdefgive_up(state:State):return{answer:没有找到能够回答这个问题的信息。}graphStateGraph(State)graph.add_node(retrieve,retrieve)graph.add_node(grade,grade)graph.add_node(generate,generate)graph.add_node(refine,refine)graph.add_node(fallback,fallback)graph.add_node(give_up,give_up)graph.add_edge(START,retrieve)graph.add_edge(retrieve,grade)graph.add_conditional_edges(grade,route,{generate:generate,refine:refine,fallback:fallback,give_up:give_up,})graph.add_edge(generate,END)graph.add_edge(refine,END)graph.add_edge(fallback,retrieve)# 换源后再走一遍检索评分graph.add_edge(give_up,END)appgraph.compile()注意fallback → retrieve这条回边它构成了换源重试的循环靠attempts计数防止无限循环。五、成本评估环节额外开销打分1 次 batchk 条文档精炼仅触发时1 次 batch换源重试仅触发时1 轮完整流程用便宜模型打分gpt-4o-mini 足够成本可控。实测下来相比普通 RAG总成本增加约 20%~40%但答案质量尤其是不编造这一项提升明显。省钱的技巧只对 top-5 打分不要对 top-20 全打打分用最小最便宜的模型这任务很简单缓存打分结果——相同文档 相似问题可以复用。六、效果验证上一批的评测篇讲过怎么做测试集。CRAG 上线前后重点对比这三个指标指标期望变化忠实度faithfulness提升噪声被过滤拒答准确率明显提升不相关时不再硬编答案相关性略降部分问题改成拒答了注意第三条CRAG 会让一部分原本硬答的问题变成拒答所以相关性分数可能略微下降——这是好事不是退步。要分开看答了的对不对和该拒的拒了没。七、什么时候用场景用不用检索质量不稳定有时准有时不准强烈推荐对不编造要求高医疗、法务、财务强烈推荐检索已经很准top-3 全都相关收益有限白花钱延迟要求极严打分环节会增加 1~2 秒先测一下你的检索质量随机抽 20 个问题人工看 top-5 里有几条是相关的。如果平均只有 2~3 条相关CRAG 的价值就很大如果稳定 4~5 条相关就没必要。八、和相似方案的区别方案做什么区别重排rerank重新排序只排序不判断够不够上下文压缩抽句精简每次都做不按质量分流CRAG打分 分流 换源有明确的质量不够就换路逻辑三者可以叠加rerank 排序 → CRAG 打分分流 → 压缩精炼。九、小结CRAG 三步逐条打分relevant/ambiguous/irrelevant→ 按质量分流generate / refine / fallback→ 换源重试打分用batch并发 便宜模型 max_tokens10成本很低refine本质是按质量触发的上下文压缩fallback换检索源企业场景慎用网页搜索用 LangGraph 实现时务必用attempts计数防止fallback → retrieve死循环预期效果忠实度和拒答准确率提升相关性可能略降这是正常的适用检索质量不稳定、对不编造要求高的场景。下一篇讲 Text-to-SQL——让不会写 SQL 的人查数据库。
RELATED

相关推荐

气温预测项目实战:五模型对比与部署全流程解析

气温预测项目实战:五模型对比与部署全流程解析

简介:基于Python机器学习的气温预测与可视化课程设计项目,覆盖数据获取、特征工程、模型训练、结果评估与界面展示完整流程,可直接作为期末大作业或课程设计的高分参考。压缩包共38个文件,大小约12.17MB,主要包含.py训…

📅 2026/10/12 22:46:24
VOC/COCO/YOLO标签格式转换与YOLOv8实战指南

VOC/COCO/YOLO标签格式转换与YOLOv8实战指南

简介:面向YOLO目标检测学习者和工业安全场景开发者,这份YOLO安全帽手套检测数据集汇集5000张真实场景图片,标注质量高、场景多样,可有效支撑工地、工厂等区域的智能安全监测模型训练。压缩包共2000个文件,以1987个XML标…

📅 2026/10/12 22:46:24
C++类和对象进阶:static成员、友元、内部类与匿名对象详解

C++类和对象进阶:static成员、友元、内部类与匿名对象详解

学C学到类和对象,很多人会卡在几个看起来很“别扭”的概念上:static成员、友元、内部类、匿名对象。这四个东西不像构造函数、析构函数那样“天天见面”,但在实际工程和面试题里出现频率极高。尤其是static成员和友元,一个管“归属…

📅 2026/10/12 22:46:24
MORE NEWS

更多资讯

📰

PHP后端活体检测集成:服务端签名验签全流程实现

1. 项目概述:为什么PHP项目现在必须嵌入活体识别能力最近三个月,我连续接手了三个不同行业的风控系统改造需求——某在线教育平台的教师资质核验、某本地生活服务平台的骑手实名认证、还有某社区团购系统的团长身份绑定。它们表面业务差异很大&#xff0…

📰

one_gadget实战:攻防世界babystack从栈溢出到getshell

1. 从一道栈溢出题说起:one_gadget 到底解决什么问题如果你打过一段时间的 CTF pwn 题,大概率会遇到这样一种情况:题目给了栈溢出,程序里也有现成的 system 函数或者后门函数,但偏偏没有给你一个干净的/bin/sh字符串&a…

📰

从简历海啸到智能初筛:我用 ModelEngine Nexent 零代码搭建了一个招聘 Agent

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Anaconda+Jupyter数据分析环境配置与实战指南

1. 为什么数据分析老手都绕不开Anaconda这套组合刚接触Python做数据分析的人,十有八九会在环境配置这一步卡住。你可能只是想跑个pandas读CSV,结果装完Python发现缺numpy,装完numpy又提示scipy版本不兼容,好不容易凑齐了&#xff…

📰

企业AI落地第一道坎:为什么你的公司凑不出一份能喂给AI的文档

1. 一个让不少团队沉默的现场前阵子跟几个做企业数字化的朋友吃饭,聊到一个特别有意思的现象。有个朋友说他们公司年初批了一笔预算,兴致勃勃地采购了一套AI知识助手,想着终于能让内部问答、客服辅助、新人培训这些事省点人力。结果项目启动会…

📰

12GB显存跑125B大模型:量化与分层卸载实战

1. 大模型本地部署的显存困局与破局思路1.1 为什么125B参数模型会成为显存噩梦先把账算清楚。125B参数,如果以FP16精度加载,每个参数占2字节,光权重就需要250GB显存。即便用INT8量化,也要125GB;再压到INT4,…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬