知识的外挂:分块、Embedding、Rerank、GraphRAG 与多路融合 —— RAG 检索增强六脉

摘要

RAG 是大模型接入外部知识的核心架构。本文从文档分块 Chunking、向量化 Embedding、检索 Rerank、GraphRAG 图谱增强、多路召回与融合、生产级 RAG 流水线六个切口,给出源码级实现与企业级 RAG 决策框架。

1. 文档分块 Chunking:切分策略影响召回

文档需切分为语义完整的块(chunk)才能精准检索。切分策略直接决定召回质量------过大召回噪声多,过小语义断裂。
#mermaid-svg-IaJAotC873NMqFg0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IaJAotC873NMqFg0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IaJAotC873NMqFg0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IaJAotC873NMqFg0 .error-icon{fill:#552222;}#mermaid-svg-IaJAotC873NMqFg0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IaJAotC873NMqFg0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IaJAotC873NMqFg0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IaJAotC873NMqFg0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IaJAotC873NMqFg0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IaJAotC873NMqFg0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IaJAotC873NMqFg0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IaJAotC873NMqFg0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IaJAotC873NMqFg0 .marker.cross{stroke:#333333;}#mermaid-svg-IaJAotC873NMqFg0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IaJAotC873NMqFg0 p{margin:0;}#mermaid-svg-IaJAotC873NMqFg0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-IaJAotC873NMqFg0 .cluster-label text{fill:#333;}#mermaid-svg-IaJAotC873NMqFg0 .cluster-label span{color:#333;}#mermaid-svg-IaJAotC873NMqFg0 .cluster-label span p{background-color:transparent;}#mermaid-svg-IaJAotC873NMqFg0 .label text,#mermaid-svg-IaJAotC873NMqFg0 span{fill:#333;color:#333;}#mermaid-svg-IaJAotC873NMqFg0 .node rect,#mermaid-svg-IaJAotC873NMqFg0 .node circle,#mermaid-svg-IaJAotC873NMqFg0 .node ellipse,#mermaid-svg-IaJAotC873NMqFg0 .node polygon,#mermaid-svg-IaJAotC873NMqFg0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IaJAotC873NMqFg0 .rough-node .label text,#mermaid-svg-IaJAotC873NMqFg0 .node .label text,#mermaid-svg-IaJAotC873NMqFg0 .image-shape .label,#mermaid-svg-IaJAotC873NMqFg0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-IaJAotC873NMqFg0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IaJAotC873NMqFg0 .rough-node .label,#mermaid-svg-IaJAotC873NMqFg0 .node .label,#mermaid-svg-IaJAotC873NMqFg0 .image-shape .label,#mermaid-svg-IaJAotC873NMqFg0 .icon-shape .label{text-align:center;}#mermaid-svg-IaJAotC873NMqFg0 .node.clickable{cursor:pointer;}#mermaid-svg-IaJAotC873NMqFg0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IaJAotC873NMqFg0 .arrowheadPath{fill:#333333;}#mermaid-svg-IaJAotC873NMqFg0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IaJAotC873NMqFg0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IaJAotC873NMqFg0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IaJAotC873NMqFg0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IaJAotC873NMqFg0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IaJAotC873NMqFg0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IaJAotC873NMqFg0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IaJAotC873NMqFg0 .cluster text{fill:#333;}#mermaid-svg-IaJAotC873NMqFg0 .cluster span{color:#333;}#mermaid-svg-IaJAotC873NMqFg0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IaJAotC873NMqFg0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IaJAotC873NMqFg0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-IaJAotC873NMqFg0 .icon-shape,#mermaid-svg-IaJAotC873NMqFg0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IaJAotC873NMqFg0 .icon-shape p,#mermaid-svg-IaJAotC873NMqFg0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IaJAotC873NMqFg0 .icon-shape .label rect,#mermaid-svg-IaJAotC873NMqFg0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IaJAotC873NMqFg0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IaJAotC873NMqFg0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IaJAotC873NMqFg0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-IaJAotC873NMqFg0 .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-IaJAotC873NMqFg0 .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-IaJAotC873NMqFg0 .default tspan{fill:#000000!important;} 文档分块策略
固定长度: 简单但断裂
按句/段: 语义完整
递归分块: 层次切分
语义分块: embedding聚类
500-1000字符, 10%重叠
保留句子完整
Markdown按标题层次
语义连贯最优但慢

python 复制代码
# 来源:分块策略实现 / LangChain 0.1
from typing import List
import re

class FixedSizeChunker:
    """固定长度分块器"""
    def __init__(self, chunk_size=500, overlap=50):
        self.size = chunk_size    # 每块字符数
        self.overlap = overlap    # 重叠字符数

    def split(self, text: str) -> List[str]:
        """按固定长度分块, 带重叠"""
        chunks = []
        start = 0
        while start < len(text):
            end = start + self.size
            chunks.append(text[start:end])
            start = end - self.overlap  # 重叠
        return chunks

# 量化: 固定500字符+50重叠, 简单但可能截断句子
# 适合: 纯文本无结构, 速度快

class RecursiveChunker:
    """递归分块器: 按结构层次切分"""
    SEPARATORS = ['\n\n\n', '\n\n', '\n', '。', '!', '?', '.', ' ', '']

    def __init__(self, chunk_size=500, chunk_overlap=50):
        self.size = chunk_size
        self.overlap = chunk_overlap

    def split(self, text: str) -> List[str]:
        """递归用分隔符切分至目标大小"""
        return self._recursive_split(text, self.SEPARATORS)

    def _recursive_split(self, text, separators):
        if len(text) <= self.size:
            return [text] if text.strip() else []
        if not separators:
            return self._fixed_split(text)
        sep = separators[0]
        if sep:
            parts = text.split(sep)
        else:
            parts = list(text)
        # 逐块处理
        chunks = []
        current = ""
        for part in parts:
            candidate = current + sep + part if current else part
            if len(candidate) <= self.size:
                current = candidate
            else:
                if current:
                    chunks.append(current)
                if len(part) > self.size:
                    # 递归用下一级分隔符
                    chunks.extend(self._recursive_split(part, separators[1:]))
                else:
                    current = part
        if current:
            chunks.append(current)
        return [c for c in chunks if c.strip()]

    def _fixed_split(self, text):
        return FixedSizeChunker(self.size, self.overlap).split(text)

