跳转至

RAG与长文本处理

⚠️ 时效性说明:本章涉及前沿模型/价格/榜单等信息,可能随版本快速变化;请以论文原文、官方发布页和 API 文档为准。

📌 定位说明:本章侧重RAG研究前沿与长文本建模技术。 - 📖 RAG工程基础请参考 LLM应用/05-RAG系统构建 - 📖 高级RAG实战请参考 LLM应用/18-高级RAG技术

目录

  1. RAG概述与架构
  2. 检索组件详解
  3. 生成组件优化
  4. 长文本建模技术
  5. RAG系统实践
  6. 前沿进展与挑战

RAG概述与架构

1.1 什么是RAG

检索增强生成(Retrieval-Augmented Generation, RAG) 是一种将外部知识检索与大语言模型生成能力相结合的技术范式。RAG通过动态检索相关文档来增强LLM的知识边界,解决知识截止、幻觉问题和领域适配等挑战。

Text Only
┌─────────────────────────────────────────────────────────────────┐
│                        RAG 架构概览                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────────────┐  │
│  │   用户查询   │───▶│   检索器    │───▶│   相关文档集合       │  │
│  │   (Query)   │    │  (Retriever) │    │   (Documents)       │  │
│  └─────────────┘    └─────────────┘    └─────────────────────┘  │
│         │                                           │            │
│         │                                           ▼            │
│         │                              ┌─────────────────────┐  │
│         │                              │   上下文构建        │  │
│         │                              │  (Context Builder)  │  │
│         │                              └─────────────────────┘  │
│         │                                           │            │
│         ▼                                           ▼            │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                    增强提示 (Augmented Prompt)           │    │
│  │  "基于以下文档回答问题:[文档内容]\n\n问题:[用户查询]"      │    │
│  └─────────────────────────────────────────────────────────┘    │
│                              │                                   │
│                              ▼                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                    大语言模型 (LLM)                       │    │
│  │              生成基于事实的回答                           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                              │                                   │
│                              ▼                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                    最终回答 (Answer)                      │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

1.2 RAG的核心优势

优势 说明 对比纯LLM
知识时效性 可接入实时更新的知识库 受限于训练数据截止时间
可解释性 回答可追溯至具体来源文档 黑盒生成,难以验证
领域适配 无需训练即可适配专业领域 需要昂贵的领域微调
幻觉抑制 基于检索事实生成,减少编造 容易产生幻觉
数据隐私 敏感数据无需上传至模型提供商 需要发送给外部API

1.3 RAG的发展阶段

Text Only
RAG 演进时间线
═══════════════════════════════════════════════════════════════════

Naive RAG (2020)
├── 简单检索 + 直接拼接 + 基础生成
├── 代表:Facebook的原始RAG论文
└── 局限:检索噪声、上下文长度限制

Advanced RAG (2021-2022)
├── 查询重写、HyDE、重排序
├── 递归检索、多跳推理
├── 代表:Self-RAG, REPLUG
└── 改进:检索质量、生成可控性

Modular RAG (2023-2024)
├── 模块化设计、自适应检索
├── 多模态RAG、Agentic RAG
├── 代表:RAG-Fusion, CRAG
└── 特点:灵活组合、动态决策

Agentic RAG (2024+)
├── 智能体驱动的RAG系统
├── 自主规划、工具使用、反思
├── 代表:RAG Agent, Self-Reflective RAG
└── 趋势:与Agent系统深度融合

═══════════════════════════════════════════════════════════════════

检索组件详解

2.1 文档预处理与分块

分块策略(Chunking)

分块是RAG系统的关键预处理步骤,直接影响检索精度和上下文完整性。

Python
# 分块策略对比
class ChunkingStrategies:
    """
    不同分块策略的实现与对比
    """

    @staticmethod  # @staticmethod无需实例即可调用
    def fixed_size_chunk(text, chunk_size=512, overlap=50):
        """
        固定大小分块 - 最简单,但可能切断语义单元
        """
        chunks = []
        start = 0
        while start < len(text):
            end = start + chunk_size
            chunk = text[start:end]
            chunks.append({
                'content': chunk,
                'start': start,
                'end': end
            })
            start = end - overlap  # 重叠保持上下文
        return chunks

    @staticmethod
    def recursive_character_chunk(text, chunk_size=512, separators=["\n\n", "\n", ". ", " "]):
        """
        递归字符分块 - LangChain默认策略
        优先在语义边界处分割
        """
        chunks = []
        # 实现逻辑:递归尝试不同分隔符
        # 优先使用大分隔符(段落),必要时使用小分隔符(句子、单词)
        return chunks

    @staticmethod
    def semantic_chunk(text, embedding_model, similarity_threshold=0.8):
        """
        语义分块 - 基于语义相似性动态分块
        当相邻句子语义相似度低于阈值时分割
        """
        # 1. 分割为句子
        sentences = sent_tokenize(text)

        # 2. 计算句子嵌入
        embeddings = embedding_model.encode(sentences)

        # 3. 基于相似度聚类
        chunks = []
        current_chunk = [sentences[0]]

        for i in range(1, len(sentences)):
            similarity = cosine_similarity(
                embeddings[i-1:i],
                embeddings[i:i+1]
            )[0][0]

            if similarity < similarity_threshold:
                # 语义断裂,保存当前块
                chunks.append(" ".join(current_chunk))
                current_chunk = [sentences[i]]
            else:
                current_chunk.append(sentences[i])

        if current_chunk:
            chunks.append(" ".join(current_chunk))

        return chunks

    @staticmethod
    def agentic_chunk(text, llm, target_chunk_size=512):
        """
        Agentic分块 - 使用LLM智能分块
        让模型决定最佳分割点,保持主题一致性
        """
        prompt = f"""
        将以下文本分割为语义完整的段落,每个段落约{target_chunk_size}字符。
        确保每个段落围绕单一主题,在段落之间标注[CHUNK_BOUNDARY]。

        文本:
        {text[:4000]}  # 限制输入长度
        """

        response = llm.generate(prompt)
        chunks = response.split("[CHUNK_BOUNDARY]")
        return [c.strip() for c in chunks if c.strip()]  # 链式调用:strip去除空白

