基于文本結構分塊 - 文本分塊(Text Splitting),RAG不可缺失的重要環節
在 RAG 的核心步驟中,有一個至關重要的步驟:“文本分塊(Text Splitting)”。
它的主要作用就是把一大段文本切分成更小、更合理的片段,這樣模型才能更好地理解、處理或者存儲這些內容。
如果一整篇文章不拆開,那 embedding 的顆粒度太粗,問答的時候很容易不準。所以切得好不好,直接影響最后答案的相關性和準確性。
最基本的分塊方法是根據文檔的長度進行拆分。這種簡單而有效的方法確保每個塊不會超過指定的大小限制。
基于長度拆分的主要好處:簡單明了的實現、一致的塊大小、易于適應不同模型的要求。缺點就是: 過于死板,忽視文本結構
1. 基于文本結構分塊
一般的文本會自然地組織成層次單位,如段落、句子和詞。
我們可以利用這種固有結構來指導我們的拆分策略,創建保持自然語言流暢、保持拆分內的語義連貫,并適應不同文本粒度水平的拆分。
LangChain 的 ??RecursiveCharacterTextSplitter?
? 實現了這個概念:
- ?
?RecursiveCharacterTextSplitter?
? 嘗試保持較大單位(例如段落)的完整性。 - 如果一個單位超出了塊大小,它將移到下一個層級(例如句子)。
- 如果必要,這個過程將繼續到單詞級別。
2. RecursiveCharacterTextSplitter的實現思路
2.1 挑選分隔符
- 從提供的分隔符列表中找到第一個在文本中存在的分隔符
- 如果找到合適的分隔符,將其后的所有分隔符保存起來,用于后續可能的遞歸分割
- 如果找不到任何分隔符,就使用最后一個分隔符(通常是空字符串)
舉個例子:
假設分隔符列表是 ["\n\n", "\n", " ", ""],對于文本 "Hello\nWorld" :
- 首先檢查 "\n\n" ,文本中不存在
- 然后檢查 "\n" ,文本中存在
- 選擇 "\n" 作為分隔符
- 保存 [" ", ""] 作為 new_separators ,供后續使用
separator = separators[-1]
new_separators = []
for i, _s in enumerate(separators):
_separator = _s if self._is_separator_regex else re.escape(_s)
if _s == "":
separator = _s
break
if re.search(_separator, text):
separator = _s
new_separators = separators[i + 1 :]
break
_separator = separator if self._is_separator_regex else re.escape(separator)
2.2 按分隔符分割文本
splits = _split_text_with_regex(text, _separator, self._keep_separator)
2.3 整理分割好的塊
- 對每個分割后的文本塊進行處理:
- 如果文本塊小于指定大小,添加到臨時列表
- 如果文本塊大于指定大小,且還有其他分隔符可用,則遞歸分割
- 如果文本塊大于指定大小,但沒有其他分隔符,則直接添加
- 合并所有符合大小要求的文本塊
- 返回最終的分割結果
for s in splits:
if self._length_function(s) < self._chunk_size:
_good_splits.append(s)
else:
if _good_splits:
merged_text = self._merge_splits(_good_splits, _separator)
final_chunks.extend(merged_text)
_good_splits = []
ifnot new_separators:
final_chunks.append(s)
else:
other_info = self._split_text(s, new_separators)
final_chunks.extend(other_info)
if _good_splits:
merged_text = self._merge_splits(_good_splits, _separator)
final_chunks.extend(merged_text)
return final_chunks
3. 代碼實現
from langchain.text_splitter import RecursiveCharacterTextSplitter
text = """
《誅仙》
作者:蕭鼎
第一集
序章
時間:不明,應該在很早很早以前。
地點:神州浩土。
自太古以來,人類眼見周遭世界,諸般奇異之事,電閃雷鳴,狂風暴雨,又有天災人禍,傷亡無數,哀鴻遍野,絕非人力所能為,所能抵擋。遂以為九天之上,有諸般神靈,九幽之下,亦是陰魂歸處,閻羅殿堂。
于是神仙之說,流傳于世。無數人類子民,誠心叩拜,向著自己臆想創造出的各種神明頂禮膜拜,祈福訴苦,香火鼎盛。
自古以來,凡人無不有一死。但世人皆惡死愛生,更有地府閻羅之說,平添了幾分苦懼,在此之下,遂有長生不死之說。
相較其它生靈物種,人類或在體質上處于劣勢,但萬物靈長,卻是絕無虛言。在追求長生的原動力下,一代代聰明才智之士,前赴后繼,投入畢生精力,苦苦鉆研。
至今為止,雖然真正意義上的長生不死仍未找到,卻有一些修真煉道之士參透些許天地造化,以凡人之身,掌握強橫力量,借助各般秘寶法器之力,竟可震撼天地,有雷霆之威。
而一些得道高深的前輩,更傳說已活上千年之久而不死。世上之人以為得道成仙,便有更多人投入修真煉道之路。
神州浩土,廣瀚無邊。唯有中原大地,最是豐美肥沃,天下人口十之八九聚居于此。而東南西北邊荒之地,山險水惡,多兇獸猛禽,多惡瘴毒物,亦多蠻族夷民,茹毛飲血,是以人跡罕至。而人間自古相傳,有洪荒遺種,殘存人世,藏于深山密谷,壽逾萬年,卻是無人得見。
時至今日,人間修真煉道之人,多如過江之鯽,數不勝數。又以神州浩土之廣闊,人間奇人異士之多,故修煉之法道林林總總,俱不相同。長生之法還未找到,彼此間卻逐漸有了門派之分,正邪之別。由之而起的門戶之見,勾心斗角乃至爭伐殺戮,在所多有。
當長生不死看起來那般遙遠而不可捉摸,修煉中所帶來的力量,便逐漸成了許多人的目標。
方今之世,正道大昌,邪魔退避。中原大地山靈水秀,人氣鼎盛,物產豐富,為正派諸家牢牢占據。其中尤以「青云門」、「天音寺」和「焚香谷」為三大支柱,是為領袖。
這個故事,便是從「青云門」開始的。
"""
text_splitter = RecursiveCharacterTextSplitter(chunk_size=150)
docs = text_splitter.create_documents([text])
for doc in docs:
print('-' * 50)
print(doc)
4. 拆分結果
通過觀察文本的分塊結果,可以看出 RecursiveCharacterTextSplitter 在 chunk_size=150 的設置下,將整個文本分成了7個完整的塊。
分割時優先考慮了段落間的自然分隔(\n\n),使每個塊都保持了相對獨立的主題。
這種分塊方式既保證了每塊內容的語義連貫性,又控制了文本長度在合理范圍內,為后續的文本處理和分析提供了良好的基礎。
--------------------------------------------------
page_cnotallow='《誅仙》
作者:蕭鼎
第一集
序章
時間:不明,應該在很早很早以前。
地點:神州浩土。
自太古以來,人類眼見周遭世界,諸般奇異之事,電閃雷鳴,狂風暴雨,又有天災人禍,傷亡無數,哀鴻遍野,絕非人力所能為,所能抵擋。遂以為九天之上,有諸般神靈,九幽之下,亦是陰魂歸處,閻羅殿堂。'
--------------------------------------------------
page_cnotallow='于是神仙之說,流傳于世。無數人類子民,誠心叩拜,向著自己臆想創造出的各種神明頂禮膜拜,祈福訴苦,香火鼎盛。
自古以來,凡人無不有一死。但世人皆惡死愛生,更有地府閻羅之說,平添了幾分苦懼,在此之下,遂有長生不死之說。'
--------------------------------------------------
page_cnotallow='相較其它生靈物種,人類或在體質上處于劣勢,但萬物靈長,卻是絕無虛言。在追求長生的原動力下,一代代聰明才智之士,前赴后繼,投入畢生精力,苦苦鉆研。'
--------------------------------------------------
page_cnotallow='至今為止,雖然真正意義上的長生不死仍未找到,卻有一些修真煉道之士參透些許天地造化,以凡人之身,掌握強橫力量,借助各般秘寶法器之力,竟可震撼天地,有雷霆之威。
而一些得道高深的前輩,更傳說已活上千年之久而不死。世上之人以為得道成仙,便有更多人投入修真煉道之路。'
--------------------------------------------------
page_cnotallow='神州浩土,廣瀚無邊。唯有中原大地,最是豐美肥沃,天下人口十之八九聚居于此。而東南西北邊荒之地,山險水惡,多兇獸猛禽,多惡瘴毒物,亦多蠻族夷民,茹毛飲血,是以人跡罕至。而人間自古相傳,有洪荒遺種,殘存人世,藏于深山密谷,壽逾萬年,卻是無人得見。'
--------------------------------------------------
page_cnotallow='時至今日,人間修真煉道之人,多如過江之鯽,數不勝數。又以神州浩土之廣闊,人間奇人異士之多,故修煉之法道林林總總,俱不相同。長生之法還未找到,彼此間卻逐漸有了門派之分,正邪之別。由之而起的門戶之見,勾心斗角乃至爭伐殺戮,在所多有。'
--------------------------------------------------
page_cnotallow='當長生不死看起來那般遙遠而不可捉摸,修煉中所帶來的力量,便逐漸成了許多人的目標。
方今之世,正道大昌,邪魔退避。中原大地山靈水秀,人氣鼎盛,物產豐富,為正派諸家牢牢占據。其中尤以「青云門」、「天音寺」和「焚香谷」為三大支柱,是為領袖。
這個故事,便是從「青云門」開始的。'
5. 圖形化顯示分塊
通過www.chunkviz.com可以以圖形化的方式看到分塊結果
總結
文本分塊不僅是技術實現的問題,更是影響 RAG 系統最終效果的核心策略。
簡單分塊雖易上手但效果有限,結構化遞歸分塊則在保留語義、提升相關性方面表現更優。
想要構建高質量問答系統,分塊方式絕不能隨便選,而是要結合文本特點和應用場景精細設計。
本文轉載自??AI取經路??,作者:AI取經路
