Skip to content

RAG 检索增强生成

AI 技术 ⭐⭐⭐ 高级 🔥🔥 高频

💡 核心要点

RAG(Retrieval-Augmented Generation,检索增强生成)通过在生成前从外部知识库中检索相关信息,为大语言模型提供实时、准确的上下文,解决 LLM 的幻觉问题(Hallucination)知识截止(Knowledge Cutoff)限制。随着大模型上下文窗口突破百万 Token(如 Gemini、Claude),长上下文方案正在改变 RAG 的应用格局,但 RAG 在大规模知识库、动态数据、成本控制等场景下仍然不可替代。

为什么需要 RAG

大语言模型存在以下固有局限:

问题说明RAG 如何解决
幻觉(Hallucination)模型可能生成看似合理但事实错误的内容提供检索到的真实文档作为生成依据
知识截止(Knowledge Cutoff)训练数据有时间截止,无法获取最新信息从实时更新的知识库中检索
领域知识不足通用模型缺乏特定行业的专业知识接入企业内部文档和专业数据库
数据隐私不便将私有数据用于模型训练数据留在本地,仅在推理时检索

与微调相比,RAG 的显著优势在于无需重新训练模型,且知识库可随时更新。


RAG 核心架构

RAG 的工作流程分为三个核心阶段:索引(Indexing)→ 检索(Retrieval)→ 生成(Generation)


RAG vs 长上下文窗口

2024-2025 年,主流大模型的上下文窗口从 4K/8K 迅速扩展到 100 万+ Token(Gemini 1.5 Pro、Claude 等),这对 RAG 的定位产生了深远影响:直接将文档塞进上下文窗口成为一种可行方案。

对比分析

对比维度RAG长上下文窗口混合方案
架构复杂度高 — 需要分块、Embedding、向量数据库、检索链路低 — 直接将文档拼接到 Prompt中等
单次查询成本低 — 只传入检索到的少量片段高 — 每次查询都传入全量文档中等
知识库规模可扩展到数百万文档受限于上下文窗口(通常 < 1M Token)大规模知识库 + 精细推理
实时性支持动态更新,索引后即可检索需要每次查询时重新加载灵活
准确性依赖检索质量,可能漏召回模型可"看到"全部内容,不会遗漏最高 — 检索缩小范围 + 全量推理
延迟较低(检索 + 生成)较高(处理大量 Token 耗时)中等

决策指南

💡 如何选择

  • 文档量 < 200K Token(约一本书)→ 优先考虑长上下文方案,架构简单、无检索遗漏风险
  • 文档量 > 1M Token 或数据持续更新 → 使用 RAG,成本可控且可扩展
  • 对准确性要求极高(如医疗、法律、金融)→ 采用混合方案:RAG 检索缩小候选范围,再将候选文档放入长上下文让模型精细推理

混合架构(Hybrid RAG + Long Context)

混合方案正在成为 2025 年的行业共识。典型流程:

与传统 RAG 只传入少量片段不同,混合方案利用长上下文窗口传入更多候选文档(如 Top-50 甚至 Top-100),让模型在更大范围内进行交叉验证和推理,显著降低因检索遗漏或截断导致的信息损失。


RAG vs Skill vs Fine-tuning

"如何让 LLM 掌握外部知识/能力"在 2025 年有了三条主流路线:RAG(注入知识)、Skill(注入流程,详见 Agent Skills 编写指南)、Fine-tuning(注入到权重)。这是 2025-2026 年 AI 面试中高频追问——很多候选人只会讲 RAG,不会区分这三者的边界。

三方核心区别

维度RAGSkillFine-tuning
解决什么问题模型不知道事实/知识模型不知道怎么做某类任务的流程模型不具备某种风格/能力/隐式知识
加载方式运行时检索 → 注入 Prompt 上下文路由触发 → 注入指令 + 工具子集到上下文训练时写入模型权重
更新成本重新索引(分钟级)改 Markdown / 配置(秒级)重新训练(小时-天级)
可解释性高(能给出引用)高(指令可读)低(权重不可读)
是否消耗推理 Token是(每次注入检索片段)是(每次注入 Skill 指令)否(能力已固化)
典型载体向量数据库 + EmbeddingMarkdown / YAML / CodeLoRA / 全量权重
失败模式检索遗漏、片段截断路由错配、Skill 指令冲突灾难性遗忘、过拟合