# 量化: 递归分块保留语义边界, 召回率比固定提升 10-15%
# 适合: Markdown/HTML/有结构文档
python 复制代码
# 来源:语义分块 / Semantic Chunker 2024
class SemanticChunker:
    """语义分块: 按embedding相似度切分"""
    def __init__(self, embedder, threshold=0.5):
        self.embedder = embedder
        self.threshold = threshold  # 相似度阈值, 低于则切分

    def split(self, text: str) -> List[str]:
        """按语义连贯性分块"""
        # 1. 先按句子切分
        sentences = re.split(r'[。!?.!?]', text)
        sentences = [s for s in sentences if s.strip()]
        # 2. 计算相邻句子embedding相似度
        embeddings = [self.embedder.embed(s) for s in sentences]
        # 3. 相似度低于阈值处切分
        chunks = []
        current = [sentences[0]]
        for i in range(1, len(sentences)):
            sim = self._cosine(embeddings[i-1], embeddings[i])
            if sim < self.threshold:
                chunks.append('。'.join(current))
                current = [sentences[i]]
            else:
                current.append(sentences[i])
        if current:
            chunks.append('。'.join(current))
        return chunks

    def _cosine(self, a, b):
        import numpy as np
        return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

# 量化: 语义分块召回率比固定再提升 5-10%
# 代价: 需embedding计算, 速度慢 10 倍
# 适合: 高质量知识库, 非实时场景

量化:固定 500 字符+50 重叠简单但可能截断句子。递归分块保留语义边界,召回率比固定提升 10-15%。语义分块再提升 5-10% 但需 embedding 计算慢 10 倍。chunk_size 500 是经验最优------300 召回不全,1000 引入噪声。重叠 10% 防边界信息丢失。

边界:表格/代码块需特殊处理------固定切分会破坏表格行与代码语法,需按结构切分。超长文档分块数爆炸------1M token 文档产 2000 块,需层级索引(先摘要后细节)。语义分块阈值需调参------阈值过低不切分,过高切碎语义。

2. 向量化 Embedding:检索质量基石

Embedding 模型将文本转为向量,向量相似度决定召回质量。选型需平衡:维度(存储/速度)、语种、领域适配、指令遵循。
#mermaid-svg-UpjeLuB2Po88GP3S{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UpjeLuB2Po88GP3S .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UpjeLuB2Po88GP3S .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UpjeLuB2Po88GP3S .error-icon{fill:#552222;}#mermaid-svg-UpjeLuB2Po88GP3S .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UpjeLuB2Po88GP3S .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UpjeLuB2Po88GP3S .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UpjeLuB2Po88GP3S .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UpjeLuB2Po88GP3S .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UpjeLuB2Po88GP3S .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UpjeLuB2Po88GP3S .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UpjeLuB2Po88GP3S .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UpjeLuB2Po88GP3S .marker.cross{stroke:#333333;}#mermaid-svg-UpjeLuB2Po88GP3S svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UpjeLuB2Po88GP3S p{margin:0;}#mermaid-svg-UpjeLuB2Po88GP3S .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-UpjeLuB2Po88GP3S .cluster-label text{fill:#333;}#mermaid-svg-UpjeLuB2Po88GP3S .cluster-label span{color:#333;}#mermaid-svg-UpjeLuB2Po88GP3S .cluster-label span p{background-color:transparent;}#mermaid-svg-UpjeLuB2Po88GP3S .label text,#mermaid-svg-UpjeLuB2Po88GP3S span{fill:#333;color:#333;}#mermaid-svg-UpjeLuB2Po88GP3S .node rect,#mermaid-svg-UpjeLuB2Po88GP3S .node circle,#mermaid-svg-UpjeLuB2Po88GP3S .node ellipse,#mermaid-svg-UpjeLuB2Po88GP3S .node polygon,#mermaid-svg-UpjeLuB2Po88GP3S .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UpjeLuB2Po88GP3S .rough-node .label text,#mermaid-svg-UpjeLuB2Po88GP3S .node .label text,#mermaid-svg-UpjeLuB2Po88GP3S .image-shape .label,#mermaid-svg-UpjeLuB2Po88GP3S .icon-shape .label{text-anchor:middle;}#mermaid-svg-UpjeLuB2Po88GP3S .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UpjeLuB2Po88GP3S .rough-node .label,#mermaid-svg-UpjeLuB2Po88GP3S .node .label,#mermaid-svg-UpjeLuB2Po88GP3S .image-shape .label,#mermaid-svg-UpjeLuB2Po88GP3S .icon-shape .label{text-align:center;}#mermaid-svg-UpjeLuB2Po88GP3S .node.clickable{cursor:pointer;}#mermaid-svg-UpjeLuB2Po88GP3S .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UpjeLuB2Po88GP3S .arrowheadPath{fill:#333333;}#mermaid-svg-UpjeLuB2Po88GP3S .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UpjeLuB2Po88GP3S .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UpjeLuB2Po88GP3S .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UpjeLuB2Po88GP3S .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UpjeLuB2Po88GP3S .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UpjeLuB2Po88GP3S .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UpjeLuB2Po88GP3S .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UpjeLuB2Po88GP3S .cluster text{fill:#333;}#mermaid-svg-UpjeLuB2Po88GP3S .cluster span{color:#333;}#mermaid-svg-UpjeLuB2Po88GP3S div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-UpjeLuB2Po88GP3S .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UpjeLuB2Po88GP3S rect.text{fill:none;stroke-width:0;}#mermaid-svg-UpjeLuB2Po88GP3S .icon-shape,#mermaid-svg-UpjeLuB2Po88GP3S .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UpjeLuB2Po88GP3S .icon-shape p,#mermaid-svg-UpjeLuB2Po88GP3S .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UpjeLuB2Po88GP3S .icon-shape .label rect,#mermaid-svg-UpjeLuB2Po88GP3S .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UpjeLuB2Po88GP3S .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UpjeLuB2Po88GP3S .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UpjeLuB2Po88GP3S :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-UpjeLuB2Po88GP3S .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-UpjeLuB2Po88GP3S .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-UpjeLuB2Po88GP3S .default tspan{fill:#000000!important;} Embedding 选型
维度: 768/1024/1536
语种: 中英/多语
领域: 通用/法律/医疗
指令遵循: 支持task指令
高维度精度高但慢
BGE-m3多语种
领域微调模型
MTE支持task前缀

python 复制代码
# 来源:Embedding 模型对比 / MTEB 2024
import numpy as np

class EmbeddingModel:
    """Embedding 模型封装"""
    MODELS = {
        'bge-large-zh': {'dim': 1024, 'lang': 'zh', 'max_seq': 512},
        'bge-m3': {'dim': 1024, 'lang': 'multi', 'max_seq': 8192},
        'gte-large': {'dim': 1024, 'lang': 'en', 'max_seq': 512},
        'text-embedding-3-large': {'dim': 3072, 'lang': 'multi', 'max_seq': 8192},
    }

    def __init__(self, model_name='bge-large-zh'):
        self.name = model_name
        self.config = self.MODELS[model_name]
        self.model = self._load(model_name)

    def embed(self, texts, instruction=None):
        """生成embedding, 可选指令前缀"""
        if instruction:
            # BGE 支持 instruction: "为这个句子生成表示用于检索相关文章"
            texts = [f'{instruction}\n{t}' for t in texts]
        embeddings = self.model.encode(texts, normalize_embeddings=True)
        return embeddings

    def embed_query(self, query):
        """查询embedding (带检索指令)"""
        return self.embed([query], instruction='为检索相关文档生成查询表示')[0]

    def embed_document(self, doc):
        """文档embedding (不带指令)"""
        return self.embed([doc])[0]

