LangChain 文本分割器详解:字符分割、Token 分割、硬约束递归分割实战

我挖掘了一个巨牛的 人工智能 学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。

前言

在 RAG 检索增强生成完整流程中,文档加载器负责读取 PDF、Markdown、TXT 等各类文件,并统一转换为 LangChain 的 Document 文档对象。但原始文档往往篇幅很长,无法直接送入向量库与大模型:

  1. 大模型存在固定上下文窗口上限,超长文本会直接超出限制;
  2. 整块大文本检索粒度粗糙,用户查询很难精准匹配到关键信息;
  3. 完整长文本嵌入向量时算力消耗更高,检索准确率偏低。

因此,文本分割(Text Splitters) 是 RAG 不可或缺的核心环节,作用就是将长篇文档拆分为短小、语义连贯、便于检索与模型读取的文本块。本文结合实战代码,拆解 LangChain 三类主流文本分割方案:基于字符分割、基于 Token 分割、递归式硬约束分割,同时讲解中文文档适配优化方案。

一、文本分割核心设计思路

文本拆分本质是把大文本切割为可管理的小块,行业主流实现思路分为两大分支:

  1. 按文本长度切割:分为字符长度统计Token 长度统计两种计量标准;
  2. 兼顾语义完整性:优先按照段落、换行、标点分层切割,尽可能不破坏完整句子、段落语义;
  3. 重叠块设计:通过 chunk_overlap 让相邻文本块保留重复内容,避免关键信息被分割在两块边界而检索丢失。

二、基于字符长度分割 CharacterTextSplitter

2.1 基础原理

CharacterTextSplitter 是最简单的文本分割器,通过内置 len() 函数统计字符数量判断块大小,仅支持单一分隔符切割,默认推荐分隔符优先级 ["\n\n", "\n", " ", ""]。 核心特点:软性长度约束,优先保全完整段落语义 。 当一段文本使用指定分隔符切割后,字符长度依旧超过 chunk_size,分割器不会强行截断文本,而是完整保留整块内容,并打印日志提示块超长,避免一句话、专业术语被劈成无意义片段,影响向量检索效果。

2.2 完整实战代码

python 复制代码
from langchain_community.document_loaders import UnstructuredMarkdownLoader
from langchain_text_splitters import CharacterTextSplitter

# 加载本地Markdown文档
markdown_path = "../Docs/Markdown/脚手架级微服务租房平台Q&A.md"
loader = UnstructuredMarkdownLoader(markdown_path)
data = loader.load()  # 加载完成,得到Document列表

# 初始化字符文本分割器
text_splitter = CharacterTextSplitter(
    separator="\n\n",        # 段落分隔符,双换行区分不同段落
    chunk_size=100,          # 目标单块字符上限
    chunk_overlap=20,        # 相邻文本块重叠字符数
    length_function=len,     # 使用len统计字符长度
    is_separator_regex=False # 分隔符非正则表达式
)

# 执行文档分割
texts = text_splitter.split_documents(data)

# 循环打印前10个分割后的文本块
for document in texts[:10]:
    print("*" * 30)
    print(f"{document}\n")

2.3 超长日志说明与解决方案

运行代码后控制台会持续输出如下提示:

复制代码
Created a chunk of size 128, which is longer than the specified 100
Created a chunk of size 133, which is longer than the specified 100
Created a chunk of size 1171, which is longer than the specified 100

很多新手会误以为是程序报错,实际这是预期内的提示日志:

  1. 分割器优先使用 \n\n 段落分隔,若单段文本全部字符超过设定 chunk_size
  2. 无更小分隔符可以拆分该段落时,有两种选择:
    • 方案 A:强行截断文本,会拆分完整语句、专有名词,语义破碎;
    • 方案 B:完整保留整个超长段落,输出日志告知用户; 分割器默认选择方案 B,保证语义完整。

优化处理方式 : 观察日志大部分文本块长度集中在 100~200 字符,直接调大 chunk_size 参数即可大幅减少超长块提示:

我设置的是 "\n\n" 双换行,这通常代表段落之间

python 复制代码
text_splitter = CharacterTextSplitter(
    separator="\n\n",
    chunk_size=200,
    chunk_overlap=20,
    length_function=len,
    is_separator_regex=False,
)

三、基于 Token 长度分割(适配 OpenAI 大模型)

3.1 Token 与 tiktoken 基础认知

所有 OpenAI 系列大模型(GPT-3.5、GPT-4)不直接识别字符,底层以 Token 为最小计算单位,输入输出都受 Token 数量限制。因此对接 OpenAI 接口时,基于 Token 计数分割是最精准的方案