一句话区分

RAG 是"教模型查资料",Skill 是"教模型按 SOP 办事",Fine-tuning 是"教模型形成肌肉记忆"。

决策指南

💡 如何选

  • 回答需要最新/海量/可追溯的事实** → RAG
  • 任务有固定的多步流程(代码审查、PR 总结、定制报告)→ Skill
  • 需要改变模型说话风格 / 输出格式 / 领域语感 → Fine-tuning
  • 三者并不互斥:一个生产级 Agent 通常 = Fine-tuned 基模 + 多个 Skill + RAG 知识库

组合架构(最常见的生产形态)

面试加分点

  • Skill 内部可以调用 RAG("先检索知识库再按 SOP 输出"),但 RAG 本身不是 Skill
  • Fine-tuning 不能替代 RAG——再大的模型也无法记住实时变化的事实
  • Skill 不能替代 Fine-tuning——Prompt 难以稳定改变模型的输出风格和审美

文档处理与分块策略

文档分块(Chunking)的质量直接影响检索效果,是 RAG 系统中最关键的环节之一。

分块策略对比

策略原理优点缺点
固定大小分块按固定字符/Token 数切分实现简单、速度快可能在语义中间切断
递归分块按层级分隔符(段落→句子→字符)递归切分尽量保留语义完整性实现相对复杂
语义分块基于 Embedding 相似度检测语义边界语义完整性最好计算开销大
文档结构分块按 Markdown 标题、HTML 标签等结构切分保留文档层级关系依赖文档格式

分块参数选择

  • Chunk Size(分块大小):通常 256~1024 Token。太小会丢失上下文,太大会引入噪声
  • Chunk Overlap(重叠):通常 10%~20%,防止关键信息被分隔符截断

Python 分块示例

python
from typing import List

def recursive_split(text: str, chunk_size: int = 512,
                    chunk_overlap: int = 64) -> List[str]:
    """
    递归分块:优先按段落 → 句子 → 字符分割。

    Args:
        text: 原始文本
        chunk_size: 每个分块的最大字符数
        chunk_overlap: 相邻分块重叠的字符数
    Returns:
        分块后的文本列表
    """
    separators = ["\n\n", "\n", "。", ".", " ", ""]
    chunks: List[str] = []

    def _split(text: str, sep_idx: int) -> List[str]:
        if len(text) <= chunk_size:
            return [text]

        sep = separators[sep_idx]
        if not sep:
            # 最后的回退:按字符硬切
            return [text[i:i + chunk_size]
                    for i in range(0, len(text), chunk_size - chunk_overlap)]

        parts = text.split(sep)
        current_chunk = ""
        result = []

        for part in parts:
            candidate = current_chunk + sep + part if current_chunk else part
            if len(candidate) <= chunk_size:
                current_chunk = candidate
            else:
                if current_chunk:
                    result.append(current_chunk)
                # 如果单个 part 超过 chunk_size,递归使用下一个分隔符
                if len(part) > chunk_size:
                    result.extend(_split(part, sep_idx + 1))
                    current_chunk = ""
                else:
                    current_chunk = part

        if current_chunk:
            result.append(current_chunk)
        return result

    raw_chunks = _split(text, 0)

    # 添加重叠
    for i, chunk in enumerate(raw_chunks):
        if i > 0 and chunk_overlap > 0:
            overlap_text = raw_chunks[i - 1][-chunk_overlap:]
            chunk = overlap_text + chunk
        chunks.append(chunk.strip())

    return chunks

Embedding 与向量数据库

RAG 系统依赖 Embedding 模型将文本转换为向量,并通过向量数据库进行高效检索。关于 Embedding 模型的选择、相似度度量、向量数据库对比和 ANN 索引算法的详细介绍,请参阅 Embedding 与向量数据库