# 量化 (MTEB 中文检索任务):
# bge-large-zh:  1024维, 准确率 71.2
# bge-m3:        1024维, 多语, 准确率 73.5
# gte-large:     1024维, 英文, 准确率 68.0
# text-embedding-3-large: 3072维, 准确率 75.0
# 维度越高精度越高, 但存储和检索成本也高
python 复制代码
# 来源:向量索引构建 / FAISS / Milvus
class VectorIndex:
    """向量索引管理器"""
    def __init__(self, dim=1024, index_type='ivf', n_clusters=256):
        self.dim = dim
        self.type = index_type
        self.n_clusters = n_clusters
        self.index = self._build_index()
        self.id_map = {}  # 向量id -> 文档id

    def _build_index(self):
        import faiss
        if self.type == 'flat':
            # 精确检索, 100%召回但慢
            return faiss.IndexFlatIP(self.dim)
        elif self.type == 'ivf':
            # 倒排索引, 99%召回快10倍
            quantizer = faiss.IndexFlatIP(self.dim)
            return faiss.IndexIVFFlat(quantizer, self.dim, self.n_clusters)
        elif self.type == 'hnsw':
            # 图索引, 98%召回快20倍, 适合大规模
            return faiss.IndexHNSWFlat(self.dim, 32)

    def add(self, embeddings, doc_ids):
        """添加向量"""
        if self.type == 'ivf':
            if not self.index.is_trained:
                self.index.train(embeddings)
        self.index.add(embeddings)
        for i, did in enumerate(doc_ids):
            self.id_map[i] = did

    def search(self, query_vec, k=10, nprobe=10):
        """检索top-k"""
        if self.type == 'ivf':
            self.index.nprobe = nprobe  # 搜索簇数, 越大越准越慢
        distances, ids = self.index.search(query_vec.reshape(1, -1), k)
        return [(self.id_map[i], d) for i, d in zip(ids[0], distances[0])]

# 量化 (100万向量, 1024维):
# Flat:  准确率100%, 延迟 500ms
# IVF:   准确率99%,  延迟 50ms (nprobe=10)
# HNSW:  准确率98%,  延迟 25ms, 内存2倍
# IVF是规模/速度/精度最优平衡

量化(MTEB 中文检索):bge-large-zh 1024 维准确率 71.2,bge-m3 多语 73.5,text-embedding-3-large 3072 维 75.0。100 万向量检索:Flat 100%召回 500ms,IVF 99%召回 50ms,HNSW 98%召回 25ms。IVF 是规模/速度/精度最优平衡。BGE 指令前缀使检索准确率再提 3-5 分。

边界:Embedding 模型有 max_seq 限制------超长文档需先分块再 embedding,单块不超 512 token。多语种场景需多语模型------单语模型跨语检索性能骤降。领域适配需微调------通用模型在法律/医疗领域准确率低 15-20 分。指令前缀仅部分模型支持------BGE/MTE 支持,OpenAI text-embedding-3 不需要。

3. 检索 Rerank:两阶段召回提升精度

向量检索召回相关但未必精确的候选,Rerank 用更重的模型对候选重排序,将最相关的排到前面。两阶段架构(召回+重排)是 RAG 标配。
#mermaid-svg-wR6jY4V40TscLRvD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wR6jY4V40TscLRvD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wR6jY4V40TscLRvD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wR6jY4V40TscLRvD .error-icon{fill:#552222;}#mermaid-svg-wR6jY4V40TscLRvD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wR6jY4V40TscLRvD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wR6jY4V40TscLRvD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wR6jY4V40TscLRvD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wR6jY4V40TscLRvD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wR6jY4V40TscLRvD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wR6jY4V40TscLRvD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wR6jY4V40TscLRvD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wR6jY4V40TscLRvD .marker.cross{stroke:#333333;}#mermaid-svg-wR6jY4V40TscLRvD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wR6jY4V40TscLRvD p{margin:0;}#mermaid-svg-wR6jY4V40TscLRvD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wR6jY4V40TscLRvD .cluster-label text{fill:#333;}#mermaid-svg-wR6jY4V40TscLRvD .cluster-label span{color:#333;}#mermaid-svg-wR6jY4V40TscLRvD .cluster-label span p{background-color:transparent;}#mermaid-svg-wR6jY4V40TscLRvD .label text,#mermaid-svg-wR6jY4V40TscLRvD span{fill:#333;color:#333;}#mermaid-svg-wR6jY4V40TscLRvD .node rect,#mermaid-svg-wR6jY4V40TscLRvD .node circle,#mermaid-svg-wR6jY4V40TscLRvD .node ellipse,#mermaid-svg-wR6jY4V40TscLRvD .node polygon,#mermaid-svg-wR6jY4V40TscLRvD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wR6jY4V40TscLRvD .rough-node .label text,#mermaid-svg-wR6jY4V40TscLRvD .node .label text,#mermaid-svg-wR6jY4V40TscLRvD .image-shape .label,#mermaid-svg-wR6jY4V40TscLRvD .icon-shape .label{text-anchor:middle;}#mermaid-svg-wR6jY4V40TscLRvD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wR6jY4V40TscLRvD .rough-node .label,#mermaid-svg-wR6jY4V40TscLRvD .node .label,#mermaid-svg-wR6jY4V40TscLRvD .image-shape .label,#mermaid-svg-wR6jY4V40TscLRvD .icon-shape .label{text-align:center;}#mermaid-svg-wR6jY4V40TscLRvD .node.clickable{cursor:pointer;}#mermaid-svg-wR6jY4V40TscLRvD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wR6jY4V40TscLRvD .arrowheadPath{fill:#333333;}#mermaid-svg-wR6jY4V40TscLRvD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wR6jY4V40TscLRvD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wR6jY4V40TscLRvD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wR6jY4V40TscLRvD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wR6jY4V40TscLRvD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wR6jY4V40TscLRvD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wR6jY4V40TscLRvD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wR6jY4V40TscLRvD .cluster text{fill:#333;}#mermaid-svg-wR6jY4V40TscLRvD .cluster span{color:#333;}#mermaid-svg-wR6jY4V40TscLRvD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wR6jY4V40TscLRvD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wR6jY4V40TscLRvD rect.text{fill:none;stroke-width:0;}#mermaid-svg-wR6jY4V40TscLRvD .icon-shape,#mermaid-svg-wR6jY4V40TscLRvD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wR6jY4V40TscLRvD .icon-shape p,#mermaid-svg-wR6jY4V40TscLRvD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wR6jY4V40TscLRvD .icon-shape .label rect,#mermaid-svg-wR6jY4V40TscLRvD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wR6jY4V40TscLRvD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wR6jY4V40TscLRvD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wR6jY4V40TscLRvD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-wR6jY4V40TscLRvD .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-wR6jY4V40TscLRvD .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-wR6jY4V40TscLRvD .default tspan{fill:#000000!important;} 两阶段检索
召回: 向量检索 top-50
重排: Cross-encoder top-10
快但粗, 召回率优先
慢但精, 准确率优先
双塔模型: 独立编码
交叉编码: 联合编码
可预计算, 检索快
必须实时计算, 精度高

