從搜尋到導航:我把 10,005 筆知識做成 Knowledge Atlas
今天,我把自己的知識庫從「可以搜尋」往前推成了「可以看懂」。
原本的 Knowledge MCP 已經能用全文與向量搜尋回答問題,但當資料累積到 18 個 namespace、941 份來源文件與 10,005 筆條目後,我開始遇到另一種問題:搜尋可以告訴我哪幾段文字相關,卻很難讓我快速理解整座知識庫的形狀。
哪些 namespace 最大?哪些內容是手寫整理,哪些是從研究文件切出來的章節?某個命中位於哪一份來源、哪一章、前後還有哪些段落?這些問題不是再加一個搜尋框就能解決的。
所以我做了 Knowledge Atlas:一個只在家庭內網開放、完全唯讀的知識導航介面。
先還原真實結構,不急著畫一張漂亮的圖
最容易做的版本,是把所有條目當成節點,再用 embedding 距離畫成一張知識圖譜或 UMAP 星圖。它看起來很有未來感,卻可能把「數學上靠近」誤寫成「真的有關係」。如果沒有穩定的 relation schema、producer 與 consumer,那些線很可能只是裝飾。
我選擇先忠實還原已經存在、而且能被直接證明的結構:
namespace
├─ curated knowledge
└─ synced sources
└─ file
└─ section
└─ entry detail + provenance
正式資料裡有 358 筆 curated knowledge,以及 9,647 筆由文件同步而來的 file-sync 條目。Atlas 不把兩者混成同一種東西,也不拿 UUID 或檔案修改時間假裝成知識演進史。每一層只呈現資料庫能證明的來源、順序與狀態。
Overview 有依真實比例呈現 namespace 的 treemap,但它只是概覽,不是唯一入口。旁邊保留可排序、可用鍵盤操作的 tree table;手機版預設也以表格和單欄導覽為主。視覺化若沒有等價的文字入口,就只是漂亮的障礙。
瀏覽器看到的是快照,不是資料庫
Atlas 沒有讓瀏覽器直連 Knowledge backend,也沒有把 MCP 的 Ed25519 私鑰放進前端。它沿用既有的內網 status service,在伺服器端以 SQLite URI mode=ro 開啟 canonical indexes,再加上 PRAGMA query_only=ON。
資料先被整理成記憶體 snapshot,路由只提供固定格式的 metadata 與 lazy entry JSON。首批 metadata 原始大小約 12.15 MB,gzip 後約 2.21 MB;全部正文若展開約 22.58 MB,因此正文只在使用者打開條目時載入。opaque lookup key 取代 filesystem path,未知 key 與路徑穿越形狀都只會得到 404。
這個設計還有一個我很在意的故障語意:refresh 失敗時,不會把空白畫面冒充成最新結果。服務保留上一份成功 snapshot,並明確標示 stale 或 error。舊資料可以暫時被看見,但不能被偽裝成新資料。
Review Basket 可以整理證據,但不能直接刪資料
Atlas 裡有一個 review basket,可以把可疑或待整理的條目暫存在瀏覽器 localStorage,最後匯出 JSON 或 Markdown manifest。
它刻意沒有 edit、delete 或 apply 按鈕,也沒有任何寫入 endpoint。匯出物只是 review evidence,後續仍要經既有的 manifest、備份與人工審查流程。這個限制看似保守,實際上是在保護產品邊界:探索介面不應該因為「順手」就變成另一套未受控的知識管理後台。
LAN-only 不是一句標籤,而是三層邊界
Atlas 掛在既有的內網服務埠,沒有新增 ngrok tunnel。網路層由防火牆只允許家庭 LAN,應用層再檢查來源 allowlist;前端則有 CSP、no-store、nosniff 與 frame deny。
對外提供 MCP 的 backend 在整次發布中沒有重啟,也沒有改線。Atlas 讀取同一份 canonical indexes,但瀏覽器永遠不接觸對外服務的認證材料。把「資料來源共用」和「攻擊面共用」拆開,是這次架構裡最重要的界線之一。
發布時又抓到一個熟悉的假綠燈
部署時,我先結束 Windows 排程裡的 status service。排程操作回報成功,但舊的子行程仍然占著監聽埠。
如果只看排程狀態,這一步已經完成;如果看真正承載流量的 listener,它根本還活著。最後我以埠號找到 PID、核對完整 command line,確認它只屬於 status service 後精確終止,再驗證埠真的消失,才啟動新版本。
這和我最近反覆遇到的教訓完全一致:控制面的成功,不等於資料面或執行面的成功。驗收必須觀察真正做事的那一層。
我量了什麼,也誠實留下沒量到的東西
正式 snapshot 建立耗時 0.6716 秒。20 次正式機 loopback 測試中,gzip metadata route 的 p95 是 52.90 ms,lazy entry route 的 p95 是 22.09 ms,兩者都低於 200 ms gate。服務行程的 working set 約 44.9 MB,private memory 約 106.5 MB。
Codex 完整回歸得到 98 tests / OK;Claude 在獨立 release gate 以另一個 runner 得到 109 passed、52 個 subtests、零失敗,最後判定 APPROVE。兩邊計數不同是測試 runner 與 subtest 口徑差異,不把數字湊成一致反而比較誠實。
仍有一項沒有完成:當時應用內瀏覽器沒有可用 instance,因此沒有取得 375、768、1024、1440 四個斷點的 runtime screenshot,也沒有實際鍵盤與 screen reader 的瀏覽器證據。static UI gate、HTTP contract、security headers 與 responsive CSS 都通過,但我沒有把它包裝成 browser PASS。這會留在後續驗收清單裡。
這個 MVP 真正改變的是問題的問法
Knowledge Atlas 沒有讓知識庫突然變聰明,也沒有聲稱解決了知識圖譜。它做的是更基礎、但更關鍵的事:讓我看見自己到底擁有什麼、它從哪裡來、它位於什麼脈絡,以及哪些地方值得下一輪整理。
搜尋回答「哪一段可能相關」;Atlas 回答「這整座知識庫是怎麼組成的」。
在做更炫的 semantic map、cross-namespace edges 或 query telemetry 之前,我會先觀察這張忠實的地圖能否解決真實任務。因為知識工具最危險的失敗,不是畫面不夠漂亮,而是把推測畫得像事實。