检索策略

稠密检索 Dense Retrieval

基于 Embedding 向量的语义相似度搜索,使用余弦相似度或内积计算距离:

优点:捕获深层语义关系(如同义词、意译)。缺点:对精确关键词匹配可能不如稀疏检索。

稀疏检索 Sparse Retrieval (BM25)

基于词频统计的经典检索算法,BM25 的评分公式为:

优点:精确关键词匹配能力强、可解释性好。缺点:无法处理语义相似但词汇不同的情况。

混合检索 Hybrid Retrieval

结合稠密检索和稀疏检索的优势,通过加权融合提升检索质量:

python
def hybrid_search(query: str, top_k: int = 10,
                  alpha: float = 0.7) -> list:
    """
    混合检索:结合稠密检索和 BM25 稀疏检索。

    Args:
        query: 用户查询
        top_k: 返回的结果数
        alpha: 稠密检索权重(1-alpha 为稀疏检索权重)
    Returns:
        排序后的文档列表
    """
    # 稠密检索: 通过 Embedding 向量相似度搜索
    dense_results = vector_db.similarity_search(
        query_embedding=embed_model.encode(query),
        top_k=top_k * 2
    )

    # 稀疏检索: 通过 BM25 关键词匹配
    sparse_results = bm25_index.search(
        query=query,
        top_k=top_k * 2
    )

    # 使用 Reciprocal Rank Fusion (RRF) 融合排名
    scores = {}
    k = 60  # RRF 常数

    for rank, doc in enumerate(dense_results):
        scores[doc.id] = scores.get(doc.id, 0) + alpha / (k + rank + 1)

    for rank, doc in enumerate(sparse_results):
        scores[doc.id] = scores.get(doc.id, 0) + (1 - alpha) / (k + rank + 1)

    # 按融合分数排序返回 top_k
    sorted_docs = sorted(scores.items(), key=lambda x: x[1], reverse=True)
    return [doc_id for doc_id, _ in sorted_docs[:top_k]]

重排序 Reranking

初次检索(First-stage Retrieval)召回的结果可能包含噪声。**重排序(Reranking)**使用交叉编码器(Cross-Encoder)对 Query-Document 对进行精细打分,显著提升最终结果的相关性。

为什么需要两阶段

  • 第一阶段(Bi-Encoder):Query 和 Document 独立编码,检索速度快但精度有限
  • 第二阶段(Cross-Encoder):Query 和 Document 拼接后联合编码,精度高但计算开销大,只能用于少量候选

常用重排序模型包括 bge-reranker-v2-m3Cohere Rerank 等。


高级 RAG 技术

查询改写 Query Transformation

原始用户查询可能模糊或不完整,通过 LLM 改写查询以提升检索效果:

python
def query_rewrite(original_query: str, llm) -> list:
    """
    使用 LLM 将原始查询改写为多个检索友好的查询。
    """
    prompt = f"""请将以下用户问题改写为 3 个更适合检索的查询,
每个查询从不同角度描述同一个信息需求。

原始问题: {original_query}

改写查询:"""
    response = llm.generate(prompt)
    queries = response.strip().split("\n")
    return [original_query] + queries  # 包含原始查询

HyDE(Hypothetical Document Embeddings)

先让 LLM 生成一个假设性回答文档,再用该文档的 Embedding 去检索,利用"文档-文档"相似度通常优于"查询-文档"相似度的特性。

Self-RAG

让模型自己决定是否需要检索,以及对检索结果进行自我评估。模型生成特殊标记来控制流程:

  • [Retrieve]:判断是否需要检索
  • [IsRel]:判断检索结果是否与查询相关
  • [IsUse]:判断检索结果是否对生成有用
  • [IsSup]:判断生成内容是否被检索结果支持

Corrective RAG(CRAG)

Corrective RAG 在 Self-RAG 的基础上引入显式的纠错动作:用一个轻量评估器(grader)对检索结果打分,根据置信度走三条不同路径。