python 复制代码
# 来源:Cross-encoder Rerank / BGE-Reranker 2024
class CrossEncoderReranker:
    """Cross-encoder 重排器"""
    def __init__(self, model_name='BAAI/bge-reranker-large'):
        from transformers import AutoModelForSequenceClassification, AutoTokenizer
        self.tokenizer = AutoTokenizer.from_pretrained(model_name)
        self.model = AutoModelForSequenceClassification.from_pretrained(model_name)

    def rerank(self, query: str, documents: list, top_k=5):
        """对候选文档重排序"""
        # 构造 query-document 对
        pairs = [[query, doc] for doc in documents]
        # Cross-encoder 联合编码, 输出相关性分
        features = self.tokenizer(pairs, padding=True, truncation=True,
                                 return_tensors='pt', max_length=512)
        with torch.no_grad():
            scores = self.model(**features).logits.squeeze(-1)
        # 按分数排序取 top_k
        ranked = sorted(zip(documents, scores.tolist()),
                       key=lambda x: x[1], reverse=True)
        return [(doc, score) for doc, score in ranked[:top_k]]

# 量化: Cross-encoder 重排使准确率提升 10-15 分
# 双塔召回 top-50 -> Cross-encoder 重排 top-10
# 延迟: 双塔 50ms + 重排 100ms (50候选) = 150ms 总
# 对比仅双塔: 50ms 但准确率低 15 分
python 复制代码
# 来源:两阶段检索流水线 / 生产实践 2024
class TwoStageRetriever:
    """两阶段检索器"""
    def __init__(self, embedder, vector_index, reranker, k_recall=50, k_final=5):
        self.embedder = embedder
        self.index = vector_index
        self.reranker = reranker
        self.k_recall = k_recall  # 召回数
        self.k_final = k_final    # 最终返回数

    def retrieve(self, query: str):
        """两阶段检索"""
        # 1. 向量召回 top-50
        query_vec = self.embedder.embed_query(query)
        candidates = self.index.search(query_vec, k=self.k_recall)
        docs = [self._get_doc(c[0]) for c in candidates]
        # 2. Cross-encoder 重排 top-5
        reranked = self.reranker.rerank(query, docs, top_k=self.k_final)
        return reranked

    def _get_doc(self, doc_id):
        return f"文档{doc_id}内容"  # 占位

import torch
# 量化: 两阶段比纯向量检索准确率高 15 分
# 召回50重排5, 平衡延迟与精度
# 关键: 召回需高召回率(>95%), 重排负责精度

量化:Cross-encoder 重排使准确率提升 10-15 分。双塔召回 top-50(50ms)+ Cross-encoder 重排 top-10(100ms)总延迟 150ms,比纯双塔(50ms)准确率高 15 分。召回需高召回率(>95%),重排负责精度。k_recall=50、k_final=5 是经验最优。

边界:Cross-encoder 有 max_seq 限制------长文档需先分块,重排的是块而非全文。重排候选数影响延迟------50 候选 100ms,200 候选 400ms,需权衡。重排模型需与召回模型同语种------跨语种重排性能降。混合检索(向量+BM25)后重排效果更佳------多路召回互补。

4. GraphRAG:知识图谱增强检索

传统 RAG 基于向量相似度,缺乏关系推理。GraphRAG 引入知识图谱,通过实体关系检索多跳关联信息,解决"跨文档推理"难题。
#mermaid-svg-Xazr5EDRzrM9XRhR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Xazr5EDRzrM9XRhR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Xazr5EDRzrM9XRhR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Xazr5EDRzrM9XRhR .error-icon{fill:#552222;}#mermaid-svg-Xazr5EDRzrM9XRhR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Xazr5EDRzrM9XRhR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Xazr5EDRzrM9XRhR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Xazr5EDRzrM9XRhR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Xazr5EDRzrM9XRhR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Xazr5EDRzrM9XRhR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Xazr5EDRzrM9XRhR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Xazr5EDRzrM9XRhR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Xazr5EDRzrM9XRhR .marker.cross{stroke:#333333;}#mermaid-svg-Xazr5EDRzrM9XRhR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Xazr5EDRzrM9XRhR p{margin:0;}#mermaid-svg-Xazr5EDRzrM9XRhR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Xazr5EDRzrM9XRhR .cluster-label text{fill:#333;}#mermaid-svg-Xazr5EDRzrM9XRhR .cluster-label span{color:#333;}#mermaid-svg-Xazr5EDRzrM9XRhR .cluster-label span p{background-color:transparent;}#mermaid-svg-Xazr5EDRzrM9XRhR .label text,#mermaid-svg-Xazr5EDRzrM9XRhR span{fill:#333;color:#333;}#mermaid-svg-Xazr5EDRzrM9XRhR .node rect,#mermaid-svg-Xazr5EDRzrM9XRhR .node circle,#mermaid-svg-Xazr5EDRzrM9XRhR .node ellipse,#mermaid-svg-Xazr5EDRzrM9XRhR .node polygon,#mermaid-svg-Xazr5EDRzrM9XRhR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Xazr5EDRzrM9XRhR .rough-node .label text,#mermaid-svg-Xazr5EDRzrM9XRhR .node .label text,#mermaid-svg-Xazr5EDRzrM9XRhR .image-shape .label,#mermaid-svg-Xazr5EDRzrM9XRhR .icon-shape .label{text-anchor:middle;}#mermaid-svg-Xazr5EDRzrM9XRhR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Xazr5EDRzrM9XRhR .rough-node .label,#mermaid-svg-Xazr5EDRzrM9XRhR .node .label,#mermaid-svg-Xazr5EDRzrM9XRhR .image-shape .label,#mermaid-svg-Xazr5EDRzrM9XRhR .icon-shape .label{text-align:center;}#mermaid-svg-Xazr5EDRzrM9XRhR .node.clickable{cursor:pointer;}#mermaid-svg-Xazr5EDRzrM9XRhR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Xazr5EDRzrM9XRhR .arrowheadPath{fill:#333333;}#mermaid-svg-Xazr5EDRzrM9XRhR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Xazr5EDRzrM9XRhR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Xazr5EDRzrM9XRhR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Xazr5EDRzrM9XRhR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Xazr5EDRzrM9XRhR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Xazr5EDRzrM9XRhR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Xazr5EDRzrM9XRhR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Xazr5EDRzrM9XRhR .cluster text{fill:#333;}#mermaid-svg-Xazr5EDRzrM9XRhR .cluster span{color:#333;}#mermaid-svg-Xazr5EDRzrM9XRhR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Xazr5EDRzrM9XRhR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Xazr5EDRzrM9XRhR rect.text{fill:none;stroke-width:0;}#mermaid-svg-Xazr5EDRzrM9XRhR .icon-shape,#mermaid-svg-Xazr5EDRzrM9XRhR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Xazr5EDRzrM9XRhR .icon-shape p,#mermaid-svg-Xazr5EDRzrM9XRhR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Xazr5EDRzrM9XRhR .icon-shape .label rect,#mermaid-svg-Xazr5EDRzrM9XRhR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Xazr5EDRzrM9XRhR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Xazr5EDRzrM9XRhR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Xazr5EDRzrM9XRhR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-Xazr5EDRzrM9XRhR .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-Xazr5EDRzrM9XRhR .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-Xazr5EDRzrM9XRhR .default tspan{fill:#000000!important;} GraphRAG 架构
实体抽取
关系构建
图谱检索
子图融合
NER识别实体
实体间关系边
查询实体+多跳邻居
子图文本化喂LLM

