一句話說明
Chunking 就是把一份長文件切成一段一段的小段落,讓 AI 能夠有效搜尋和處理裡面的內容。
什麼是 Chunking?
想像你有一本 500 頁的員工手冊,有人問你「請假要提前幾天申請?」。你不會把整本手冊丟給他,而是翻到請假辦法那一頁,指出對應的段落。
Chunking 做的就是這件事。它把大文件切成適當大小的段落(chunk),每個段落各自轉成 Embedding 向量,存進 向量資料庫。當有人問問題時,系統用 語意搜尋 找到最相關的段落,再把這些段落餵給 LLM 來回答。
這是 RAG 系統的必要步驟。切得好,AI 找到的段落就精準;切得不好,AI 可能找到不相關的內容,或者一個段落只有半句話,缺少上下文。
Chunking 怎麼運作?
最基本的概念很簡單:拿到一份文件,按照某種規則把它切成小塊。但實際操作時,有很多細節要考慮。
一個 chunk 的大小通常用 token 數量來衡量。常見的大小從 100 到 2000 個 token 不等,取決於你的應用場景。
切完之後,相鄰的 chunk 之間通常會保留一段重疊(overlap)。這是為了避免重要資訊剛好被切在兩個 chunk 的交界處。例如前一個 chunk 的最後 50 個 token 會出現在下一個 chunk 的開頭。
整個流程是:載入文件 → 前處理(去除多餘空白、格式化)→ 選擇切割策略 → 設定 chunk 大小和 overlap → 執行切割 → 產出 chunk 清單。
常見的 Chunking 策略
不同的切割策略適合不同類型的文件:
固定大小切割(Fixed-size Chunking):最簡單的方式,每 N 個字元或 token 切一段。優點是實作簡單、效果可預測。缺點是可能把一段完整的段落從中間切斷。
from langchain_text_splitters import CharacterTextSplitter
splitter = CharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separator="\n"
)
chunks = splitter.split_text(document)
遞迴切割(Recursive Character Splitting):這是目前最常用的方式。它會先嘗試用段落分隔符切(\n\n),如果段落太大就用換行符切(\n),再不行就用句號切,最後才用字元數硬切。這樣盡量保持語意完整。
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", "。", ",", " ", ""]
)
chunks = splitter.split_text(document)
注意中文要用中文的標點符號(。,)而不是英文的(. ,)。
語意切割(Semantic Chunking):用 Embedding 模型判斷句子之間的語意相似度,在語意轉換的地方切割。這樣每個 chunk 都是一個語意完整的段落。缺點是需要額外的 Embedding 計算,速度比較慢。
結構化切割(Document-specific Splitting):根據文件的結構來切。Markdown 文件就用標題(#、##)切割,HTML 用標籤切割,程式碼用函式或類別切割。這個方式保留了文件原有的結構。
from langchain_text_splitters import MarkdownTextSplitter
splitter = MarkdownTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_text(markdown_text)
Chunk 大小怎麼選?
這是 Chunking 裡最常被問的問題。答案是:看你的應用場景。
| Chunk 大小 | 適合場景 | 優點 | 缺點 |
|---|---|---|---|
| 小(100-200 token) | 精確問答、FAQ | 搜尋精準度高 | 缺少上下文,LLM 可能看不懂 |
| 中(300-500 token) | 通用 RAG | 平衡精準度和上下文 | 大部分場景的合理選擇 |
| 大(500-1000 token) | 摘要、長文分析 | 上下文完整 | 搜尋精準度降低,塞滿 context window |
| 很大(1000+ token) | 文件分段、章節層級 | 保留完整論述 | 不適合精確搜尋 |
一般建議從 300-500 token 開始,overlap 設在 chunk 大小的 10-20%(例如 chunk_size=500, overlap=50-100)。然後根據實際搜尋結果的品質來調整。
如果你發現搜尋回來的 chunk 常常少了重要的上下文,就把 chunk 加大。如果搜尋回來的 chunk 裡有太多不相關的內容,就把 chunk 縮小。
實際應用場景
企業知識庫:公司的 SOP、規章制度、技術文件。這類文件通常有明確的標題結構,適合用結構化切割,按照標題層級來分段。
法律文件:合約、法規條文。法律文件的每個條款通常是獨立的語意單位,用結構化切割(依條號分段)效果最好。需要注意有些條款會引用其他條款,單獨一個 chunk 可能不夠完整。
技術文件:API 文件、程式碼說明。可以按照函式或 API endpoint 來切割。程式碼片段盡量保持完整,不要把一個函式切成兩半。
客服對話紀錄:每一組問答作為一個 chunk。如果對話很長,可以用語意切割在話題轉換的地方分段。
研究報告:學術論文、市場調查報告。通常按照章節切割,每個章節的摘要可以額外建立一個 chunk 來提升搜尋效果。
台灣使用情境
台灣企業在做 Chunking 時,有幾個特別需要注意的地方:
中文斷詞問題:中文不像英文有空格分隔單字,token 的計算方式不同。用 OpenAI 的 tokenizer,一個中文字通常是 1-2 個 token。設定 chunk_size 的時候要考慮這個差異。
中英混用:台灣的技術文件、商業文件常常中英夾雜。切割的時候要注意不要把英文術語從中間切斷,建議用遞迴切割搭配適當的分隔符。
法規格式:台灣的法規文件有自己的格式慣例(第X條、第Y項、第Z款)。如果你要處理法規文件,寫一個自訂的切割器來辨識這些結構會比通用切割器效果好。
安全考量與限制
Chunking 本身看似是個簡單的文字處理步驟,但有幾個容易忽略的問題:
資訊外洩:切割後的 chunk 會被送去做 Embedding,如果用的是雲端 API,代表你的文件內容會被送到外部。機敏文件需要評估風險,或使用本地 Embedding 模型。
上下文遺失:切割必然會遺失部分上下文。例如一份報告的表格跟它的說明被切到不同的 chunk,搜尋到表格時看不到說明。可以在每個 chunk 前面加上文件標題和章節標題作為補充上下文。
切割後的敏感資訊:一份文件裡可能只有一小段是機密的,但切割後那段機密內容會變成一個獨立的 chunk,被搜尋引擎索引。需要在切割前先做好資料分類和脫敏。
品質不一致:同一種切割策略在不同類型的文件上效果差異很大。PDF 轉出來的文字可能有格式問題(表格變亂碼、頁首頁尾重複出現),直接切割效果會很差。建議先做好文件前處理。
過度切割:chunk 切太小,每個 chunk 的資訊密度太低,搜尋回來一堆片段但每個都不完整。過度切割比切太大更容易造成問題。
怎麼開始?
-
確認你的文件格式:PDF、Word、Markdown、HTML 各有不同的處理方式。先把文件轉成乾淨的純文字。
-
選擇切割策略:大部分情境從 RecursiveCharacterTextSplitter 開始就好。如果文件有明確結構(Markdown 標題、法規條號),用結構化切割。
-
設定參數:chunk_size 從 400-500 token 開始,overlap 設 50-100 token。
-
測試切割結果:實際看看切出來的 chunk 品質如何。每個 chunk 是否語意完整?有沒有重要資訊被切斷?
-
用實際查詢測試:準備 10-20 個測試問題,檢查搜尋回來的 chunk 是否包含正確答案。根據結果調整參數。
-
持續優化:不同類型的文件可能需要不同的切割策略。建立一套評估機制,定期檢查 RAG 的回答品質。
一個完整的處理範例:
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyPDFLoader
# 載入 PDF
loader = PyPDFLoader("company_handbook.pdf")
pages = loader.load()
# 切割
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=80,
separators=["\n\n", "\n", "。", ",", " ", ""]
)
chunks = splitter.split_documents(pages)
# 檢查結果
for i, chunk in enumerate(chunks[:3]):
print(f"--- Chunk {i} ({len(chunk.page_content)} chars) ---")
print(chunk.page_content[:200])
print()
知識檢測
讀完文章後,測試一下你對這個主題的理解。
常見問題
Chunk 大小要設多少?
沒有放諸四海皆準的答案。一般建議從 300-500 token 開始,然後根據實際搜尋結果調整。如果你的問答比較精確(FAQ 類型),chunk 可以小一點(200-300)。如果需要更多上下文(分析報告),chunk 可以大一點(500-1000)。
Overlap 一定要設嗎?
強烈建議設。Overlap 的目的是確保重要資訊不會因為剛好被切在兩個 chunk 中間而遺失。通常設在 chunk 大小的 10-20% 就夠了。例如 chunk_size=500,overlap=50-100。
中文和英文的 chunk 大小設定一樣嗎?
概念上一樣,但實際的字元數會不同。因為中文字的 token 密度比英文高(一個中文字約 1-2 token,一個英文字約 1-4 token),設定 chunk_size=500 token 時,中文大約是 250-400 個中文字,英文大約是 125-500 個英文字。建議用 token 數量而不是字元數量來設定。
除了文字,圖片和表格怎麼處理?
這是 Chunking 的一大挑戰。目前常見的做法是:表格轉成 Markdown 格式再切割、圖片用 多模態模型 生成描述文字後再切割、或者把表格和圖片連同周圍的文字一起作為一個 chunk。沒有完美的解決方案,需要根據你的資料特性來處理。
總結
Chunking 是 RAG 系統中看似簡單但影響很大的步驟。切得好,搜尋結果精準,AI 回答品質高。切得不好,後面再怎麼調都很難補救。
實務上,從 RecursiveCharacterTextSplitter 開始,chunk 大小 400-500 token、overlap 50-100 token,然後根據實際測試結果來調整。不同類型的文件適合不同的策略,沒有一招打天下的做法。
參考資料
- LangChain Text Splitters — LangChain Text Splitters 使用指南
- LlamaIndex Documentation — LlamaIndex 文件,含多種 chunking 策略說明
- OpenAI Tokenizer — OpenAI Tokenizer 工具,可測試文字的 token 數