實戰指南:從 .ctxfst.md 到 chunks.json,破解 export_to_lancedb.py 的角色與輸出協定
實戰指南:從 .ctxfst.md 到 chunks.json,破解 export_to_lancedb.py 的角色與輸出協定
在本系列教學中,我們談過了 Chunk 與 Entity 的差別,確立了 Skill Chunk MD 格式規範,甚至看見了 下游程式如何吃 JSON 自動生長圖譜。
但很多人在這時會有個大疑問:「為什麼還需要 Exporter?既然我寫的 Markdown 已經符合 CtxFST 格式了,系統或 Graph DB 不能直接讀 Markdown 嗎?」
答案是:export_to_lancedb.py 的真正價值,不只是把 Markdown 轉成 JSON,而是把 CtxFST 這種「人可以閱讀與編輯的文件格式」,轉成「向下游程式宣示的穩定資料交換格式協定(contract)」。
在這一步之後,下游系統(不論是 LanceDB 向量檢索、還是建立圖譜的 Python 腳本)都不需要再耗費心力去解析 Markdown、不需要猜測文字段落邊界在哪、更不需要驗證 Metadata 結構。它們只需要讀取 chunks.json。
這篇文章將帶你實戰這支 Exporter 的最小用法,以及完全搞懂它到底「做什麼」與「不做什麼」。
讓我們先看懂一條龍體系(Pipeline)
要了解 Exporter 的位置,我們直接看一次完美的資料流展演:
Markdown (.ctxfst.md)(人類可讀、可編輯的標準文件)
⬇️
validate_chunks.py(合法性把關)
⬇️
export_to_lancedb.py(中介轉換器,我們今天的主角!)
⬇️
chunks.json(機器可讀、穩定 Schema 的 exchange format)
⬇️
build_entity_profiles.py->entity-profiles.json(衍生 representation 層)
⬇️
build_entity_graph.py->entity-graph.json(下游 similarity graph 邊界層)
你可以看到,export_to_lancedb.py 是一個承先啟後的「中游閘門」。它是進入現代向量檢索庫或機器學習前的「整流器」。
這支 Exporter 到底在做什麼?
它的工作非常單純、職責極度分明。它主要進行以下 4 個步驟:
- 讀取 Frontmatter:解析 Markdown 檔頭的 YAML Metadata。
- 讀取內文 XML:掃描並截取內文裡被
<Chunk id="...">...</Chunk>包裹的實質內容。 - 對齊兩者:把 Frontmatter 裡的
chunks[]宣告,與內文區塊對應的id一一配對。 - 輸出標準 JSON:轉成符合
schema.json驗證機制的乾淨格式。
如果一篇文章沒有包含 chunks: 陣列,它不會硬轉,而是優雅地印出 Warning 並跳過。
Exporter 設計哲學:文件不合法就警告 / 跳過;文件合法就輸出乾淨 JSON。
CLI 指令與最小用法
這支 Python 腳本具備很直觀的 Command Line 介面:
- 匯出單一檔案:
python3 scripts/export_to_lancedb.py your-document.md --output chunks.json - 匯出整個目錄的知識庫:
python3 scripts/export_to_lancedb.py docs/ --output chunks.json - 產生漂亮排版(Pretty Print),方便人類用肉眼 Debug:
python3 scripts/export_to_lancedb.py docs/ --output chunks.json --pretty
Output 協定:這包 JSON 到底長怎樣?
它的最終輸出永遠是固定型態的 Object,這份格式正是符合我们在上一篇定義的 schema.json 通訊協定。
核心頂層有兩個節點:
{
"entities": [...],
"chunks": [...]
}
(註解:目前協定規範了可加上 title,但這支 Reference Exporter 目前主要聚焦為 entities 與 chunks)
關於 chunks 陣列轉換
為了確保下游系統能用同一套標準處理資料,轉換後的每一個記錄(Record)欄位都與 Markdown 端的對應關係如下:
| 欄位名稱 | 在 JSON 中的狀態 | 歷史來源 | 實質功能與說明 |
|---|---|---|---|
id |
✅ 必填 (v1.0) | frontmatter: chunks[].id |
Chunk 主鍵(Primary Key) |
context |
✅ 必填 (v1.0) | frontmatter: chunks[].context |
結構化上下文(餵給 AI 判讀脈絡) |
content |
✅ 必填 (v1.0) | <Chunk id="...">...</Chunk> |
真正要送給 LLM 或 Retriever 的原始 markdown 內容 |
source |
✅ 必填 (自動加入) | export_to_lancedb.py 注入 |
標記這段文字是從哪個 Markdown 檔案來的路徑 |
tags |
🔸 選填 (v1.0) | frontmatter: chunks[].tags |
提供基礎分類或強過濾機制(Filter metadata) |
entities |
🔸 選填 (v1.1) | frontmatter: chunks[].entities |
Chunk ➡️ Entity 之間的圖譜關聯指標 |
created_at 等 |
🔸 選填 (v1.2) | frontmatter: extension fields |
進階延伸應用欄位(版本標記、時間軸、相依關係等) |
(⚠️ 溫馨提醒:在先前的初稿探討中曾提及 tags 也是必填,但依據正式的 schema.json 與 ctxfst-spec.md 規範中,tags 已經正名為 Optional (選填),真正必須填寫的只有:id, context 以及 content 這三本柱。)
關於 entities 目錄聚合
這裡有個設計尤為巧妙,並且值得特別強調的轉換邏輯:
Exporter 不會重新幫你從文章中抽取(Extract)新的 Entities。它只會把 YAML 裡已經存在的
entities[]原樣搬過來。
更好用的是,當你**「匯出整個知識庫目錄(批次匯出)」**時,腳本會怎麼處理散落在數十篇 Markdown 中的同名 Entities 呢?
答案是:依 id 自動去重 (Deduplication)。
chunks:所有檔案的所有片段全部累積。entities:系統會自動辨識entity:python,即使十個檔案裡宣告了十次 Python,最終匯出的chunks.json頂層也只會留下乾淨的 1 個 Python Node 實體。
這巧妙的幫我們做到了:「跨多文件匯出時,Entity Catalog 可以自動自然地合併成一個無雜訊的全域節點集(Global Node Catalog)」。
強調:這支 Exporter 不做什麼
這一點非常重要。許多人會誤以為這支腳本就是 Ingestion Pipeline 的「全集」,期待它跑完就出現一個 GraphRAG 大絕招。
這支腳本被設計成最純粹的狀態轉換層。所以它:
- 不做 Chunking(切塊):它不負責把長文自動斬斷,切塊是你用手或 LLM 在寫 Markdown 時就決定的。
- 不做 Entity Extraction(實體萃取):它不會智能幫你找出內文有什麼關鍵字,實體也是前置標記好的。
- 不做 Validation 修復:這是在上一步
validate_chunks.py處理。 - 不做 Embedding(向量嵌入):把字轉成高維度向量是進入 LanceDB 陣列時的事情。
- 不做 Graph Construction(建立相似度圖譜邊界):在上一篇文章的
build_entity_graph.py才有這個運算。
結論總結
export_to_lancedb.py 只做一件事:
- 把已經合法的 CtxFST Markdown 文件,轉換成穩定且無法被曲解的 JSON Contract。
當這件事完成後,所有的下游程式都能睡得安穩,因為它們手上的這份 chunks.json 就是打造智慧檢索系統最踏實的基底板塊!