python 复制代码
# 来源:GraphRAG 实现 / Microsoft GraphRAG 2024
class GraphRAGBuilder:
    """GraphRAG 图谱构建器"""
    def __init__(self, llm, embedder):
        self.llm = llm
        self.embedder = embedder
        self.graph = {'entities': {}, 'relations': []}

    def build_from_documents(self, documents):
        """从文档构建知识图谱"""
        for doc in documents:
            # 1. LLM 抽取实体与关系
            entities_relations = self._extract_entities(doc)
            for entity in entities_relations['entities']:
                if entity['name'] not in self.graph['entities']:
                    self.graph['entities'][entity['name']] = {
                        'type': entity['type'],
                        'description': entity['description'],
                        'mentions': [doc['id']]
                    }
            for rel in entities_relations['relations']:
                self.graph['relations'].append({
                    'source': rel['source'],
                    'target': rel['target'],
                    'type': rel['type'],
                    'description': rel['description']
                })
        # 2. 构建社区 (聚类相关实体)
        self._build_communities()

    def _extract_entities(self, doc):
        """用 LLM 抽取实体与关系"""
        prompt = f"""从以下文本抽取实体和关系, 输出JSON:
文本: {doc['content']}

输出格式:
{{"entities": [{{"name": "", "type": "", "description": ""}}],
"relations": [{{"source": "", "target": "", "type": "", "description": ""}}]}}"""
        import json
        result = self.llm.generate(prompt)
        return json.loads(result)

    def _build_communities(self):
        """构建社区: 聚类相关实体"""
        # 用图聚类算法 (如 Leiden) 划分实体社区
        # 每个社区生成摘要
        pass

# 量化: GraphRAG 在多跳问答比向量RAG准确率高 20-30 分
# 例: "A公司CEO的母校在哪?" 需跨文档推理
# 向量RAG: 检索A公司文档+CEO文档, 可能遗漏母校
# GraphRAG: 沿A公司-CEO-母校关系边直接找到
python 复制代码
# 来源:图谱检索查询 / GraphRAG 2024
class GraphRAGRetriever:
    """GraphRAG 检索器"""
    def __init__(self, graph, llm):
        self.graph = graph
        self.llm = llm

    def retrieve(self, query: str, n_hops=2):
        """图谱检索: 实体+多跳邻居"""
        # 1. 识别查询中的实体
        entities = self._identify_entities(query)
        # 2. 沿关系边扩展 n_hops 跳
        subgraph = self._expand_subgraph(entities, n_hops)
        # 3. 子图文本化
        context = self._subgraph_to_text(subgraph)
        return context

    def _identify_entities(self, query):
        """从查询识别实体"""
        prompt = f"从问题中识别关键实体: {query}\n实体列表:"
        result = self.llm.generate(prompt)
        return result.split(',')

    def _expand_subgraph(self, entities, n_hops):
        """扩展子图: 沿关系边走 n_hops 跳"""
        visited = set(entities)
        frontier = list(entities)
        subgraph = {'entities': {}, 'relations': []}
        for hop in range(n_hops):
            new_frontier = []
            for entity in frontier:
                # 找该实体的所有关系
                for rel in self.graph['relations']:
                    if rel['source'] == entity and rel['target'] not in visited:
                        visited.add(rel['target'])
                        new_frontier.append(rel['target'])
                        subgraph['relations'].append(rel)
                    elif rel['target'] == entity and rel['source'] not in visited:
                        visited.add(rel['source'])
                        new_frontier.append(rel['source'])
                        subgraph['relations'].append(rel)
            frontier = new_frontier
        # 填充实体信息
        for e in visited:
            if e in self.graph['entities']:
                subgraph['entities'][e] = self.graph['entities'][e]
        return subgraph

    def _subgraph_to_text(self, subgraph):
        """子图转文本"""
        text = "相关实体:\n"
        for name, info in subgraph['entities'].items():
            text += f"- {name}({info['type']}): {info['description']}\n"
        text += "\n关系:\n"
        for rel in subgraph['relations']:
            text += f"- {rel['source']} --{rel['type']}--> {rel['target']}\n"
        return text

# 量化: 2跳子图覆盖90%多跳问题
# 图谱构建成本: 1万文档约 $50 (LLM抽取实体)
# 检索延迟: 比向量检索多 50ms (图遍历)

量化:GraphRAG 在多跳问答比向量 RAG 准确率高 20-30 分。2 跳子图覆盖 90% 多跳问题。图谱构建成本:1 万文档约 50 美元(LLM 抽取实体)。检索延迟比向量检索多 50ms(图遍历)。适合需要跨文档推理的复杂问答。

边界:图谱构建成本高------每文档需 LLM 抽取实体,万级文档 50 美元。图谱需定期更新------新增文档需增量抽取实体入图。实体消歧难------同名实体(如两个"张三")需消歧。GraphRAG 对简单事实问答无优势------单文档可答的问题用向量 RAG 更快。

5. 多路召回与融合:互补提升召回率

