RAG應用如何進行有效的文本切分
在RAG(檢索增強生成,Retrieval-Augmented Generation)應用中,文本分塊(Text Chunking)是連接“知識存儲”與“檢索-生成”的核心預處理步驟,其重要性體現在對檢索效率、相關性、生成質量及系統靈活性的多維度影響。
圖片
首先松哥和大家討論第一個問題,就是為什么我們要重視文本切分。
一、為什么文本切分很重要
1.1 提升檢索相關性:精準匹配用戶需求
RAG 的核心是“先檢索、后生成”,而檢索的本質是從知識庫中找到與用戶查詢語義最相關的信息。文本分塊直接影響檢索的精準度:
- 若文本未分塊(或塊過大),單個塊可能包含多個無關主題。例如,一篇同時討論“人工智能倫理”和“機器學習算法”的文章,若作為一個整塊存儲,當用戶查詢“人工智能倫理”時,檢索結果會包含大量“算法”的冗余信息,干擾相關性判斷。
- 合理分塊(如按段落、主題或邏輯單元分割)可使每個塊聚焦單一語義。例如,將上述文章拆分為“倫理爭議”“算法原理”兩個塊,用戶查詢時能精準匹配目標塊,減少無關信息干擾。
1.2 優化檢索效率:降低計算成本與延遲
RAG 依賴向量數據庫存儲文本的向量表示(通過嵌入模型生成),檢索時需計算“用戶查詢向量”與“文本塊向量”的相似度。分塊對效率的影響體現在:
- 減少冗余計算:大塊文本的向量表示可能“平均化”細節信息(如長文檔中某句話的關鍵信息被稀釋),導致檢索時需對比更多無效內容。分塊后,每個塊的向量更“聚焦”,相似度計算更高效。
- 適配數據庫性能:向量數據庫的檢索速度與單個向量的處理成本相關。相同長度的文檔,分塊后單個塊的字符數更少,生成向量的計算量更低,且批量檢索時可通過并行處理提速(尤其對長文檔,如書籍、報告等)。
1.3 保障生成質量:為LLM提供精準上下文
LLM(大語言模型)生成回答時,依賴檢索到的文本塊作為“事實依據”。分塊質量直接影響輸入 LLM 的上下文有效性:
- 減少噪聲干擾:若檢索到的塊包含大量無關信息,LLM 可能被誤導(如生成與查詢無關的內容,或混淆不同主題的信息)。例如,用戶問“如何預防新冠”,若檢索到的塊同時包含“新冠預防”和“流感治療”,LLM 可能錯誤關聯兩者。
- 適配 LLM 上下文窗口:所有 LLM 均有最大輸入長度限制(如 GPT-3.5 的 4k tokens)。若文本塊過大,可能導致檢索到的相關塊因長度超限被截斷,或擠壓 LLM 的生成空間。分塊后可靈活選擇多個小而精的塊,在窗口限制內納入更多關鍵信息。
一般我們通過嵌入模型來生成嵌入向量,而嵌入向量往往也有上下文窗口大小的限制。以常見的 bge-m3 為例,我們可以在介紹文檔中看到最大 token 數的限制。
圖片
當然,我們也可以直接在模型的配置文件中找到 token 上限:
圖片
上圖是在 tokenizer_config.json 文件中找到的配置,有的是在 config.json 配置文件中。
1.4 適配長文檔處理:突破“上下文窗口限制”
現實中,RAG 的知識庫常包含長文檔(如論文、手冊、法律條文等),其長度遠超 LLM 的上下文窗口(例如,一本 300 頁的技術手冊)。文本分塊是處理這類文檔的核心手段:
- 拆分后“分而治之”:通過分塊將長文檔拆解為多個子單元,每個子單元可獨立存儲和檢索。例如,將技術手冊按“安裝步驟”“故障排除”“維護指南”分塊,用戶查詢“如何排除啟動故障”時,僅需檢索“故障排除”相關塊,無需處理整本書。
- 保留細節信息:長文檔的整體向量可能丟失局部細節(如某段關鍵操作步驟),而分塊后每個子單元的向量能更精準地代表其細節,確保檢索時不遺漏重要信息。
1.5 平衡“完整性”與“聚焦性”:避免信息割裂
分塊的核心挑戰是“如何劃分邊界”,而合理分塊能在“信息完整”與“聚焦性”間找到平衡:
- 若塊太小(如單句),可能割裂語義邏輯(如拆分一個完整的論證過程),導致檢索到的信息碎片化,LLM 無法理解上下文關聯;
- 若塊太大(如整章),則回到“冗余信息”的問題。
針對這里第一小點,松哥舉個例子。
假設原文是一段關于"氣候變化對農業影響"的論證:
①. 全球平均氣溫上升會導致極端天氣事件增加。
②. 干旱和洪澇頻率上升會直接破壞農作物生長周期。
③. 這將導致主要糧食產區的產量下降。
④. 最終可能引發全球糧食供應緊張和價格波動。
這四句話構成一個完整的因果鏈論證:氣溫上升→極端天氣→作物受損→糧食危機。
如果按單句分塊存儲,當用戶查詢"氣候變化為何會導致糧食價格上漲"時:
- 系統可能只檢索到第 ④ 句(直接提到價格波動)
- 但缺失了 ①②③ 句的因果鋪墊,導致 LLM 無法理解"氣候變化→價格上漲"的完整邏輯鏈條
- 生成的回答可能變得牽強(如"糧食價格上漲可能與氣候變化有關"),而非基于完整論證的確定性結論(如"氣候變化通過影響農作物生長,最終導致價格波動")
這種情況下,過度細碎的分塊割裂了語義關聯,使檢索到的信息失去了上下文支撐,LLM自然無法生成邏輯完整的回答。
因此,優質分塊策略(如按語義、段落、標點符號分塊)需確保每個塊既能獨立表達完整信息,又不包含無關內容,這是 RAG 系統性能的關鍵前提。
二、文本該如何切分
那么文本該如何切?
雖然文本切分策略很多,但是 Java 在這塊現有框架目前支持的比較少,Spring AI 中僅有一個 TokenTextSplitter,而在 Spring AI Alibaba 中則多支持了一個 SentenceSplitter。
我們來簡單看看這兩種切分策略。
2.1 TokenTextSplitter
在 Spring AI 中,TokenTextSplitter 是一個用于文本分割的工具類,主要功能是將長文本按照令牌(Token)數量進行拆分,適用于處理超出大語言模型(LLM)上下文窗口限制的文本內容。它是實現文本分塊(Text Chunking)的重要組件,通常在構建檢索增強生成(RAG)等應用時使用。
TokenTextSplitter 通常使用與 LLM 一致的令牌化邏輯(通常基于 OpenAI 的 tiktoken 庫),確保分割結果與模型的令牌計數一致;并且 TokenTextSplitter 會盡量在自然邊界(如句子結束處)進行分割,避免將完整語義拆分到不同文本塊中。
我們來看一段簡單的示例代碼:
List<Document> documents = myTikaDocumentReader.loadText();
TokenTextSplitter splitter = new TokenTextSplitter(30,10,3,2000,true);
List<Document> chunks = splitter.apply(documents);
for (Document chunk : chunks) {
System.out.println("內容塊: " + chunk.getText());
System.out.println("元數據: " + chunk.getMetadata()); // 繼承原始文檔元數據
}在構建 TokenTextSplitter 的時候,有五個參數,含義分別如下:
參數名 | 默認值 | 功能描述 |
defaultChunkSize | 30 | 目標塊大?。钆茢担?/span> |
minChunkSizeChars | 10 | 最小塊字符數(低于此值不分割) |
minChunkLengthToEmbed | 3 | 有效塊最小長度(字符數,短于此值丟棄) |
maxNumChunks | 2000 | 單文本最大分塊數(防長文本爆炸) |
keepSeparator | true | 是否保留分隔符(如換行符) |
TokenTextSplitter 處理流程如下:
- 編碼階段:使用 CL100K_BASE 編碼將輸入文本轉換為令牌序列。
- 分塊切割:按 defaultChunkSize 將令牌序列切割為塊。
- 斷點優化:對每個塊嘗試在 minChunkSizeChars 后尋找自然斷點(句號、問號、感嘆號或換行符)。找到斷點則截斷,否則保留原始切割點。
- 格式處理:修剪空白字符,按 keepSeparator 決定是否保留換行符。僅保留長度 ≥minChunkLengthToEmbed 的塊。
- 循環處理:重復切割直至處理完所有令牌或達到 maxNumChunks 限制。
以上代碼最終切塊后的結果如下:
圖片
2.2 SentenceSplitter
Spring AI Alibaba 的 SentenceSplitter 是一個專為文本分塊設計的組件,主要用于優化 RAG(檢索增強生成) 流程中的文檔預處理階段。它通過智能拆分長文本為語義連貫的句子組,確保后續向量化處理能保留上下文完整性,從而提升大模型在問答、知識檢索等任務中的準確性。
SentenceSplitter 的工作流程分為兩步:
- 句子拆分:基于預訓練的 OpenNLP 句子檢測模型(opennlp-en-ud-ewt-sentence-1.2-2.5.0.bin),將原始文本分割為獨立句子。
- 動態分塊合并:根據預設的 chunkSize(默認 1024 tokens),計算每個句子的 token 數量(使用 CL100K_BASE 編碼),將相鄰句子合并為不超過 token 上限的文本塊。
代碼案例如下:
List<Document> documents = myTikaDocumentReader.loadText();
SentenceSplitter splitter = new SentenceSplitter(128);
List<Document> chunks = splitter.apply(documents);
for (Document chunk : chunks) {
System.out.println("內容塊: " + chunk.getText());
System.out.println("元數據: " + chunk.getMetadata()); // 繼承原始文檔元數據
}這是目前我們在 Java 相關框架中支持的分塊方案。
還有其他一些分塊思路,這里也和大家聊聊,大家可以結合自己的項目需求,自行實現。
2.3 固定長度分塊(Fixed-Length Chunking)
思路
將文本按照預設的固定長度(如字符數、token 數)進行均勻拆分,不考慮文本的語義、結構或標點等信息,當剩余文本長度不足固定長度時,作為最后一個塊保留。
原理
- 核心是“機械切割”,以量化的長度為唯一標準,不涉及對文本內容的理解。
- 例如:設定固定長度為 100 字符,無論文本是句子、段落還是代碼,均從開頭每 100 字符切分一次。
適用場景
- 文本結構簡單、語義連貫性較弱的場景(如日志、長串無標點的字符)。
- 快速實現分塊的初步方案,作為復雜分塊策略的基礎參考。
優缺點
- 優點:實現簡單、效率高,分塊結果可預測。
- 缺點:容易割裂完整語義(如拆分一個句子、一個論證過程),導致塊內信息碎片化或跨塊語義不連貫。
2.4 遞歸分塊(Recursive Chunking)
思路
以“先大后小”的層級邏輯分塊,優先按大粒度分隔符(如段落、章節)切分,若分塊后仍超過預設長度,則遞歸使用更小粒度的分隔符(如句子、逗號)繼續切割,直到所有塊的長度符合要求。
原理
- 基于“自然分隔符優先級”設計,假設文本中天然存在的分隔符(如換行、句號)能體現語義停頓,優先保留這些分隔符劃分的完整單元。
- 例如:預設長度為 500 字符,先按段落(換行符)切分,若某段落超過 500 字符,則按句號(句子)切分該段落;若某句子仍過長,則按逗號切分,以此類推。
適用場景
- 結構化較強、存在多層級語義分隔的文本(如文章、書籍、長文檔)。
- 希望在控制塊長度的同時,最大程度保留語義完整性的場景。
優缺點
- 優點:平衡了長度控制和語義連貫性,減少對完整語義單元的割裂。
- 缺點:實現較復雜,需要定義分隔符優先級;對無明顯分隔符的文本效果有限。
2.5 按結構分塊(Structural Chunking)
思路
根據文本的固有結構特征(如格式標記、邏輯層級)進行分塊,將具有相同結構屬性的內容劃分為一個塊。
原理
- 依賴文本的“結構性標記”,這些標記可能是顯式的(如 HTML 標簽、Markdown 標題、PDF 的章節標題),也可能是隱式的(如表格、代碼塊、列表的格式)。
- 例如:
對 HTML 文本,按<h1>(一級標題)、<p>(段落)、<table>(表格)等標簽分塊,每個標簽內的內容作為獨立塊。
對 Markdown 文本,按#(標題)、-(列表項)、(代碼塊)分塊。
適用場景
- 結構化文檔(如網頁、Markdown文檔、PDF報告、帶格式的Word文檔)。
- 需要保留特定結構單元(如表格、代碼塊、章節)的場景,避免結構被破壞。
優缺點
- 優點:能精準提取結構化單元,塊內信息關聯性強,適合后續針對特定結構的處理(如單獨解析表格、代碼)。
- 缺點:依賴文本結構的規范性,對無顯式結構的文本(如純文本小說)效果差。
2.6 按標點分塊(Punctuation-Based Chunking)
思路
以標點符號作為分塊的主要依據,將標點符號(如句號、問號、感嘆號、分號)分隔的內容劃分為獨立塊,通常優先使用表示語義停頓較強的標點。
原理
- 假設標點符號是語義完整的邊界,例如句號、問號通常標志一個完整句子的結束,分號標志句內的邏輯分隔,基于此劃分的塊能保留完整的短句或分句。
- 例如:按句號(.)分塊,每個句子作為一個塊;若句子過長,可進一步按分號(;)或逗號(,)細分。
適用場景
- 以句子為基本語義單元的文本(如散文、論文、對話記錄)。
- 希望塊內信息是完整短句,方便后續按句子級進行檢索或處理的場景。
優缺點
- 優點:塊內語義相對完整,符合人類閱讀的自然停頓習慣。
- 缺點:對長句(如無標點的長段落)或標點使用不規范的文本(如大量省略標點)效果差,可能導致塊過長或過短。
總結
分塊策略 | 核心依據 | 核心目標 | 典型應用場景 |
固定長度分塊 | 量化長度(字符/token) | 快速、均勻切割 | 日志、簡單無結構文本 |
遞歸分塊 | 分隔符優先級+長度控制 | 平衡長度與語義完整性 | 文章、書籍等長文檔 |
按結構分塊 | 文本格式/邏輯結構 | 保留結構化單元 | 網頁、Markdown、PDF報告 |
按標點分塊 | 標點符號(語義停頓) | 保留完整句子/分句 | 散文、論文、對話文本 |
實際應用中,常結合多種策略(如遞歸分塊中融入標點和結構信息),以適應復雜文本的分塊需求。























