Link Search Menu Expand Document

文檔之內嵌

Table of contents

區塊尺寸

與文檔之匹配原則

文檔經過Embedded之後、反而不容易有正確的查詢結果,這是否與區塊(chunk)的大小與文檔沒有適當匹配有關係?

是的,你的推測是正確的。文檔經過嵌入(Embedding)之後查詢效果變差,通常與區塊大小設定不當、或 區塊切割方式與文檔結構不匹配有密切關係。我們可以從幾個面向來看:

  1. 區塊太小
  • 如果 區塊太小(例如每段只有 100 tokens),會導致語意過於片段化,模型無法理解完整上下文。這樣會讓查詢時拿到的相關 區塊沒有足夠資訊回應問題。
  • 典型現象:查詢時只回傳含有關鍵字的句子,但沒辦法回答「這是什麼意思」、「適用條件是什麼」這類需要上下文的問題。
  1. 區塊太大
  • 若 區塊太大(例如超過 1000 tokens),會導致 embedding 時「語意過於稀釋」,且增加向量搜尋時的語意干擾。
  • 典型現象:查詢結果跟關鍵詞接近但內容不相關,或提取到的 區塊包含太多不相干資訊。
  1. 區塊切割方式不尊重文檔邏輯
  • 例如直接用固定長度切段(如每 500 tokens 一段),會打斷段落、標題與表格,導致一段文字在語意上不完整。
  • 典型現象:查詢某段落或標題相關內容時,模型無法正確定位資訊來源。
  1. 文檔本身的結構與語言特性
  • 有些報告、法規、規範類文件,本身語言偏硬、結構層層嵌套(如條文、細項、附錄),若沒有針對這類型做「結構感知型 chunking」,很難取得有用資訊。

建議改善方向

  1. 嘗試不同 區塊大小測試效果:通常 200~500 tokens 是較穩定的範圍,視文檔結構調整。
  2. 使用結構感知切割(Structured chunking)
  • 例如根據 Markdown 標題、PDF 標題、段落、清單符號切段。
  • 可結合 recursive text splitter 或自行設計分段邏輯。
  1. 加入 metadata(例如標題、頁碼)來輔助查詢與回應生成
  2. 做 embedding 前,先經過語義優化與清洗處理。去除多餘格式、雜訊、重複資訊。

結構性尺寸

我目前是用llama index程式庫。Structured chunking要怎麼進行!

太好了!既然你是用 LlamaIndex,那就非常適合進行 Structured chunking,它內建了許多適合這類需求的工具。

什麼是 Structured chunking?

Structured chunking 指的是根據文檔的結構(例如標題、段落、條列清單、表格等)智能地切割文本,比起單純按 token 數切割效果更好。

LlamaIndex 中進行 Structured chunking 的方式:

LlamaIndex 提供了內建的 HierarchicalNodeParser (階層式節點解析器)與 SentenceSplitter 等工具,可以結合使用來實現Structured chunking。

基本範例:用 HierarchicalNodeParser + SentenceSplitter

from llama_index.node_parser import HierarchicalNodeParser
from llama_index.text_splitter import SentenceSplitter

# 設定 Sentence Splitter,這裡控制每個 區塊約 512 tokens
text_splitter = SentenceSplitter( 區塊_size=512,  區塊_overlap=50)

# 建立階層式 區塊parser(支援標題等結構)
node_parser = HierarchicalNodeParser.from_defaults(text_splitter=text_splitter)

# 假設你已經有 document 物件(可以是從 PDF, Markdown, txt 讀進來的)
nodes = node_parser.get_nodes_from_documents(documents)

說明

  • SentenceSplitter :會根據句子邊界切割,而不是硬切 token。
  • HierarchicalNodeParser :會偵測文檔的結構(例如標題、段落等)自動分層處理,並保留這些結構做為 metadata。
  • 區塊_overlap:讓相鄰區塊有重疊,避免上下文斷裂。

結合 Markdown / HTML 結構切割

若你的文檔是 Markdown 或 HTML,可以使用:

from llama_index.node_parser import MarkdownNodeParser

parser = MarkdownNodeParser()
nodes = parser.get_nodes_from_documents(documents)

這樣會自動根據 # 標題、段落等結構進行分區塊。