单一检索(向量或 BM25)各有盲区。多路召回(向量+BM25+图谱)融合互补,召回率提升 15-25%。RRF(Reciprocal Rank Fusion)是主流融合算法。
#mermaid-svg-jTvP15mNRCOYTVN9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jTvP15mNRCOYTVN9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jTvP15mNRCOYTVN9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jTvP15mNRCOYTVN9 .error-icon{fill:#552222;}#mermaid-svg-jTvP15mNRCOYTVN9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jTvP15mNRCOYTVN9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jTvP15mNRCOYTVN9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jTvP15mNRCOYTVN9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jTvP15mNRCOYTVN9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jTvP15mNRCOYTVN9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jTvP15mNRCOYTVN9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jTvP15mNRCOYTVN9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jTvP15mNRCOYTVN9 .marker.cross{stroke:#333333;}#mermaid-svg-jTvP15mNRCOYTVN9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jTvP15mNRCOYTVN9 p{margin:0;}#mermaid-svg-jTvP15mNRCOYTVN9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jTvP15mNRCOYTVN9 .cluster-label text{fill:#333;}#mermaid-svg-jTvP15mNRCOYTVN9 .cluster-label span{color:#333;}#mermaid-svg-jTvP15mNRCOYTVN9 .cluster-label span p{background-color:transparent;}#mermaid-svg-jTvP15mNRCOYTVN9 .label text,#mermaid-svg-jTvP15mNRCOYTVN9 span{fill:#333;color:#333;}#mermaid-svg-jTvP15mNRCOYTVN9 .node rect,#mermaid-svg-jTvP15mNRCOYTVN9 .node circle,#mermaid-svg-jTvP15mNRCOYTVN9 .node ellipse,#mermaid-svg-jTvP15mNRCOYTVN9 .node polygon,#mermaid-svg-jTvP15mNRCOYTVN9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jTvP15mNRCOYTVN9 .rough-node .label text,#mermaid-svg-jTvP15mNRCOYTVN9 .node .label text,#mermaid-svg-jTvP15mNRCOYTVN9 .image-shape .label,#mermaid-svg-jTvP15mNRCOYTVN9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-jTvP15mNRCOYTVN9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jTvP15mNRCOYTVN9 .rough-node .label,#mermaid-svg-jTvP15mNRCOYTVN9 .node .label,#mermaid-svg-jTvP15mNRCOYTVN9 .image-shape .label,#mermaid-svg-jTvP15mNRCOYTVN9 .icon-shape .label{text-align:center;}#mermaid-svg-jTvP15mNRCOYTVN9 .node.clickable{cursor:pointer;}#mermaid-svg-jTvP15mNRCOYTVN9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jTvP15mNRCOYTVN9 .arrowheadPath{fill:#333333;}#mermaid-svg-jTvP15mNRCOYTVN9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jTvP15mNRCOYTVN9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jTvP15mNRCOYTVN9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jTvP15mNRCOYTVN9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jTvP15mNRCOYTVN9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jTvP15mNRCOYTVN9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jTvP15mNRCOYTVN9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jTvP15mNRCOYTVN9 .cluster text{fill:#333;}#mermaid-svg-jTvP15mNRCOYTVN9 .cluster span{color:#333;}#mermaid-svg-jTvP15mNRCOYTVN9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jTvP15mNRCOYTVN9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jTvP15mNRCOYTVN9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-jTvP15mNRCOYTVN9 .icon-shape,#mermaid-svg-jTvP15mNRCOYTVN9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jTvP15mNRCOYTVN9 .icon-shape p,#mermaid-svg-jTvP15mNRCOYTVN9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jTvP15mNRCOYTVN9 .icon-shape .label rect,#mermaid-svg-jTvP15mNRCOYTVN9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jTvP15mNRCOYTVN9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jTvP15mNRCOYTVN9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jTvP15mNRCOYTVN9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-jTvP15mNRCOYTVN9 .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-jTvP15mNRCOYTVN9 .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-jTvP15mNRCOYTVN9 .default tspan{fill:#000000!important;} 多路召回融合
向量召回: 语义相关
BM25召回: 关键词匹配
图谱召回: 关系推理
各路 top-50
RRF融合排序
融合后 top-10
重排
最终 top-5

python 复制代码
# 来源:多路召回融合 / 生产实践 2024
class MultiRouteRetriever:
    """多路召回融合检索器"""
    def __init__(self, vector_idx, bm25_idx, graph_idx, rrf_k=60):
        self.vector = vector_idx
        self.bm25 = bm25_idx
        self.graph = graph_idx
        self.rrf_k = rrf_k  # RRF 参数

    def retrieve(self, query, k=10):
        """多路召回+RRF融合"""
        # 1. 各路独立召回
        vec_results = self.vector.search(query, k=50)
        bm25_results = self.bm25.search(query, k=50)
        graph_results = self.graph.retrieve(query) if self.graph else []
        # 2. RRF 融合排序
        fused = self._rrf_fuse([vec_results, bm25_results, graph_results])
        return fused[:k]

    def _rrf_fuse(self, result_lists):
        """RRF 融合: 1/(k+rank) 累加"""
        scores = {}
        for results in result_lists:
            for rank, (doc_id, _) in enumerate(results):
                scores[doc_id] = scores.get(doc_id, 0) + 1 / (self.rrf_k + rank + 1)
        # 按融合分数排序
        return sorted(scores.items(), key=lambda x: x[1], reverse=True)

# 量化: 向量+BM25 融合比单路召回率高 15-20%
# 加图谱再提 5-10% (多跳问题)
# RRF 比加权融合简单且无需调参
python 复制代码
# 来源:BM25 检索实现 / rank_bm25
class BM25Retriever:
    """BM25 关键词检索"""
    def __init__(self, documents):
        from rank_bm25 import BM25Okapi
        self.docs = documents
        self.tokenized = [doc.split() for doc in documents]
        self.bm25 = BM25Okapi(self.tokenized)

    def search(self, query, k=10):
        """BM25 检索"""
        tokens = query.split()
        scores = self.bm25.get_scores(tokens)
        ranked = sorted(enumerate(scores), key=lambda x: x[1], reverse=True)
        return [(self.docs[i], s) for i, s in ranked[:k]]

# 量化: BM25 对精确关键词匹配优于向量
# 例: 查"GB38248-2023" (国标号), 向量可能召回不到, BM25精确匹配
# 向量强在语义, BM25强在精确, 互补

量化:向量+BM25 融合比单路召回率高 15-20%,加图谱再提 5-10%(多跳问题)。RRF 比加权融合简单且无需调参。BM25 对精确关键词(如国标号 GB38248-2023)优于向量------向量强语义,BM25 强精确,互补。RRF 参数 k=60 是经验值。

边界:多路召回增加延迟------3 路并行 50ms vs 单路 50ms(并行无增益需串行融合)。RRF 假设各路独立------相关路(向量+重排向量)融合增益小。BM25 对中文需分词------未分词的中文 BM25 无效。图谱路可选------简单问题图谱无增益徒增延迟。

6. 生产级 RAG 流水线:端到端架构

