1. RAG技术中的文档分块与向量化概述
在构建RAG(检索增强生成)系统时,文档分块和向量化是决定系统检索质量的两大核心技术。作为从业者,我经常遇到这样的场景:当用户查询"如何优化Python代码性能"时,系统需要从海量文档中精准找到相关内容,而这个过程的核心就是文档分块和向量化的质量。
文档分块决定了检索的粒度,就像图书馆的书籍分类系统。如果把整本书作为一个单元,查找特定知识点会非常困难;但如果把每个句子都分开,又失去了上下文关联。理想的分块应该像精心设计的章节划分,每个部分既能独立表达完整概念,又保持与整体的逻辑联系。
向量化则是将文本转换为计算机能理解的数学表示。想象给每个词汇和句子分配一个独特的"数字指纹",相似的语义会有相似的指纹。这种转换使得计算机能够进行语义级别的检索,而不仅仅是关键词匹配。在实际项目中,我见过太多因为向量化不当导致的检索失败案例,比如使用不同模型处理查询和文档,结果完全无法匹配。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 文档分块技术详解
2.1 分块的核心原则
优质的分块需要满足四个关键特性:
- 语义完整性:每个块应该能独立表达一个完整的概念或主题。例如在技术文档中,一个函数说明应该完整包含函数定义、参数说明和返回值,而不是被分割到不同块中。
- 长度适配:块的大小需要匹配Embedding模型的输入限制。主流模型如BGE-large-zh支持512个token,而OpenAI的text-embedding-3支持高达8191个token。
- 上下文连贯:保持文本的逻辑关联性。比如在分割对话记录时,应该保持完整的问答对,而不是将问题和答案分开。
- 检索友好:优化块的结构以提高检索准确性。这包括合理的重叠策略和元数据标注。
2.2 五大分块策略实战
2.2.1 固定长度分割
这是最简单的分块方法,按固定字符数切分。在Python中可以使用如下实现:
python复制def fixed_size_chunking(text, chunk_size=200):
return [text[i:i+chunk_size] for i in range(0, len(text), chunk_size)]
这种方法的优势是处理速度快,适合快速原型验证。但在实际项目中,我发现它经常切断重要概念,比如把"机器学习"拆分成"机器"和"学习"两个不完整的部分。
2.2.2 语义边界分割
更智能的方法是按照自然语言边界(如句子、段落)进行分割。使用spaCy库可以高效实现:
python复制import spacy
nlp = spacy.load("zh_core_web_sm")
def sentence_split(text):
doc = nlp(text)
return [sent.text for sent in doc.sents]
这种方法保持了语义完整性,适合大多数结构清晰的文档。我在处理技术文档和新闻文章时经常使用它。
2.2.3 递归层次分割
LangChain框架默认采用的分块策略,它按层次递归处理:
- 首先尝试按段落分割
- 如果块仍然过大,按句子分割
- 继续直到满足大小要求
实现代码示例:
python复制from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=300,
chunk_overlap=50,
separators=["\n\n", "\n", "。", "?", "!"]
)
chunks = splitter.split_text(document)
这种策略在混合格式文档中表现优异,是我处理复杂文档的首选方法。
2.2.4 结构感知分割
对于HTML、Markdown等结构化文档,可以基于文档结构分块。使用BeautifulSoup解析HTML:
python复制from bs4 import BeautifulSoup
from markdown import markdown
def html_chunking(html_text):
soup = BeautifulSoup(html_text, 'html.parser')
chunks = []
for header in soup.find_all(['h1', 'h2', 'h3']):
chunk = header.text + "\n"
next_node = header.next_sibling
while next_node and next_node.name not in ['h1', 'h2
