人工智能实践(语言智能)
第4讲:RAG

4.2 索引与分块

切分策略决定检索上限——从固定长度到语义切分,再到父子文档、多索引与嵌入选型的工程决策

分块:RAG 最被低估的环节

你可以把 RAG 想象成一条流水线:嵌入模型决定"距离",但切分策略决定了被嵌入的是什么。一个错误的切分——把表格的标题与数据分离、把条例的条件与结论切到两块——会让再强的检索器也无能为力。

90% 的 RAG "检索不准"问题其实是切分问题。在升级嵌入模型或加 reranker 之前,先检查你的 chunk 是否保留了完整的语义单元。


四种主流切分策略

1. 固定大小切分(Fixed-size)

最朴素的方案:每 N 个 token 切一块,相邻块之间保留 M 个 token 的重叠以避免边界问题。

  • 推荐参数:中文 300-500 字符 / 英文 400-512 token;重叠 10-20%
  • 优势:简单、可预测、易于批量处理
  • 劣势:会切断句子、段落和语义单元

2. 递归字符切分(Recursive Character)

层级分隔符逐级拆分——先按 \n\n,块太大则按 \n,再按 ,最后按字符。LangChain 的默认策略。

from langchain_text_splitters import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=400,
    chunk_overlap=60,
    separators=["\n\n", "\n", "。", ";", ",", " ", ""],
    length_function=len,
)
chunks = splitter.split_text(document)

3. 语义切分(Semantic Chunking)

计算相邻句子的嵌入相似度,在相似度骤降处切分。直觉是:主题切换点会在向量空间留下"缺口"。

break(i)=1[cos(ei,ei+1)<μασ]\text{break}(i) = \mathbb{1}\left[\cos(e_i, e_{i+1}) < \mu - \alpha \sigma\right]

其中 eie_i 是第 ii 个句子的嵌入,μ,σ\mu, \sigma 是滑窗内相似度的均值与标准差,α\alpha 通常取 1.0-1.5。论文报告可带来约 9% 的召回提升,但计算成本也显著增加。

4. 父子文档索引(Parent-Child)

用小块检索、返回大块给 LLM——兼顾检索精度与上下文完整性。

对比基线可达 65% 胜率,是生产环境中强烈推荐的策略。


策略对比表

策略切分单元适用场景典型代价
Fixed-size固定 token短文本、纯文字段落语义断裂
Recursive自然分隔符结构化文档(默认)仍可能切断长句
Semantic语义边界长文章、百科需额外嵌入计算
Parent-Child小块索引+大块召回技术手册、规章索引体积加倍
Late Chunking先整文编码后切向量上下文依赖极强Jina 2024 新方法

研究生手册、产品规章这类"层级清晰、条目结构化"的文档,先按章节结构拆分再做 Recursive 切分比任何"通用最优切法"都更有效。不要迷信算法,先研究文档本身。


嵌入模型选型

Embedding 模型的核心权衡是体积 vs. 质量 vs. 支持语言。主流选择:

# 小而快,适合课堂演示(GPU 非必需)
model_name = "BAAI/bge-small-zh-v1.5"
# 参数:约 24M;维度 512;单 V100 可千 QPS

优势:下载快(<100MB)、CPU 可跑、免 API 配额。劣势:语义能力弱于大模型。

# 中文 MTEB 榜单常驻前三
model_name = "BAAI/bge-large-zh-v1.5"
# 参数:约 326M;维度 1024

优势:中文 MTEB 综合分最高;短文本(<512 token)效果极佳。劣势:长文档需先切分。

# 同时支持稠密/稀疏/多向量的统一模型
model_name = "BAAI/bge-m3"
# 参数:约 568M;支持 8192 token 上下文,100+ 语言

优势:单模型覆盖多语言、多粒度(稠密 + 稀疏 + ColBERT)。劣势:体积大、推理慢。

决策流程:先在 MTEB-CN 或 C-MTEB 上看中文综合分 → 检查是否支持目标语言 → 评估最大输入长度(应 ≥ chunk_size)→ 测试推理吞吐能否满足 SLA。


索引结构

平面向量索引

最基础的方案,所有向量存入单一索引,用 ANN 算法(HNSW、IVF-PQ)加速搜索。FAISS 是研究首选:

import faiss
import numpy as np

dim = 1024
index = faiss.IndexFlatIP(dim)          # 暴力搜索,精度上限
# 大规模场景用分层索引:
# index = faiss.IndexHNSWFlat(dim, 32)  # HNSW,百万级向量
index.add(embeddings.astype(np.float32))
scores, ids = index.search(query_emb, k=5)

多索引策略

当文档有多种属性或视角时,为每个视角建独立索引并路由:

索引视角嵌入对象查询场景
原文块段落原文细节查询
摘要每章/每条目的摘要概览类问题
关键词提取的实体和术语精确术语匹配
问答对预生成 QA 对的问题FAQ 式查询

在用户查询到来时,用一个 Router(可以是规则或 LLM)决定走哪个或哪几个索引。

分层索引(RAPTOR)

递归地对文本块聚类并生成摘要,形成树状索引,兼顾"全局概述"与"细节查询":

        [全书摘要]         ← Level 2(高层主题)
        /        \
[章摘要 A]   [章摘要 B]    ← Level 1(子主题)
  /   \         /    \
[块1][块2]  [块3][块4]    ← Level 0(原始块)

在查询时可并行检索多层,用跨层检索结果组合上下文,适合对长文档做概括类问答。


本节小结

维度推荐默认
分块策略Recursive + 按章节预切
chunk_size中文 300-500 字符 / 英文 400-512 token
chunk_overlap10-20%
嵌入模型BAAI/bge-m3(多语言)或 bge-large-zh(纯中文)
索引结构FAISS HNSW(百万级)或 Chroma(原型)
进阶父子文档 + 多索引路由