tiktoken 是 OpenAI 官方分词工具,

cl100k_baseOpenAI tiktoken 提供的一套 BPE 分词编码规则 ,专门给 gpt-3.5-turboGPT-4、OpenAI 向量嵌入模型使用,用来把文字拆成模型能识别的最小单元 Token,词汇表总量约 100256 个。

名字拆解含义

  • cl = Chat models(对话类模型专用)
  • 100k = 词汇表约 10 万条 token
  • base = 基础标准版编码
tiktoken 简单演示代码
python 复制代码
import tiktoken
# 加载GPT系列通用编码规则
enc = tiktoken.get_encoding("cl100k_base")
# 文本编码为Token数字列表
enc_output = enc.encode("my name is LiHua!")
print(f"编码后的token:{enc_output}")

# 遍历每个Token,还原对应文本片段
for token in enc_output:
    byte_text = enc.decode_single_token_bytes(token)
    real_text = byte_text.decode("utf-8")
    print(f"token:{token} 对应文本片段:{real_text}")

输出结果:

复制代码
编码后的token:[2465, 836, 374, 14851, 39, 4381, 0]
token:2465 对应文本片段:my
token:836 对应文本片段: name
token:374 对应文本片段: is
token:14851 对应文本片段: Li
token:39 对应文本片段:H
token:4381 对应文本片段:ua
token:0 对应文本片段:!

3.2 LangChain Token 分割器使用

CharacterTextSplitter.from_tiktoken_encoder() 快速创建基于 Token 计数的分割器,底层自动调用 tiktoken 统计 Token 数量,参数 chunk_size 代表 Token 上限。

python 复制代码
from langchain_community.document_loaders import UnstructuredMarkdownLoader
from langchain_text_splitters import CharacterTextSplitter

markdown_path = "../Docs/Markdown/脚手架级微服务租房平台Q&A.md"
loader = UnstructuredMarkdownLoader(markdown_path)
data = loader.load()

# 创建基于tiktoken的Token分割器
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
    encoding_name="cl100k_base",
    chunk_size=200,    # 单块最大200个token
    chunk_overlap=50   # 块重叠50个token
)

texts = text_splitter.split_documents(data)
# 打印前10个分割结果
for document in texts[:10]:
    print("*" * 30)
    print(f"{document}\n")

该分割器依旧是软性约束,超长段落不会强制截断,控制台同样会打印 Token 数量超限日志,适合优先保证段落完整、对接 OpenAI 的知识库场景。

基于字符串长度分割和基于Token长度分割的代码写法区别:

一、核心 4 大差异点

1. 长度计算标准(最关键区别)

1)普通写法 length_function=len

  • 统计字符个数,中文 1 个字 = 1、英文 1 个字母 = 1、标点 = 1;
  • 和模型真实消耗的 token 数量完全无关;
  • 适合简单测试、本地开源模型粗略分割。

2)from_tiktoken_encoder() 快捷方法

  • 底层自动替换 length_function,使用 tiktoken(cl100k_base) 统计GPT 真实 token 数量
  • chunk_size=200 代表最多 200 个大模型输入 token;
  • 精准匹配 OpenAI GPT3.5/GPT4 上下文窗口,线上生产必用。

2. 分隔符传参方式不同

1)普通构造器 CharacterTextSplitter(separator="\n\n")

  • 参数是单个字符串 separator ,仅使用 \n\n一种分隔符切割;
  • 没有分层递归逻辑,只会按双换行一刀切;
  • 文档一段超长时,不会尝试换行、空格二次分割,直接整块保留。

2)from_tiktoken_encoder 内部自带 separators=["\n\n", "\n", " ", ""] 列表

  • 多层优先级分隔符,自动递归切割:先段落\n\n → 单行\n → 空格;
  • 会多层拆分,尽可能把文本压缩到 chunk_size 以内,语义完整性更好;
  • 代码看不到分隔符参数,是方法内部封装写死的。

3. 依赖与适用场景不同

1)普通字符分割

  • 无需安装 tiktoken;
  • 仅按字符切割,不贴合 GPT 模型限制;
  • 适合演示、非 OpenAI 模型、简单短文。

2)tiktoken token 分割

  • 必须安装 tiktoken 库;
  • 严格对齐 GPT 输入限制,防止调用接口时报上下文超限;
  • 企业 RAG、对接 OpenAI 接口标准写法。

四、硬性长度约束:RecursiveCharacterTextSplitter 递归分割器

4.1 核心优势