生产 RAG 需处理文档更新、增量索引、缓存、监控、评测。端到端流水线涵盖:文档接入->分块->向量化->索引->检索->重排->生成->评测。
#mermaid-svg-e2NUJmKOv4L5UaO4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-e2NUJmKOv4L5UaO4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-e2NUJmKOv4L5UaO4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-e2NUJmKOv4L5UaO4 .error-icon{fill:#552222;}#mermaid-svg-e2NUJmKOv4L5UaO4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-e2NUJmKOv4L5UaO4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-e2NUJmKOv4L5UaO4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-e2NUJmKOv4L5UaO4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-e2NUJmKOv4L5UaO4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-e2NUJmKOv4L5UaO4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-e2NUJmKOv4L5UaO4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-e2NUJmKOv4L5UaO4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-e2NUJmKOv4L5UaO4 .marker.cross{stroke:#333333;}#mermaid-svg-e2NUJmKOv4L5UaO4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-e2NUJmKOv4L5UaO4 p{margin:0;}#mermaid-svg-e2NUJmKOv4L5UaO4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-e2NUJmKOv4L5UaO4 .cluster-label text{fill:#333;}#mermaid-svg-e2NUJmKOv4L5UaO4 .cluster-label span{color:#333;}#mermaid-svg-e2NUJmKOv4L5UaO4 .cluster-label span p{background-color:transparent;}#mermaid-svg-e2NUJmKOv4L5UaO4 .label text,#mermaid-svg-e2NUJmKOv4L5UaO4 span{fill:#333;color:#333;}#mermaid-svg-e2NUJmKOv4L5UaO4 .node rect,#mermaid-svg-e2NUJmKOv4L5UaO4 .node circle,#mermaid-svg-e2NUJmKOv4L5UaO4 .node ellipse,#mermaid-svg-e2NUJmKOv4L5UaO4 .node polygon,#mermaid-svg-e2NUJmKOv4L5UaO4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-e2NUJmKOv4L5UaO4 .rough-node .label text,#mermaid-svg-e2NUJmKOv4L5UaO4 .node .label text,#mermaid-svg-e2NUJmKOv4L5UaO4 .image-shape .label,#mermaid-svg-e2NUJmKOv4L5UaO4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-e2NUJmKOv4L5UaO4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-e2NUJmKOv4L5UaO4 .rough-node .label,#mermaid-svg-e2NUJmKOv4L5UaO4 .node .label,#mermaid-svg-e2NUJmKOv4L5UaO4 .image-shape .label,#mermaid-svg-e2NUJmKOv4L5UaO4 .icon-shape .label{text-align:center;}#mermaid-svg-e2NUJmKOv4L5UaO4 .node.clickable{cursor:pointer;}#mermaid-svg-e2NUJmKOv4L5UaO4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-e2NUJmKOv4L5UaO4 .arrowheadPath{fill:#333333;}#mermaid-svg-e2NUJmKOv4L5UaO4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-e2NUJmKOv4L5UaO4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-e2NUJmKOv4L5UaO4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-e2NUJmKOv4L5UaO4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-e2NUJmKOv4L5UaO4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-e2NUJmKOv4L5UaO4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-e2NUJmKOv4L5UaO4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-e2NUJmKOv4L5UaO4 .cluster text{fill:#333;}#mermaid-svg-e2NUJmKOv4L5UaO4 .cluster span{color:#333;}#mermaid-svg-e2NUJmKOv4L5UaO4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-e2NUJmKOv4L5UaO4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-e2NUJmKOv4L5UaO4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-e2NUJmKOv4L5UaO4 .icon-shape,#mermaid-svg-e2NUJmKOv4L5UaO4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-e2NUJmKOv4L5UaO4 .icon-shape p,#mermaid-svg-e2NUJmKOv4L5UaO4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-e2NUJmKOv4L5UaO4 .icon-shape .label rect,#mermaid-svg-e2NUJmKOv4L5UaO4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-e2NUJmKOv4L5UaO4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-e2NUJmKOv4L5UaO4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-e2NUJmKOv4L5UaO4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-e2NUJmKOv4L5UaO4 .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-e2NUJmKOv4L5UaO4 .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-e2NUJmKOv4L5UaO4 .default tspan{fill:#000000!important;} 文档接入
分块
向量化
索引存储
查询检索
重排
上下文拼接
LLM生成
评测监控
反馈优化

python 复制代码
# 来源:生产级 RAG 流水线 / 生产实践 2024
class ProductionRAGPipeline:
    """生产级 RAG 流水线"""
    def __init__(self, config):
        self.chunker = RecursiveChunker(config['chunk_size'], config['overlap'])
        self.embedder = EmbeddingModel(config['embedding_model'])
        self.index = VectorIndex(config['dim'], config['index_type'])
        self.reranker = CrossEncoderReranker(config['reranker_model'])
        self.llm = config['llm']
        self.cache = {}  # 查询缓存
        self.metrics = {'queries': 0, 'cache_hits': 0, 'latencies': []}

    def ingest(self, documents):
        """文档接入流水线"""
        for doc in documents:
            chunks = self.chunker.split(doc['content'])
            embeddings = self.embedder.embed(chunks)
            self.index.add(embeddings, [f"{doc['id']}_{i}" for i in range(len(chunks))])

    def query(self, question: str):
        """查询流水线"""
        self.metrics['queries'] += 1
        # 1. 缓存检查
        if question in self.cache:
            self.metrics['cache_hits'] += 1
            return self.cache[question]
        # 2. 检索
        import time
        t0 = time.time()
        results = self._retrieve(question)
        # 3. 生成
        context = '\n'.join([r[0] for r in results])
        prompt = f"基于以下信息回答问题:\n{context}\n\n问题: {question}\n答:"
        answer = self.llm.generate(prompt)
        # 4. 缓存与指标
        self.cache[question] = answer
        self.metrics['latencies'].append(time.time() - t0)
        return answer

    def _retrieve(self, question):
        """检索+重排"""
        query_vec = self.embedder.embed_query(question)
        candidates = self.index.search(query_vec, k=50)
        docs = [self._get_doc(c[0]) for c in candidates]
        return self.reranker.rerank(question, docs, top_k=5)

    def health_check(self):
        """健康检查"""
        return {
            'index_size': len(self.index.id_map),
            'cache_size': len(self.cache),
            'avg_latency': sum(self.metrics['latencies']) / max(1, len(self.metrics['latencies'])),
            'cache_hit_rate': self.metrics['cache_hits'] / max(1, self.metrics['queries']),
        }

# 量化: 生产RAG典型指标
# 文档接入: 1万文档约 10 分钟
# 查询延迟: P50 300ms, P99 800ms (含LLM生成)
# 缓存命中率: 重复问题场景 30-50%
# 准确率: RAGAS 评测 75-85% (任务相关)

量化:生产 RAG 典型指标------1 万文档接入约 10 分钟,查询 P50 300ms P99 800ms(含 LLM 生成),缓存命中率 30-50%(重复问题场景),RAGAS 评测准确率 75-85%。增量索引支持文档更新------新增文档仅向量化新块加入索引,无需重建。

边界:RAG 上下文长度有限------top-5 块约 2500 token,需与模型上下文预算权衡。文档更新需增量索引------全量重建成本高,需支持增量。缓存需 TTL 过期------知识更新后旧缓存失效。评测需定期回归------Embedding/LLM 升级可能致性能退化。

7. 边界与失败模式