关键差异:Self-RAG 是模型自身通过特殊 token 反思;CRAG 是外挂一个评估器做硬路由,工程上更易落地,对非微调模型也适用。

Adaptive RAG

Adaptive RAG 在最前端加一个查询复杂度分类器,根据问题难度选择检索策略,避免"无脑全检索"。

查询类型示例处理路径
简单事实"马斯克的出生年份?"直接 LLM 回答,不检索
单跳问答"DeepSeek R1 用的是什么 RL 算法?"单次 RAG 检索
多跳推理"DeepSeek 的 RL 算法和 RLHF 的核心差异是什么?"多步检索 + Agent 规划

收益:在生产中可降低 30-50% 的检索成本,同时提升简单问题的响应速度。

GraphRAG

GraphRAG 将传统 RAG 中的"扁平文档检索"升级为基于知识图谱的结构化检索

传统 RAG:查询 → 向量检索 → 独立文档片段 → LLM。GraphRAG:查询 → 图谱检索 → 实体 + 关系 + 社区 → LLM。

核心流程

  1. 构建知识图谱:使用 LLM 从文档中提取 (实体, 关系, 实体) 三元组
  2. 社区检测:对知识图谱进行层次聚类,识别主题社区
  3. 社区摘要:为每个社区生成摘要,作为检索单元
  4. 全局问答:综合多个社区的信息回答全局性问题

优势:擅长回答需要跨文档推理的全局性问题(如"这个领域的主要趋势是什么?"),传统 RAG 由于只检索局部片段难以处理此类问题。

Microsoft GraphRAG 完整流水线(2024 论文级实现)

面试 2026 高频追问:"GraphRAG 怎么实现?比向量 RAG 强在哪?成本多少?"——能讲清完整 5 阶段 + 真实成本数字立刻区分高级。

text
┌────────────── 索引阶段(贵)──────────────┐
① 文档切分(chunk ~600 token + overlap)

② LLM 提取实体 + 关系 (用 Claude/GPT-4 跑全文)
   输出: [("Apple", "founded_by", "Steve Jobs"),
          ("Apple", "headquartered_in", "Cupertino"), ...]

③ 构建知识图谱(NetworkX / Neo4j)

④ Leiden 算法做社区检测
   多层级聚类: Level 0 (大社区) → Level 1 → ...

⑤ LLM 生成每个社区的摘要(最贵的一步)
   "Apple 社区: 由 Steve Jobs 创立,总部 Cupertino..."
└──────────────────────────────────────────┘

┌────────────── 查询阶段 ──────────────┐
Global Search: "这个领域的主要趋势是什么?"
  → 用所有社区摘要做 Map-Reduce
  → Map: 每个社区摘要独立回答
  → Reduce: 合并所有部分答案

Local Search: "Steve Jobs 是谁的导师?"
  → 实体匹配 + 1-2 跳邻居展开
  → 拉取相关三元组 + 原始文档块
└────────────────────────────────────┘

真实成本(10 万 token 文档)

阶段成本(GPT-4o)时间
实体抽取~$5-105-10 分钟
社区摘要~$10-2010-20 分钟
单次 Global 查询~$0.5-130-60 秒
单次 Local 查询~$0.05-0.15-10 秒

⚠️ GraphRAG 不是银弹

索引成本是普通 RAG 的 50-100×——单次 100K 文档要花 $15-30; ② 全局查询慢(30-60 秒),不适合实时对话; ③ 必须周期重建——文档更新不能增量改图谱; ④ 小规模数据没必要:< 10 篇文档用传统 RAG 完全够。

适用场景判断

  • ✅ 跨文档推理的全局问题("竞品总览"、"研究趋势")
  • ✅ 关系密集型领域(人物关系 / 法律案例 / 医学)
  • ❌ FAQ / 客服(用 BM25 + 向量足够)
  • ❌ 实时对话(延迟过高)

开源实现对比

项目厂商特色
Microsoft GraphRAG微软论文官方实现,最完整
LightRAG港大 2024.10速度快、成本低 50%、增量更新
nano-graphrag社区极简(< 800 行),可读性最佳
Neo4j GraphRAGNeo4j商业级,集成 LangChain
LlamaIndex GraphRAGLlamaIndex集成度高、易上手

