高级 RAG:详解 LongRAG、Self-RAG 和 GraphRAG

LongRAG、Self-RAG、GraphRAG - 下一代技术

目录

检索增强生成(RAG) 已经远远超越了简单的向量相似性搜索。 LongRAG、Self-RAG 和 GraphRAG 代表了这些能力的前沿。

现代 RAG 系统需要处理大量文档,理解复杂的实体关系等。

a-pirate-captain 这张漂亮的图片由 AI 模型 Flux 1 dev 生成。

超越基础 RAG 的演变

传统 RAG 系统遵循一个简单的模式:对文档进行分块,将其嵌入向量,通过余弦相似性检索相似的块,并将其馈送到 LLM。虽然对许多用例有效,但这种方法在以下三个关键场景中存在困难:

  1. 长距离依赖:重要上下文可能跨越多个块的数千个标记
  2. 检索置信度:系统无法评估检索到的内容是否确实相关
  3. 关系复杂性:向量相似性无法捕捉实体之间的复杂连接

先进的 RAG 变体通过专门的架构来解决这些限制,这些架构针对特定挑战进行了优化。

LongRAG:征服扩展上下文

架构概述

LongRAG 通过利用具有扩展上下文窗口(32K、100K 或甚至 1M 个标记)的 LLM 彻底重新思考了分块策略。LongRAG 不是将文档分成小的 512 个标记块,而是使用一种分层方法:

文档级嵌入:整个文档(或非常大的部分)作为单个单元进行处理。文档级嵌入捕获整体语义含义,同时保留完整文本以供后续处理。

最小碎片化:当需要分块时,LongRAG 使用更大的块(4K-8K 个标记)并具有较大的重叠(20-30%)。这保留了叙述流畅性并减少了上下文碎片化。

上下文组装:在检索时,LongRAG 返回完整的文档或大型连贯部分,而不是零散的片段。LLM 接收到的上下文是连续的,保留了结构和语义关系。

实施策略

以下是使用 Python 和现代嵌入模型的概念性实现:

from typing import List, Dict
import numpy as np

class LongRAGRetriever:
    def __init__(self, model, chunk_size=8000, overlap=1600):
        self.model = model
        self.chunk_size = chunk_size
        self.overlap = overlap
        self.doc_embeddings = []
        self.documents = []
    
    def create_long_chunks(self, text: str) -> List[str]:
        """创建重叠的大块"""
        chunks = []
        start = 0
        while start < len(text):
            end = start + self.chunk_size
            chunk = text[start:end]
            chunks.append(chunk)
            start += (self.chunk_size - self.overlap)
        return chunks
    
    def index_document(self, doc: str, metadata: Dict):
        """使用分层嵌入索引文档"""
        # 嵌入整个文档
        doc_embedding = self.model.embed(doc)
        
        # 创建带重叠的大块
        chunks = self.create_long_chunks(doc)
        chunk_embeddings = [self.model.embed(c) for c in chunks]
        
        self.doc_embeddings.append({
            'doc_id': len(self.documents),
            'doc_embedding': doc_embedding,
            'chunk_embeddings': chunk_embeddings,
            'chunks': chunks,
            'full_text': doc,
            'metadata': metadata
        })
        self.documents.append(doc)
    
    def retrieve(self, query: str, top_k: int = 3) -> List[Dict]:
        """检索相关长文本内容"""
        query_embedding = self.model.embed(query)
        
        # 首先在文档级别评分
        doc_scores = [
            np.dot(query_embedding, doc['doc_embedding'])
            for doc in self.doc_embeddings
        ]
        
        # 获取顶级文档
        top_doc_indices = np.argsort(doc_scores)[-top_k:][::-1]
        
        results = []
        for idx in top_doc_indices:
            doc_data = self.doc_embeddings[idx]
            
            # 对每个文档,找到最佳块
            chunk_scores = [
                np.dot(query_embedding, emb)
                for emb in doc_data['chunk_embeddings']
            ]
            best_chunk_idx = np.argmax(chunk_scores)
            
            # 返回最佳块周围的扩展上下文
            context_chunks = self._get_extended_context(
                doc_data['chunks'], 
                best_chunk_idx
            )
            
            results.append({
                'text': ''.join(context_chunks),
                'score': doc_scores[idx],
                'metadata': doc_data['metadata']
            })
        
        return results
    
    def _get_extended_context(self, chunks: List[str], 
                             center_idx: int) -> List[str]:
        """获取相关块周围的扩展上下文"""
        start = max(0, center_idx - 1)
        end = min(len(chunks), center_idx + 2)
        return chunks[start:end]

