CRAG 架构与置信度路由

系列导航

📍 第 1 篇:CRAG 架构与置信度路由(本文)

📄 第 2 篇:RRF 混合检索 + BGE 重排序

📄 第 3 篇:语义切片 + Ragas 评估体系

📄 第 4 篇:生产环境部署与优化

摘要

传统 RAG 系统的致命缺陷在于"盲目自信"------无法判断检索质量,即使返回的文档完全不相关也会硬着头皮生成答案。本文介绍 CRAG(Corrective RAG)架构,通过"置信度评估→动态路由"机制让 RAG 具备自我反思能力,在 Agentic RAG 知识库专家系统中实现 Context Recall +26%(0.62→0.78)、Faithfulness 0.85 的效果提升。

环境依赖:Python 3.11+, LangGraph 0.2.0+, OpenAI SDK 1.12.0+, Qdrant Client 1.7.0+


引言:一个让所有 RAG 开发者头疼的场景

你花了两周时间搭建 RAG 系统:精心调优 Embedding 模型、配置向量数据库、设计 Prompt 模板。上线第一天,用户问了一个问题:"公司的远程办公政策是什么?"

检索模块返回了 5 个文档:

  • 文档 1:2019 年的考勤制度(已过期)
  • 文档 2:会议室预订流程(完全无关)
  • 文档 3:2023 年远程办公政策(相关!)
  • 文档 4:某个员工的请假申请(噪音)
  • 文档 5:IT 设备借用规定(弱相关)

传统 RAG 会怎么做?把这 5 个文档全部塞进 Prompt,让 LLM 生成答案。结果?模型被噪音文档误导,输出了一个看似专业但实际错误的答案------混合了过期政策和无关信息。

这不是个例。在实际生产环境中,检索质量问题是 RAG 系统失败的主要原因之一。问题的本质在于:传统 RAG 缺少"评估"环节,无法判断检索结果是否可信,更无法采取补救措施

如何让 RAG 系统"知道它不知道"?这是本文要解决的核心问题。


传统 RAG 的"盲目自信"困境

传统 RAG 的工作流程是一条固定流水线:

复制代码
用户提问 → 向量检索 → 拼接上下文 → LLM 生成答案

这个流程有三个致命缺陷:

1. 无质量评估

检索模块返回 Top-K 文档后,系统不会判断这些文档是否真的相关。即使相似度分数很低(比如 0.3),也会被当作"有效上下文"送入 LLM。

2. 无反馈机制

当检索失败时(比如知识库中根本没有相关文档),系统不会尝试其他策略,而是直接让 LLM 基于空洞的上下文"硬编"答案。

3. 无动态路由

所有问题都走同一条路径,无论是简单的事实查询("公司地址是什么?")还是复杂的推理问题("如何优化跨部门协作流程?"),都用相同的检索策略处理。

用架构图表示:

markdown 复制代码
┌─────────┐     ┌─────────┐     ┌─────────┐     ┌─────────┐
│ 用户提问 │ ──→ │ 向量检索 │ ──→ │ 拼接上下文│ ──→ │ LLM 生成 │
└─────────┘     └─────────┘     └─────────┘     └─────────┘
                                                      ↓
                                                  返回答案

这是一条单向直线,没有分支、没有回退、没有自我修正。

量化这个问题:在我的测试数据集上,传统向量检索的 Context Recall 只有 0.62------意味着 38% 的相关信息根本没被检索到。更糟糕的是,Faithfulness(答案忠实度)只有 0.72,说明生成的答案经常偏离事实。


CRAG 登场:给 RAG 装上"自我反思"模块

CRAG(Corrective RAG)由 Shi-Qi Yan 等人在 2024 年提出(arXiv:2401.15884),核心思想只有一句话:

在检索和生成之间插入一个"相关性评估"节点,根据评估结果动态选择后续路径。

这个简单的改动带来了本质变化:RAG 从"流水线"变成了"状态机"。

CRAG 的完整流程:

markdown 复制代码
                    ┌─────────────┐
                    │   用户提问   │
                    └──────┬──────┘
                           ↓
                    ┌─────────────┐
                    │  向量检索    │
                    └──────┬──────┘
                           ↓
                    ┌─────────────┐
                    │ 置信度评估   │ ← 核心创新
                    └──────┬──────┘
                           ↓
              ┌────────────┼────────────┐
              ↓            ↓            ↓
         置信度≥7      3≤置信度<7    置信度<3
              ↓            ↓            ↓
        ┌─────────┐  ┌─────────┐  ┌─────────┐
        │直接生成  │  │查询重写  │  │Web搜索  │
        └─────────┘  └────┬────┘  └────┬────┘
                          ↓            ↓
                     ┌─────────┐  ┌─────────┐
                     │二次检索  │  │外部知识  │
                     └────┬────┘  └────┬────┘
                          ↓            ↓
                     ┌─────────────────┐
                     │   LLM 生成答案   │
                     └─────────────────┘

对比传统 RAG,CRAG 的本质变化是:

