iT邦幫忙

2025 iThome 鐵人賽

DAY 13
0

Day 12,我們理解了 Embedding 這個將「語意」轉化為「數學座標」的魔法。在我們迫不及待地想把所有筆記都向量化之前,還有一個極其重要、卻也最常被忽略的準備工作 -- Chunking (文本分割)。

如果說 Embedding 是烹飪,那 Chunking 就是廚師在料理前的「備料」與「刀工」。你如何切割食材,將直接決定這道菜最終的口感與風味。同樣地,你如何分割你的筆記,也將直接決定你的 AI 助理最終的回答品質。

1. 為什麼需要 Chunking?

為什麼我們不能直接把一整篇 Notion 頁面,直接丟給 Embedding 模型呢?原因有三:

  1. Embedding 模型的理解極限
    Embedding 模型在處理小而精、語意集中的文本時,效果最好。如果你將一篇長達三千字的文章直接向量化,它的「語意座標」會變得非常模糊、平均化,失去了細節的精準度。就像問一台相機:「這張照片的主題是什麼?」,給它一張單人特寫,遠比給它一張千人大合照,更能得到精確的答案。

  2. 檢索的精準度 (最關鍵!)
    使用者的問題通常是簡短而具體的,例如:「RAG 的 Reranking 是什麼?」如果你的知識庫裡,每個 Chunk 都是一篇長文,那麼包含「Reranking」的那個 Chunk,可能還混合了 Embedding、Chunking 等大量無關內容。這會「稀釋」掉關鍵資訊的語意權重,導致在向量搜索時,相關性分數下降,反而不容易被找到。

  3. LLM 的上下文視窗 (Context Window) 限制
    當我們檢索到相關的 Chunks 後,是需要把它們連同問題一起塞給 LLM 的。如果 Chunks 太大,很容易就塞爆了 LLM 的上下文視窗,導致無法提供足夠的參考資料,或是增加大量的運算成本。

2. 常見的 Chunking 策略

2.1 Fixed-size chunking 固定長度切割

  • 以固定字元、單詞或 token 長度切割文本。
  • 需加入少量重疊(overlap),避免破壞語句連貫性。
  • 優點:實作簡單、便於批量處理。
  • 缺點:可能切斷語意或句子中斷。

2.2 Semantic chunking 語意切割

  • 根據語義單位(句子、段落)切割,保持內容連貫。
  • 使用 embeddings 計算相似度,當相似度下降時開始新 chunk。
  • 優點:保留整體語意,提高檢索準確度。
  • 挑戰:需設 threshold,可能隨文件變動。

2.3 Recursive chunking 遞迴式切割

  • 先依段落等結構切割,再遞迴將過長 chunk 分割成小段。
  • 保留語意流暢性,同時符合 token 長度限制。
  • 缺點:實作複雜、運算開銷較高。

2.4 Document structure-based chunking 結構化切割

  • 利用文件既有結構(標題、段落)作 chunk 邊界。
  • 保留文件邏輯,可讀性高。
  • 若 chunk 太長,建議搭配 recursive 切割使用。

2.5 LLM-based chunking由 LLM 切割

  • 利用 LLM 產生語意完整、獨立的 chunk。
  • 生成品質高,但計算代價高,且需考慮 LLM 的 context window 限制。

3. 我們的 Chunking 策略設計

針對 Notion 筆記,我們可以採用「段落優先,字數補充」的混合策略:

  • 以 Notion block 作為基本單位。
  • 如果 block 過長(例如 > 500 字),就依字數再切分。
  • 切分時加入 10–20% 重疊內容,保留上下文。

這樣做能兼顧:

  • 短 block:保持精簡,不必切分。
  • 長 block:不會超過模型限制,檢索時也能有上下文。

4. 實作:Chunking 函式

我們可以在 src/chunk_utils.py 新增一個簡單的工具:

def chunk_text(text: str, max_len: int = 200, overlap: int = 50) -> list:
    """
    將長文字切成多個 chunk,並保留 overlap
    :param text: 原始文字
    :param max_len: 每個 chunk 最長字數
    :param overlap: chunk 與 chunk 之間的重疊字數
    """
    if not text:
        return []

    chunks = []
    start = 0
    text_len = len(text)

    while start < text_len:
        end = min(start + max_len, text_len)
        chunk = text[start:end]
        chunks.append(chunk)

        if end == text_len:
            break
        start = end - overlap  # 往回 overlap 保留上下文

    return chunks


# 範例
if __name__ == "__main__":
    txt = "物件導向程式設計(Object-Oriented Programming, OOP)是一種程式設計範式..."
    result = chunk_text(txt, max_len=20, overlap=5)
    for i, r in enumerate(result):
        print(f"Chunk {i+1}: {r}")

執行結果:

Chunk 1: 物件導向程式設計(Object
Chunk 2: 設計(Object-Oriented P
Chunk 3: Oriented Programming, O
...

5. 小結與下篇預告

今天,我們認識了 Chunking 策略,了解為什麼要在 Embedding 前切分文字,並且實作了一個簡單的 chunk_text 函式。

在 Day 14,我們將把這個 Chunking 工具串接到 SQLite → Embedding 流程裡,從 notion_blocks.block_text 自動生成向量,為真正的 語意檢索 (Semantic Search) 做好準備!

參考資料


上一篇
【Day 12】向量化的準備:Embedding 與向量資料庫
下一篇
【Day 14】資料 Chunking 與 Embedding 成本評估
系列文
Notion遇上LLM:30天打造我的AI知識管理系統21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言