应用场景和性能

LongRAG 在上下文至关重要的场景中表现出色:

  • 法律文件分析:合同和法律简报通常有跨越数十页的依赖关系
  • 研究论文检索:理解方法论需要连贯的章节,而不是孤立的段落
  • 代码仓库:函数和类必须在其模块上下文中理解

性能特征:

  • 延迟:由于处理大块,比标准 RAG 慢 2-5 倍
  • 准确性:在长文本问答基准测试中提高 15-25%
  • 内存:需要 3-4 倍于标准 RAG 的内存

Self-RAG:反思检索

核心原则

Self-RAG 为 RAG 系统引入了一个元认知层。系统通过特殊的反思标记主动反思其自身过程,而不是盲目检索和生成:

检索标记:决定是否需要检索特定查询 相关性标记:评估检索到的文档是否确实相关 支持标记:检查生成的答案是否由检索到的内容支持 批评标记:评估生成响应的整体质量

架构组件

Self-RAG 架构由三个交错的阶段组成:

class SelfRAGSystem:
    def __init__(self, retriever, generator, critic):
        self.retriever = retriever
        self.generator = generator
        self.critic = critic
    
    def generate_with_reflection(self, query: str, 
                                 max_iterations: int = 3):
        """生成答案并进行自我反思"""
        
        # 阶段 1:决定是否需要检索
        retrieve_decision = self.critic.should_retrieve(query)
        
        if not retrieve_decision:
            # 不进行检索直接生成
            return self.generator.generate(query)
        
        # 阶段 2:检索并评估相关性
        retrieved_docs = self.retriever.retrieve(query)
        relevant_docs = []
        
        for doc in retrieved_docs:
            relevance_score = self.critic.assess_relevance(
                query, doc
            )
            if relevance_score > 0.7:  # 阈值
                relevant_docs.append(doc)
        
        if not relevant_docs:
            # 回退到不进行检索的生成
            return self.generator.generate(query)
        
        # 阶段 3:生成并验证支持
        best_answer = None
        best_score = -1
        
        for _ in range(max_iterations):
            # 生成候选答案
            answer = self.generator.generate(
                query, context=relevant_docs
            )
            
            # 评估支持和质量
            support_score = self.critic.check_support(
                answer, relevant_docs
            )
            quality_score = self.critic.assess_quality(answer)
            
            total_score = 0.6 * support_score + 0.4 * quality_score
            
            if total_score > best_score:
                best_score = total_score
                best_answer = answer
            
            # 如果达到高质量则提前停止
            if total_score > 0.9:
                break
        
        return {
            'answer': best_answer,
            'confidence': best_score,
            'sources': relevant_docs,
            'reflections': {
                'retrieved': retrieve_decision,
                'relevance': len(relevant_docs),
                'support': support_score
            }
        }

训练反思机制

Self-RAG 需要训练批评组件以进行可靠的评估。这通常涉及:

  1. 监督微调:在带有相关性判断的标注数据集上进行
  2. 强化学习:使用准确预测的奖励
  3. 对比学习:区分支持和不支持的主张

