食用指南
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