维度 传统 RAG CRAG
架构模式 固定流水线 动态状态机
质量评估 置信度打分(0-10)
失败处理 硬着头皮生成 查询重写 / Web 回退
路由策略 单一路径 3 条分支(高/中/低置信度)
自我修正 不支持 支持(最多 2 次重试)

这就是"自我反思"的含义:系统能够评估自己的检索结果,并根据评估结果调整行为


置信度路由:3/7 阈值是怎么来的?

CRAG 的核心是"置信度评估器"。但如何设计这个评估器?阈值怎么定?

评估器设计

我使用 gpt-4o-mini 作为评估器(而不是 gpt-4o),原因有三:

  1. 分类任务足够:判断文档相关性是分类问题,不需要强推理能力
  2. 延迟降低 10-20 倍:gpt-4o-mini 平均延迟 200ms,gpt-4o 需要 2-3s
  3. 成本降低 50 倍:评估器会被频繁调用,成本敏感

评估器的输入是"用户问题 + 检索到的文档",输出是:

  • 置信度分数(0-10):文档与问题的相关性
  • 推理理由:为什么给这个分数(用于调试)

代码实现

python 复制代码
from openai import OpenAI
from typing import List, Dict

client = OpenAI()

def evaluate_relevance(query: str, documents: List[str]) -> Dict:
    """
    评估检索文档与查询的相关性
    
    Args:
        query: 用户查询
        documents: 检索到的文档列表
    
    Returns:
        {
            "confidence": float,  # 0-10 的置信度分数
            "reasoning": str      # 评估理由
        }
    """
    # 构建评估 prompt
    docs_text = "\n\n".join([f"文档 {i+1}:\n{doc}" for i, doc in enumerate(documents)])
    
    prompt = f"""你是一个文档相关性评估专家。请评估以下检索文档与用户查询的相关性。

用户查询:{query}

检索到的文档:
{docs_text}

请按以下格式输出:
1. 置信度分数(0-10):
   - 0-3: 文档完全不相关或严重偏离主题
   - 4-6: 文档部分相关但信息不完整
   - 7-10: 文档高度相关且包含足够信息

2. 评估理由:简要说明为什么给出这个分数

输出格式:
分数: [0-10的数字]
理由: [你的评估理由]"""

    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        temperature=0  # 降低随机性,保证评估稳定
    )
    
    # 解析输出
    content = response.choices[0].message.content
    lines = content.strip().split('\n')
    
    confidence = 5.0  # 默认值
    reasoning = ""
    
    for line in lines:
        if line.startswith("分数:"):
            try:
                confidence = float(line.split(":")[1].strip())
            except:
                pass
        elif line.startswith("理由:"):
            reasoning = line.split(":", 1)[1].strip()
    
    return {
        "confidence": confidence,
        "reasoning": reasoning
    }

# 使用示例
query = "Transformer 的注意力机制是什么?"
documents = [
    "Transformer 使用自注意力机制来处理序列数据...",
    "CNN 是一种卷积神经网络..."
]

result = evaluate_relevance(query, documents)
print(f"置信度: {result['confidence']}")
print(f"理由: {result['reasoning']}")

阈值选择的消融实验

论文建议的阈值是 (3, 7),但我不想盲目照搬。我在 50 个测试问题上尝试了三组阈值:

阈值组合 直接生成率 查询重写率 Web 回退率 Context Recall 平均延迟
(5, 8) 12% 64% 24% 0.71 3.8s
(4, 7) 38% 48% 14% 0.75 2.9s
(3, 7) 58% 32% 10% 0.78 2.3s

为什么 (3, 7) 最优?

  • (5, 8) 的问题:高置信度区间过窄(8-10),导致大量中等质量的检索被降级到"查询重写",效率低下。比如"公司地址是什么?"这种简单问题,检索到的文档置信度是 7.5,本应直接生成,却被强制重写。

  • (4, 7) 的问题:效果中等,但低置信度阈值 4 太高,一些"勉强相关"的文档(置信度 3.5)被直接回退到 Web 搜索,浪费了知识库中的有效信息。

  • (3, 7) 的优势

    • 简单问题(事实查询)99% 的置信度≥7,直接生成,节省 token
    • 边界问题(模糊查询)触发查询重写,成功率高
    • 只有真正"检索不到"的问题(置信度<3)才回退 Web,避免过度依赖外部搜索

三条路由详解

路由 1:置信度≥7 → 直接生成

适用场景:检索到的文档高度相关,可以直接回答问题。

示例:"公司的年假政策是什么?" → 检索到《员工手册-休假制度》,置信度 9.2 → 直接生成。

路由 2:3≤置信度<7 → 查询重写

适用场景:检索结果中等质量,可能是用户问题表述不清或关键词不匹配。

示例:"怎么申请在家办公?" → 检索到《考勤管理办法》(弱相关),置信度 5.1 → 重写为"远程办公申请流程" → 二次检索到《远程办公管理规定》,置信度 8.7 → 生成答案。

路由 3:置信度<3 → Web 搜索

适用场景:知识库中没有相关信息,需要外部知识补充。

示例:"2026 年劳动法对加班的最新规定是什么?" → 知识库中没有最新法规,置信度 1.8 → 回退到 Web 搜索 → 获取外部信息 → 生成答案。

