LangChain CRAG 深度剖析:检索纠错到底怎么纠的?幻觉检测每次都查吗?

发布时间:2026/9/13 22:45:03

LangChain CRAG 深度剖析:检索纠错到底怎么纠的?幻觉检测每次都查吗? LangChain CRAG 深度剖析检索纠错到底怎么纠的幻觉检测每次都查吗普通 RAG 是检索 → 生成一条路走到底——检索回来的文档可能根本不相关模型照样硬着头皮编答案。Corrective RAGCRAG号称能自我纠错但纠错到底纠的是什么是改答案还是改检索幻觉检测是不是每次都调 LLM本文拆开 langchainrust 的 CRAG 实现逐行讲清楚整条纠错链路。一、CRAG 全流程6 步状态机CRAG 的核心逻辑是一个状态机6 步走完1. 检索(retrieve) → 向量检索拿回 N 篇文档 2. 评分(grade_documents) → LLM 给每篇文档打相关度分 3. 纠错(correct) → 平均分低于阈值? 触发纠错 4. 过滤(filter) → 扔掉低于阈值的文档 5. 生成(generate) → 基于过滤后的文档生成答案 6. 幻觉检测(hallucination) → LLM 检查答案有没有依据下面逐步拆解。二、评分LLM 怎么判断文档相不相关评分不是算余弦相似度是让 LLM 读文档后判断。评分 PromptYou are a document relevance grader. Given a user query and a document, determine if the document is relevant. Query: {query} Document: {document_content} Respond in this exact format: Relevance: [relevant/irrelevant] Score: [0.0 to 1.0] Reasoning: [brief explanation]LLM 返回类似Relevance: relevant, Score: 0.9, Reasoning: 文档直接描述了...代码再解析。解析规则4 种情况LLM 回了什么得分说明有明确数字Score: 0.750.75直接取最可靠含relevant不含irrelevant0.8默认相关分含irrelevant0.2默认不相关分啥都没有模糊响应0.4故意低于阈值标记is_ambiguous这里有个坑模糊响应给 0.4 是 v0.5.1 才调的。早期版本给 0.5恰好等于旧阈值也是 0.5导致 LLM 说不清时——纠不纠全看运气。改成 0.4 后模糊就触发纠错保守策略宁多纠不漏纠。评分是并行的4 篇文档不是串行评是用join_all并行调 4 次 LLMletfutures:Vec_documents.iter().map(|doc|self.grade(query,doc)).collect();letresultsjoin_all(futures).await;// 4 次同时发出延迟 ≈ 1 次 LLM 调用的时间不是 4 倍。三、纠错纠的不是答案是检索这是 CRAG 最核心的设计。当平均分低于阈值默认 0.6触发correct()ifstate.avg_scoreself.grade_threshold{// 默认 0.6self.correct(mutstate).await?;}纠错不是改答案是换一种方式重新检索。correct()做四件事3.1 生成 3 个备选查询让 LLM 把原问题换个问法生成 3 个变体Generate 3 alternative versions of the following query to improve retrieval coverage. Original query: {query} Each alternative should approach the information need from a different angle.比如原问题Rust 和 Go 的区别LLM 可能生成Rust 与 Go 语言特性对比Go 和 Rust 性能差异分析Rust Go 适用场景选择为什么要换问法向量检索有角度偏差——同一个意思换个说法可能检索不到。多角度提问能提高召回。3.2 用备选查询并行检索 去重3 个查询各检索 4 篇 → 最多 12 篇 → 按前 200 字符判重合并letretrieve_futures:Vec_alternatives.iter().map(|q|self.retriever.retrieve(q,self.retrieve_k)).collect();letall_resultsjoin_all(retrieve_futures).await;// 3 路并行letmutseen_contentHashSet::new();fordocinall_results{ifseen_content.insert(doc.content[..200]){// 按内容去重merged_docs.push(doc);}}3.3 联网搜索兜底可选如果配了 web 搜索工具还会去网上搜一次补料ifletSome(tool)self.web_fallback{letweb_resulttool.run(state.current_query.clone()).await?;state.web_resultsSome(web_result);}3.4 重新评分纠错后还会再评一轮分确保新文档的质量self.grade_documents(state).await?;// 对新文档重新打分不是换回来就直接用要验证新检索的文档确实更好。四、过滤宁可没文档不留烂文档纠错后低于阈值的文档直接扔掉letfiltered:Vec(Document,f64)state.documents.iter().zip(state.grade_scores.iter()).filter(|(_,score)|scoreself.grade_threshold).collect();// 全不达标 → 宁可空也不留烂文档误导生成letsource_docsiffiltered.is_empty(){Vec::new()}else{filtered};这是个重要设计宁可没文档生成我不知道也不要用烂文档生成看似合理实则胡编的答案。五、生成评分理由也喂给模型生成答案时不只喂文档还把评分时 LLM 说的为什么这篇相关也塞进 promptletcontextbuild_context(source_docs,state.web_results,...);letreasoning_textformat_reasoning(source_docs,reasoning);// 评分理由letuser_messagebuild_generate_prompt(context,reasoning_text,state.query);这样生成模型能理解每篇文档的价值更好地组织答案。另外有个Token 预算截断build_context()按分数从高到低排超预算的低分文档直接丢letmutsorted:Vec_docs.iter().collect();sorted.sort_by(|a,b|b.1.partial_cmp(a.1)...);// 按分降序for(doc,_score)insorted{iftoken_countentry_tokensmax{continue;// 超预算直接跳过}...}六、幻觉检测每次都查且最好用另一个模型生成答案后最后一步是幻觉检测——检查答案是不是编的。什么是幻觉LLM 没有可靠资料时会一本正经地胡编。问张三的生日文档没写它可能编一个1985 年 3 月 12 日。这就是幻觉。怎么检测把文档 答案一起喂给 LLM让它以怀疑态度核查You are a skeptical fact-checking assistant. Be skeptical. Is the above answer fully grounded in and supported by the provided context? - grounded if every claim is directly supported - not grounded if any information is not found in or contradicted by the context解析 LLM 回复ifwords 包含not grounded或unsupported{state.groundedfalse;// 判定编的}elseifwords 包含grounded或yes{state.groundedtrue;// 判定有据}else{state.groundedfalse;// 模糊 → 默认当编的保守}三个关键设计点① 为什么用独立 LLM生成答案和检测幻觉默认用同一个 LLM。问题是模型倾向于认同自己的输出——自己写的自己看当然觉得对这就是自检偏差。所以可以注入独立 LLM 做检测letagentCorrectiveRAGAgent::new(main_llm,retriever).with_grader_llm(checker_llm);// 用另一个模型检测代码里letgrader_llmself.grader_llm.unwrap_or(self.llm);// 优先独立 LLM没配就用主 LLM向后兼容。② 检测失败不中断检测本身调用失败网络断了、限流了不会让整个流程崩而是降级标记grounded falsematchgrader_llm.chat_with_system(...).await{Ok(r)r,Err(_){state.groundedfalse;// 标记未验证不报错returnOk(());}}答案照常返回只是标了未验证。③ 结果怎么用CRAGResult.grounded告诉调用方答案有没有经过验证letresult:CRAGResultagent.invoke(问题).await?;if!result.grounded{// 答案可能不靠谱提示用户或走别的策略}七、灵魂拷问每次都调 LLM 吗调几次这是性能和成本的核心问题。数一下invoke()一次的 LLM 调用步骤调 LLM次数1. 检索❌向量检索不调 LLM2. 评分✅每篇文档 1 次4 篇 4 次并行3. 纠错触发时✅生成备选查询 1 次 重评分 ~4 次并行 web 1 次4. 过滤❌纯逻辑5. 生成✅1 次6. 幻觉检测✅1 次如果开启最少几次文档质量好不纠错评分 4 次 生成 1 次 幻觉检测 1 次 6 次最多几次触发纠错 联网搜索评分 4 次 生成备选查询 1 次 重评分 4 次 web 1 次 生成 1 次 幻觉检测 1 次 ≈ 12 次没有缓存代码里没有上次评分过就跳过的缓存机制每次invoke()都完整跑一遍。但有两个优化评分并行join_all4 次同时发不是串行等备选查询检索并行3 路同时搜所以实际延迟 ≈ 几个 LLM 往返不是十几次串行相加。八、完整链路一图流用户提问 │ ▼ [1] 检索 ─── 向量检索 N 篇文档 │ ▼ [2] 评分 ─── LLM 并行给每篇打分4 次并行调用 │ ▼ 平均分 0.6? ├─ 否 ───────────────────────────────────┐ │ │ ├─ 是 → [3] 纠错 │ │ ├─ 生成 3 个备选查询1 次 LLM │ │ ├─ 并行检索 去重3 路并行 │ │ ├─ 联网搜索兜底可选1 次 │ │ └─ 重新评分~4 次 LLM 并行 │ │ │ ▼ ▼ [4] 过滤 ─── 扔掉低分文档宁可空不留烂 │ ▼ [5] 生成 ─── 文档 评分理由 → LLM 生成答案1 次 │ ▼ [6] 幻觉检测 ── 文档 答案 → LLM 怀疑式核查1 次 │ └─ 优先用独立 LLM避免自检偏差 │ └─ 失败不中断标记 groundedfalse │ ▼ 输出 CRAGResult ├─ answer: 答案 ├─ grounded: 是否有据幻觉检测结论 ├─ sources: 来源文档 ├─ grade_scores: 各文档评分 └─ grade_reasoning: 评分理由九、一句话总结CRAG 的纠错不是改答案是改检索——评分发现检索回来的文档不行就换个问法重新搜、去网上搜搜到更好的文档后再生成。最后还用最好独立的LLM 验一遍答案是不是胡编的。代价是 LLM 调用次数多6~12 次但换来了可观测的质量信号grounded字段和更高的答案可靠性。十、什么时候该用 CRAG适合对答案准确性要求高、容忍延迟的场景客服质检、医疗法律咨询检索质量不稳定文档库杂、向量模型一般需要质量信号做后续决策groundedfalse时转人工不适合实时聊天6~12 次 LLM 调用延迟太高成本敏感的大规模批处理调用量翻倍简单 FAQ普通 RAG 足够CRAG 是杀鸡用牛刀仓库github.com/atliliw/langchainrust
延伸阅读

