Task2 数据加载与分块
task2资料参考:all-in-rag/docs/chapter2 at main · datawhalechina/all-in-rag
这一章主要详细介绍了如何从不同种类格式的数据源加载得到纯文本,同时将得到的纯文本进行分块处理。当然这部分的实现都是去学会如何去借用已有工具,不需要自己从头开始造轮子。当然我们仍然需要去理解这中间发生了什么。
(美图养眼一下~)
1. 数据加载
作为RAG系统的第一个部分,数据加载部分负责将各种非结构化文档(如PDF、Word、Markdown、HTML等)转换为程序可以处理的结构化数据。数据加载的质量会直接影响后续的索引构建、检索效果和最终的生成质量。比如加载的文本内容和原文相比乱序,或者不一致,显然会直接影响我们后续的文本向量相似度比较排序。
1.1 Unstructured文档处理库
当前主流RAG文档加载器:
工具名称 | 特点 | 适用场景 | 性能表现 |
---|---|---|---|
PyMuPDF4LLM | PDF→Markdown转换,OCR+表格识别 | 科研文献、技术手册 | 开源免费,GPU加速 |
TextLoader | 基础文本文件加载 | 纯文本处理 | 轻量高效 |
DirectoryLoader | 批量目录文件处理 | 混合格式文档库 | 支持多格式扩展 |
Unstructured | 多格式文档解析 | PDF、Word、HTML等 | 统一接口,智能解析 |
FireCrawlLoader | 网页内容抓取 | 在线文档、新闻 | 实时内容获取 |
LlamaParse | 深度PDF结构解析 | 法律合同、学术论文 | 解析精度高,商业API |
Docling | 模块化企业级解析 | 企业合同、报告 | IBM生态兼容 |
Marker | PDF→Markdown,GPU加速 | 科研文献、书籍 | 专注PDF转换 |
MinerU | 多模态集成解析 | 学术文献、财务报表 | 集成LayoutLMv3+YOLOv8 |
虽然目前有这么多现成的工具,这里我们主要介绍的是Unstructured,其作为目前最受欢迎的文档加载解决方案之一,其多种文档格式:PDF、Word、Excel、HTML、Markdown等同时api接口已经统一封装好,无需写格外的代码,同时其还会识别文档结构:标题、段落、表格、列表等同时保留文档元数据信息。
这里主要参考官方文档Overview – Unstructured
1.2 支持的文档元素类型
Unstructured能够识别和分类以下文档元素:
元素类型 | 描述 |
---|---|
Title |
文档标题 |
NarrativeText |
由多个完整句子组成的正文文本,不包括标题、页眉、页脚和说明文字 |
ListItem |
列表项,属于列表的正文文本元素 |
Table |
表格 |
Image |
图像元数据 |
Formula |
公式 |
Address |
物理地址 |
EmailAddress |
邮箱地址 |
FigureCaption |
图片标题/说明文字 |
Header |
文档页眉 |
Footer |
文档页脚 |
CodeSnippet |
代码片段 |
PageBreak |
页面分隔符 |
PageNumber |
页码 |
UncategorizedText |
未分类的自由文本 |
CompositeElement |
分块处理时产生的复合元素* |
注:
CompositeElement
是通过分块(chunking)处理产生的特殊元素类型,由一个或多个连续的文本元素组合而成。例如,多个列表项可能会被组合成一个单独的块。
1.3基于LangChain的Unstructured使用样例
这里我们尝试对一个pdf文档进行数据加载
pdf文件内容:all-in-rag/data/C2/pdf/rag.pdf at main · datawhalechina/all-in-rag
大致内容为对应RAG(信息检索增强)的百度科普页面:
代码部分:
from unstructured.partition.auto import partition
# PDF文件路径
pdf_path = "../../data/C2/pdf/rag.pdf"
# 使用Unstructured加载并解析PDF文档
elements = partition(
filename=pdf_path,
content_type="application/pdf"
)
# 打印解析结果
print(f"解析完成: {len(elements)} 个元素, {sum(len(str(e)) for e in elements)} 字符")
# 统计元素类型
from collections import Counter
types = Counter(e.category for e in elements)
print(f"元素类型: {dict(types)}")
# 显示所有元素
print("\n所有元素:")
for i, element in enumerate(elements, 1):
print(f"Element {i} ({element.category}):")
print(element)
print("=" * 60)
上面的代码即将对应pdf文档内容,转化为对应纯文本的每个元素:
解析完成: 279 个元素, 7500 字符
元素类型: {'Header': 22, 'Title': 195, 'UncategorizedText': 41, 'NarrativeText': 3, 'Footer': 15, 'ListItem': 3}
所有元素:
Element 1 (Header):
网页
============================================================
Element 2 (Header):
新闻
============================================================
Element 3 (Header):
贴吧
============================================================
Element 4 (Header):
知道
============================================================
Element 5 (Header):
网盘
============================================================
Element 6 (Header):
图片
============================================================
Element 7 (Header):
视频
============================================================
Element 8 (Header):
地图
.........
其中partition
具体常用参数介绍为:
参数名 | 类型 | 作用说明 |
---|---|---|
filename |
str | 本地文件路径 |
file |
file-like | 文件对象(如 open(..., "rb") ) |
url |
str | 远程 URL 地址 |
content_type |
str | MIME 类型提示,如 "application/pdf" |
include_page_breaks |
bool | 是否输出分页符元素 |
strategy |
str | 分区策略:"auto" (默认)、"fast" 、"hi_res" 、"ocr_only" |
encoding |
str | 文本编码方式,默认 UTF-8 |
languages |
List[str] | OCR 使用语言列表 |
ssl_verify |
bool | URL 模式下是否验证 SSL,默认 True |
其他都比较好理解,但是其中参数strategy
看上去有点奇怪,下面我们详细介绍这个参数:
strategy
参数说明
"auto"
(默认)
- 作用:自动选择最合适的策略。
- 逻辑:
- 如果文件是文本型 PDF(含嵌入的文本层),优先直接用 fast 提取。
- 如果文件是扫描 PDF 或图片(无文本层),会自动切换到 hi_res 或 OCR 模式。
- 适用场景:大多数情况,推荐直接用
"auto"
,省去判断。
"fast"
- 作用:快速模式,主要依赖 内嵌文本层。
- 特征:
- 速度快,几乎不需要图像处理。
- 不能正确处理扫描版 PDF(因为没文字层)。
- 对复杂版式(表格、公式、竖排文字)适配较弱。
- 适用场景:电子生成的 PDF(比如 Word 导出的 PDF、网页导出的 PDF)。
"hi_res"
- 作用:高精度模式,结合 版面分析 + OCR。
- 特征:
- 使用布局分析模型(比如 LayoutParser / Detectron2)识别段落、表格、标题等结构。
- 可对扫描版 PDF 和图片进行高质量提取。
- 相比
fast
慢很多,内存占用也更高。
- 适用场景:扫描版文档、结构复杂(图文混排、表格较多)的 PDF。
"ocr_only"
- 作用:只做 OCR,不做版面分析。
- 特征:
- 直接逐页 OCR 出文本,没有段落/标题/表格结构。
- 提取速度比
hi_res
略快,但信息粒度较低。 - 不适合需要文档结构的场景。
- 适用场景:只关心 文字内容,不在意版面格式;对图片/扫描文档进行纯文本 OCR。
1.4 不同策略效果对比
接下来我们不妨使用partition_pdf
替换当前partition
函数并分别尝试用hi_res
,fast
和ocr_only
进行解析,不过这里特别说明因为 partition_pdf(strategy="hi_res" / "ocr_only")
会把 PDF 转成图片,再走版面分析/OCR 流程,而这个转换依赖 Poppler。
我们需要去 Poppler for Windows 下载预编译包,下载解压完成之后再添加到系统路径上,最后在命令行中输入:
pdfinfo -v
查看是否配置成功:
例如我显示:
PS C:\Users\kkk> pdfinfo -v
pdfinfo version 25.07.0
Copyright 2005-2025 The Poppler Developers - http://poppler.freedesktop.org
Copyright 1996-2011, 2022 Glyph & Cog, LLC
接下来测试代码:
from unstructured.partition.auto import partition
from unstructured.partition.pdf import partition_pdf
# PDF文件路径
pdf_path = "../../data/C2/pdf/rag.pdf"
# 使用Unstructured加载并解析PDF文档
# elements = partition(
# filename=pdf_path,
# content_type="application/pdf"
# )
# 使用partition_pdf加载并且解析PDF文档
elements = partition_pdf(
filename=pdf_path,
strategy="hi_res"
)
elements1 = partition_pdf(
filename=pdf_path,
strategy="fast"
)
elements2 = partition_pdf(
filename=pdf_path,
strategy="ocr_only"
)
# 打印解析结果
print(f"hi_res: 解析完成: {len(elements)} 个元素, {sum(len(str(e)) for e in elements)} 字符")
print(f"fast: 解析完成: {len(elements1)} 个元素, {sum(len(str(e)) for e in elements1)} 字符")
print(f"ocr_only: 解析完成: {len(elements2)} 个元素, {sum(len(str(e)) for e in elements2)} 字符")
# 统计元素类型
from collections import Counter
types = Counter(e.category for e in elements)
types1 = Counter(e.category for e in elements1)
types2 = Counter(e.category for e in elements2)
print(f"hi_res: 元素类型: {dict(types)}")
print(f"fast: 元素类型: {dict(types1)}")
print(f"ocr_only: 元素类型: {dict(types2)}")
# 显示所有元素
print("\n hi_res: 所有元素:")
for i, element in enumerate(elements, 1):
print(f"Element {i} ({element.category}):")
print(element)
print("=" * 60)
print("\n fast: 所有元素:")
for i, element in enumerate(elements1, 1):
print(f"Element {i} ({element.category}):")
print(element)
print("=" * 60)
print("\n ocr_only: 所有元素:")
for i, element in enumerate(elements, 1):
print(f"Element {i} ({element.category}):")
print(element)
print("=" * 60)
结果统计分析:
hi_res: 解析完成: 221 个元素, 8265 字符
fast: 解析完成: 279 个元素, 7500 字符
ocr_only: 解析完成: 137 个元素, 8282 字符
hi_res: 元素类型: {'Image': 22, 'UncategorizedText': 85, 'Header': 4, 'NarrativeText': 68, 'Table': 4, 'FigureCaption': 4, 'Title': 30, 'ListItem': 4}
fast: 元素类型: {'Header': 22, 'Title': 195, 'UncategorizedText': 41, 'NarrativeText': 3, 'Footer': 15, 'ListItem': 3}
ocr_only: 元素类型: {'UncategorizedText': 54, 'Title': 55, 'NarrativeText': 27, 'ListItem': 1}
具体打印的内容我同样只截取部分片段:
hi_res :
Element 1 (Image):
============================================================
Element 2 (UncategorizedText):
Bh fe
============================================================
Element 3 (UncategorizedText):
Se «6 Be BR 8H Me OE 6B CR OBS
============================================================
Element 4 (Header):
百度首页 登录 注册
============================================================
Element 5 (UncategorizedText):
0 0 Bai @ Bil | eme22n
============================================================
Element 6 (UncategorizedText):
x
============================================================
Element 7 (UncategorizedText):
进⼊词条
============================================================
Element 8 (UncategorizedText):
全站搜索
============================================================
Element 9 (UncategorizedText):
| mm
============================================================
Element 10 (NarrativeText):
近期有不法分子冒充百度百科官方人员,以删除词条为由威胁并敲诈相关企业。在此严正声明:百度百科是免费编辑平台,绝不存在收费代编服务,请勿上当受骗! 详情>>
fast(auto默认是它)
Element 1 (Header):
网页
============================================================
Element 2 (Header):
新闻
============================================================
Element 3 (Header):
贴吧
============================================================
Element 4 (Header):
知道
============================================================
Element 5 (Header):
网盘
============================================================
Element 6 (Header):
图片
============================================================
Element 7 (Header):
视频
============================================================
Element 8 (Header):
地图
============================================================
Element 9 (Header):
文库
============================================================
Element 10 (Header):
资讯
============================================================
Element 11 (Header):
采购
============================================================
Element 12 (Header):
百科
============================================================
Element 13 (Header):
百度首页 登录 注册
============================================================
Element 14 (Title):
检索增强生成
============================================================
Element 15 (Title):
进⼊词条
============================================================
Element 16 (Title):
全站搜索
============================================================
Element 17 (Title):
帮助
============================================================
Element 18 (Title):
近期有不法分子冒充百度百科官方人员,以删除词条为由威胁并敲诈相关企业。在此严正声明:百度百科是免费编辑平台,绝不存在收费代编服务,请勿上当受骗! 详情>>
ocr_only:
Element 1 (Image):
============================================================
Element 2 (UncategorizedText):
Bh fe
============================================================
Element 3 (UncategorizedText):
Se «6 Be BR 8H Me OE 6B CR OBS
============================================================
Element 4 (Header):
百度首页 登录 注册
============================================================
Element 5 (UncategorizedText):
0 0 Bai @ Bil | eme22n
============================================================
Element 6 (UncategorizedText):
x
============================================================
Element 7 (UncategorizedText):
进⼊词条
============================================================
Element 8 (UncategorizedText):
全站搜索
============================================================
Element 9 (UncategorizedText):
| mm
============================================================
Element 10 (NarrativeText):
近期有不法分子冒充百度百科官方人员,以删除词条为由威胁并敲诈相关企业。在此严正声明:百度百科是免费编辑平台,绝不存在收费代编服务,请勿上当受骗! 详情>>
hi_res使用 pdfplumber + OCR 的混合方式可以识别版面结构(表格、图片、标题、段落),结构化信息最完整(从结果统计也可以发现,其中hi_res对应识别出来的种类最多),但是从结构输出来看,其实并不理想甚至不如fast,其中ocr_only
也类似。
个人按照输入结果接受程度排序是 fast > hi_res > ocr_only。当然我觉得着也得看具体应用环境,如果对应标签或者版面结构没那么看重,我们就可以使用fast,如果对版面结构有高要求就可以使用hi_res。
2. 文本分块
2.1 分块的定义
文本分块(Text Chunking)是构建RAG流程的关键步骤。其核心原理是将加载后的长篇文档,切分成更小、更易于处理的单元。这些被切分出的文本块,是后续向量检索和模型处理的基本单位。
2.2 分块的意义
将文本分块的首要原因,是为了适应RAG系统中两个核心组件的硬性限制:
- 嵌入模型 (Embedding Model): 负责将文本块转换为向量。这类模型有严格的输入长度上限。例如,许多常用的嵌入模型(如
bge-base-zh-v1.5
)的上下文窗口为512个token。任何超出此限制的文本块在输入时都会被截断,导致信息丢失,生成的向量也无法完整代表原文的语义。因此,文本块的大小必须小于等于嵌入模型的上下文窗口。 - 大语言模型 (LLM): 负责根据检索到的上下文生成答案。LLM同样有上下文窗口限制(尽管通常比嵌入模型大得多,从几千到上百万token不等)。检索到的所有文本块,连同用户问题和提示词,都必须能被放入这个窗口中。如果单个块过大,可能会导致只能容纳少数几个相关的块,限制了LLM回答问题时可参考的信息广度。
因此,分块是确保文本能够被两个模型完整、有效处理的基础。
2.3 常见分块策略
LangChain
提供了丰富且易于使用的文本分割器(Text Splitters),下面将介绍几种最核心的策略
2.3.1 固定大小分块
下面是datawhale
对固定大小分块的描述:
这是最简单直接的分块方法。根据
LangChain
源码,这种方法的工作原理分为两个主要阶段:
- 按段落分割:
CharacterTextSplitter
采用默认分隔符"\n\n"
,使用正则表达式将文本按段落进行分割,通过_split_text_with_regex
函数处理。- 智能合并:调用继承自父类的
_merge_splits
方法,将分割后的段落依次合并。该方法会监控累积长度,当超过chunk_size
时形成新块,并通过重叠机制(chunk_overlap
)保持上下文连续性,同时在必要时发出超长块的警告。需要注意的是,
CharacterTextSplitter
实际实现的并非严格的固定大小分块。根据_merge_splits
源码逻辑,这种方法会:
- 优先保持段落完整性:只有当添加新段落会导致总长度超过
chunk_size
时,才会结束当前块- 处理超长段落:如果单个段落超过
chunk_size
,系统会发出警告但仍将其作为完整块保留- 应用重叠机制:通过
chunk_overlap
参数在块之间保持内容重叠,确保上下文连续性因此,LangChain的实现更准确地应该称为"段落感知的自适应分块",块大小会根据段落边界动态调整。
简单总结规律即为:
优先保障段落完整性,当段落没有填满当前块时才考虑再加入一段。如果再加入一段之后长度超过当前块,就会放弃加入这一段。同时这时下一个块的开头部分就会采取重叠机制,新块会从“上一块末尾的最后 chunk_overlap
个字符”开始,再加入新的段落。不过说实话这个重叠机制我还没搞懂,效果复现不出来啊
如果有超长段大于chunk的大小,仍然会将这个超长段作为单独的一块。
测试文本内容:
012345678901234567890,12345
012345,6789
0123456,789
ABCDEFGHIJKLMNOPQRSTUVWXYZ
01234567
测试代码:
from langchain.text_splitter import CharacterTextSplitter
from langchain_community.document_loaders import TextLoader
# 1. 文档加载
loader = TextLoader("../../data/C2/txt/test.txt", encoding="utf-8")
docs = loader.load()
# 2. 初始化固定大小分块器
text_splitter = CharacterTextSplitter(
chunk_size=20, # 每个块的大小
chunk_overlap=3 # 块之间的重叠大小
)
# 3. 执行分块
chunks = text_splitter.split_documents(docs)
# 4. 打印结果
print(f"文本被切分为 {len(chunks)} 个块。\n")
print("--- 前8个块内容示例 ---")
for i, chunk in enumerate(chunks[:8]):
print("=" * 60)
# chunk 是一个 Document 对象,需要访问它的 .page_content 属性来获取文本
print(f'块 {i+1} (长度: {len(chunk.page_content)}): "{chunk.page_content}"')
测试结果(块大小设置为20,重叠大小设置为3):
============================================================
块 1 (长度: 27): "012345678901234567890,12345"
============================================================
块 2 (长度: 11): "012345,6789"
============================================================
块 3 (长度: 11): "0123456,789"
============================================================
块 4 (长度: 26): "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
============================================================
块 5 (长度: 8): "01234567"
显然我们发现确实保留了超长段,但是未发现重叠机制,很奇怪???
这个疑问先放在这里
2.3.2 递归字符分块
根据datawhale
学习资料,递归字符分块定义如下:
在前面的章节中,已经尝试了使用
RecursiveCharacterTextSplitter
的默认配置来处理文档分块。现在让我们深入了解RecursiveCharacterTextSplitter
的实现。这种分块器通过分隔符层级递归处理,相对与固定大小分块,改善了超长文本的处理效果。算法流程:
寻找有效分隔符: 从分隔符列表中从前到后遍历,找到第一个在当前文本中存在的分隔符。如果都不存在,使用最后一个分隔符(通常是空字符串
""
)。
- 切分与分类处理
使用选定的分隔符切分文本,然后遍历所有片段:
如果片段不超过块大小: 暂存到
_good_splits
中,准备合并
- 如果片段超过块大小
- 首先,将暂存的合格片段通过
_merge_splits
合并成块- 然后,检查是否还有剩余分隔符:
- 有剩余分隔符: 递归调用
_split_text
继续分割- 无剩余分隔符: 直接保留为超长块
最终处理: 将剩余的暂存片段合并成最后的块
实现细节:
- 批处理机制: 先收集所有合格片段(
_good_splits
),遇到超长片段时才触发合并操作。- 递归终止条件: 关键在于
if not new_separators
判断。当分隔符用尽时(new_separators
为空),停止递归,直接保留超长片段。确保算法不会无限递归。与固定大小分块的关键差异:
- 固定大小分块遇到超长段落时只能发出警告并保留。
- 递归分块会继续使用更细粒度的分隔符(句子→单词→字符)直到满足大小要求。
个人感觉实际区别在于递归分块只要分隔符列表设置的合理,就保证不会出现超长段的情况。接下来是实际代码效果展示
测试文本内容:
012345678901234567890,12345
012345,6789
0123456,789
ABCDEFGHIJKLMNOPQRSTUVWXYZ
01234567
测试代码:
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import TextLoader
loader = TextLoader("../../data/C2/txt/test.txt")
docs = loader.load()
text_splitter = RecursiveCharacterTextSplitter(
# 针对中英文混合文本,定义一个更全面的分隔符列表
separators=["\n\n", "\n", "。", ",", " ", ""], # 按顺序尝试分割
chunk_size=20,
chunk_overlap=3
)
chunks = text_splitter.split_documents(docs)
print(f"文本被切分为 {len(chunks)} 个块。\n")
print("--- 前8个块内容示例 ---")
for i, chunk in enumerate(chunks[:8]):
print("=" * 60)
print(f'块 {i+1} (长度: {len(chunk.page_content)}): "{chunk.page_content}"')
测试结果(块大小设置为20,重叠大小设置为3):
============================================================
块 1 (长度: 20): "01234567890123456789"
============================================================
块 2 (长度: 10): "7890,12345"
============================================================
块 3 (长度: 11): "012345,6789"
============================================================
块 4 (长度: 11): "0123456,789"
============================================================
块 5 (长度: 19): "ABCDEFGHIJKLMNOPQRS"
============================================================
块 6 (长度: 10): "QRSTUVWXYZ"
============================================================
块 7 (长度: 8): "01234567"
特别提出,这里的分割符配置除了我们设置的默认分隔符,还支持中文,日文等等。
同时考虑到我们平时不仅要对文本进行分块,可能还需要对代码分段,为了支持编程语言的,RecursiveCharacterTextSplitter
能够针对特定的编程语言(如Python, Java等)使用预设的、更符合代码结构的分隔符。它们通常包含语言的顶级语法结构(如类、函数定义)和次级结构(如控制流语句),以实现更符合代码逻辑的分割。
# 针对代码文档的优化分隔符
splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.PYTHON, # 支持Python、Java、C++等
chunk_size=500,
chunk_overlap=50
)
总结:
分块器首先尝试使用最高优先级的分隔符(如段落标记)来切分文本。如果切分后的块仍然过大,会继续对这个大块应用下一优先级分隔符(如句号),如此循环往复,直到块满足大小限制。
2.3.3 语义分块
这个部分因为都是调用现有的模型,同时还是高度封装好的,所以我也不太清楚具体的实习。先将对应介绍记录如下:
语义分块(Semantic Chunking)是一种更智能的方法,这种方法不依赖于固定的字符数或预设的分隔符,而是尝试根据文本的语义内涵来切分。其核心是:在语义主题发生显著变化的地方进行切分。这使得每个分块都具有高度的内部语义一致性。
LangChain 提供了
langchain_experimental.text_splitter.SemanticChunker
来实现这一功能。实现原理
SemanticChunker
的工作流程可以概括为以下几个步骤:
- 句子分割 (Sentence Splitting): 首先,使用标准的句子分割规则(例如,基于句号、问号、感叹号)将输入文本拆分成一个句子列表。
- 上下文感知嵌入 (Context-Aware Embedding): 这是
SemanticChunker
的一个关键设计。该分块器不是对每个句子独立进行嵌入,而是通过buffer_size
参数(默认为1)来捕捉上下文信息。对于列表中的每一个句子,这种方法会将其与前后各buffer_size
个句子组合起来,然后对这个临时的、更长的组合文本进行嵌入。这样,每个句子最终得到的嵌入向量就融入了其上下文的语义。- 计算语义距离 (Distance Calculation): 计算每对相邻句子的嵌入向量之间的余弦距离。这个距离值量化了两个句子之间的语义差异——距离越大,表示语义关联越弱,跳跃越明显。
- 识别断点 (Breakpoint Identification):
SemanticChunker
会分析所有计算出的距离值,并根据一个统计方法(默认为percentile
)来确定一个动态阈值。例如,它可能会将所有距离中第95百分位的值作为切分阈值。所有距离大于此阈值的点,都被识别为语义上的“断点”。- 合并成块 (Merging into Chunks): 最后,根据识别出的所有断点位置,将原始的句子序列进行切分,并将每个切分后的部分内的所有句子合并起来,形成一个最终的、语义连贯的文本块。
断点识别方法 (
breakpoint_threshold_type
)如何定义“显著的语义跳跃”是语义分块的关键。
SemanticChunker
提供了几种基于统计的方法来识别断点:
percentile
(百分位法 – 默认方法):
- 逻辑: 计算所有相邻句子的语义差异值,并将这些差异值进行排序。当一个差异值超过某个百分位阈值时,就认为该差异值是一个断点。
- 参数:
breakpoint_threshold_amount
(默认为95
),表示使用第95个百分位作为阈值。这意味着,只有最显著的5%的语义差异点会被选为切分点。standard_deviation
(标准差法):
- 逻辑: 计算所有差异值的平均值和标准差。当一个差异值超过“平均值 + N * 标准差”时,被视为异常高的跳跃,即断点。
- 参数:
breakpoint_threshold_amount
(默认为3
),表示使用3倍标准差作为阈值。interquartile
(四分位距法):
- 逻辑: 使用统计学中的四分位距(IQR)来识别异常值。当一个差异值超过
Q3 + N * IQR
时,被视为断点。- 参数:
breakpoint_threshold_amount
(默认为1.5
),表示使用1.5倍的IQR。gradient
(梯度法):
- 逻辑: 这是一种更复杂的方法。它首先计算差异值的变化率(梯度),然后对梯度应用百分位法。对于那些句子间语义联系紧密、差异值普遍较低的文本(如法律、医疗文档)特别有效,因为这种方法能更好地捕捉到语义变化的“拐点”。
- 参数:
breakpoint_threshold_amount
(默认为95
)。
但是这里我测试了一下实际效果在我之前的txt文本上的无意义内容只会分成一段,这里有一个游戏角色介绍文档,我测试了它的效果
测试文本内容:
# 蜂医
游戏《三角洲行动》中的支援型干员
蜂医是2024年琳琅天上发行的《三角洲行动》中的支援型干员之一,在早期版本是唯一一个支援型干员。
蜂医在游戏中能够使用战术装备“激素枪”:远程治疗队友或'自我治疗',还可以使用兵种道具“烟幕无人机”:释放长烟幕,和“蜂巢科技烟雾弹”:产生一团白色烟雾(使用激素枪对烟雾射击换变成绿色烟雾,可起到治疗作用),干员特长为“高效救援”:救援倒地队友时速度更快,在全面战场模式中约1.4秒就能救起队友,且被救起的队友能恢复更多生命值。在烽火地带中,还能够移除队友血量上限减少的负面效果。 \[1-2]****
* 中文名
罗伊•斯米
* 外文名
Roy smee \[2]**
* 别 名
罗伊、蜂医
* 性 别
男
- 登场作品
[三角洲行动](/item/%E4%B8%89%E8%A7%92%E6%B4%B2%E8%A1%8C%E5%8A%A8/63251542?fromModule=lemma_inlink)
- 生 日
2008年2月23日 \[3]**
- 身 高
176 cm \[3]**
- 体 重
75 kg \[3]**
## 目录
1. 1[角色设定](#1)
2. 2[角色定位](#2)
3. 3[技能](#3)
1) ▪[战术装备 - 激素枪](#3-1)
2) ▪[战术道具 - 烟幕无人机](#3-2)
3) ▪[战术道具 - 蜂巢科技烟雾弹](#3-3)
1. ▪[干员特长 - 高效救援](#3-4)
## 角色设定
三角洲行动:医疗角色蜂医!让你不再为血量担忧
蜂医是游戏中的一名战地医生,拥有丰富的参军履历。
蜂医在干员档案中标明他有一个妻子和女儿。
## 角色定位
-蜂医在团队中主要承担支援和治疗的职责,是团队生存能力的重要保障。他能在激烈的战斗中为队友提供及时的治疗,确保队友保持良好的战斗状态,还能利用烟幕技能为团队创造有利的战术环境,如掩护队友转移、推进或突袭等。
## 技能
### 战术装备 - 激素枪
-可发射多目标追踪子弹,能同时锁定并缓慢治疗多名友方干员,治疗期间还能抑制队友的疼痛感等异常状态。
-开镜时,仅会锁定一个目标。
-全面战场中,开镜瞄准敌人可以扣除敌人20滴血
-长按按键可以对自己进行治疗,在全面战场中,可以恢复自身血量。烽火地带模式中,可以消除自身如骨折的负面效果。
### 战术道具 - 烟幕无人机
-能控制手势感应无人机向前飞行并喷射烟幕,形成一条长15米的烟雾长廊
-无人机飞行时,长按开火键引导无人机转向,可改变烟幕走向,用来分割战场、掩护队友撤退或进攻。
### 战术道具 - 蜂巢科技烟雾弹
-投掷后会在区域内产生一团白色烟雾,可作常规烟雾掩护使用。
-用激素枪向其烟雾射击,能将其染色为绿色的治疗烟雾,使在烟雾中的队友获得缓慢回血效果。
### 干员特长 - 高效救援
-救援倒地队友时速度更快。
-在全面战场模式中大概1.4秒就能救起队友,且被救起的队友不会存在扣血效果,能够恢复至满血。
-在烽火地带中,还能够移除队友血量上限减少的负面效果。
参考资料
* 1
[](#sup-1)
[《三角洲行动》PC/移动今日上线](/reference/65245483/533aYdO6cr3_z3kATKCLyvz3ZnmWYNWt7OfaWrBzzqIPmGapB4rjFJkjrtQw87hkBETK4sgwNIZMxr6sWxla).腾讯游戏 \[引用日期2025-01-20]**
* 2
[](#sup-2)
[《三角洲行动》干员档案-罗伊·斯米——代号:蜂医](/reference/65245483/533aYdO6cr3_z3kATPGIza6jYXqSN9ul6LOGBuNzzqIPmGapB5nyTcYw6ds2_P5rHUXIv5Utdt8Rk-fleC1Eu50Uc-xtRdNUgnm8E26tkOqJqZtux9xHoIldUaQemfHvtlK6hkzJhvWX5WjulmPM4YewIHHGSYAdj4VcVsyHC6syegGFn0a7eVi3FOY).哔哩哔哩.2024-03-16 \[引用日期2025-01-30]**
* 3
[](#sup-3)
[三角洲干员背景小细节,蜂医的女儿是谁?](/reference/65245483/533aYdO6cr3_z3kATKLdz_32YXnEZ9qsu-LUAeNzzqIPmGapB5nyTcYw6ds2_P5rHUXIv5Utdt8Rk-fleC1En6pmD7hvN9drgnm8E26tkOqJqZtux9xHoIldW64QmbKk5wavij7ZiPKBqGy_yHrOuty2czaCG-pe1ZQcAc-PDfpldFDVl0PqLgmyQLGbxP34imiXDI6z_zKMaYGUK5M).哔哩哔哩.2025-01-26 \[引用日期2025-01-30]**
* 4
[](#sup-4)
[《三角洲行动》全新黑夜之子赛季4月17日正式上线](/reference/65245483/533aYdO6cr3_z3kATPSPmKqhNHmQNIivurSFV-ZzzqIPmGapB4rjFJkjrtQw87hkBETK4sgwNIZMxr6nWxIb8a5OJbh9XO1mwCo).腾讯游戏 \[引用日期2025-06-13]**
测试代码:
from langchain_experimental.text_splitter import SemanticChunker
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.document_loaders import TextLoader
embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-small-zh-v1.5",
model_kwargs={'device': 'cpu'},
encode_kwargs={'normalize_embeddings': True}
)
# 初始化 SemanticChunker
text_splitter = SemanticChunker(
embeddings,
breakpoint_threshold_type="percentile" # 也可以是 "standard_deviation", "interquartile", "gradient"
)
loader = TextLoader("../../data/C2/txt/game_role.txt", encoding="utf-8")
documents = loader.load()
docs = text_splitter.split_documents(documents)
print(f"文本被切分为 {len(docs)} 个块。\n")
print("--- 前2个块内容示例 ---")
for i, chunk in enumerate(docs[:2]):
print("=" * 60)
print(f'块 {i+1} (长度: {len(chunk.page_content)}):\n"{chunk.page_content}"')
测试结果:
参考资料
* 1
[](#sup-1)
[《三角洲行动》PC/移动今日上线](/reference/65245483/533aYdO6cr3_z3kATKCLyvz3ZnmWYNWt7OfaWrBzzqIPmGapB4rjFJkjrtQw87hkBETK4sgwNIZMxr6sWxla).腾讯游戏 \[引用日期2025-01-20]**
* 2
[](#sup-2)
[《三角洲行动》干员档案-罗伊·斯米——代号:蜂医](/reference/65245483/533aYdO6cr3_z3kATPGIza6jYXqSN9ul6LOGBuNzzqIPmGapB5nyTcYw6ds2_P5rHUXIv5Utdt8Rk-fleC1Eu50Uc-xtRdNUgnm8E26tkOqJqZtux9xHoIldUaQemfHvtlK6hkzJhvWX5WjulmPM4YewIHHGSYAdj4VcVsyHC6syegGFn0a7eVi3FOY).哔哩哔哩.2024-03-16 \[引用日期2025-01-30]**
* 3
[](#sup-3)
[三角洲干员背景小细节,蜂医的女儿是谁?](/reference/65245483/533aYdO6cr3_z3kATKLdz_32YXnEZ9qsu-LUAeNzzqIPmGapB5nyTcYw6ds2_P5rHUXIv5Utdt8Rk-fleC1En6pmD7hvN9drgnm8E26tkOqJqZtux9xHoIldW64QmbKk5wavij7ZiPKBqGy_yHrOuty2czaCG-pe1ZQcAc-PDfpldFDVl0PqLgmyQLGbxP34imiXDI6z_zKMaYGUK5M).哔哩哔哩.2025-01-26 \[引用日期2025-01-30]**
* 4
[](#sup-4)
[《三角洲行动》全新黑夜之子赛季4月17日正式上线](/reference/65245483/533aYdO6cr3_z3kATPSPmKqhNHmQNIivurSFV-ZzzqIPmGapB4rjFJkjrtQw87hkBETK4sgwNIZMxr6nWxIb8a5OJbh9XO1mwCo).腾讯游戏 \[引用日期2025-06-13]**
"
测试结果:
============================================================
块 1 (长度: 566):
"# 蜂医
游戏《三角洲行动》中的支援型干员
蜂医是2024年琳琅天上发行的《三角洲行动》中的支援型干员之一,在早期版本是唯一一个支援型干员。
蜂医在游戏中能够使用战术装备“激素枪”:远程治疗队友或'自我治疗',还可以使用兵种道具“烟幕无人机”:释放长烟幕,和“蜂巢科技烟雾弹”:产生一团白色烟雾(使用激素枪对烟雾射击换变成绿色烟雾,可起到治疗作用),干员特长为“高效救援”:救援倒地队友时速度更快,在全面战场模式中约1.4秒就能救起队友,且被救起的队友能恢复更多生命值。在烽火地带中,还能够移除队友血量上限减少的负面效果。 \[1-2]****
* 中文名
罗伊•斯米
* 外文名
Roy smee \[2]**
* 别 名
罗伊、蜂医
* 性 别
男
- 登场作品
[三角洲行动](/item/%E4%B8%89%E8%A7%92%E6%B4%B2%E8%A1%8C%E5%8A%A8/63251542?fromModule=lemma_inlink)
- 生 日
2008年2月23日 \[3]**
- 身 高
176 cm \[3]**
- 体 重
75 kg \[3]**
## 目录
1. 1[角色设定](#1)
2."
============================================================
块 2 (长度: 1776):
"2[角色定位](#2)
3. 3[技能](#3)
1) ▪[战术装备 - 激素枪](#3-1)
2) ▪[战术道具 - 烟幕无人机](#3-2)
3) ▪[战术道具 - 蜂巢科技烟雾弹](#3-3)
1. ▪[干员特长 - 高效救援](#3-4)
## 角色设定
三角洲行动:医疗角色蜂医!让你不再为血量担忧
蜂医是游戏中的一名战地医生,拥有丰富的参军履历。
蜂医在干员档案中标明他有一个妻子和女儿。
## 角色定位
-蜂医在团队中主要承担支援和治疗的职责,是团队生存能力的重要保障。他能在激烈的战斗中为队友提供及时的治疗,确保队友保持良好的战斗状态,还能利用烟幕技能为团队创造有利的战术环境,如掩护队友转移、推进或突袭等。
## 技能
### 战术装备 - 激素枪
-可发射多目标追踪子弹,能同时锁定并缓慢治疗多名友方干员,治疗期间还能抑制队友的疼痛感等异常状态。
-开镜时,仅会锁定一个目标。
-全面战场中,开镜瞄准敌人可以扣除敌人20滴血
-长按按键可以对自己进行治疗,在全面战场中,可以恢复自身血量。烽火地带模式中,可以消除自身如骨折的负面效果。
### 战术道具 - 烟幕无人机
-能控制手势感应无人机向前飞行并喷射烟幕,形成一条长15米的烟雾长廊
-无人机飞行时,长按开火键引导无人机转向,可改变烟幕走向,用来分割战场、掩护队友撤退或进攻。
### 战术道具 - 蜂巢科技烟雾弹
-投掷后会在区域内产生一团白色烟雾,可作常规烟雾掩护使用。
-用激素枪向其烟雾射击,能将其染色为绿色的治疗烟雾,使在烟雾中的队友获得缓慢回血效果。
### 干员特长 - 高效救援
-救援倒地队友时速度更快。
-在全面战场模式中大概1.4秒就能救起队友,且被救起的队友不会存在扣血效果,能够恢复至满血。
-在烽火地带中,还能够移除队友血量上限减少的负面效果。
参考资料
* 1
[](#sup-1)
[《三角洲行动》PC/移动今日上线](/reference/65245483/533aYdO6cr3_z3kATKCLyvz3ZnmWYNWt7OfaWrBzzqIPmGapB4rjFJkjrtQw87hkBETK4sgwNIZMxr6sWxla).腾讯游戏 \[引用日期2025-01-20]**
* 2
[](#sup-2)
[《三角洲行动》干员档案-罗伊·斯米——代号:蜂医](/reference/65245483/533aYdO6cr3_z3kATPGIza6jYXqSN9ul6LOGBuNzzqIPmGapB5nyTcYw6ds2_P5rHUXIv5Utdt8Rk-fleC1Eu50Uc-xtRdNUgnm8E26tkOqJqZtux9xHoIldUaQemfHvtlK6hkzJhvWX5WjulmPM4YewIHHGSYAdj4VcVsyHC6syegGFn0a7eVi3FOY).哔哩哔哩.2024-03-16 \[引用日期2025-01-30]**
* 3
[](#sup-3)
[三角洲干员背景小细节,蜂医的女儿是谁?](/reference/65245483/533aYdO6cr3_z3kATKLdz_32YXnEZ9qsu-LUAeNzzqIPmGapB5nyTcYw6ds2_P5rHUXIv5Utdt8Rk-fleC1En6pmD7hvN9drgnm8E26tkOqJqZtux9xHoIldW64QmbKk5wavij7ZiPKBqGy_yHrOuty2czaCG-pe1ZQcAc-PDfpldFDVl0PqLgmyQLGbxP34imiXDI6z_zKMaYGUK5M).哔哩哔哩.2025-01-26 \[引用日期2025-01-30]**
* 4
[](#sup-4)
[《三角洲行动》全新黑夜之子赛季4月17日正式上线](/reference/65245483/533aYdO6cr3_z3kATPSPmKqhNHmQNIivurSFV-ZzzqIPmGapB4rjFJkjrtQw87hkBETK4sgwNIZMxr6nWxIb8a5OJbh9XO1mwCo).腾讯游戏 \[引用日期2025-06-13]**
"
显然我们发现按照语义分块,每块大小没有限制,同时分块的边界不清晰,我感觉要求细粒度的场景下这种语义分块至少还得优化后才能使用。
2.3.4 其他分块
在最后参考资料还提到了基于markdown的分块方法MarkdownHeaderTextSplitter
,支持提取结构化的数据。
同时还提到了其他两种开源框架的分块处理Unstructured
和LlamaIndex
。
当然由于时间有限,这些留到后续再进一步探索。