Agentic RAG

将 RAG 与 AI Agent 结合,由 Agent 自主决定检索策略,是 Self-RAG / CRAG / Adaptive RAG 的进一步泛化——把"是否检索、检索几次、检索什么、用哪个数据源"全部交给 LLM 的规划循环。

能力传统 RAGAgentic RAG
是否检索始终检索由 Agent 判断
检索次数一次多轮迭代(基于上一轮结果决定下一轮)
数据源固定向量库在向量检索 / SQL / 网络搜索 / API 间动态选择
查询分解直接用原始查询拆解为子查询并行/串行检索
失败恢复检索结果不满足时换策略重试

典型场景:跨多个知识库的复杂分析、需要计算或最新数据的混合问答、企业内多源数据问答。

与 CRAG/Self-RAG 的关系:CRAG 和 Self-RAG 可视为 Agentic RAG 的特定退化形式——只有一个固定的"评估-纠错"循环;而 Agentic RAG 的工具集和循环结构是开放的。

高级 RAG 变体一览

变体核心思想适用场景落地复杂度
HyDE用假设性回答的 Embedding 检索查询过短或与文档语义差距大
Self-RAG模型自带反思 token需要细粒度自我评估,但需微调高(需训练)
CRAG外挂评估器 + 三档路由通用,对非微调模型友好
Adaptive RAG按查询复杂度路由流量大、查询分布差异大
GraphRAG知识图谱 + 社区摘要全局性问题、跨文档推理
Agentic RAGAgent 自主多轮规划复杂、开放式、多源数据
ColBERT v2Late Interaction(词级延迟交互)长文档 / 高 recall 要求

ColBERT v2 / Late Interaction(2026 必知)

ColBERT(Stanford 2020 + v2 2022)是和 dense bi-encoder 完全不同的另一条技术路线——词级别延迟交互,常被忽视但效果出色。

与传统 dense retrieval 的本质区别

Dense Bi-Encoder(OpenAI / BGE):
  Doc:   [token_1, ..., token_N] → Encoder → 1 个向量 (1024d)
  Query: [token_1, ..., token_M] → Encoder → 1 个向量 (1024d)
  Score: cos(query_vec, doc_vec)
  → 把整段文档压成 1 个向量,丢失细粒度信息

ColBERT (Late Interaction):
  Doc:   [token_1, ..., token_N] → Encoder → N 个向量 (每 token 128d)
  Query: [token_1, ..., token_M] → Encoder → M 个向量 (每 token 128d)
  Score: Σ_q max_d (cos(q_vec, d_vec))   ← MaxSim 操作
  → 保留每个 token 的语义,匹配更精细

优势

  • 召回精度比 dense 高 10-30%(长文档场景尤其明显)
  • OOD 泛化好(不易被陌生领域打败)
  • 可解释(能看到哪个 query token 匹配了 doc 哪个 token)

劣势

  • 存储大 100×(一个 1000 token 文档 = 1000 × 128d 向量)
  • 检索复杂:需特殊索引(PLAID)

何时用 ColBERT

  • 高 recall 要求的法律 / 医学 / 科研搜索
  • 数据量 < 1000 万文档(再多存储成本顶不住)
  • 已尝试 dense + Reranker 仍然 recall 不够

主流实现RAGatouille(最易用)、Pyserini、Vespa 原生支持。

多模态 RAG

将 RAG 扩展到图文混合场景:

策略说明适用场景
图像描述索引(Caption Indexing)用多模态模型生成图像描述,按文本索引和检索图表、截图类文档
多模态 Embedding用 CLIP / SigLIP / Voyage-Multimodal 等模型生成图文统一的 Embedding图文混合检索
文档解析(Document Parsing)使用 OCR + 版面分析提取图表中的结构化信息PDF、扫描件
VLM 直接读图(ColPali / Visual RAG)跳过 OCR,VLM 直接看页面截图复杂版面 PDF