分块策略选择指南

Text Only
┌─────────────────────────────────────────────────────────────────┐
│                     分块策略选择决策树                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  文档类型是什么?                                                  │
│       │                                                          │
│       ├── 结构化数据(表格、代码)                                  │
│       │       └──▶ 使用结构化分块,保持行列/函数完整性              │
│       │                                                          │
│       ├── 长文档(论文、书籍)                                     │
│       │       └──▶ 使用语义分块或递归字符分块                       │
│       │                                                          │
│       └── 短文档(网页、聊天记录)                                  │
│               └──▶ 使用固定大小分块,简单高效                       │
│                                                                  │
│  对延迟要求?                                                     │
│       │                                                          │
│       ├── 低延迟(实时应用)                                       │
│       │       └──▶ 预计算分块,使用固定大小                         │
│       │                                                          │
│       └── 可接受较高延迟                                          │
│               └──▶ 使用语义分块或Agentic分块                        │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

2.2 嵌入模型与向量表示

嵌入模型演进

Text Only
嵌入模型发展
═══════════════════════════════════════════════════════════════════

第一代:统计方法 (2013-2018)
├── Word2Vec, GloVe, FastText
├── 静态词嵌入,无法处理一词多义
└── 已淘汰,不推荐用于RAG

第二代:预训练语言模型 (2019-2022)
├── BERT, RoBERTa, DistilBERT
├── 上下文相关嵌入,语义理解强
├── 需要微调适应特定领域
└── 适合需要精细语义匹配的场景

第三代:句子嵌入专用模型 (2022-2023)
├── SBERT, GTE, E5, BGE
├── 针对句子相似度优化
├── 开箱即用,效果优异
└── 当前RAG主流选择

第四代:多任务嵌入模型 (2023-2024)
├── OpenAI text-embedding-3, Cohere Embed
├── Jina Embeddings, voyage-ai
├── 支持长文本、多语言、多任务
└── 商业API首选

═══════════════════════════════════════════════════════════════════

主流嵌入模型对比

⚠️ 时效性与来源说明(2026-02 复核):嵌入模型维度、上下文长度与榜单排名更新频繁,请以官方文档和 MTEB 榜单为准。 - OpenAI Embeddings 文档:https://developers.openai.com/api/docs/guides/embeddings/ - MTEB Leaderboard:https://github.com/embeddings-benchmark/mteb

模型 维度 上下文长度 特点 适用场景
text-embedding-3-large 3072 8192 OpenAI 高性能嵌入模型 通用场景,预算充足
text-embedding-3-small 1536 8192 性价比高 大规模应用
BGE-large-zh 1024 512 中文优化 中文RAG
GTE-large 1024 512 开源高性能方案 自托管部署
E5-mistral-7b-instruct 4096 32768 指令微调 复杂查询理解
Jina-embeddings-v2 768 8192 长文本支持 长文档RAG
voyage-3 1024 32000 高质量检索 企业级应用

嵌入模型使用示例

Python
from sentence_transformers import SentenceTransformer
import numpy as np

# 加载开源嵌入模型
model = SentenceTransformer('BAAI/bge-large-zh-v1.5')

# 对文档进行编码
documents = [
    "RAG是一种结合检索和生成的技术",
    "Transformer架构是现代NLP的基础",
    "向量数据库用于存储高维嵌入"
]

doc_embeddings = model.encode(documents, normalize_embeddings=True)

# 查询编码
query = "什么是RAG技术?"
query_embedding = model.encode([query], normalize_embeddings=True)

# 计算相似度
similarities = np.dot(doc_embeddings, query_embedding.T).squeeze()
ranked_indices = np.argsort(similarities)[::-1]

print("检索结果:")
for idx in ranked_indices[:3]:
    print(f"  [{similarities[idx]:.4f}] {documents[idx]}")

2.3 向量数据库

主流向量数据库对比