區塊分完後的流程

完成 Structured Chunking 後,你就可以把 nodes 放進你的 VectorStoreIndex 或其他 LlamaIndex pipeline 中,例如:

from llama_index import VectorStoreIndex

index = VectorStoreIndex(nodes)
query_engine = index.as_query_engine()
response = query_engine.query("你的查詢內容")

結語與建議

  • 使用 Structured Chunking 可以 大幅提升查詢的準確度與回應品質。
  • 若你的文檔很長或結構複雜(如報告、規範、合約),建議強烈採用 HierarchicalNodeParser

表格化之文檔

如果是表格化的文件呢?如法律條文、政策說明、或簡短文字的表格,該如何做內嵌? 這是個非常關鍵的問題!表格化的文件(如法律條文、政策說明、合約、表格式規範等)在做內嵌(Embedding)時,需要特別處理格式與語意的保持,否則會造成資訊失真或查詢失效。

以下是針對這類「表格化文檔」的建議方式與工具操作方式:

一、理解表格文檔的特性

表格文檔常見問題:

  • 表格內容有「上下文依賴」,像是「某欄的內容解釋需要標題來理解」。
  • 表格本身的結構、層次、合併儲存格都是關鍵資訊。
  • 傳統純文本 chunking 容易打破表格結構,導致 embedding 語意混亂。

二、處理策略總覽

類型 處理策略 簡單表格(如兩欄對應:條文 vs 說明) 可轉為句子或字典格式後再做 embedding 結構複雜表格(合約、法規) 建議轉成結構化 JSON 或 key-value 形式保留欄位意義 PDF 表格掃描件 需先經過表格抽取(如 pdfplumber、unstructured 或 OCR)

三、LlamaIndex 中的實作建議

  1. 將表格轉為結構化文字描述

舉例表格原文如下:

條號條文內容備註
第1條本辦法依據 XX 條規定訂定之法規依據
第2條用人單位應每月通報人員異動情形含試用期人員

轉換後成:

text = """
第1條:本辦法依據 XX 條規定訂定之。(備註:法規依據)
第2條:用人單位應每月通報人員異動情形。(備註:含試用期人員)
"""

然後再使用 SentenceSplitterSimpleNodeParser 切割。

  1. 如果表格具欄位意義,可轉為 dict 進行嵌入(推薦)

將表格轉為:

records = [
    {"條號": "第1條", "內容": "本辦法依據 XX 條規定訂定之", "備註": "法規依據"},
    {"條號": "第2條", "內容": "用人單位應每月通報人員異動情形", "備註": "含試用期人員"}
]

然後列成通式(schema):

from llama_index.schema import Document

documents = [
    Document(text=f"{r['條號']}{r['內容']}(備註:{r['備註']})") for r in records
]

再放進你原本的 NodeParser 去切區塊。

  1. 進階做法:使用 Table-aware 模型或結構保留工具
  • 可搭配 unstructuredpdfplumber 做結構保留的抽取。
  • LlamaIndex 尚未原生支援表格語意建模(Table Semantic Modeling),但你可以將每一個儲存格的單筆資料當成獨立區塊,保留 key-value 結構後做 embedding。
  • 若你用的 LLM 支援 JSON 格式查詢與理解(如 GPT-4 Turbo、Claude 等),可以用 dict 結構強化查詢品質。

四、查詢技巧建議

查詢表格化內容時,也建議你在 query 時加一些 context hint:

  • 例:”第2條的通報規定是什麼?”
  • 可啟用 query rewriting / reranking 提高正確命中率。

五、範例程式(總結)

from llama_index.node_parser import SimpleNodeParser
from llama_index import VectorStoreIndex, ServiceContext

# 假設你已將表格轉成簡單敘述
text = """
第1條:本辦法依據 XX 條規定訂定之。(備註:法規依據)
第2條:用人單位應每月通報人員異動情形。(備註:含試用期人員)
"""

documents = [Document(text=text)]
parser = SimpleNodeParser()
nodes = parser.get_nodes_from_documents(documents)

index = VectorStoreIndex(nodes)
query_engine = index.as_query_engine()

print(query_engine.query("第2條的規定內容是什麼?"))

Table of contents