前面两种分割器都存在软性约束,会产生超长文本块。如果业务场景严格限制单块长度(比如模型窗口极小、向量库对文 本长度有硬性限制),需要使用 RecursiveCharacterTextSplitter 递归分割器:

  1. 支持多层级分隔符优先级列表,递归分层切割;
  2. 硬长度约束 ,所有分割后的文本块长度一定小于等于设定 chunk_size
  3. 同样支持字符计数、Token 计数两种模式。

默认分隔符优先级:["\n\n", "\n", " ", ""],切割逻辑自上而下递归执行:

  1. 先用双换行 \n\n 按段落切割;
  2. 切割后块仍超限,使用单换行 \n 分行切割;
  3. 依旧超限,使用空格分词切割;
  4. 无可用分隔符时,直接强制截断文本,保证长度合规。

4.2 Token 版递归分割实战代码

python 复制代码
from langchain_community.document_loaders import UnstructuredMarkdownLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter

markdown_path = "../Docs/Markdown/脚手架级微服务租房平台Q&A.md"
loader = UnstructuredMarkdownLoader(markdown_path)
data = loader.load()

# 递归式Token分割器,硬约束块长度
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
    encoding_name="cl100k_base",
    chunk_size=100,
    chunk_overlap=0,
)

texts = text_splitter.split_documents(data)
# 打印前10个分割结果
for document in texts[:10]:
    print("*" * 30)
    print(f"{document}\n")

运行后不会再输出块超长日志,但会出现完整句子、词语被强制拆分的情况,牺牲部分语义完整性,换取严格统一的文本块长度。

五、中文文档分割优化方案

原生默认分隔符仅适配英文文本,中文长句仅依靠空格、换行分割,极易将完整词语、一句话拆分为零散汉字,破坏语义。 自定义分隔符列表,新增中文句号、逗号,优先按完整句子切割,大幅提升中文知识库分割效果:

python 复制代码
text_splitter = RecursiveCharacterTextSplitter(
    separators=[
        "\n\n",  # 段落
        "\n",    # 单行
        "。",    # 中文句号,分句
        ",",    # 中文逗号,分句
        " ",
        "",
    ],
    chunk_size=200,
    chunk_overlap=30,
    length_function=len
)

切割优先级:段落 → 单行 → 完整中文句子 → 空格,最大程度保留中文语句完整性。

六、三类分割器选型总结

分割器 长度计量 长度约束 适用场景
CharacterTextSplitter 字符 len () 软性,保留超长段落 Markdown、分段清晰文档,追求完整语义
CharacterTextSplitter(tiktoken) Token 计数 软性 对接 GPT 系列模型、段落完整优先
RecursiveCharacterTextSplitter 字符 / Token 双支持 硬性,全部块合规 严格限制文本长度、模型窗口小、线上生产强约束场景

通用参数配置建议

  1. chunk_size:中文知识库推荐 150~300,Token 模式 100~250;
  2. chunk_overlap:设置为 chunk_size 的 1/4 ~ 1/3,平衡上下文完整性与冗余;
  3. 纯中文文档必须自定义分隔符,添加 分句符号;
  4. 只要调用 OpenAI 相关模型,优先选用 tiktoken Token 分割,避免上下文超限报错。

结语

文本分割是决定 RAG 问答准确率的关键步骤,没有万能分割器,需要根据文档格式、使用的大模型、业务长度限制灵活选择。分段清晰的 Markdown、问答文档优先使用软性约束分割器保证语义;无分段纯长文本、有严格长度限制场景,选用递归硬约束分割器,并针对中文场景优化分隔符列表,兼顾检索精度与系统稳定性。

相关推荐
风哥2号1 小时前
数据库教程FGMT21‑Oracle RAC+DG生产实战(1):Oracle19c/26ai for RHEL8/9安装配置+补丁
数据库·oracle
掘金挖土1 小时前
前端手摸手跑路之 AI 应用开发(四)
前端
sbjdhjd1 小时前
ThinkPHP 5.0.10 缓存写入型 RCE 复盘:从手动搭建、换行绕过到源码单步验证 | 05
java·后端·安全·spring·网络安全·数据挖掘·php
LEE1 小时前
Agent 的控制权,为什么正在回到模型手里?
前端·后端
沃夫上校1 小时前
跨域这件事,其实浏览器在替你挡刀
前端·后端·nginx
十年一梦惊觉醒1 小时前
快手视频发布助手,全自动视频发布带货工具
开发语言·前端·javascript
Solara1 小时前
中文字体子集化实录:3.2MB → 200KB 的完整脚本、踩坑清单与三条反直觉经验
前端·css·架构
海鸥-w1 小时前
事务四大特性
数据库
旺仔不是程序员1 小时前
致命索引失效:索引列运算与函数索引不匹配
数据库·后端·面试