Text Only
┌─────────────────────────────────────────────────────────────────┐
│                     向量数据库对比                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Chroma                                                          │
│  ├── 类型:嵌入式/本地                                           │
│  ├── 特点:轻量、易用、Python原生                                 │
│  ├── 适用:原型开发、小规模应用                                   │
│  └── 代码:pip install chromadb                                  │
│                                                                  │
│  FAISS (Facebook)                                                │
│  ├── 类型:库/嵌入式                                             │
│  ├── 特点:高性能、多种索引算法、GPU支持                          │
│  ├── 适用:研究、需要极致性能                                     │
│  └── 代码:pip install faiss-cpu                                 │
│                                                                  │
│  Pinecone                                                        │
│  ├── 类型:托管云服务                                            │
│  ├── 特点:全托管、自动扩缩容、元数据过滤                         │
│  ├── 适用:生产环境、无需运维                                     │
│  └── 成本:按使用量付费                                          │
│                                                                  │
│  Milvus / Zilliz                                                 │
│  ├── 类型:开源/托管                                             │
│  ├── 特点:云原生、分布式、十亿级规模                             │
│  ├── 适用:企业级大规模部署                                       │
│  └── 代码:支持多种SDK                                           │
│                                                                  │
│  Weaviate                                                        │
│  ├── 类型:开源/托管                                             │
│  ├── 特点:GraphQL接口、模块化、多模态                            │
│  ├── 适用:需要复杂查询、多模态RAG                                │
│  └── 特点:内置向量化模块                                        │
│                                                                  │
│  pgvector (PostgreSQL)                                           │
│  ├── 类型:PostgreSQL扩展                                        │
│  ├── 特点:关系数据+向量统一存储、ACID                            │
│  ├── 适用:已有PostgreSQL基础设施                                 │
│  └── 代码:CREATE EXTENSION vector                               │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

FAISS索引类型详解

Python
import faiss
import numpy as np

# 准备数据
dimension = 768  # 嵌入维度
nb = 100000      # 数据库大小
nq = 1000        # 查询数量

# 生成随机数据(实际使用真实嵌入)
xb = np.random.random((nb, dimension)).astype('float32')
xq = np.random.random((nq, dimension)).astype('float32')

# 1. Flat Index - 精确搜索,最慢但最准
index_flat = faiss.IndexFlatL2(dimension)
index_flat.add(xb)
D, I = index_flat.search(xq, k=10)  # 返回距离和索引

# 2. IVF Index - 倒排文件,加速搜索
nlist = 100  # 聚类中心数
quantizer = faiss.IndexFlatL2(dimension)
index_ivf = faiss.IndexIVFFlat(quantizer, dimension, nlist)
index_ivf.train(xb)  # 需要训练
index_ivf.add(xb)
index_ivf.nprobe = 10  # 搜索时访问的聚类数

# 3. HNSW Index - 图索引,高召回率
index_hnsw = faiss.IndexHNSWFlat(dimension, 32)  # 32为M参数
index_hnsw.hnsw.efConstruction = 200
index_hnsw.add(xb)
index_hnsw.hnsw.efSearch = 128  # 搜索时扩展因子

# 4. PQ Index - 乘积量化,内存高效
m = 16  # 子空间数
code_size = 8  # 每个子空间编码位数
index_pq = faiss.IndexPQ(dimension, m, code_size)
index_pq.train(xb)
index_pq.add(xb)

# 5. IVF-PQ - 组合索引,平衡速度与精度
index_ivfpq = faiss.IndexIVFPQ(quantizer, dimension, nlist, m, code_size)
index_ivfpq.train(xb)
index_ivfpq.add(xb)

# 性能对比
print("索引类型对比:")
print("  Flat:    精确搜索,内存占用高,速度慢")
print("  IVF:     近似搜索,速度较快,需要调nprobe")
print("  HNSW:    图索引,高召回率,构建较慢")
print("  PQ:      高压缩,内存小,精度损失较大")
print("  IVF-PQ:  综合方案,大规模数据首选")

2.4 检索优化技术

查询重写(Query Rewriting)

Python
class QueryRewriter:
    """
    查询重写技术提升检索质量
    """

    def __init__(self, llm):
        self.llm = llm

    def hyde_rewrite(self, query):
        """
        HyDE (Hypothetical Document Embeddings)
        生成假设文档,用假设文档的嵌入进行检索
        """
        prompt = f"""
        请根据以下问题,写一段可能包含答案的文档段落。
        这段文档应该看起来像是知识库中的真实内容。

        问题:{query}

        文档段落:
        """

        hypothetical_doc = self.llm.generate(prompt)
        return hypothetical_doc

    def query_expansion(self, query, num_expansions=3):
        """
        查询扩展:生成多个相关查询,合并检索结果
        """
        prompt = f"""
        针对以下问题,生成{num_expansions}个不同表述但语义相同的查询。
        每个查询关注问题的不同方面。

        原问题:{query}

        请用JSON格式返回:{{"queries": ["查询1", "查询2", ...]}}
        """

        response = self.llm.generate(prompt)
        expanded_queries = json.loads(response)["queries"]  # json.loads将JSON字符串→Python对象
        expanded_queries.append(query)  # 包含原查询

        return expanded_queries

    def step_back_prompting(self, query):
        """
        Step-back Prompting: 从具体问题抽象到一般原理
        """
        prompt = f"""
        将以下具体问题抽象为更一般的原理或概念。
        用更通用的术语重新表述。

        具体问题:{query}

        一般原理表述:
        """

        step_back_query = self.llm.generate(prompt)
        return [step_back_query, query]  # 同时检索抽象和具体层面

    def decompose_complex_query(self, query):
        """
        复杂查询分解:将多跳问题分解为子问题
        """
        prompt = f"""
        将以下复杂问题分解为2-3个简单的子问题。
        这些子问题的答案组合起来可以回答原问题。

        复杂问题:{query}

        请用JSON格式返回:{{"sub_questions": ["子问题1", "子问题2", ...]}}
        """

        response = self.llm.generate(prompt)
        sub_questions = json.loads(response)["sub_questions"]
        return sub_questions