更多相关文章

2026/9/13 9:24:15

Ryujinx终极指南:如何快速配置高性能Switch模拟器

Ryujinx终极指南:如何快速配置高性能Switch模拟器 【免费下载链接】Ryujinx 用 C# 编写的实验性 Nintendo Switch 模拟器 项目地址: https://gitcode.com/GitHub_Trending/ry/Ryujinx Ryujinx是一款用C#编写的高性能Nintendo Switch模拟器,让你在…

2026/9/13 8:55:46

校园讲解设备按功率选错了,声场适配才是隐形分水岭

【文章概述】之前做过几个学校的项目,都是看起来很简单但落地翻车的类型。需求一句话:参观的时候前面的人讲,后面的人听。校方立项的时候关注的都是续航、音量、覆盖范围,这些在招标参数表上能对比的东西。设备到了现场实地跑一趟…

2026/9/13 22:43:19

Appium+Python自动化测试实战源代码资料

自动化测试源码在 IT 行业内, 自动化测试属于提升软件开发效率与质量的要紧办法, 对移动应用开发范畴而言更是如此, 而这里提及的是“自动化测试源码”。自动化测试关于自动化测试的详细解析, 在正式运用结合并开展自动化测试之前, 首先得完成一系列基础环境的搭建, 这是第一步…

2026/9/13 22:43:19

ansible 实例 -- 用于 Linux 文件系统的操作 -- 修改文件或目录权限

如何使用 Ansible 修改文件或目录权限? 我将向你展示如何更改 test.txt 文件的 chmod 和所属组。 如何自动化修改文件/目录权限。 今天我们来谈谈 Ansible 模块 file。 全名是 ansible.builtin.file,这意味着它是 Ansible 自带的 “builtin” 集合的一部分。 这是一个非…

2026/9/13 22:43:19

Firecrawl 实战:将网站转换为大模型可用数据

本文摘要:传统爬虫直接获取的 HTML 包含导航、脚本、广告等噪音,无法作为大语言模型(LLM)的优质上下文。Firecrawl 是一款开源的网页数据转换引擎,它提供了一条清晰的管线:输入 URL → 智能爬取/渲染 → 输…

2026/9/13 0:01:16

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/13 0:01:16

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/12 6:29:36

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/12 14:32:17

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/13 11:18:28

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码