RAG 中文大模型文本分块核心指南:入门实践与进阶技巧
文本分块是RAG(检索增强生成)开发的基础环节,直接影响检索精度、模型性能与成本控制。中文文本因无天然空格分词的特性,分块配置需兼顾语义完整性与中文大模型token限制。本文分为「入门实践」和「进阶技巧」两大模块,全程提供可直接落地的代码示例与实践指南。
第一部分:入门实践(初/中级开发者必备)
本部分聚焦中文RAG文本分块的核心基础,涵盖主流切割方式、核心工具用法、切割原理及基础适配实践。
一、主流文本切割方式总结
文本分块本质可分为两大类,不同方式适配不同场景,选择正确的切割方式是分块成功的前提,其中语义切分是中文大模型RAG场景的首选。
1.1 固定切分(基础款,不推荐中文RAG场景)
核心逻辑:按固定字符数或token数直接切割,无需考虑文本语义结构,实现简单、速度快。
优点:开发成本低,适合短文本快速测试、临时处理场景;
缺点:极易切断句子、段落,导致语义碎片化,严重影响RAG检索效果,无法适配中文长文本、复杂文档的分块需求。
1.2 语义切分(主流款,推荐中文/RAG场景)
核心逻辑:按文本语义结构(段落、句子、标点、代码结构等)分层切割,优先保证块内语义完整,避免生硬截断。
优点:语义连贯性强,检索精度高,适配中文、代码、Markdown等多种复杂场景,是工业级开发的首选方式;
缺点:实现稍复杂,需配置合理的分隔符、块大小及重叠比例,需结合文本类型针对性调整。
核心结论:中文大模型RAG分块,优先选择语义切分,其中LangChain的RecursiveCharacterTextSplitter是最常用、最适配的工具。
二、langchain_text_splitters 的 RecursiveCharacterTextSplitter 详解
RecursiveCharacterTextSplitter是LangChain旗下langchain_text_splitters库的核心组件,也是工业级中文文本分块的标准工具,核心优势是“递归降级切割,优先保留语义完整”,其核心配置参数(chunk_size、chunk_overlap、separators)直接决定分块质量。
2.1 核心参数详解(必掌握)
2.1.1 chunk_size:分块大小(核心参数)
定义:每个文本块的最大长度,其单位完全由length_function参数决定,并非自带固定单位。
核心作用:控制分块大小,避免超出大模型上下文窗口限制,同时保证块内语义完整。
正确表述为:当配置length_function=token_count(token计数函数)时,chunk_size的单位为token数;若未自定义length_function(默认按字符计数),则单位为字符数。中文RAG场景推荐配置token计数,避免token溢出。
中文场景最佳实践:
-
常规中文文本(新闻、文档、博客):512~1024 token;
-
长文本、严谨文档(法律、论文):1024~2048 token;
-
短文本问答场景:256~512 token;
-
需根据实际模型上下文窗口调整(如GPT-4 Turbo支持128K);
-
禁忌:中文场景不建议按字符数设置,避免token溢出(1中文字符≈1.2~1.8 token)。
-
长文本、严谨文档(法律、论文):800~1500 token;
-
禁忌:中文场景不建议按字符数设置,避免token溢出(1中文字符≈1.2~1.8 token)。
2.1.2 chunk_overlap:分块重叠大小(关键参数)
定义:相邻两个文本块之间的重叠长度,单位与chunk_size一致(由length_function决定)。
核心作用:防止语义断裂,避免一句话、一个知识点被拆分成两个块,保证检索时能完整匹配跨块信息。
中文场景最佳实践:
-
黄金比例:chunk_size的10%~15%,既保证语义连贯,又避免token冗余;
-
示例:chunk_size=512 token时,chunk_overlap建议设为50~75 token;chunk_size=800 token时,建议设为120 token。
2.1.3 separators:分隔符(中文适配核心)
定义:分块时的语义分隔优先级列表,RecursiveCharacterTextSplitter会按列表优先级递归尝试切割,优先级越高,越优先使用该分隔符。
核心作用:决定分块的语义完整性,中文场景需自定义适配中文标点的分隔符,避免依赖英文空格。
中文专属推荐分隔符(优先级从高到低):
cn_separators = [
"\n\n", # 双换行(段落分隔,优先级最高)
"\n", # 单换行(行分隔)
"。", # 中文句号(句子结束)
"!", # 中文感叹号(句子结束)
"?", # 中文问号(句子结束)
";", # 中文分号(分句分隔)
",", # 中文逗号(短语分隔)
" ", # 空格(兜底分隔)
"" # 单个字符(最后的兜底手段,几乎用不到)
]
说明:分隔符优先级需遵循“段落→句子→短语→兜底”,最大程度保留中文文本的语义结构。
2.2 工具核心优势(适配中文场景)
-
语义优先:递归降级切割逻辑,避免生硬切断句子、段落,最大程度保留中文语义完整性;
-
灵活可控:支持自定义分隔符、长度计算函数,可精准适配中文、代码、Markdown等多种场景;
-
性能高效:递归逻辑轻量,处理百万字符级文本无压力,适配生产环境;
-
兼容性强:可直接搭配token计数工具(如tiktoken)实现精准控量,完美适配大模型上下文窗口限制。
三、切割原理分析(RecursiveCharacterTextSplitter 核心逻辑)
很多开发者误解RecursiveCharacterTextSplitter是“逐字分块”,实则其核心是“递归降级、语义优先”,只有在高优先级分隔符无法满足要求时,才会降级分块,具体原理可分为4个步骤,结合中文场景通俗解读:
3.1 核心原理(通俗版)
核心逻辑:“能不切细就不切细”,按预设的separators优先级递归尝试分块,直到所有块的大小都符合chunk_size要求,全程优先保留最大语义单元。
3.2 具体切割流程(以中文专属分隔符为例)
-
第一步:用最高优先级分隔符(\n\n,段落分隔)分块文本,分块后检查每一块的长度是否≤chunk_size;若所有块都符合要求,分块结束,无需使用后续分隔符;
-
第二步:若某一块长度超出chunk_size,降级用下一级分隔符(\n,换行分隔)分块该块,再次检查块大小;
-
第三步:重复降级流程,依次使用“。、!、?”(句子分隔)、“;、,”(短语分隔)、空格分块,直到所有块都符合chunk_size要求;
-
第四步:兜底处理:若上述所有分隔符分块后,仍有块超出chunk_size,才会用空字符串("")进行单字分块(中文场景几乎用不到,仅针对无任何标点的乱码串)。
3.3 关键注意点
-
并非所有分隔符都会用到:分块流程是“能停则停”,高优先级分隔符满足要求后,低优先级分隔符不会触发;
-
中文场景几乎不会切到单字:正常中文文本都有标点、换行,只有无任何分隔符的长串文本(如“人工智能大模型RAG技术实践”),才会触发兜底单字分块;
-
keep_separator参数作用:建议设为True,保留分隔符(尤其是中文标点),避免切割后出现“半截句子”,保证文本连贯性。
四、tiktoken 中文适配(基础代码示例,可直接运行)
中文分块的核心禁忌是“按字符数计数”,大模型实际按token计费、限容,因此必须用token计数工具实现精准计数,搭配RecursiveCharacterTextSplitter可实现中文分块最优效果。tiktoken是OpenAI官方开源的token计数器,适用于OpenAI生态模型,使用时需注意其局限性。
4.1 tiktoken 核心作用与局限性(修正补充)
-
核心作用:精准统计token数量,解决中文“字符数≠token数”的问题,避免分块超出大模型上下文窗口;高效轻量,百万字符级文本秒级计数,不影响生产环境性能。
-
cl100k_base是OpenAI模型(如GPT-4、text-embedding-3等)使用的编码,对于开源Llama 3、阿里通义千问、百度文心一言等模型,各有专属tokenizer,用cl100k_base计数会导致token数量不准。
-
最佳实践:使用你最终要调用的大模型对应的tokenizer进行计数,例如使用Llama 3则用其专属tokenizer,使用OpenAI模型则用cl100k_base编码。
4.2 基础代码示例(RecursiveCharacterTextSplitter + tiktoken 适配OpenAI模型)
import tiktoken
from langchain_text_splitters import RecursiveCharacterTextSplitter
from pydantic import Field
# 1. 配置分块核心参数(中文RAG场景最优,token计数)
# 说明:因使用tiktoken的cl100k_base编码,chunk_size单位为token数
chunk_size: int = 512 # 简化配置,避免未完整定义Pydantic模型的问题
chunk_overlap: int = 55
# 2. 初始化tiktoken,实现OpenAI模型的中文token精准计数(优化效率,全局初始化一次)
enc = tiktoken.get_encoding("cl100k_base") # 全局初始化,避免每次调用重复获取encoding
def token_count(text: str) -> int:
# cl100k_base编码:仅适配OpenAI生态模型(GPT-4、text-embedding等)
return len(enc.encode(text))
# 3. 中文专属分隔符(遵循“段落→句子→短语→兜底”优先级)
cn_separators = [
"\n\n", # 双换行(段落分隔)
"\n", # 单换行(行分隔)
"。", # 中文句号(句子结束)
"!", # 中文感叹号(句子结束)
"?", # 中文问号(句子结束)
";", # 中文分号(分句分隔)
",", # 中文逗号(短语分隔)
" ", # 空格(兜底分隔)
"" # 单个字符(最后的兜底手段,几乎用不到)
]
# 4. 初始化中文适配的分块器(整合所有最优配置)
cn_text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
separators=cn_separators,
length_function=token_count, # 用tiktoken计数,避免token溢出
keep_separator=True, # 保留分隔符,保证中文句子完整性
is_separator_regex=False # 不使用正则,提升分块效率
)
# 5. 测试分块效果(中文长文本示例)
test_cn_text = """在大模型开发(尤其是RAG检索增强生成)中,文本分块是决定检索精度、模型性能和成本控制的核心环节。很多开发者在配置分块参数时,容易陷入“比例合理即正确”的误区,忽略了中文场景与大模型token计费逻辑的特殊性。中文文本与英文文本的核心差异是无天然空格分词,因此分块时需重点关注分隔符适配、token计数等问题,否则极易出现语义断裂、分块不合理的问题。RecursiveCharacterTextSplitter的核心优势是递归降级分块,优先保留语义完整,搭配tiktoken可实现中文分块的精准控量。"""
# 执行分块操作
chunks = cn_text_splitter.split_text(test_cn_text)
# 输出分块结果(含token计数,验证分块效果)
for idx, chunk in enumerate(chunks, 1):
print(f"【分块{idx}】token数:{token_count(chunk)}")
print(f"内容:{chunk}\n---")
4.3 代码关键说明
-
token_count函数:采用cl100k_base编码,仅适配OpenAI生态模型,若使用其他模型需替换为对应tokenizer;
-
参数配置:chunk_size=512 token、chunk_overlap=55 token(10%比例),符合中文RAG分块黄金标准,且明确标注单位由length_function决定;
-
可扩展性:如需适配其他模型,替换token_count函数中的编码方式即可;
-
运行依赖:需提前安装依赖(pip install tiktoken langchain-text-splitters pydantic)。
五、入门实践总结
中文RAG文本分块入门核心是“语义完整+token精准控量”,总结3条最佳实践:
-
分块方式:优先选择语义分块,RecursiveCharacterTextSplitter是中文RAG场景的首选工具;
-
参数配置:chunk_size根据场景设为256~2048 token(短文本问答256~512 token、常规场景512~1024 token、长文档摘要1024~2048 token),chunk_overlap设为chunk_size的10%~15%,使用中文专属分隔符,明确chunk_size单位由length_function决定;
-
计数方式:使用对应大模型的tokenizer计数,OpenAI模型用tiktoken(cl100k_base),其他模型替换为专属tokenizer,避免按字符数设置。
第二部分:进阶技巧(从“能用”到“专业”)
本部分基于入门实践,补充高阶分块策略、前沿趋势、文档解析技巧及原子块处理方法。
一、进阶分块策略:层级结构与元数据传递
入门级的句子/段落分块无法满足结构良好文档(如Markdown、HTML)的分块需求,层级分块结合元数据传递,能显著提升RAG检索精度。
1.1 核心思路
对结构清晰的文档,先按标题层级(如Markdown的#、##、###)分块,将文档拆分为多个章节;再对每个章节内部的文本,使用RecursiveCharacterTextSplitter进行细粒度分块;同时将标题层级、章节名称作为元数据,绑定到每个细分块中,让检索器既能找到相关内容,也能明确内容所属的文档位置。
1.2 工具推荐与代码示例
LangChain提供专门处理结构化文档的工具:MarkdownHeaderTextSplitter(处理Markdown文档)、HTMLHeaderTextSplitter(处理HTML文档),可自动识别标题层级并提取元数据。
from langchain_text_splitters import MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter
# 补充token_count函数定义(复用第一部分逻辑,全局初始化提升效率)
import tiktoken
enc = tiktoken.get_encoding("cl100k_base")
def token_count(text: str) -> int:
return len(enc.encode(text))
# 1. 定义Markdown标题层级(key为标题标记,value为元数据键名)
headers_to_split_on = [
("#", "一级标题"),
("##", "二级标题"),
("###", "三级标题"),
]
# 2. 初始化Markdown层级分块器(先按标题分块,提取元数据)
markdown_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=headers_to_split_on,
strip_headers=False # 不删除标题,保留在块内容中
)
# 3. 测试Markdown文本(带标题层级)
test_markdown = """# RAG文本分块指南
## 一、入门实践
### 1.1 主流分块方式
文本分块分为固定分块和语义分块,中文RAG场景优先选择语义分块。
### 1.2 核心工具介绍
RecursiveCharacterTextSplitter是语义分块的首选工具,支持自定义分隔符。
## 二、进阶技巧
### 2.1 层级分块策略
层级分块结合元数据传递,能提升检索精度。
"""
# 4. 第一步:按标题层级分块,获取带元数据的块
header_chunks = markdown_splitter.split_text(test_markdown)
# 5. 第二步:对每个层级块内部进行细粒度分块(复用入门级配置)
cn_separators = [
"\n\n", "\n", "。", "!", "?", ";", ",", " ", ""
]
fine_splitter = RecursiveCharacterTextSplitter(
chunk_size=200,
chunk_overlap=20,
separators=cn_separators,
length_function=token_count, # 复用token计数函数
keep_separator=True
)
# 6. 执行细粒度分块,保留元数据
final_chunks = []
for chunk in header_chunks:
fine_chunks = fine_splitter.split_text(chunk.page_content)
# 为每个细粒度块添加元数据(继承上级标题元数据)
for fine_chunk in fine_chunks:
final_chunks.append({
"content": fine_chunk,
"metadata": chunk.metadata
})
# 输出结果(含元数据)
for idx, chunk in enumerate(final_chunks, 1):
print(f"【分块{idx}】元数据:{chunk['metadata']}")
print(f"内容:{chunk['content']}\n---")
1.3 核心价值
当用户提问时,检索器不仅能匹配到相关文本块,还能通过元数据知道该块的文档位置,为LLM提供更丰富的上下文。
二、处理“不可分割”的原子块
在复杂文档(如代码块、法律条文、JSON对象)中,部分内容具有强相关性,硬要拆分会导致信息支离破碎,需对这类“原子块”进行特殊保护。
2.1 核心思路
预处理文本,用特殊标记(如<nosplit>和</nosplit>)包裹不可分割的原子块,然后自定义分块器的分隔符逻辑,让分块器遇到该标记时,直接整体保留原子块,不进行任何分块。
2.2 代码示例(保护原子块)
import re
import tiktoken
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 初始化token计数函数(全局初始化,提升效率)
enc = tiktoken.get_encoding("cl100k_base")
def token_count(text: str) -> int:
return len(enc.encode(text))
# 1. 预处理文本:用<nosplit>包裹原子块(如代码块、法律条文)
raw_text = """
以下是一段不可分割的Python代码(原子块):
<nosplit>
def token_count(text: str) -> int:
enc = tiktoken.get_encoding("cl100k_base")
return len(enc.encode(text))
</nosplit>
以下是一条不可分割的法律条文(原子块):
<nosplit>
当事人订立合同,可以采用书面形式、口头形式或者其他形式。书面形式是合同书、信件、电报、电传、传真等可以有形地表现所载内容的形式。
</nosplit>
其他可分割的文本:文本分块是RAG开发的基础环节,核心是保证语义完整。
"""
# 2. 自定义分隔符逻辑:避开<nosplit>包裹的内容
def custom_separators(text):
# 先匹配<nosplit>...</nosplit>,保留原子块
pattern = r'(<nosplit>.*?</nosplit>)'
# 分割文本:原子块作为独立单元,其他内容按常规分隔符分块
parts = re.split(pattern, text, flags=re.DOTALL)
# 过滤空字符串,整理分割结果
return [part for part in parts if part.strip()]
# 3. 初始化分块器,结合自定义分隔逻辑
splitter = RecursiveCharacterTextSplitter(
chunk_size=300,
chunk_overlap=30,
separators=["\n\n", "\n", "。", ",", " "],
length_function=token_count,
keep_separator=True
)
# 4. 先按原子块分割,再对非原子块进行细粒度分块(优化匹配逻辑,提升健壮性)
final_chunks = []
atomic_parts = custom_separators(raw_text)
for part in atomic_parts:
if "<nosplit>" in part:
# 用正则提取内容,避免标签内换行/空格导致匹配失败,同时去除首尾空白
content = re.sub(r'</?nosplit>', '', part)
final_chunks.append(content.strip())
else:
# 非原子块:常规细粒度分块
final_chunks.extend(splitter.split_text(part))
# 输出结果(原子块完整保留)
for idx, chunk in enumerate(final_chunks, 1):
print(f"【分块{idx}】token数:{token_count(chunk)}")
print(f"内容:{chunk}\n---")
2.3 核心价值
保证代码、法律条文等强相关内容的完整性,避免因切割导致的信息丢失,适用于技术文档、法律合同等场景。
三、前沿趋势:语义分块与混合分块
入门级的RecursiveCharacterTextSplitter本质是“基于规则”的分块,依赖人工配置的分隔符,更前沿的分块方式是“语义分块”和“混合分块”,能更智能地适配复杂文本。
3.1 语义分块(Semantic Chunking)
核心逻辑:不依赖人工规则和分隔符,而是利用embedding模型(如text-embedding-3-small、m3e-base)计算句子间的语义相似度,在语义发生显著转折的地方进行分块,保证每个块内的内容属于同一个主题。
优势:无需手动配置分隔符,适配无标点、结构混乱的文本,分块语义连贯性更强;
缺点:速度比规则分块慢,需额外调用embedding模型,增加一定成本;
工具推荐:LangChain的SemanticChunker;也可通过SentenceTransformers结合语义相似度计算实现自定义语义分块;或使用spacy的sentencizer进行句子分割后,再计算句子间相似度完成语义分块。
3.2 混合分块(Hybrid Chunking)
核心逻辑:结合规则分块和语义分块的优势,先按规则(如标题、标点)进行粗分块,得到较大的文本块;再对过大的块,使用语义分块进行细分块,兼顾效率和语义完整性。
优势:既保证了分块效率,又避免了规则分块的局限性,是目前先进RAG框架(如LangChain、LlamaIndex)的默认选择;
实践建议:粗分块用MarkdownHeaderTextSplitter(结构化文档)或RecursiveCharacterTextSplitter(非结构化文档),细分块用SemanticChunker。
四、RAG系统的“前一步”:高质量文档解析
文本分块的前提是“高质量的文本提取”,即文档解析。如果提取的文本混乱,再好的分块策略也无法保证RAG效果。
4.1 核心认知
文档解析是决定RAG系统上限的第一步,其核心目标是精准提取文本内容,并保留原始结构。
推荐工具:
-
Unstructured:开源工具,由Unstructured.io社区开发,支持多种文本格式,能智能处理复杂版面和文本,适配多语言场景,同时支持25种以上文件格式,可智能识别文档中的标题、段落等元素;使用注意事项:需注意API版本,本地部署时需安装额外依赖。
-
MinerU:由上海人工智能实验室(Shanghai AI Laboratory)的OpenDataLab团队研发,擅长处理中文文本,支持文本提取,精度较高,尤其擅长处理含复杂公式、表格的中文文档,可输出结构化Markdown格式;使用注意事项:对中文公式识别效果好,但对纯英文文档可能过重。
-
Docling:由IBM研究院(IBM Research)发布的开源工具,轻量高效,参数量仅2.58亿,支持文本提取,在表格识别和代码提取方面表现突出,适配中文场景,易于企业级部署和集成;使用注意事项:IBM持续更新中,建议使用v2.0+版本;补充:字节跳动开源的Dolphin(参数量3.22亿),同样轻量高效,在文档布局分析和内容提取方面性能优异;使用注意事项:字节开源项目,社区活跃度需关注。
五、进阶实践总结与注意事项
-
层级分块+元数据传递:处理结构化文档的核心技巧,能显著提升RAG检索精度,优先使用LangChain的MarkdownHeaderTextSplitter等工具;
-
原子块保护:对代码、法律条文等强相关内容,用特殊标记预处理,避免分块导致的信息丢失;
-
前沿趋势:语义分块适合复杂文本,混合分块兼顾效率与效果,是生产环境的首选;
-
文档解析优先:选择Unstructured、MinerU、Docling、Dolphin等工具,保证文本提取质量;其中Unstructured支持格式广泛,MinerU擅长复杂中文文档,Docling表格识别突出,Dolphin布局分析优异;
-
token计数规范:始终使用目标大模型的专属tokenizer,避免用错编码导致的计数不准。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)