RAG 文本分块与向量索引流程
从预处理、Embedding 选型到 FAISS/Pinecone 索引优化
原题:在构建RAG系统时,如何对文本进行分块并生成向量化表示,进而利用向量数据库建立高效索引以支持精准检索?请详细说明从文本预处理、嵌入模型选择、向量存储到索引优化的关键技术流程及常用工具(如Sentence-BERT、FAISS、Pinecone等)。
文档处理 · 字节真题
回答与解析
一、文本分块(Chunking)
核心原则:语义完整性与检索粒度的平衡
| 策略 | 适用场景 | 工具 |
|---|---|---|
| 固定长度候选 | 实现简单,参数需评测 | LangChain RecursiveCharacterTextSplitter |
| 语义分块 | 文档结构清晰(论文、合同) | 按标题/段落边界切分 |
| 递归分块 | 长文档层次化结构 | 先按章节,再按段落 |
| 滑动窗口 | 需要上下文连贯 | 比较无重叠、短重叠和较长重叠 |
关键细节:chunk size 必须低于实际嵌入模型的输入限制,并在同一评测集比较;过小可能造成语义碎片化,过大可能稀释关键信息。
二、嵌入模型选择
选型维度:
- 通用场景:
sentence-transformers/all-MiniLM-L6-v2(轻量,384维) - 中文场景:
BAAI/bge-large-zh、智谱Embedding-2 - 领域适配:领域数据继续预训练或对比学习微调
优化技巧:
- 查询端与文档端使用不同prompt(如"为这个句子生成表示:")
- 维度压缩:PCA或模型蒸馏降低存储成本
三、向量存储与索引
| 方案 | 特点 | 适用规模 |
|---|---|---|
| FAISS | 内存索引,HNSW/IVF算法,零成本 | 百万级,单机 |
| Pinecone | 全托管,元数据过滤,自动扩缩容 | 千万级+,生产环境 |
| Milvus/Zilliz | 开源/云原生,GPU加速 | 十亿级 |
索引优化:
- HNSW:构建多层图结构,查询复杂度O(logN),召回率>95%
- IVF-PQ:倒排+乘积量化,内存压缩10-20倍,适合海量数据
- 动态更新:增量索引避免全量重建
四、检索增强
# 混合检索示例:向量相似度 + BM25关键词
def hybrid_search(query, alpha=0.7):
dense_score = vector_db.similarity_search(query)
sparse_score = bm25_search(query)
return weighted_fusion(dense_score, sparse_score, alpha)
# 重排序(Rerank)
cross_encoder = CrossEncoder('bge-reranker-base')
candidates = vector_db.search(query, top_k=100)
reranked = cross_encoder.rank(query, candidates)[:10]
关键指标:召回率@K、MRR、延迟P99。线上建议向量召回+精排两阶段架构。
学习建议
建议先掌握文本分块策略与嵌入模型原理,再动手实践使用Hugging Face和FAISS构建小型向量库,理解检索全流程。
口语版讲法(约4分钟)
- 本质是平衡语义完整性与检索粒度的工程问题
- 分块策略:固定切分 vs 语义切分,落地常混用
- 嵌入选型:通用 vs 中文 vs 领域,维度与prompt优化
- 索引:HNSW与IVF-PQ的取舍,动态更新与一致性
- 检索:混合检索+重排两步走,风险与兜底
这道题其实问的是,怎么把非结构化文本切碎、变成向量,再建索引,让RAG能又快又准地召回。本质上是一个语义完整性和检索粒度之间的平衡问题,而且落地时不是单纯选一个方案,往往是几种策略组合。
先说分块。最直接的是固定长度切分,实现简单,但照搬一个固定值容易把一句话从中间断开,导致向量表示"碎"了。所以我在做企业知识库的时候,比如客服退款政策文档,会用递归切分,先按章节,再按段落,最后按句子边界切。这样每个chunk语义相对完整。但切得过细,检索时容易漏上下文;切得过大,关键信息又可能被稀释。所以真正落地时,我会把固定长度、递归切分与不同 overlap 设为候选,在同一评测集比较召回、答案质量、冗余成本和延迟。这里有个前提:chunk size 必须低于实际使用的嵌入模型输入限制,超出时要截断、分层或改用支持更长输入的模型。
接下来是嵌入模型。通用场景我会选sentence-transformers的all-MiniLM-L6-v2,384维,轻量快速。中文场景换成BAAI/bge-large-zh,效果更好。但如果是垂直领域,比如金融合同,我会用领域数据做对比学习微调,或者直接用开源的领域版。这里有个优化点:查询和文档可以用不同的prompt前缀,比如查询加"为这个句子生成表示:",文档加"为这个段落生成表示:",能提升匹配精度。维度上,384维已经够用,如果存储成本敏感,可以用PCA压到128维,召回率损失不大。
向量存储和索引,我分两个场景说。单机百万级,用Faiss的HNSW索引,构建多层图,查询复杂度O(logN),召回率能到95%以上。生产环境千万级以上,我倾向用Pinecone或Milvus,全托管,自动扩缩容。索引优化上,HNSW适合高召回,但内存大;IVF-PQ可以压缩10-20倍,适合海量数据,但召回会掉一点。我的取舍是:如果延迟敏感,用HNSW;如果存储成本敏感,用IVF-PQ。动态更新是个坑:知识库频繁更新时,直接原地改图索引会导致召回质量崩。我会把更新分成新增、修改、删除三类。新增直接增量插入;修改当成旧版本软删除加新版本新增;删除不原地动索引,而是维护删除标记,后台异步重建。整体用base加delta双索引,查询时合并再按版本过滤。上线前会用固定query回放,对比Recall@K和延迟,通过才原子切换。
检索阶段,我通常用混合检索加重排两步走。先用Hybrid Search,把向量相似度和BM25关键词分数加权融合,比如alpha=0.7,兼顾语义和精确匹配。然后对top 100候选用Cross-Encoder重排,比如bge-reranker-base,精排到top 10。这样延迟不会太高,但召回和精度都稳。这里有个风险:如果知识库里有大量相似文档,比如不同版本的合同,重排后可能还是混淆,我会加元数据过滤,比如版本号、日期,先缩小候选集。
还有一个点是,当知识库更新频繁时,增量索引的实时性和一致性很难兼顾。我上面提到用双缓冲,但实际工程中还要考虑索引重建的时机和回滚机制。这块如果做得不好,线上会出现搜不到刚更新的内容,或者搜到旧版本,影响用户信任。
所以整体上,我更倾向于把RAG检索看成一个多阶段pipeline,从分块到嵌入到索引到检索,每个环节都有trade-off,而且必须用业务数据验证。如果只让我抓一个关键,那就是分块粒度决定了检索的上限,后面再优化也只是在补分块的坑。
关键一句:增量索引的实时性与一致性平衡:双缓冲+异步重建+回滚机制
面试官还可能这样问
- 问法 1 · 场景切入
假设你在做一个电商客服的RAG系统,用户问“这款手机拍照怎么样”,你要从产品手册里找答案。手册那么长,你打算怎么切段?切太小怕丢上下文,切太大又怕检索不准,你怎么平衡?
- 问法 2 · 层层追问
文档检索系统里,文本是怎么变成向量的?……那你存到向量数据库之后,怎么建索引才能查得快?如果数据量到百万级,你用什么索引结构?……那如果还要支持关键词匹配呢?
- 问法 3 · 直球架构
从原始文档到检索结果,你一步步把整个流程讲清楚:文本怎么分块、用什么嵌入模型、怎么存到向量数据库、索引怎么优化才能既快又准。中间遇到chunk size、维度、召回率这些trade-off,你怎么选?