iT邦幫忙

2026 iThome 鐵人賽

DAY 3
0
AI Engineering

企業知識檢索與 RAG 工程:標註、微調、混合檢索、Rerank 與可重現評測系列 第 3 篇

Day 03|Corpus Hygiene:垃圾進去,Embedding 不會幫你洗乾淨

  • 分享至 

  • xImage
  •  

昨天我先把 retrieval task 寫清楚:7 份文件、7 筆 query、9 筆 relevance judgment,每一筆 query 都有至少一份正相關文件。資料契約通過後,原本可以直接開始做 embedding。

但我把 documents.jsonl 逐筆重新看一次後,意識到 Day 02 檢查的只是「這份 retrieval task 有沒有斷掉」,還沒有回答:

  • 同一份文件是不是被收了兩次?
  • 新舊版本同時存在時,要保留哪一份?
  • 解析後只剩標題、導覽列或 OCR 亂碼的內容,會不會被索引?
  • metadata 缺少語言、來源、版本或可見範圍時,後面要怎麼過濾?

Embedding 只會把收到的內容變成向量,不會知道某份規章已經過期,也不會主動刪除網頁頁首或合併重複內容。如果 corpus 本身有問題,換更大的 embedding model 通常只是更穩定地把問題寫進 index。


把 Corpus Hygiene 拆成五道關卡

1. Identity:這份文件到底是誰?

每份文件要有穩定、唯一的 doc_id。檔名不一定適合當 ID,因為搬動目錄或改名就會產生新身份。如果 ID 不穩定,後面的 qrels、版本記錄和刪除操作都會跟著漂移。

2. Content:解析後還剩下什麼?

不只檢查原始檔案能不能打開,而是看送進 index 前的實際文字。空字串、只剩標題、重複頁首頁尾、亂碼與過短內容,都要在這一層被擋下來。

3. Version:新版不一定只是另一份文件

去重不能只做 byte hash。同一份內容多了空白、換行或 Unicode 形態差異,原始 hash 就會不同,但對 retrieval 來說可能仍然是同一份文件。

反過來,內容相似也不代表可以直接刪除。如果是新舊版本,應該先根據 effective date、version 與 status 選出生效版,而不是單純保留「最像」的一份。

4. Metadata:過濾條件不能從內文猜

本次公開 corpus 的每份文件至少有:

{
  "domain": "synthetic-it-support",
  "language": "zh-Hant",
  "classification": "synthetic/public-safe"
}

真正的系統還會需要 source、version、effective time、owner 與 access scope。這些欄位要由 ingestion pipeline 明確產生,不能期待 embedding 或 LLM 之後從內容猜回來。

5. Snapshot:今天的 corpus 要能和明天區分

如果 corpus 持續改變,但實驗只記「使用最新資料」,後面的 BM25、Dense、Hybrid 與 Rerank 就沒有公平對照組。因此會對整份 JSONL 產生 snapshot hash,實驗只引用固定版本。


我寫了一個不需要 Embedding 的 Corpus Audit

這一天還不需要 GPU,也不需要下載模型。labs/retrieval/audit_corpus.py 直接從 JSONL 檢查:

  • JSON 是否合法。
  • doc_id 是否唯一且非空。
  • title 與 text 是否非空。
  • domain、language、classification 是否完整。
  • classification 是否明確標示 synthetic/public-safe。
  • 原始文字與正規化文字是否重複。
  • 這份 corpus snapshot 的 SHA-256。

正規化處理只做 Unicode NFKC、case folding,並將連續空白壓成單一空白:

def normalize_text(value: str) -> str:
    normalized = unicodedata.normalize("NFKC", value).casefold()
    return re.sub(r"\s+", " ", normalized).strip()

沒有在這裡加入「語意相似就自動刪除」。語意 near-duplicate 可以當候選訊號,但是不是同一份文件、哪個版本生效,還是需要 domain rule 或人工複核。

實際檢查現有的 7 份公開文件

執行:

python3 labs/retrieval/audit_corpus.py

這次結果是:

status: clean
document_count: 7
exact_duplicate_group_count: 0
normalized_duplicate_group_count: 0
issue_count: 0
snapshot_sha256: 3e0d4ad0945574f34f9d6ed976f89f8fbab19eac99bbcd368cb3178f74e541a7

對這個 clean 的解讀很保守,只代表現有 7 份文件通過了這六類結構性檢查,不代表文件內容必然正確,也不代表 query 一定找得到答案。

另一個值得保留的現象是:Day 02 的 task validator 與 Day 03 的 corpus audit 都可以通過,但回答的問題不同。前者檢查 documents、queries 與 qrels 有沒有斷掉;後者檢查送進 index 的文件是否有明顯污染。兩個 success 不能互相取代。

如果 Audit 發現問題,不會直接原地改檔案

會保留 raw corpus,再產生新的 processed snapshot,並記錄每一種處理的數量:

raw documents
   → parse
   → normalize
   → reject empty / malformed
   → exact duplicate grouping
   → version selection
   → metadata validation
   → processed snapshot + manifest

希望後面能回答「哪些文件被排除、為什麼排除」,而不是只留一份被手動改好的最終檔。否則一旦清理規則寫錯,原始內容也一起消失,就無法複現或回滾。

Corpus Hygiene 不只是「把數據洗乾淨」

這一步還決定了後面實驗的邊界:

  • 去掉重複文件,會改變 corpus size 與 ranking 競爭。
  • 只保留生效版本,會改變 qrels 應該指向的 doc_id。
  • 補上 metadata,才能公平測試 filter 與 access control。
  • 改變文件單位,會影響後面的 chunking 與 retrieval metric。

所以 corpus cleaning 規則也必須版本化。如果清理規則改了,就應該產生新 snapshot,而不是讓同一個 corpus 名稱在實驗過程中悄悄變化。

今天的結論

今天沒有執行 BM25、Embedding 或 Reranker。做的是把 corpus 從「一堆可以讀取的文件」,變成「有身份、有 metadata、能檢查重複,也能用 hash 固定的 snapshot」。

Embedding 能學習文字的語意關係,卻不會替我們決定文件身份、生效版本、存取邊界與品質規則。

明天會在固定 corpus snapshot 之後,開始設計 Query Set。到時我要避免的是另一種污染:只挑自己知道系統會答對的問題。

下一篇:Day 04|Query Set 怎麼設計,才不會只測自己想看到的答案


操作交接

要對新 corpus 重跑 audit,或刻意加入重複、空內容與缺漏 metadata 驗證失敗路徑時,請使用 docs/day03-operations/03-ai-engineering-corpus-hygiene.md。只可使用 synthetic/public-safe 資料。

參考資料

  1. Hugging Face Datasets, The cache and dataset fingerprints
  2. BEIR: A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models
  3. Unicode Standard Annex #15, Unicode Normalization Forms

本篇只使用為公開實驗從零建立的 synthetic corpus,與我手上的Case無關。


上一篇
Day 02|先定義任務:企業知識檢索到底要回答什麼?
下一篇
Day 04|Query Set 怎麼設計,才不會只測自己想看到的答案
系列文
企業知識檢索與 RAG 工程:標註、微調、混合檢索、Rerank 與可重現評測 共 13 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言