知识的外挂:分块、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 约束+引用标注三层。

相关推荐
2501_938140077 分钟前
做内容很省心,2026好用AI笔记,支持抖音小红书视频无水印保存
人工智能
sell-cloud3 小时前
Prompt Engineering要凉了?Fable 5.5能“猜“意图,但银行正在封杀AI Agent——企业AI的真正护城河是什么
人工智能·prompt
Navicat中国4 小时前
Navicat 18 内置 AI 助手让数据库开发从繁琐操作迈向智能协作
大数据·人工智能·数据库开发
4SAPI5 小时前
多模型请求路由怎么设计?成本、速度和能力如何平衡
人工智能·python·gateway·php
魔乐社区5 小时前
翼芯・智算测试与适配优化专区,让国产算力“选得准、跑得通、用得好“
人工智能·开源·大模型
XCDoris7 小时前
郴州logo设计机构
大数据·人工智能·python
Goboy7 小时前
一个人带三个Agent,我用全新TRAE从0到1搭了个资讯周刊站
人工智能·trae
俊哥V7 小时前
每日 AI 研究简报 · 2026-10-19
人工智能·ai
AI_AGENT_DEV_AI7 小时前
AI英语教培智能体的开发
人工智能