三大主流方案对比(2026)

方案代表优势劣势
Caption + Text RAGLlamaIndex Multi-Modal与现有 RAG 兼容、容易部署信息损失大、依赖 caption 质量
多模态 EmbeddingVoyage-Multimodal-3 / Cohere Embed v3 / CLIP真正图文统一表示、零OCR模型选择有限、长文档难处理
ColPali(2024.6)ColPali / ColQwen2完全跳过 OCR,VLM 直接读 PDF 截图,对复杂版面无敌存储大、推理贵

ColPali:2024 现象级新范式

ColPali = ColBERT 思路 + 视觉 PDF 处理。直接把 PDF 每页变成图像 + VLM 抽特征,不再做 OCR / 版面解析。

python
# RAGatouille / colpali_engine
from colpali_engine.models import ColPali

model = ColPali.from_pretrained("vidore/colpali-v1.2")

# 索引: 直接喂 PDF 页图像
embeddings = model.encode_image(pdf_pages_images)   # 每页 N 个 token 向量

# 查询: 文本 query
query_emb = model.encode_query("营收增长率多少?")
scores = colbert_score(query_emb, embeddings)

为什么是突破

  • ✅ 复杂表格 / 图表 / 多栏排版 无需 OCR 也能精确检索
  • ✅ 财报 / 学术论文 / 招标书等"PDF 中藏宝"场景质量飞跃
  • ✅ ViDoRe benchmark 上比传统流水线 +15 NDCG@5

2026 现状:财务 / 法律 / 医学 PDF 检索新项目首选 ColPali / ColQwen2.5。


混合架构:RAG + 长上下文(2025-2026 业界共识)

核心洞察:RAG 和长上下文窗口不是替代关系,是互补关系。Gemini 2.5 Pro 已支持 2M token,但 RAG 仍未死。

决策树(必背)

text
知识库大小?
├─ < 100K token(约 1 本书)
│   └─ 直接长上下文,无需 RAG
│       优势: 召回率 100%、零索引成本、推理灵活
│       成本: 每次查询贵(2M token 上下文 = $20+)

├─ 100K - 1M token
│   └─ 混合:用 RAG 召回 50K → 塞进长上下文
│       优势: 召回保证 + 推理质量高

├─ 1M - 100M token
│   └─ 标准 RAG(chunk + dense retrieval + Reranker)

└─ > 100M token
    └─ GraphRAG / Agentic RAG / 分库 RAG

何时选长上下文 > RAG

场景长上下文优势
完整文档分析(法律合同审查、学术论文综述)RAG 切片会丢失上下文
跨章节推理RAG 难找到所有相关片段
多文档对比("对比这 5 份合同的差异")一次性塞进所有
少样本学习(few-shot examples)直接放 prompt 里

何时仍需 RAG

场景RAG 优势
超大知识库长上下文也装不下
新鲜数据长上下文每次都要重新喂、贵
隐私分级按 ACL 检索是必需的
多源数据DB / API / 文档混合
成本敏感RAG 单次查询 $0.01-0.1 vs 长上下文 $1-10

Prompt Caching:长上下文的成本救星

详见 Prompt Caching——把固定知识库前缀缓存后,命中可降本 90%,让"长上下文 + RAG"混合方案在成本上接近纯 RAG。

生产级 RAG 模式

模式说明
语义缓存对相似查询缓存结果,减少重复检索和 LLM 调用
检索监控追踪检索的 recall、precision,发现退化趋势
渐进式索引增量更新知识库,避免全量重建索引
A/B 测试对比不同分块策略、检索算法、Prompt 模板的效果

Prompt Caching 在 RAG 中的应用

RAG 场景下的 Prompt Caching 与 Agent 略有不同——RAG 的检索片段每次都在变,所以不能像 Agent 一样缓存上下文中部。正确的姿势:

💡 RAG 的 Prompt 排版黄金顺序

1. System Prompt(不变 → 必缓存)
2. 检索指引 / 输出格式约束(不变 → 必缓存)
3. Few-shot 示例(不变 → 必缓存)
4. ──── 缓存断点(cache_control)────
5. 检索到的 Top-K 文档片段(每次变)
6. 用户问题(每次变)

