食用指南
LangChain 作为大语言模型应用开发框架,文本分割器是其核心组件之一,本文以此作为切入点,详细介绍文本分割的作用、策略、以及常见的文本切割器应用。考虑到篇幅过长,故拆分为上、中、下三篇,后续会在中篇介绍代码拆分与HTML拆分,下篇介绍基于语义拆分与TOKEN拆分。若存在任何内容上的遗漏或错误,恳请不吝赐教。
一、概述
文档分割通常是许多应用中至关重要的预处理步骤。它涉及将大型文本分解为更小、更易于管理的块。此过程具有多项优势,例如确保对不同文档长度进行一致处理、克服模型的输入大小限制以及提高检索系统中使用的文本表示的质量。有几种分割文档的策略,每种策略都有其自身的优势。
二、为什么要分割文档?
- 处理非均匀文档长度:真实世界的文档集合通常包含大小不一的文本。分割确保所有文档的处理一致。
- 克服模型限制:许多嵌入模型和语言模型都有最大输入大小约束。分割使我们能够处理原本会超出这些限制的文档。
- 提高表示质量:对于较长的文档,当它们试图捕获太多信息时,嵌入或其他表示的质量可能会下降。分割可以使每个部分的表示更集中和准确。
- 提高检索精度:在信息检索系统中,分割可以提高搜索结果的粒度,从而更精确地将查询与相关文档部分匹配。
- 优化计算资源:处理较小的文本块可以更节省内存,并允许更好地并行处理任务。
三、分割文档的策略
1、基于长度
最直观的策略是根据文档的长度进行分割。这种简单而有效的方法确保每个块不超过指定的尺寸限制。
基于长度分割的关键优势:
- 实施简单直接
- 块大小一致
- 易于适应不同的模型要求
基于长度的分割类型:
- 基于 Token:基于 token 数量分割文本,这在使用语言模型时非常有用。
- 基于字符:基于字符数分割文本,这在不同类型的文本中可能更一致。
使用 LangChain
的 CharacterTextSplitter
进行基于 token
分割的示例实现:
from langchain_text_splitters import CharacterTextSplitter
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(encoding_name="cl100k_base", chunk_size=100, chunk_overlap=0
)
texts = text_splitter.split_text(document)
2、基于文本结构
文本自然地组织成层次结构单元,例如段落、句子和单词。我们可以利用这种固有的结构来指导我们的分割策略,创建保持自然语言流畅性、在分割中保持语义连贯性并适应不同文本粒度级别的分割。
LangChain
的 RecursiveCharacterTextSplitter
实现了这个概念:
RecursiveCharacterTextSplitter
尝试保持较大的单元(例如,段落)完整。- 如果一个单元超过块大小,它将移动到下一个级别(例如,句子)。
- 如有必要,此过程将继续向下到单词级别。
这是一个示例用法:
from langchain_text_splitters import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=0)
texts = text_splitter.split_text(document)
3、基于文档结构
某些文档具有固有的结构,例如 HTML
、Markdown
或 JSON
文件。在这些情况下,根据文档的结构进行分割是有益的,因为它通常自然地将语义相关的文本分组。
基于结构分割的关键优势:
- 保留文档的逻辑组织
- 在每个块中维护上下文
- 对于检索或摘要等下游任务可能更有效
基于结构的分割示例:
- Markdown:基于标题(例如,#、##、###)分割
- HTML:使用标签分割
- JSON:按对象或数组元素分割
- 代码:按函数、类或逻辑块分割
4、基于语义含义
与以前的方法不同,基于语义的分割实际上考虑了文本的内容。虽然其他方法使用文档或文本结构作为语义含义的代理,但此方法直接分析文本的语义。有几种方法可以实现这一点,但从概念上讲,该方法是在文本含义发生重大变化时分割文本。
例如,我们可以使用滑动窗口方法生成嵌入,并比较嵌入以找到显着差异:
- 从前几个句子开始并生成嵌入。
- 移动到下一组句子并生成另一个嵌入(例如,使用滑动窗口方法)。
- 比较嵌入以找到显着差异,这表明语义部分之间可能存在“断点”。
这项技术有助于创建语义上更连贯的块,从而可能提高检索或摘要等下游任务的质量。
四、RecursiveCharacterTextSplitter
RecursiveCharacterTextSplitter
这个 文本拆分器
是通用文本的推荐选择。它通过字符列表进行参数化。它尝试按顺序拆分它们,直到块足够小。默认列表是 ["\n\n", "\n", " ", ""]
。这具有尽可能将所有段落(然后是句子,然后是单词)保持在一起的效果,因为这些通常看起来是语义上最相关的文本片段。
1、通过字符递归拆分文本
明确以下两点:
- 文本如何拆分:通过字符列表。
- 块大小如何测量:通过字符数。
下面是一个使用例子:
pip install -qU langchain-text-splitters
from langchain_text_splitters import RecursiveCharacterTextSplitter# Load example document
with open("SpongeBobAndPatrick.txt") as f:sponge_bob_and_patrick = f.read()text_splitter = RecursiveCharacterTextSplitter(# Set a really small chunk size, just to show.chunk_size=100,chunk_overlap=20,length_function=len,is_separator_regex=False,
)# 创建 LangChain Document 对象(例如,用于下游任务)
texts = text_splitter.create_documents([sponge_bob_and_patrick])
print(texts[0])
print(texts[1])
page_content='SpongeBob SquarePants and Patrick Star are the iconic best friends from the beloved animated series'
page_content='animated series "SpongeBob SquarePants." SpongeBob is an energetic, optimistic yellow sea sponge'
要直接获取字符串内容,请使用 .split_text
:
text_splitter.split_text(sponge_bob_and_patrick)[:2]
['SpongeBob SquarePants and Patrick Star are the iconic best friends from the beloved animated series','animated series "SpongeBob SquarePants." SpongeBob is an energetic, optimistic yellow sea sponge']
让我们回顾一下上面为 RecursiveCharacterTextSplitter
设置的参数:
- chunk_size:块的最大大小,其中大小由
length_function
确定。 - chunk_overlap:块之间的目标重叠。重叠块有助于减轻上下文在块之间划分时信息丢失。
- length_function:确定块大小的函数。
- is_separator_regex:分隔符列表(默认为
["\n\n", "\n", " ", ""]
)是否应解释为正则表达式。
2、从没有词界符的语言拆分文本
一些书写系统没有 词界符
,例如中文、日语和泰语。使用默认分隔符列表 ["\n\n", "\n", " ", ""]
拆分文本可能会导致单词在块之间被拆分。为了保持单词完整,我们需要覆盖分隔符列表以包含其他标点符号:
- 添加 ASCII 句点".“,Unicode 全角 句点”."(用于中文文本)和 表意文字句点 “。”(用于日语和中文)
- 添加泰语、缅甸语、高棉语和日语中使用的
零宽度空格
。 - 添加 ASCII 逗号",“,Unicode 全角逗号”,“和 Unicode 表意文字逗号”、"
text_splitter = RecursiveCharacterTextSplitter(separators=["\n\n","\n"," ",".",",","\u200b", # Zero-width space"\uff0c", # Fullwidth comma"\u3001", # Ideographic comma"\uff0e", # Fullwidth full stop"\u3002", # Ideographic full stop"",],# Existing args# Set a really small chunk size, just to show.chunk_size=100,chunk_overlap=20,length_function=len,is_separator_regex=False,
)# Load example document
with open("海绵宝宝故事.txt") as f:sponge_bob_and_patrick = f.read()texts = text_splitter.create_documents([sponge_bob_and_patrick])
text_splitter.split_text(sponge_bob_and_patrick)[:2]
['有一次海绵宝宝和派大星决定玩"交换身份"游戏。派大星穿上海绵宝宝的方形裤子和领带,而海绵宝宝则把自己揉成星星形状。他们互相模仿对方说话方式来到蟹堡王上班,结果:','派大星版的"海绵宝宝"把所有的蟹堡都做成了星星形状,还理直气壮地说"这样更好吃";\n当章鱼哥质疑时,他直接躺在地上装死;']
五、CharacterTextSplitter
这是最简单的方法。这个 分割器
基于给定的字符序列进行分割,默认为 "\n\n"
。块长度按字符数衡量。
明确以下两点:
- 文本分割方式:按单个字符分隔符。
- 块大小的衡量方式:按字符数。
下面是一个使用例子:
pip install -qU langchain-text-splitters
from langchain_text_splitters import CharacterTextSplitter# Load an example document
with open("SpongeBobAndPatrick.txt") as f:sponge_bob_and_patrick = f.read()text_splitter = CharacterTextSplitter(separator="\n\n",chunk_size=1000,chunk_overlap=200,length_function=len,is_separator_regex=False,
)# 创建 LangChain Document 对象(例如,用于下游任务)
texts = text_splitter.create_documents([sponge_bob_and_patrick])
print(texts[0])
page_content='SpongeBob SquarePants and Patrick Star are the iconic best friends from the beloved animated series "SpongeBob SquarePants." SpongeBob is an energetic, optimistic yellow sea sponge who lives in a pineapple under the sea in the underwater city of Bikini Bottom. He works as a fry cook at the Krusty Krab and absolutely loves his job. His childlike enthusiasm and naivety often lead him into hilarious misadventures.'
使用 .create_documents
将与每个文档关联的元数据传播到输出块:
metadatas = [{"document": 1}, {"document": 2}]
documents = text_splitter.create_documents([sponge_bob_and_patrick, sponge_bob_and_patrick], metadatas=metadatas
)
print(documents[0]) # metadata={'document': 1}
print(documents[1]) # metadata={'document': 1}
print(documents[2]) # metadata={'document': 2}
print(documents[3]) # metadata={'document': 2}
page_content='SpongeBob SquarePants and Patrick Star are the iconic best friends from the beloved animated series "SpongeBob SquarePants." SpongeBob is an energetic, optimistic yellow sea sponge who lives in a pineapple under the sea in the underwater city of Bikini Bottom. He works as a fry cook at the Krusty Krab and absolutely loves his job. His childlike enthusiasm and naivety often lead him into hilarious misadventures.' metadata={'document': 1}
page_content='Patrick is SpongeBob's dim-witted but lovable best friend - a pink starfish who lives under a rock. Despite his lack of common sense, Patrick always supports SpongeBob through thick and thin. Their friendship is the heart of the show, showcasing unconditional loyalty and the joy of simple pleasures. Whether they're jellyfishing, blowing bubbles, or getting into trouble with Squidward, their antics never fail to entertain. Together, this dynamic duo represents the perfect balance of SpongeBob's energetic optimism and Patrick's laid-back simplicity, making them one of the most memorable cartoon pairs in television history.' metadata={'document': 1}
page_content='SpongeBob SquarePants and Patrick Star are the iconic best friends from the beloved animated series "SpongeBob SquarePants." SpongeBob is an energetic, optimistic yellow sea sponge who lives in a pineapple under the sea in the underwater city of Bikini Bottom. He works as a fry cook at the Krusty Krab and absolutely loves his job. His childlike enthusiasm and naivety often lead him into hilarious misadventures.' metadata={'document': 2}
page_content='Patrick is SpongeBob's dim-witted but lovable best friend - a pink starfish who lives under a rock. Despite his lack of common sense, Patrick always supports SpongeBob through thick and thin. Their friendship is the heart of the show, showcasing unconditional loyalty and the joy of simple pleasures. Whether they're jellyfishing, blowing bubbles, or getting into trouble with Squidward, their antics never fail to entertain. Together, this dynamic duo represents the perfect balance of SpongeBob's energetic optimism and Patrick's laid-back simplicity, making them one of the most memorable cartoon pairs in television history.' metadata={'document': 2}
使用 .split_text
直接获取字符串内容:
text_splitter.split_text(sponge_bob_and_patrick)[0]
'SpongeBob SquarePants and Patrick Star are the iconic best friends from the beloved animated series "SpongeBob SquarePants." SpongeBob is an energetic, optimistic yellow sea sponge who lives in a pineapple under the sea in the underwater city of Bikini Bottom. He works as a fry cook at the Krusty Krab and absolutely loves his job. His childlike enthusiasm and naivety often lead him into hilarious misadventures.'
六、MarkdownHeaderTextSplitter
下面是一个按标题拆分 Markdown
的示例。
1、动机
许多聊天或问答应用涉及在嵌入和向量存储之前对输入文档进行分块,而这些分块通常旨在将具有共同上下文的文本放在一起。考虑到这一点,我们可能希望特别尊重文档本身的结构。例如,markdown
文件按标题组织,在特定标题组中创建块是一个直观的想法。为了解决这个挑战,我们可以使用 MarkdownHeaderTextSplitter
,这将按指定的标题集拆分 markdown
文件。
例如,如果我们想拆分此 markdown
:
md = """
# 派大星角色特点详解## 基本特征
- **外形**:粉红色五角海星,蓝色短裤,无上衣
- **居住地**:比奇堡的石头屋
- **职业**:无固定工作## 性格特质
1. **天真单纯** - 思维极其简单直接- 经常说出富有哲理的无心之言- 对复杂事物理解困难2. **乐观豁达** - 永远保持快乐心态- 经典台词:"我准备好了!"- 把失败当作新游戏的开始
"""
我们可以指定要拆分的标题:
[("#", "Header 1"),("##", "Header 2")]
内容按公共标题分组或拆分:
{'content': '- **外形**:粉红色五角海星,蓝色短裤,无上衣\n- **居住地**:比奇堡的石头屋\n- **职业**:无固定工作', 'metadata': {'Header 1': '派大星角色特点详解', 'Header 2': '基本特征'}}
{'content': '1. **天真单纯**\n- 思维极其简单直接\n- 经常说出富有哲理的无心之言\n- 对复杂事物理解困难 \n2. **乐观豁达**\n- 永远保持快乐心态\n- 经典台词:"我准备好了!"\n- 把失败当作新游戏的开始'' , 'metadata': {'Header 1': '派大星角色特点详解', 'Header 2': '性格特质'}}
让我们看看下面的一些示例。
2、基本用法
pip install -qU langchain-text-splitters
from langchain_text_splitters import MarkdownHeaderTextSplittermarkdown_document = """
# 派大星角色特点详解## 基本特征
- **外形**:粉红色五角海星,蓝色短裤,无上衣
- **居住地**:比奇堡的石头屋
- **职业**:无固定工作## 性格特质
1. **天真单纯** - 思维极其简单直接- 经常说出富有哲理的无心之言- 对复杂事物理解困难2. **乐观豁达** - 永远保持快乐心态- 经典台词:"我准备好了!"- 把失败当作新游戏的开始
"""headers_to_split_on = [("#", "Header 1"),("##", "Header 2"),
]markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on)
md_header_splits = markdown_splitter.split_text(markdown_document)
md_header_splits
[Document(metadata={'Header 1': '派大星角色特点详解', 'Header 2': '基本特征'}, page_content='- **外形**:粉红色五角海星,蓝色短裤,无上衣\n- **居住地**:比奇堡的石头屋\n- **职业**:无固定工作'),Document(metadata={'Header 1': '派大星角色特点详解', 'Header 2': '性格特质'}, page_content='1. **天真单纯**\n- 思维极其简单直接\n- 经常说出富有哲理的无心之言\n- 对复杂事物理解困难 \n2. **乐观豁达**\n- 永远保持快乐心态\n- 经典台词:"我准备好了!"\n- 把失败当作新游戏的开始')]
type(md_header_splits[0])
langchain_core.documents.base.Document
默认情况下,MarkdownHeaderTextSplitter
会从输出块的内容中剥离正在拆分的标题。可以通过设置 strip_headers = False
来禁用此功能。
markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on, strip_headers=False)
md_header_splits = markdown_splitter.split_text(markdown_document)
md_header_splits
[Document(metadata={'Header 1': '派大星角色特点详解', 'Header 2': '基本特征'}, page_content='# 派大星角色特点详解 \n## 基本特征\n- **外形**:粉红色五角海星,蓝色短裤,无上衣\n- **居住地**:比奇堡的石头屋\n- **职业**:无固定工作'),Document(metadata={'Header 1': '派大星角色特点详解', 'Header 2': '性格特质'}, page_content='## 性格特质\n1. **天真单纯**\n- 思维极其简单直接\n- 经常说出富有哲理的无心之言\n- 对复杂事物理解困难 \n2. **乐观豁达**\n- 永远保持快乐心态\n- 经典台词:"我准备好了!"\n- 把失败当作新游戏的开始')]
注意:默认的
MarkdownHeaderTextSplitter
会剥离空格和换行符。要保留Markdown
文档的原始格式,请查看ExperimentalMarkdownSyntaxTextSplitter
。
3、将 Markdown 行作为单独的文档返回
默认情况下,MarkdownHeaderTextSplitter
根据 headers_to_split_on
中指定的标题聚合行。我们可以通过指定 return_each_line
来禁用此功能。
markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on,return_each_line=True,
)
md_header_splits = markdown_splitter.split_text(markdown_document)
md_header_splits
[Document(metadata={'Header 1': '派大星角色特点详解', 'Header 2': '基本特征'}, page_content='- **外形**:粉红色五角海星,蓝色短裤,无上衣\n- **居住地**:比奇堡的石头屋\n- **职业**:无固定工作'),Document(metadata={'Header 1': '派大星角色特点详解', 'Header 2': '性格特质'}, page_content='1. **天真单纯**\n- 思维极其简单直接\n- 经常说出富有哲理的无心之言\n- 对复杂事物理解困难'),Document(metadata={'Header 1': '派大星角色特点详解', 'Header 2': '性格特质'}, page_content='2. **乐观豁达**\n- 永远保持快乐心态\n- 经典台词:"我准备好了!"\n- 把失败当作新游戏的开始')]
请注意,这里的标题信息保留在每个文档的元数据中。
4、约束块大小
在每个 markdown
组中,我们可以应用我们想要的任何文本拆分器,例如 RecursiveCharacterTextSplitter
,它允许进一步控制块大小。
markdown_document = """
# 派大星角色特点详解## 基本特征
- **外形**:粉红色五角海星,蓝色短裤,无上衣
- **居住地**:比奇堡的石头屋
- **职业**:无固定工作## 性格特质
1. **天真单纯** - 思维极其简单直接- 经常说出富有哲理的无心之言- 对复杂事物理解困难2. **乐观豁达** - 永远保持快乐心态- 经典台词:"我准备好了!"- 把失败当作新游戏的开始
"""headers_to_split_on = [("#", "Header 1"),("##", "Header 2"),
]# Step1、MD splits
markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on, strip_headers=False
)
md_header_splits = markdown_splitter.split_text(markdown_document)# Step2、Char-level splits
from langchain_text_splitters import RecursiveCharacterTextSplitterchunk_size = 250
chunk_overlap = 30
text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap
)# Split
splits = text_splitter.split_documents(md_header_splits)
splits
[Document(metadata={'Header 1': '派大星角色特点详解', 'Header 2': '基本特征'}, page_content='# 派大星角色特点详解 \n## 基本特征\n- **外形**:粉红色五角海星,蓝色短裤,无上衣\n- **居住地**:比奇堡的石头屋\n- **职业**:无固定工作'),Document(metadata={'Header 1': '派大星角色特点详解', 'Header 2': '性格特质'}, page_content='## 性格特质\n1. **天真单纯**\n- 思维极其简单直接\n- 经常说出富有哲理的无心之言\n- 对复杂事物理解困难 \n2. **乐观豁达**\n- 永远保持快乐心态\n- 经典台词:"我准备好了!"\n- 把失败当作新游戏的开始')]
七、RecursiveJsonSplitter
此 json
分割器拆分 json
数据,同时允许控制 chunk
大小。它深度优先遍历 json
数据并构建更小的 json chunk
。它尝试保持嵌套 json
对象完整,但如果需要将 chunk
保持在 min_chunk_size
和 max_chunk_size
之间,则会分割它们。
如果值不是嵌套 json
,而是一个非常大的字符串,则该字符串不会被分割。如果需要对 chunk
大小进行硬性限制,请考虑将此分割器与这些 chunk
上的 Recursive Text
分割器组合使用。有一个可选的预处理步骤来分割列表,首先将列表转换为 json(dict)
,然后像这样分割它们。
明确以下两点:
- 文本分割方式:json 值。
- chunk 大小的度量方式:字符数。
pip install -qU langchain-text-splitters
首先我们加载一些 json
数据:
import jsonimport requests# This is a large nested json object and will be loaded as a python dict
json_data = requests.get("https://api.smith.langchain.com/openapi.json").json()
1、基本用法
指定 max_chunk_size
以约束 chunk
大小:
from langchain_text_splitters import RecursiveJsonSplittersplitter = RecursiveJsonSplitter(max_chunk_size=300)
要获取 json chunk
,请使用 .split_json
方法:
# Recursively split json data - If you need to access/manipulate the smaller json chunks
json_chunks = splitter.split_json(json_data=json_data)for chunk in json_chunks[:3]:print(chunk)
{'openapi': '3.1.0', 'info': {'title': 'LangSmith', 'version': '0.1.0'}, 'paths': {'/api/v1/sessions/{session_id}/dashboard': {'post': {'tags': ['tracer-sessions'], 'summary': 'Get Tracing Project Prebuilt Dashboard', 'description': 'Get a prebuilt dashboard for a tracing project.'}}}}
{'paths': {'/api/v1/sessions/{session_id}/dashboard': {'post': {'operationId': 'get_tracing_project_prebuilt_dashboard_api_v1_sessions__session_id__dashboard_post', 'security': [{'API Key': []}, {'Tenant ID': []}, {'Bearer Auth': []}]}}}}
{'paths': {'/api/v1/sessions/{session_id}/dashboard': {'post': {'parameters': [{'name': 'session_id', 'in': 'path', 'required': True, 'schema': {'type': 'string', 'format': 'uuid', 'title': 'Session Id'}}, {'name': 'accept', 'in': 'header', 'required': False, 'schema': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Accept'}}]}}}}
要获取 LangChain Document
对象(例如,用于下游任务),请使用 .create_documents
方法:
# The splitter can also output documents
docs = splitter.create_documents(texts=[json_data])for doc in docs[:3]:print(doc)
page_content='{"openapi": "3.1.0", "info": {"title": "LangSmith", "version": "0.1.0"}, "paths": {"/api/v1/sessions/{session_id}/dashboard": {"post": {"tags": ["tracer-sessions"], "summary": "Get Tracing Project Prebuilt Dashboard", "description": "Get a prebuilt dashboard for a tracing project."}}}}'
page_content='{"paths": {"/api/v1/sessions/{session_id}/dashboard": {"post": {"operationId": "get_tracing_project_prebuilt_dashboard_api_v1_sessions__session_id__dashboard_post", "security": [{"API Key": []}, {"Tenant ID": []}, {"Bearer Auth": []}]}}}}'
page_content='{"paths": {"/api/v1/sessions/{session_id}/dashboard": {"post": {"parameters": [{"name": "session_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Session Id"}}, {"name": "accept", "in": "header", "required": false, "schema": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Accept"}}]}}}}'
或使用 .split_text
直接获取字符串内容:
texts = splitter.split_text(json_data=json_data)print(texts[0])
print(texts[1])
{"openapi": "3.1.0", "info": {"title": "LangSmith", "version": "0.1.0"}, "paths": {"/api/v1/sessions/{session_id}/dashboard": {"post": {"tags": ["tracer-sessions"], "summary": "Get Tracing Project Prebuilt Dashboard", "description": "Get a prebuilt dashboard for a tracing project."}}}}
{"paths": {"/api/v1/sessions/{session_id}/dashboard": {"post": {"operationId": "get_tracing_project_prebuilt_dashboard_api_v1_sessions__session_id__dashboard_post", "security": [{"API Key": []}, {"Tenant ID": []}, {"Bearer Auth": []}]}}}}
2、约束列表内容的 chunk 大小
注意,此示例中的一个 chunk
大于指定的 max_chunk_size 300
。查看其中一个较大的 chunk
(第八个为 469),我们看到那里有一个列表对象:
print([len(text) for text in texts][:10])
print()
print(texts[8]) # chunk 469
[286, 238, 344, 207, 227, 224, 231, 126, 469, 210]{"paths": {"/api/v1/sessions/{session_id}": {"get": {"parameters": [{"name": "session_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Session Id"}}, {"name": "include_stats", "in": "query", "required": false, "schema": {"type": "boolean", "default": false, "title": "Include Stats"}}, {"name": "accept", "in": "header", "required": false, "schema": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Accept"}}]}}}}
这是因为默认情况下,json
分割器不分割列表。
我们可以指定 convert_lists=True
预处理 json
,将列表内容转换为以 index:item
作为 key:val
键值对的字典。
texts = splitter.split_text(json_data=json_data, convert_lists=True)
让我们看一下 chunk
的大小,现在它们都在最大值以下:
print([len(text) for text in texts][:10])
[291, 253, 214, 232, 207, 227, 224, 236, 141, 203]
列表已转换为字典,但即使分成多个 chunk
,也保留了所有需要的上下文信息:
print(texts[8])
print(texts[9])
print(texts[10])
{"paths": {"/api/v1/sessions/{session_id}": {"get": {"security": {"0": {"API Key": {}}, "1": {"Tenant ID": {}}, "2": {"Bearer Auth": {}}}}}}}
{"paths": {"/api/v1/sessions/{session_id}": {"get": {"parameters": {"0": {"name": "session_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Session Id"}}}}}}}
{"paths": {"/api/v1/sessions/{session_id}": {"get": {"parameters": {"1": {"name": "include_stats", "in": "query", "required": false, "schema": {"type": "boolean", "default": false, "title": "Include Stats"}}}}}}}
补充:中文字符显示问题
默认情况下,RecursiveJsonSplitter
类中的 create_documents
方法和 split_text
方法参数 ensure_ascii
都设置为 True
,这意味着在输出中所有 非ASCII字符
都会被转义成\uXXXX
格式的序列。这种行为确保了输出的 JSON
字符串只包含 ASCII字符
,从而可以被任何 JSON
解析器正确解析。
当我们希望在 JSON
字符串中保留 非ASCII字符
(如中文)时,将 ensure_ascii
设置为False
就可以直接输出原始的 非ASCII文本
而不是转义序列。
参考文档
- https://python.langchain.ac.cn/docs/concepts/text_splitters/
- https://python.langchain.ac.cn/docs/how_to/recursive_text_splitter/
- https://python.langchain.ac.cn/docs/how_to/character_text_splitter/
- https://python.langchain.ac.cn/docs/how_to/markdown_header_metadata_splitter/
- https://python.langchain.ac.cn/docs/how_to/recursive_json_splitter/
- https://python.langchain.com/api_reference/text_splitters/json/langchain_text_splitters.json.RecursiveJsonSplitter.html#langchain_text_splitters.json.RecursiveJsonSplitter.create_documents