反思标记可以实现为:

  • 词汇表中的特殊标记(如 [RETRIEVE][RELEVANT]
  • 模型上的单独分类器头
  • 外部批评模型(集成方法)

生产考虑

在生产系统中部署 Self-RAG 时:

延迟权衡:每个反思步骤增加 20-40% 的推理开销。根据响应时间要求平衡彻底性和效率。

置信度阈值:根据使用场景调整反思阈值。法律或医疗应用需要比通用聊天机器人更高的置信度。

监控:跟踪反思决策以识别模式。如果检索很少需要,您可能会受益于更简单的架构。

GraphRAG:知识图谱增强检索

概念基础

GraphRAG 将检索问题从向量相似性转换为图遍历。GraphRAG 不是寻找语义相似的文本块,而是识别相关实体和关系的子图。

实体提取:识别命名实体、概念及其类型 关系映射:提取实体之间的关系(时间、因果、层次) 图构建:构建一个以实体为节点、关系为边的知识图谱 子图检索:根据查询找到相关的连接子图

图构建管道

从非结构化文本构建知识图谱涉及多个阶段:

class GraphRAGBuilder:
    def __init__(self, entity_extractor, relation_extractor):
        self.entity_extractor = entity_extractor
        self.relation_extractor = relation_extractor
        self.graph = NetworkGraph()
    
    def build_graph(self, documents: List[str]):
        """从文档构建知识图谱"""
        for doc in documents:
            # 提取实体
            entities = self.entity_extractor.extract(doc)
            
            # 添加实体作为节点
            for entity in entities:
                self.graph.add_node(
                    entity['text'],
                    entity_type=entity['type'],
                    context=entity['surrounding_text']
                )
            
            # 提取关系
            relations = self.relation_extractor.extract(
                doc, entities
            )
            
            # 添加关系作为边
            for rel in relations:
                self.graph.add_edge(
                    rel['source'],
                    rel['target'],
                    relation_type=rel['type'],
                    confidence=rel['score'],
                    evidence=rel['text_span']
                )
    
    def enrich_graph(self):
        """添加派生关系和元数据"""
        # 计算节点重要性(PageRank 等)
        self.graph.compute_centrality()
        
        # 识别社区/集群
        self.graph.detect_communities()
        
        # 如果有时间戳,添加时间顺序
        self.graph.add_temporal_edges()

使用图进行查询处理

GraphRAG 查询涉及跨知识图谱的多跳推理:

class GraphRAGRetriever:
    def __init__(self, graph, embedder):
        self.graph = graph
        self.embedder = embedder
    
    def retrieve_subgraph(self, query: str, 
                         max_hops: int = 2,
                         max_nodes: int = 50):
        """为查询检索相关子图"""
        
        # 识别查询中的种子实体
        query_entities = self.entity_extractor.extract(query)
        
        # 在图中查找匹配节点
        seed_nodes = []
        for entity in query_entities:
            matches = self.graph.find_similar_nodes(
                entity['text'],
                similarity_threshold=0.85
            )
            seed_nodes.extend(matches)
        
        # 通过遍历扩展子图
        subgraph = self.graph.create_subgraph()
        visited = set()
        
        for seed in seed_nodes:
            self._expand_from_node(
                seed, 
                subgraph, 
                visited,
                current_hop=0,
                max_hops=max_hops
            )
        
        # 按相关性排序节点
        ranked_nodes = self._rank_subgraph_nodes(
            subgraph, query
        )
        
        # 提取和格式化上下文
        context = self._format_graph_context(
            ranked_nodes[:max_nodes],
            subgraph
        )
        
        return context
    
    def _expand_from_node(self, node, subgraph, visited,
                         current_hop, max_hops):
        """递归扩展子图"""
        if current_hop >= max_hops or node in visited:
            return
        
        visited.add(node)
        subgraph.add_node(node)
        
        # 获取邻居
        neighbors = self.graph.get_neighbors(node)
        
        for neighbor, edge_data in neighbors:
            # 添加边到子图
            subgraph.add_edge(node, neighbor, edge_data)
            
            # 递归扩展
            self._expand_from_node(
                neighbor,
                subgraph,
                visited,
                current_hop + 1,
                max_hops
            )
    
    def _format_graph_context(self, nodes, subgraph):
        """将子图转换为文本上下文"""
        context_parts = []
        
        for node in nodes:
            # 添加节点上下文
            context_parts.append(f"实体: {node.text}")
            context_parts.append(f"类型: {node.entity_type}")
            
            # 添加关系信息
            edges = subgraph.get_edges(node)
            for edge in edges:
                context_parts.append(
                    f"- {edge.relation_type} -> {edge.target.text}"
                )
        
        return "\n".join(context_parts)

微软的 GraphRAG 实现

微软的 GraphRAG 采用了一种独特的方法,通过生成社区摘要:

  1. 构建初始图:使用基于 LLM 的实体/关系提取从文档中构建图
  2. 检测社区:使用 Leiden 算法或其他方法
  3. 生成摘要:使用 LLM 为每个社区生成摘要
  4. 分层结构:构建多个级别的社区抽象
  5. 查询时间:检索相关社区并遍历到特定实体

这种方法特别有效于:

  • 探索性查询(“这个语料库的主要主题是什么?”)
  • 多跳推理(“A 如何通过 B 连接到 C?”)
  • 时间分析(“这个实体的关系如何演变?”)

对比分析

何时使用每种变体

使用 LongRAG 时:

  • 文档具有强烈的内部连贯性
  • LLM 的上下文窗口支持大型输入(32K+)
  • 查询答案需要理解长距离依赖
  • 正在处理结构化文档(报告、论文、书籍)

使用 Self-RAG 时:

  • 准确性和可信度至关重要
  • 需要可解释的检索决策
  • 无关检索的假阳性代价高昂
  • 查询复杂性差异广泛(一些需要检索,一些不需要)

使用 GraphRAG 时:

  • 领域有丰富的实体关系
  • 查询涉及多跳推理
  • 时间或层次关系重要
  • 需要理解实体之间的连接

性能指标比较

指标 标准 RAG LongRAG Self-RAG GraphRAG
索引时间 1x 0.8x 1.1x 3-5x
查询延迟 1x 2-3x 1.4x 1.5-2x
内存使用 1x 3-4x 1.2x 2-3x
准确性(问答) 基线 +15-25% +20-30% +25-40%*
可解释性 中等

*GraphRAG 改进高度依赖于领域

混合方法

最强大的生产系统通常结合多种技术:

LongRAG + GraphRAG:使用图结构识别相关文档集群,然后检索完整文档而不是片段

Self-RAG + GraphRAG:将反思机制应用于图遍历决策(应遵循哪些路径,何时停止扩展)

三阶段流水线:使用 GraphRAG 进行初始基于实体的检索 → 使用 Self-RAG 进行相关性过滤 → 使用 LongRAG 进行上下文组装

实施考虑

嵌入模型

不同的 RAG 变体有不同的嵌入需求:

LongRAG:需要在文档级和块级都表现良好的嵌入。考虑在长序列上使用对比学习训练的模型。

Self-RAG:从细粒度相关性评估中受益的嵌入。

GraphRAG:需要实体感知的嵌入。在实体链接任务上微调的模型表现更好。

嵌入模型的选择显著影响性能。在使用本地模型时,工具如 Ollama 提供了一种简单的方法,可以在部署到生产环境之前尝试不同的嵌入模型。

再次审视分块策略

传统的固定大小分块对于高级 RAG 不够:

语义分块:在自然边界(段落、章节、主题变化)处分块 递归分块:创建具有父子关系的层次化块 滑动窗口:使用重叠块以保留边界处的上下文 结构感知:尊重文档结构(Markdown 标题、XML 标签、代码块)

对于基于 Python 的实现,LangChain 和 LlamaIndex 等库提供了这些分块策略的内置支持。

重排序集成

重排序在所有 RAG 变体中显著提高了检索质量。初始检索后,一个专门的重排序模型根据查询-文档交互特征重新评分结果。这在巧妙集成时提供显著的准确性提升(10-20%),对延迟影响最小。

扩展到生产

索引管道

  • 使用分布式处理(Ray、Dask)处理大型文档语料库
  • 实现增量索引以进行实时更新
  • 在优化的向量数据库中存储嵌入(Pinecone、Weaviate、Qdrant)

查询优化

  • 缓存频繁查询及其结果
  • 实现查询路由(不同 RAG 变体用于不同查询类型)
  • 使用近似最近邻搜索实现次线性扩展

监控

  • 跟踪检索相关性评分
  • 监控 Self-RAG 中的反思决策
  • 测量图遍历路径和深度
  • 记录置信度评分和用户反馈

实际应用

技术文档搜索

一家主要的云提供商在其文档中实现了 GraphRAG:

  • 实体:API 端点、参数、错误代码、服务名称
  • 关系:依赖项、版本兼容性、迁移路径
  • 结果:支持票数减少 35%,解决时间加快 45%

法律发现

一家法律科技公司结合了 Self-RAG 和 LongRAG:

  • Self-RAG 早期过滤无关文档
  • LongRAG 保留文档中的上下文
  • 律师审查的假阳性减少 60%
  • 关键上下文保留从 71% 提高到 94%

研究文献回顾

使用混合方法的学术搜索引擎:

  • GraphRAG 识别引用网络和研究社区
  • LongRAG 检索完整部分以保持方法论上下文
  • 相关论文发现提高 40%
  • 将文献回顾时间从几周缩短到几天

高级主题

多模态 RAG

扩展这些变体以处理图像、表格和代码:

  • 视觉定位:将文本实体链接到文档中的图像
  • 表格理解:将结构化数据解析为图格式
  • 代码分析:从代码库中构建依赖图

自适应 RAG

根据查询特征动态选择 RAG 策略:

  • 查询复杂性分类器
  • 文档类型检测器
  • 策略选择的成本效益优化器

保护隐私的 RAG

在隐私约束下实现这些变体:

  • 跨数据孤岛的联邦检索
  • 嵌入中的差分隐私
  • 加密相似性搜索

入门指南

使用 Python 快速入门

对于希望实施这些技术的人而言,打下坚实的 Python 基础是至关重要的。Python 丰富的机器学习生态系统使其成为 RAG 开发的自然选择。

以下是一个用于实验的简单起点:

# 安装依赖
# pip install sentence-transformers faiss-cpu langchain

from sentence_transformers import SentenceTransformer
import faiss
import numpy as np

# 用于实验的长文本基本设置
model = SentenceTransformer('all-MiniLM-L6-v2')

documents = [
    # 这里放置你的长文本文档
]

# 创建嵌入向量
embeddings = model.encode(documents)

# 构建 FAISS 索引
dimension = embeddings.shape[1]
index = faiss.IndexFlatL2(dimension)
index.add(embeddings.astype('float32'))

# 查询
query = "你的问题在这里"
query_embedding = model.encode([query])
distances, indices = index.search(
    query_embedding.astype('float32'), k=3
)

框架选择

LangChain:适合快速原型开发,集成丰富
LlamaIndex:针对文档索引和检索优化
Haystack:生产就绪,强大的流程抽象
自定义:当你需要完全控制和优化时

评估框架

在生产部署之前,实施严格的评估:

检索指标

  • Precision@K,Recall@K,MRR(均倒数排名)
  • NDCG(归一化折扣累计增益)

生成指标

  • ROUGE,BLEU 用于文本相似性
  • BERTScore 用于语义相似性
  • 人工评估用于质量评估

端到端指标

  • 任务成功率
  • 用户满意度评分
  • 延迟百分位(p50,p95,p99)

结论

RAG 系统的格局已显著成熟,超越了基本的向量相似性搜索。LongRAG、Self-RAG 和 GraphRAG 各自解决了传统方法的特定限制:

LongRAG 通过采用扩展上下文窗口和最小化分块解决了上下文碎片化问题。当文档连贯性重要且你拥有处理大上下文的计算资源时,它是首选方案。

Self-RAG 为检索系统添加了关键的自我意识。通过反思自己的决策,它减少了误报并提高了可信度——这对于准确性比速度更重要的高风险应用至关重要。

GraphRAG 解锁了结构化知识表示的力量。当你的领域涉及实体之间的复杂关系时,基于图的检索可以揭示向量相似性完全忽略的联系。

RAG 的未来可能涉及 混合方法,结合这些变体的优势。一个生产系统可能使用 GraphRAG 来识别相关实体集群,使用 Self-RAG 来过滤和验证检索,使用 LongRAG 来为 LLM 组装连贯的上下文。

随着 LLM 不断改进和上下文窗口扩大,我们将看到更多复杂的 RAG 变体出现。关键是理解你特定的使用案例需求——文档结构、查询模式、准确性要求和计算约束,并选择适当的技巧或组合。

工具生态系统正在迅速成熟,像 LangChain、LlamaIndex 和 Haystack 这样的框架为这些高级模式提供了越来越复杂的支持。结合强大的本地 LLM 运行时和嵌入模型,现在比以往任何时候都更容易试验和部署生产级 RAG 系统。

从基础开始,严格衡量性能,并根据需求演变你的架构。本文中介绍的高级 RAG 变体为这种演变提供了路线图。

有用的链接

外部参考资料