把所有"不依赖检索结果"的内容全部前置,断点之后才放检索片段。在高 QPS 的 RAG 服务(如客服机器人)中,这一调整能让 System Prompt + Few-shot 部分(通常 2-5K Token)命中缓存,整体成本降低 30-50%。

详细缓存机制和定价对比见 AI Agent — Prompt Caching


RAG 评估

RAG 系统的评估需要从检索质量生成质量两个维度进行。

评估指标

指标维度衡量内容
Context Precision检索检索到的文档中相关文档的比例
Context Recall检索所有相关文档被检索到的比例
Faithfulness生成生成的回答是否忠实于检索到的上下文
Answer Relevancy生成生成的回答与原始问题的相关性
Answer Correctness端到端最终回答的准确性(与标准答案对比)

评估框架

常用的 RAG 评估框架包括:

  • RAGAS:提供自动化的 RAG 评估流程,覆盖上述所有指标
  • LlamaIndex Evaluation:内置多种评估器
  • TruLens:支持可视化的评估仪表盘
python
# 使用 RAGAS 进行评估(伪代码)
from ragas.metrics import faithfulness, answer_relevancy, context_precision

def evaluate_rag(questions: list, answers: list,
                 contexts: list, ground_truths: list) -> dict:
    """
    评估 RAG 系统的检索和生成质量。

    Args:
        questions: 测试问题列表
        answers: RAG 生成的回答列表
        contexts: 检索到的上下文列表
        ground_truths: 标准答案列表
    Returns:
        各项评估指标的得分
    """
    results = {
        "faithfulness": faithfulness.score(
            questions=questions,
            answers=answers,
            contexts=contexts
        ),
        "answer_relevancy": answer_relevancy.score(
            questions=questions,
            answers=answers
        ),
        "context_precision": context_precision.score(
            questions=questions,
            contexts=contexts,
            ground_truths=ground_truths
        ),
    }
    return results

完整 RAG Pipeline 示例

python
class SimpleRAGPipeline:
    """
    简化的 RAG 流水线实现,展示核心流程。
    """

    def __init__(self, embed_model, vector_store, llm,
                 chunk_size=512, top_k=5):
        self.embed_model = embed_model
        self.vector_store = vector_store
        self.llm = llm
        self.chunk_size = chunk_size
        self.top_k = top_k

    def ingest(self, documents: list):
        """离线索引:分块 → 向量化 → 存储"""
        for doc in documents:
            chunks = recursive_split(doc.text, self.chunk_size)
            for chunk in chunks:
                embedding = self.embed_model.encode(chunk)
                self.vector_store.add(
                    text=chunk,
                    embedding=embedding,
                    metadata=doc.metadata
                )

    def query(self, question: str) -> str:
        """在线查询:检索 → 构建 Prompt → 生成"""
        # Step 1: 将查询向量化
        query_embedding = self.embed_model.encode(question)

        # Step 2: 检索最相关的文档片段
        results = self.vector_store.similarity_search(
            query_embedding=query_embedding,
            top_k=self.top_k
        )

        # Step 3: 拼接上下文
        context = "\n\n---\n\n".join([r.text for r in results])

        # Step 4: 构建 Prompt 并调用 LLM
        prompt = f"""根据以下参考资料回答用户的问题。
如果参考资料中没有相关信息,请明确告知用户。

参考资料:
{context}

用户问题: {question}

回答:"""

        return self.llm.generate(prompt)

常见陷阱