重排序(Re-ranking)

Python
class Reranker:
    """
    重排序技术提升最终检索质量
    """

    def __init__(self):
        # 加载重排序模型
        from sentence_transformers import CrossEncoder
        self.cross_encoder = CrossEncoder('BAAI/bge-reranker-large')

    def rerank(self, query, documents, top_k=5):
        """
        使用交叉编码器进行精确重排序
        """
        # 构建查询-文档对
        pairs = [[query, doc] for doc in documents]

        # 计算相关性分数
        scores = self.cross_encoder.predict(pairs)

        # 按分数排序
        scored_docs = list(zip(documents, scores))
        scored_docs.sort(key=lambda x: x[1], reverse=True)  # lambda匿名函数

        return scored_docs[:top_k]

    def diversity_rerank(self, query, documents, top_k=5, lambda_param=0.5):
        """
        MMR (Maximal Marginal Relevance) 多样性重排序
        平衡相关性和多样性
        """
        # 计算文档嵌入
        doc_embeddings = self.embedding_model.encode(documents)
        query_embedding = self.embedding_model.encode([query])

        # 计算与查询的相似度
        query_sims = cosine_similarity(query_embedding, doc_embeddings)[0]

        selected = []
        remaining = list(range(len(documents)))

        while len(selected) < top_k and remaining:
            if not selected:
                # 第一个选择最相关的
                best_idx = remaining[np.argmax(query_sims[remaining])]
            else:
                # MMR评分
                mmr_scores = []
                for idx in remaining:
                    # 相关性
                    relevance = query_sims[idx]
                    # 与已选文档的最大相似度(冗余度)
                    redundancy = max([
                        cosine_similarity(
                            doc_embeddings[idx:idx+1],
                            doc_embeddings[s:s+1]
                        )[0][0]
                        for s in selected
                    ]) if selected else 0

                    mmr_score = lambda_param * relevance - (1 - lambda_param) * redundancy
                    mmr_scores.append(mmr_score)

                best_idx = remaining[np.argmax(mmr_scores)]

            selected.append(best_idx)
            remaining.remove(best_idx)

        return [documents[i] for i in selected]

生成组件优化

3.1 上下文压缩

Python
class ContextCompressor:
    """
    上下文压缩技术解决长文档问题
    """

    def __init__(self, llm):
        self.llm = llm

    def map_reduce_compress(self, documents, query, max_tokens=2000):
        """
        Map-Reduce压缩:先分别摘要,再合并
        """
        # Map阶段:分别摘要每个文档
        partial_summaries = []
        for doc in documents:
            prompt = f"""
            针对问题"{query}",提取以下文档中的相关信息。
            只保留与问题相关的内容,去除冗余信息。

            文档:{doc[:1000]}

            相关信息:
            """
            summary = self.llm.generate(prompt, max_tokens=200)
            partial_summaries.append(summary)

        # Reduce阶段:合并摘要
        combined = "\n".join(partial_summaries)
        if len(combined) > max_tokens:
            prompt = f"""
            将以下信息整合为简洁的参考材料,用于回答问题"{query}"。
            去除重复信息,保持关键事实。

            信息:{combined}

            整合后的参考材料:
            """
            final = self.llm.generate(prompt, max_tokens=max_tokens)
            return final

        return combined

    def selective_context(self, documents, query, embedding_model, top_k_sentences=10):
        """
        选择性上下文:只保留最相关的句子
        """
        # 分割为句子
        all_sentences = []
        for doc in documents:
            sentences = sent_tokenize(doc)
            all_sentences.extend(sentences)

        # 计算与查询的相似度
        query_emb = embedding_model.encode([query])
        sentence_embs = embedding_model.encode(all_sentences)
        similarities = cosine_similarity(query_emb, sentence_embs)[0]

        # 选择最相关的句子
        top_indices = np.argsort(similarities)[-top_k_sentences:]
        selected_sentences = [all_sentences[i] for i in sorted(top_indices)]

        return " ".join(selected_sentences)

    def refine_compression(self, documents, query):
        """
        Refine方法:迭代构建上下文
        """
        context = ""
        for doc in documents:
            prompt = f"""
            基于已有上下文和当前文档,更新回答问题的相关信息。

            问题:{query}
            已有上下文:{context}
            当前文档:{doc[:1000]}

            更新后的相关信息(整合已有和新增):
            """
            context = self.llm.generate(prompt, max_tokens=500)

        return context

3.2 引用与归因

