Ian Chou's Blog

實戰指南:從 .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 個步驟:

  1. 讀取 Frontmatter:解析 Markdown 檔頭的 YAML Metadata。
  2. 讀取內文 XML:掃描並截取內文裡被 <Chunk id="...">...</Chunk> 包裹的實質內容。
  3. 對齊兩者:把 Frontmatter 裡的 chunks[] 宣告,與內文區塊對應的 id 一一配對。
  4. 輸出標準 JSON:轉成符合 schema.json 驗證機制的乾淨格式。

如果一篇文章沒有包含 chunks: 陣列,它不會硬轉,而是優雅地印出 Warning 並跳過。

Exporter 設計哲學:文件不合法就警告 / 跳過;文件合法就輸出乾淨 JSON。

CLI 指令與最小用法

這支 Python 腳本具備很直觀的 Command Line 介面:


Output 協定:這包 JSON 到底長怎樣?

它的最終輸出永遠是固定型態的 Object,這份格式正是符合我们在上一篇定義的 schema.json 通訊協定。

核心頂層有兩個節點:

{
  "entities": [...],
  "chunks": [...]
}

(註解:目前協定規範了可加上 title,但這支 Reference Exporter 目前主要聚焦為 entitieschunks)

關於 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.jsonctxfst-spec.md 規範中,tags 已經正名為 Optional (選填),真正必須填寫的只有:id, context 以及 content 這三本柱。)

關於 entities 目錄聚合

這裡有個設計尤為巧妙,並且值得特別強調的轉換邏輯:

Exporter 不會重新幫你從文章中抽取(Extract)新的 Entities。它只會把 YAML 裡已經存在的 entities[] 原樣搬過來。

更好用的是,當你**「匯出整個知識庫目錄(批次匯出)」**時,腳本會怎麼處理散落在數十篇 Markdown 中的同名 Entities 呢?

答案是:依 id 自動去重 (Deduplication)。

這巧妙的幫我們做到了:「跨多文件匯出時,Entity Catalog 可以自動自然地合併成一個無雜訊的全域節點集(Global Node Catalog)」。


強調:這支 Exporter 不做什麼

這一點非常重要。許多人會誤以為這支腳本就是 Ingestion Pipeline 的「全集」,期待它跑完就出現一個 GraphRAG 大絕招。

這支腳本被設計成最純粹的狀態轉換層。所以它:

結論總結
export_to_lancedb.py 只做一件事:

當這件事完成後,所有的下游程式都能睡得安穩,因為它們手上的這份 chunks.json 就是打造智慧檢索系統最踏實的基底板塊!