⚠️ 常见误区

  1. 分块策略不当:分块太大导致检索结果包含大量无关信息,分块太小导致上下文不完整,丢失关键语义。应根据文档类型和查询模式选择合适的分块策略和大小。

  2. 忽视 Embedding 模型与查询语言的匹配:使用英文为主的 Embedding 模型处理中文文档,会导致语义捕获不准确。中文场景应优先选用 BGE、GTE 等针对中文优化的模型。

  3. 只用稠密检索忽视稀疏检索:稠密检索擅长语义匹配但可能漏掉精确关键词,BM25 等稀疏检索在精确匹配场景下更可靠。混合检索通常效果最佳。

  4. 检索数量 Top-K 设置不合理:K 太小可能漏掉关键信息,K 太大会引入噪声且增加 LLM 的上下文负担。建议先召回较多候选(如 Top-20),再通过 Reranking 精选(如 Top-5)。

  5. 缺乏评估体系:上线后不监控检索质量和生成质量,无法发现和修复系统退化。应建立包含 Faithfulness、Relevancy 等指标的持续评估机制。


📝 面试真题3 道高频
1. 请描述 RAG 的核心架构和工作流程中等
2. 稠密检索和稀疏检索各有什么优缺点?如何结合使用?中等
3. 如何评估一个 RAG 系统的质量?有哪些关键指标?困难

面试真题详解

Q1:请描述 RAG 的核心架构和工作流程

要点

RAG 系统分为离线索引在线查询两个阶段:

离线索引阶段

  1. 文档加载:从各种数据源(PDF、数据库、网页等)获取原始文档
  2. 文档分块:将长文档按策略切分为语义完整的小块(通常 256~1024 Token)
  3. 向量化:使用 Embedding 模型将每个分块转换为稠密向量
  4. 存储:将向量和原始文本存入向量数据库,建立索引

在线查询阶段

  1. 查询向量化:将用户问题通过同一个 Embedding 模型转换为向量
  2. 向量检索:在向量数据库中搜索与查询最相似的 Top-K 个文档片段
  3. (可选)重排序:使用 Cross-Encoder 对候选结果精排
  4. Prompt 构建:将检索到的文档片段拼接进 Prompt 的上下文部分
  5. LLM 生成:大语言模型基于上下文生成最终回答

核心设计要点:查询和文档必须使用同一个 Embedding 模型,确保在相同的向量空间中计算相似度。


Q2:稠密检索和稀疏检索各有什么优缺点?如何结合使用?

要点

对比维度稠密检索稀疏检索 (BM25)
原理基于 Embedding 语义向量相似度基于词频和逆文档频率的统计匹配
语义匹配强 —— 能理解同义词、意译弱 —— 只做字面匹配
精确匹配弱 —— 可能漏掉精确关键词强 —— 关键词完全匹配得分高
速度需要 ANN 索引,较快倒排索引,非常快
可解释性低 —— 向量空间难以直观理解高 —— 可以看到匹配的关键词

结合方式(混合检索)

  1. 并行检索:同时执行稠密检索和稀疏检索
  2. 分数融合:使用 RRF(Reciprocal Rank Fusion)或加权求和合并排名
  3. (可选)重排序:对融合后的结果使用 Cross-Encoder 精排

实践表明,混合检索相比单一检索方式通常能提升 5%~15% 的召回率。权重比例(稠密 vs 稀疏)需要根据具体场景调优,通常稠密检索权重略高(如 0.7:0.3)。


Q3:如何评估一个 RAG 系统的质量?有哪些关键指标?

要点

RAG 系统的评估需要覆盖检索和生成两个维度,核心指标包括:

检索维度

  • Context Precision(上下文精确率):检索到的 Top-K 文档中,真正与问题相关的占比。高精确率意味着检索噪声少
  • Context Recall(上下文召回率):所有相关文档被成功检索到的比例。高召回率意味着不会遗漏关键信息

生成维度

  • Faithfulness(忠实度):生成的回答是否完全基于检索到的上下文,不包含模型自行编造的信息。这是衡量幻觉问题的核心指标
  • Answer Relevancy(回答相关性):生成的回答与用户原始问题的相关程度

端到端指标

  • Answer Correctness(回答正确性):最终回答与标准答案的匹配程度

评估方法

  1. 构建包含问题、标准答案、相关文档的评测集
  2. 使用 RAGAS 等框架自动计算各项指标
  3. 结合人工评估判断回答质量
  4. 建立持续评估机制,监控系统在生产环境中的表现

延伸阅读