Python
class CitationGenerator:
    """
    生成带引用的回答,提高可验证性
    """

    def generate_with_citations(self, query, documents, llm):
        """
        生成带有明确来源引用的回答
        """
        # 为每个文档添加编号
        context = ""
        for i, doc in enumerate(documents, 1):  # enumerate同时获取索引和元素
            context += f"[文档{i}] {doc}\n\n"

        prompt = f"""
        基于以下文档回答问题。重要规则:
        1. 每个事实后必须标注来源,格式为[^文档编号^]
        2. 如果信息不在文档中,明确说明"根据提供文档无法确定"
        3. 优先使用高相关度文档的信息

        文档:
        {context}

        问题:{query}

        回答(带引用):
        """

        response = llm.generate(prompt)
        return response

    def verify_citations(self, response, documents):
        """
        验证引用是否正确
        """
        import re

        # 提取引用
        citations = re.findall(r'\[\^(\d+)\^\]', response)  # re.findall正则查找所有匹配项

        verification_results = []
        for citation in set(citations):
            doc_idx = int(citation) - 1
            if doc_idx < 0 or doc_idx >= len(documents):
                verification_results.append({
                    'citation': citation,
                    'valid': False,
                    'reason': '文档编号超出范围'
                })
            else:
                # 提取引用附近的文本
                context = self._extract_citation_context(response, citation)
                # 检查是否确实在文档中
                doc = documents[doc_idx]
                if self._check_containment(context, doc):
                    verification_results.append({
                        'citation': citation,
                        'valid': True
                    })
                else:
                    verification_results.append({
                        'citation': citation,
                        'valid': False,
                        'reason': '引用内容未在文档中找到'
                    })

        return verification_results

长文本建模技术

4.1 长文本挑战

Text Only
长文本处理挑战
═══════════════════════════════════════════════════════════════════

挑战1:注意力复杂度
├── 问题:Self-Attention是O(n²)复杂度
├── 100K tokens → 100B计算量
└── 解决方案:稀疏注意力、线性注意力

挑战2:位置编码外推
├── 问题:训练时短文本,推理时长文本
├── 相对位置编码无法处理超出训练长度的文本
└── 解决方案:ALiBi、RoPE外推、NTK-aware

挑战3:上下文遗忘
├── 问题:中间信息难以被有效利用
├── "Lost in the Middle"现象
└── 解决方案:特殊注意力模式、信息压缩

挑战4:训练成本
├── 问题:长序列训练内存和时间成本极高
└── 解决方案:序列并行、梯度检查点

═══════════════════════════════════════════════════════════════════

4.2 高效注意力机制

Python
# 稀疏注意力模式实现