RAG 失败模式集中在召回不全、上下文噪声、知识陈旧、幻觉四类。
#mermaid-svg-U8hQz0qiwna6BopY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-U8hQz0qiwna6BopY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-U8hQz0qiwna6BopY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-U8hQz0qiwna6BopY .error-icon{fill:#552222;}#mermaid-svg-U8hQz0qiwna6BopY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-U8hQz0qiwna6BopY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-U8hQz0qiwna6BopY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-U8hQz0qiwna6BopY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-U8hQz0qiwna6BopY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-U8hQz0qiwna6BopY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-U8hQz0qiwna6BopY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-U8hQz0qiwna6BopY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-U8hQz0qiwna6BopY .marker.cross{stroke:#333333;}#mermaid-svg-U8hQz0qiwna6BopY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-U8hQz0qiwna6BopY p{margin:0;}#mermaid-svg-U8hQz0qiwna6BopY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-U8hQz0qiwna6BopY .cluster-label text{fill:#333;}#mermaid-svg-U8hQz0qiwna6BopY .cluster-label span{color:#333;}#mermaid-svg-U8hQz0qiwna6BopY .cluster-label span p{background-color:transparent;}#mermaid-svg-U8hQz0qiwna6BopY .label text,#mermaid-svg-U8hQz0qiwna6BopY span{fill:#333;color:#333;}#mermaid-svg-U8hQz0qiwna6BopY .node rect,#mermaid-svg-U8hQz0qiwna6BopY .node circle,#mermaid-svg-U8hQz0qiwna6BopY .node ellipse,#mermaid-svg-U8hQz0qiwna6BopY .node polygon,#mermaid-svg-U8hQz0qiwna6BopY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-U8hQz0qiwna6BopY .rough-node .label text,#mermaid-svg-U8hQz0qiwna6BopY .node .label text,#mermaid-svg-U8hQz0qiwna6BopY .image-shape .label,#mermaid-svg-U8hQz0qiwna6BopY .icon-shape .label{text-anchor:middle;}#mermaid-svg-U8hQz0qiwna6BopY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-U8hQz0qiwna6BopY .rough-node .label,#mermaid-svg-U8hQz0qiwna6BopY .node .label,#mermaid-svg-U8hQz0qiwna6BopY .image-shape .label,#mermaid-svg-U8hQz0qiwna6BopY .icon-shape .label{text-align:center;}#mermaid-svg-U8hQz0qiwna6BopY .node.clickable{cursor:pointer;}#mermaid-svg-U8hQz0qiwna6BopY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-U8hQz0qiwna6BopY .arrowheadPath{fill:#333333;}#mermaid-svg-U8hQz0qiwna6BopY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-U8hQz0qiwna6BopY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-U8hQz0qiwna6BopY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-U8hQz0qiwna6BopY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-U8hQz0qiwna6BopY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-U8hQz0qiwna6BopY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-U8hQz0qiwna6BopY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-U8hQz0qiwna6BopY .cluster text{fill:#333;}#mermaid-svg-U8hQz0qiwna6BopY .cluster span{color:#333;}#mermaid-svg-U8hQz0qiwna6BopY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-U8hQz0qiwna6BopY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-U8hQz0qiwna6BopY rect.text{fill:none;stroke-width:0;}#mermaid-svg-U8hQz0qiwna6BopY .icon-shape,#mermaid-svg-U8hQz0qiwna6BopY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-U8hQz0qiwna6BopY .icon-shape p,#mermaid-svg-U8hQz0qiwna6BopY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-U8hQz0qiwna6BopY .icon-shape .label rect,#mermaid-svg-U8hQz0qiwna6BopY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-U8hQz0qiwna6BopY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-U8hQz0qiwna6BopY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-U8hQz0qiwna6BopY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-U8hQz0qiwna6BopY .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-U8hQz0qiwna6BopY .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-U8hQz0qiwna6BopY .default tspan{fill:#000000!important;} RAG 失败模式
召回不全
上下文噪声
知识陈旧
LLM幻觉
分块不当致信息断裂
Embedding语义偏差
召回无关文档干扰
文档未及时更新
LLM忽略上下文编造
递归分块+重叠
重排过滤噪声
增量索引+定时更新
提示约束+引用标注

实战复盘:某法律 RAG 系统召回率低------用户查"合同解除条件"时未召回相关条款。诊断发现分块固定 500 字符,将"解除条件"条款截断为两块,向量检索仅召回半块。改用递归分块按条款边界切分,召回率从 65% 升至 88%。教训:结构化文档需按结构分块,固定长度破坏语义单元。

实战复盘:某客服 RAG 频繁幻觉------模型回答中引用了不存在的政策条款。诊断发现召回的上下文含无关文档噪声,LLM 基于噪声编造。引入 Rerank 过滤噪声+Prompt 约束"仅基于上下文回答,无依据则说不知道",幻觉率从 15% 降至 2%。教训:RAG 需明确约束 LLM 仅用上下文,召回噪声是幻觉主因。

总结

RAG 核心在于分块、Embedding、检索、Rerank、GraphRAG、流水线六点。递归分块保留语义边界,bge-m3 多语 Embedding 准确率 73.5,IVF 索引 99% 召回 50ms,Cross-encoder 重排提升 15 分,GraphRAG 多跳问答提升 20-30 分,多路 RRF 融合召回率提升 15-25%。生产流水线含文档接入->分块->向量化->索引->检索->重排->生成->评测。选型决策:通用问答用向量 RAG+Rerank,多跳推理用 GraphRAG,精确匹配加 BM25,高质量场景用语义分块。防幻觉需召回去噪+Prompt 约束+引用标注三层。

相关推荐
明哥聊AI1 小时前
AI视频生成技术全景:Sora2、Veo3、可灵3.0背后的Diffusion架构深度解析
人工智能·架构·音视频
七77.1 小时前
【3D 物理布局生成】PHYSCENE: Physically Interactable 3D Scene Synthesis for Embodied AI
人工智能·3d
霸道流氓气质1 小时前
Harness Engineering 模块化指令实战:告别 600 行巨型 AGENTS.md
开发语言·人工智能·python
Akamai中国1 小时前
Akamai收购安全企业浏览器提供商LayerX
人工智能·云原生·云计算·云服务
内向的小熊猫1 小时前
从 Prompt 到 Loop:理清 AI Agent 工程的概念演进
人工智能·prompt
水如烟1 小时前
孤能子视角:体验EIS预测论,关系思维落地实体思维的例子
人工智能
老白说数智化升级1 小时前
制造企业如何确定物流数智化建设的优先级?
大数据·人工智能·制造
liukuang1101 小时前
AI PC五强争霸:联想赌算力,华为改规则,荣耀卷价格
人工智能·华为
西安老张(AIGC&ComfyUI)2 小时前
第024章:掉坑里了!ComfyUI文生音频下载IndexTTS2 插件后Qwen3-TTS工作流崩了!
人工智能·aigc