这三条路由的设计哲学是:优先使用内部知识(快且可控),必要时才求助外部(慢但全面)


实验对比:CRAG 到底好在哪里?

我在 Agentic RAG 知识库专家系统中进行了对比实验,测试数据集包含 120 个真实用户问题(涵盖事实查询、推理问题、时效性问题)。

策略 Context Recall Faithfulness 说明
纯向量检索 0.62 0.72 Baseline(传统 RAG)
混合检索(RRF) 0.70 0.78 +13% / +8%
混合 + Reranker 0.74 0.82 +19% / +14%
完整 CRAG 0.78 0.85 +26% / +18%

为什么 CRAG 在 Context Recall 上提升最大?

Context Recall 衡量的是"相关信息的召回率"------系统是否找到了所有应该找到的信息。CRAG 的提升来自两个机制:

  1. 查询重写:当初次检索失败时,重写查询可以用不同的关键词再试一次。比如用户问"如何请假",初次检索用"请假"作为关键词,可能匹配不到《休假管理制度》;重写为"休假申请流程"后,成功匹配。

  2. Web 回退:当知识库中确实没有信息时,Web 搜索可以补充外部知识。这直接解决了"检索不到"的问题------传统 RAG 会返回空结果,CRAG 会去外部找答案。

Faithfulness 的提升则来自"高质量上下文":通过置信度过滤,低质量文档不会进入生成阶段,减少了 LLM 被噪音误导的概率。


实践中的踩坑与优化

坑 1:评估器延迟过高

问题 :最初使用 gpt-4o 作为评估器,每次评估耗时 2-3s,导致整体延迟飙升到 5-6s。
解决:换成 gpt-4o-mini,延迟降低到 200ms,效果几乎无损(分类任务不需要强推理)。

坑 2:查询重写陷入死循环

问题 :某些问题重写后仍然检索不到相关文档,系统会不断重写→检索→重写,陷入死循环。
解决:设置最大重试次数为 2。如果两次重写后置信度仍<7,直接回退到 Web 搜索。

坑 3:Web 搜索结果格式不统一

问题 :Web 搜索返回的是 HTML 片段,格式与知识库文档不一致,导致后续处理出错。
解决:封装统一的 Document 结构,所有外部来源的内容都转换为标准格式(包含 page_content、metadata、source 字段)。

坑 4:置信度分数不稳定

问题 :同一个问题多次评估,置信度分数波动较大(比如 6.8 → 7.2 → 6.5),导致路由不稳定。
解决:在 Prompt 中增加 few-shot 示例,明确"7 分代表什么程度的相关性",稳定输出格式。同时在评估器中加入温度参数 temperature=0,减少随机性。


总结与下期预告

CRAG 的核心创新在于"评估→路由"机制,让 RAG 系统具备了自我反思能力:

  • 能评估:通过置信度打分判断检索质量
  • 能修正:通过查询重写和 Web 回退补救失败
  • 能决策:根据置信度动态选择最优路径

在 Agentic RAG 知识库专家系统中,CRAG 实现了:

  • Context Recall +26%(0.62 → 0.78)
  • Faithfulness 0.85
  • P95 延迟 2.3s

但 CRAG 只是第一步。置信度评估依赖的是"检索到的文档是否相关",但如何提升检索本身的质量?下期我将深入解析:

  • RRF 混合检索:如何融合向量检索和关键词检索的优势
  • BGE 重排序:如何用 Reranker 模型进一步过滤噪音

开源地址github.com/Yunzenn/age...

欢迎 Star / Issue / PR,一起探索 Agentic RAG 的更多可能性。


本文是"从零开始理解 Agentic RAG"系列的第 1 篇,聚焦 CRAG 核心架构。后续文章将深入检索优化、评估体系、生产部署等话题。

相关推荐
逛逛GitHub5 小时前
YC 总裁开源了自己亲手写的 AI Agent 大脑,1 周就 1 万点赞。
github
CoderJia程序员甲7 小时前
GitHub 热榜项目 - 日榜(2026-04-21)
ai·大模型·llm·github·ai教程
Garfield20058 小时前
基于 GitHub 开源项目二次开发:Upstream 同步、Merge / Rebase 边界与实践
开源·github
KD8 小时前
「OpenClaw」我写了个桌面控制Skill,让龙虾接管电脑!(MacOS版)
人工智能·开源·github
研究点啥好呢8 小时前
Github热榜项目推荐 | Fireworks Tech Graph:告别手动绘图时代
python·开源·github·claude·skills
Jempo M9 小时前
为GitHub Copilot手搓一个可调用工具的AI Agent
人工智能·github·copilot
of Watermelon League9 小时前
SQL server配置ODBC数据源(本地和服务器)
运维·服务器·github
❀͜͡傀儡师9 小时前
GitHub Copilot for VS Code 中文使用完整教程
vscode·github·copilot
MapleWan320639 小时前
告别 AI IDE 配置碎片化:用 MSR-cli 打造你的本地 MCP / Rules / Skills 统一仓库
python·github