class SparseAttention:
    """
    稀疏注意力机制降低长序列计算复杂度
    """

    @staticmethod
    def sliding_window_attention(Q, K, V, window_size=512):
        """
        滑动窗口注意力:每个token只关注附近的token
        复杂度:O(n × window_size)
        """
        batch_size, num_heads, seq_len, head_dim = Q.shape

        # 创建滑动窗口掩码
        mask = torch.zeros(seq_len, seq_len, dtype=torch.bool)
        for i in range(seq_len):
            start = max(0, i - window_size // 2)
            end = min(seq_len, i + window_size // 2 + 1)
            mask[i, start:end] = True

        # 应用掩码的注意力
        scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(head_dim)
        scores = scores.masked_fill(~mask, float('-inf'))
        attn = torch.softmax(scores, dim=-1)
        output = torch.matmul(attn, V)

        return output

    @staticmethod
    def dilated_attention(Q, K, V, dilation_rate=4):
        """
        空洞注意力:间隔采样,关注稀疏位置
        """
        batch_size, num_heads, seq_len, head_dim = Q.shape

        # 对K, V进行间隔采样
        K_dilated = K[:, :, ::dilation_rate, :]
        V_dilated = V[:, :, ::dilation_rate, :]

        # 计算与采样后key的注意力
        scores = torch.matmul(Q, K_dilated.transpose(-2, -1)) / math.sqrt(head_dim)
        attn = torch.softmax(scores, dim=-1)
        output = torch.matmul(attn, V_dilated)

        return output

    @staticmethod
    def global_local_attention(Q, K, V, num_global_tokens=16, local_window=512):
        """
        全局-局部混合注意力:部分token全局可见,其余局部可见
        """
        batch_size, num_heads, seq_len, head_dim = Q.shape

        # 选择全局token(如开头和结尾的特殊token)
        global_indices = list(range(num_global_tokens // 2)) + \
                        list(range(seq_len - num_global_tokens // 2, seq_len))

        # 全局token关注所有token
        # 局部token关注自己和全局token

        outputs = []
        for i in range(seq_len):
            if i in global_indices:
                # 全局注意力
                scores = torch.matmul(Q[:, :, i:i+1], K.transpose(-2, -1)) / math.sqrt(head_dim)
            else:
                # 局部注意力 + 全局token
                local_start = max(0, i - local_window // 2)
                local_end = min(seq_len, i + local_window // 2 + 1)
                attend_indices = global_indices + list(range(local_start, local_end))
                attend_indices = list(set(attend_indices))  # 去重

                K_local = K[:, :, attend_indices, :]
                scores = torch.matmul(Q[:, :, i:i+1], K_local.transpose(-2, -1)) / math.sqrt(head_dim)

            attn = torch.softmax(scores, dim=-1)
            if i in global_indices:
                out = torch.matmul(attn, V)
            else:
                V_local = V[:, :, attend_indices, :]
                out = torch.matmul(attn, V_local)

            outputs.append(out)

        return torch.cat(outputs, dim=2)

4.3 位置编码外推

Python
class LongContextPositionEncoding:
    """
    支持长上下文的位置编码技术
    """

    @staticmethod
    def apply_ntk_scaling(base=10000, seq_len=32768, training_len=2048, dim=128):
        """
        NTK-aware RoPE缩放
        通过调整base实现长度外推,不降低短文本性能
        """
        # NTK缩放公式
        scaling_factor = seq_len / training_len

        # 调整base
        adjusted_base = base * (scaling_factor ** (dim / (dim - 2)))

        return adjusted_base

    @staticmethod
    def yarn_scaling(seq_len=128000, training_len=4096, beta_fast=32, beta_slow=1):
        """
        YaRN (Yet another RoPE extensioN)
        结合NTK和非均匀插值
        """
        # 计算频率因子
        scale = seq_len / training_len

        # 高频和低频的不同处理
        # 高频(短波长):直接插值
        # 低频(长波长):NTK-aware缩放

        def yarn_factor(dim_idx, dim=128):
            freq = 1.0 / (10000 ** (dim_idx / dim))

            # 波长
            wavelength = 2 * math.pi / freq

            # 根据波长决定缩放策略
            if wavelength < beta_fast:
                return 1 / scale  # 直接插值
            elif wavelength > beta_slow * training_len:
                return 1.0  # 不缩放
            else:
                # 平滑过渡
                t = (wavelength - beta_fast) / (beta_slow * training_len - beta_fast)
                return (1 - t) / scale + t

        factors = [yarn_factor(i) for i in range(dim)]
        return factors

    @staticmethod
    def alibi_biases(seq_len, num_heads):
        """
        ALiBi (Attention with Linear Biases)
        通过距离惩罚实现外推,无需位置编码
        """
        # 创建距离矩阵
        distances = torch.arange(seq_len).unsqueeze(0) - torch.arange(seq_len).unsqueeze(1)  # unsqueeze增加一个维度
        distances = distances.abs()

        # 为每个头设置不同的惩罚斜率
        slopes = torch.tensor([
            2 ** (-8 * (head_idx + 1) / num_heads)
            for head_idx in range(num_heads)
        ])

        # 计算偏置
        biases = slopes.unsqueeze(1).unsqueeze(2) * distances.unsqueeze(0)

        return -biases  # 负值作为注意力偏置

4.4 长文本模型架构

Text Only
长文本模型对比
═══════════════════════════════════════════════════════════════════

Longformer (2020)
├── 注意力模式:滑动窗口 + 全局注意力
├── 复杂度:O(n × window_size)
├── 最大长度:4096
└── 特点:早期稀疏注意力工作

BigBird (2021)
├── 注意力模式:随机 + 窗口 + 全局
├── 复杂度:O(n)
├── 理论保证:Universal Approximator
└── 特点:理论上可近似全注意力

LongChat / LongAlpaca (2023)
├── 基础模型:LLaMA
├── 扩展方法:位置插值
├── 最大长度:32K
└── 特点:指令微调的长文本模型

Mistral-7B (2023)
├── 注意力:滑动窗口注意力
├── 窗口大小:4K,但可处理更长
├── 特点:高效实现,商业友好
└── 应用:RAG、文档理解

Claude 3 / GPT-4 Turbo (2024)
├── 上下文:200K / 128K
├── 技术:未公开,推测为分层注意力
├── 特点:生产级长文本能力
└── 应用:代码库理解、长文档分析

Gemini 1.5 Pro (2024)
├── 上下文:1M-10M tokens
├── 技术:Mixture-of-Experts + 高效注意力
├── 特点:当前最长上下文
└── 应用:视频理解、大规模代码库

═══════════════════════════════════════════════════════════════════

RAG系统实践

5.1 完整RAG系统实现

Python
# 完整RAG系统实现

import os
from typing import List, Dict, Any
import numpy as np
from sentence_transformers import SentenceTransformer, CrossEncoder
import faiss

class AdvancedRAGSystem:
    """
    高级RAG系统实现,包含所有优化组件
    """

    def __init__(
        self,
        embedding_model_name: str = 'BAAI/bge-large-zh-v1.5',
        reranker_model_name: str = 'BAAI/bge-reranker-large',
        llm_client = None,  # OpenAI或其他LLM客户端
        chunk_size: int = 512,
        chunk_overlap: int = 50,
        top_k_retrieve: int = 20,
        top_k_rerank: int = 5
    ):
        # 初始化模型
        self.embedding_model = SentenceTransformer(embedding_model_name)
        self.reranker = CrossEncoder(reranker_model_name)
        self.llm = llm_client

        # 配置
        self.chunk_size = chunk_size
        self.chunk_overlap = chunk_overlap
        self.top_k_retrieve = top_k_retrieve
        self.top_k_rerank = top_k_rerank

        # 存储
        self.documents = []
        self.chunks = []
        self.index = None
        self.dimension = self.embedding_model.get_sentence_embedding_dimension()

    def add_documents(self, documents: List[str], metadata: List[Dict] = None):
        """
        添加文档到知识库
        """
        if metadata is None:
            metadata = [{} for _ in documents]

        self.documents.extend(documents)

        # 分块
        for doc_idx, (doc, meta) in enumerate(zip(documents, metadata)):  # zip配对文档与元数据,enumerate添加索引,(doc, meta)解包配对结果
            doc_chunks = self._chunk_document(doc)
            for chunk_idx, chunk in enumerate(doc_chunks):
                self.chunks.append({
                    'content': chunk,
                    'doc_idx': doc_idx,
                    'chunk_idx': chunk_idx,
                    'metadata': meta
                })

        # 构建索引
        self._build_index()

    def _chunk_document(self, document: str) -> List[str]:
        """
        递归字符分块
        """
        chunks = []
        separators = ["\n\n", "\n", "。", ". ", " ", ""]

        def recursive_split(text, sep_idx=0):
            if sep_idx >= len(separators):
                return [text]

            separator = separators[sep_idx]
            parts = text.split(separator)

            result = []
            current_chunk = ""

            for part in parts:
                if separator:
                    part = part + separator

                if len(current_chunk) + len(part) <= self.chunk_size:
                    current_chunk += part
                else:
                    if current_chunk:
                        result.append(current_chunk.strip())

                    # 如果单个部分太长,递归分割
                    if len(part) > self.chunk_size:
                        result.extend(recursive_split(part, sep_idx + 1))
                    else:
                        current_chunk = part

            if current_chunk:
                result.append(current_chunk.strip())

            return result

        return recursive_split(document)

    def _build_index(self):
        """
        构建FAISS索引
        """
        if not self.chunks:
            return

        # 编码所有块
        texts = [chunk['content'] for chunk in self.chunks]
        embeddings = self.embedding_model.encode(
            texts,
            normalize_embeddings=True,
            show_progress_bar=True
        )

        # 创建HNSW索引
        self.index = faiss.IndexHNSWFlat(self.dimension, 32)
        self.index.hnsw.efConstruction = 200
        self.index.add(np.array(embeddings).astype('float32'))
        self.index.hnsw.efSearch = 128

    def query(
        self,
        query: str,
        use_rerank: bool = True,
        use_citation: bool = True
    ) -> Dict[str, Any]:
        """
        执行RAG查询
        """
        # 1. 查询重写(可选)
        expanded_queries = self._expand_query(query)

        # 2. 检索
        retrieved_chunks = self._retrieve(expanded_queries)

        # 3. 重排序
        if use_rerank:
            final_chunks = self._rerank(query, retrieved_chunks)
        else:
            final_chunks = retrieved_chunks[:self.top_k_rerank]

        # 4. 生成回答
        answer = self._generate(query, final_chunks, use_citation)

        return {
            'query': query,
            'answer': answer,
            'sources': final_chunks,
            'num_sources': len(final_chunks)
        }

    def _expand_query(self, query: str) -> List[str]:
        """
        简单的查询扩展
        """
        # 可以集成LLM进行更复杂的扩展
        return [query]

    def _retrieve(self, queries: List[str]) -> List[Dict]:
        """
        多查询检索并去重
        """
        all_indices = set()

        for q in queries:
            query_embedding = self.embedding_model.encode(
                [q],
                normalize_embeddings=True
            )
            distances, indices = self.index.search(
                np.array(query_embedding).astype('float32'),
                self.top_k_retrieve
            )
            all_indices.update(indices[0])

        retrieved = [self.chunks[i] for i in all_indices]
        return retrieved

    def _rerank(self, query: str, chunks: List[Dict]) -> List[Dict]:
        """
        交叉编码器重排序
        """
        pairs = [[query, chunk['content']] for chunk in chunks]
        scores = self.reranker.predict(pairs)

        # 添加分数并排序
        for chunk, score in zip(chunks, scores):  # zip按位置配对多个可迭代对象
            chunk['rerank_score'] = float(score)

        sorted_chunks = sorted(
            chunks,
            key=lambda x: x['rerank_score'],
            reverse=True
        )

        return sorted_chunks[:self.top_k_rerank]

    def _generate(
        self,
        query: str,
        chunks: List[Dict],
        use_citation: bool
    ) -> str:
        """
        使用LLM生成回答
        """
        # 构建上下文
        if use_citation:
            context = ""
            for i, chunk in enumerate(chunks, 1):
                context += f"[^{i}^] {chunk['content']}\n\n"

            prompt = f"""基于以下参考文档回答问题。请遵循以下规则:
1. 每个事实后标注来源,格式为[^编号^]
2. 如果信息不足,明确说明
3. 保持回答简洁准确

参考文档:
{context}

问题:{query}

回答:"""
        else:
            context = "\n".join([c['content'] for c in chunks])
            prompt = f"""基于以下信息回答问题:

{context}

问题:{query}

回答:"""

        # 调用LLM
        response = self.llm.generate(prompt)
        return response

# 使用示例
if __name__ == "__main__":
    # 初始化RAG系统
    rag = AdvancedRAGSystem(
        llm_client=YourLLMClient(),  # 替换为你的LLM客户端
        chunk_size=512,
        top_k_retrieve=20,
        top_k_rerank=5
    )

    # 添加文档
    documents = [
        "RAG(检索增强生成)是一种结合信息检索和文本生成的技术...",
        "Transformer架构由Vaswani等人在2017年提出...",
        # 更多文档...
    ]
    rag.add_documents(documents)

    # 查询
    result = rag.query("什么是RAG技术?")
    print(f"回答:{result['answer']}")
    print(f"引用来源:{result['num_sources']}个")

5.2 评估RAG系统

Python
class RAGEvaluator:
    """
    RAG系统评估框架
    """

    def __init__(self):
        self.metrics = {}

    def evaluate_retrieval(
        self,
        queries: List[str],
        ground_truth_docs: List[List[int]],
        retrieved_docs: List[List[int]]
    ) -> Dict[str, float]:
        """
        评估检索质量
        """
        metrics = {
            'hit_rate@1': 0,
            'hit_rate@5': 0,
            'mrr': 0,  # Mean Reciprocal Rank
            'recall@5': 0
        }

        for query, gt, retrieved in zip(queries, ground_truth_docs, retrieved_docs):
            # Hit Rate@1
            if any(r in gt for r in retrieved[:1]):  # any()任一为True则返回True
                metrics['hit_rate@1'] += 1

            # Hit Rate@5
            if any(r in gt for r in retrieved[:5]):
                metrics['hit_rate@5'] += 1

            # MRR
            for rank, doc in enumerate(retrieved, 1):
                if doc in gt:
                    metrics['mrr'] += 1.0 / rank
                    break

            # Recall@5
            retrieved_set = set(retrieved[:5])
            gt_set = set(gt)
            metrics['recall@5'] += len(retrieved_set & gt_set) / len(gt_set)

        # 平均化
        n = len(queries)
        return {k: v / n for k, v in metrics.items()}

    def evaluate_generation(
        self,
        predictions: List[str],
        references: List[str]
    ) -> Dict[str, float]:
        """
        评估生成质量
        """
        from rouge import Rouge
        from bert_score import score

        # ROUGE分数
        rouge = Rouge()
        rouge_scores = rouge.get_scores(predictions, references, avg=True)

        # BERTScore
        P, R, F1 = score(predictions, references, lang='zh')

        return {
            'rouge-1': rouge_scores['rouge-1']['f'],
            'rouge-2': rouge_scores['rouge-2']['f'],
            'rouge-l': rouge_scores['rouge-l']['f'],
            'bertscore_f1': F1.mean().item()
        }

    def evaluate_faithfulness(
        self,
        answers: List[str],
        contexts: List[str]
    ) -> float:
        """
        评估回答对上下文的忠实度(幻觉检测)
        """
        # 使用NLI模型或LLM判断
        faithful_count = 0

        for answer, context in zip(answers, contexts):
            # 提取回答中的陈述
            claims = self._extract_claims(answer)

            # 检查每个陈述是否被上下文支持
            supported = 0
            for claim in claims:
                if self._verify_claim(claim, context):
                    supported += 1

            if supported == len(claims):
                faithful_count += 1

        return faithful_count / len(answers)

    def _extract_claims(self, text: str) -> List[str]:
        """从文本中提取事实陈述"""
        # 使用LLM或规则提取
        # 简化实现:按句子分割
        import re
        sentences = re.split(r'[。!?]', text)
        return [s.strip() for s in sentences if len(s.strip()) > 10]

    def _verify_claim(self, claim: str, context: str) -> bool:
        """验证陈述是否被上下文支持"""
        # 使用NLI模型或相似度判断
        # 简化实现:检查关键词重叠
        claim_words = set(claim.lower().split())
        context_words = set(context.lower().split())
        overlap = len(claim_words & context_words) / len(claim_words)
        return overlap > 0.5

前沿进展与挑战

6.1 最新研究方向

Text Only
RAG前沿研究方向 (2024)
═══════════════════════════════════════════════════════════════════

1. Agentic RAG
├── 智能体驱动的动态检索决策
├── 自主规划多步检索策略
├── 工具使用增强检索能力
└── 代表:RAG Agent, Self-RAG

2. 多模态RAG
├── 图像、视频、音频检索
├── 跨模态对齐与理解
├── 统一的多模态嵌入空间
└── 代表:ColPali, MM-RAG

3. 图RAG (Graph RAG)
├── 知识图谱增强检索
├── 实体关系推理
├── 结构化知识与非结构化文本结合
└── 代表:Microsoft GraphRAG

4. 自适应RAG
├── 根据问题难度选择检索策略
├── 动态决定是否需要检索
├── 检索与生成的迭代优化
└── 代表:FLARE, Self-RAG

5. 联邦RAG
├── 隐私保护下的分布式检索
├── 多源知识整合
├── 安全多方计算
└── 新兴研究方向

═══════════════════════════════════════════════════════════════════

6.2 关键挑战

挑战 描述 潜在解决方案
检索噪声 不相关文档干扰生成 重排序、查询重写、置信度过滤
上下文长度 长文档超出模型限制 压缩、摘要、分层检索
多跳推理 需要多个文档组合回答 迭代检索、图遍历、推理链
领域适配 专业领域检索效果差 领域微调、专用嵌入模型
延迟优化 检索+生成延迟较高 缓存、预检索、流式生成
评估困难 缺乏统一评估标准 综合指标、人工评估、A/B测试

6.3 推荐学习资源

论文: - Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks - RAG开山之作 - Self-RAG: Learning to Retrieve, Generate, and Critique through Self-Reflection - Corrective Retrieval Augmented Generation

开源项目: - LangChain - RAG应用框架 - LlamaIndex - 数据框架 for LLM - RAGFlow - 开源RAG引擎

实践建议: 1. 从简单RAG开始,逐步添加优化组件 2. 重视数据质量,分块策略至关重要 3. 建立评估体系,持续迭代优化 4. 关注延迟与成本的平衡


下一步

完成本章节学习后,建议继续阅读: - 04-模型架构创新 - 探索MoE、Mamba等新型架构 - 05-大模型安全与对齐 - 理解RLHF、安全训练

实践项目: - 实战项目 - 动手构建完整RAG应用


最后更新日期:2026-02-12 适用版本:LLM学习教程 v2026