--- status: draft note: leo 2026-07-19「藏書地圖可以走」=design confirmed 可動工(M1/M2 起跑);active 標記待對照 Arcrun 現行 active SDD 收斂。 --- # library-map(藏書地圖)— Design ## 1. 資料結構:`library_map` Template(照 leo spec §3) 每庫一個 map block。slots:`library`(text)/`narrative`(text,抽自該庫頂層 wiki 首段)/`top_entities`(array,degree 排序 top-N)/`relation_profile`(array,predicate 分布=庫的性格)/`bridges`(array,同 entity 跨庫 join)/`triplet_count`(number)/`commit_hash`(text)/`status`(active|superseded)。 三元組按庫過濾:先核實現況——rag-ingest-cards v2 的 triplet 已帶 `source_uri`、entries 已有 `metadata.library`(portal-auth P1);若 triplet 定位庫仍不足,在 Triplet **Template schema** 加 optional `library` slot(改 template 不動表)。 > **核實結果(2026-07-19,M1 對 prod 實查)**:triplet template(prod 名 `triplet`,非 MCP 註解寫的 `graph_triplet`)slots=subject/predicate/object/…/source_uri/…,**無 `library` slot**;entries 的 `metadata.library` 機制在但既有資料未標記(`?library=kb` → 0 筆)→ **定位庫不足,走預案**:M1 於 recompute 時冪等地在 triplet template 加 optional `library` slot(`ensureTripletLibrarySlot`,改 template 不動表);slot 值由 ingest 端補寫(M3)。過渡期 `/map/recompute` 收 optional `source_prefix` 參數,對「無 library 值的舊 triplet」以 `source_uri` 前綴歸庫(如 `gitea:Leo/kb@`)——前綴由 caller 提供,base 不寫死 URI 語意;bridges 的「對面庫」只認 library slot 標記值,M3 backfill 前會偏稀疏(誠實限制)。 ## 2. 重算的家:SQL 只能住基本盤(D6 推論,本 SDD 關鍵歸屬裁定) degree 排序/predicate 統計/跨庫 join 是聚合 SQL——**D6 鐵律:插件與 workflow 全程禁 SQL**。故重算實作=**kbdb base 新增內建端點 `POST /map/recompute?library=`**(基本盤內 SQL 合法,一段交易:算→建新 block→舊標 superseded)。ingest workflow(A 類)尾端用 `http_request` 呼此端點——A 類接 B 類 API,牆不破。 讀取端點 `GET /map`(全館,每庫一行)/`GET /map/:library`(詳圖),MCP/GUI 共用。 ## 3. 更新機制(leo spec §4) ingest 完成 → 取本次 commit diff 涉及的庫集合 → 逐庫呼 `/map/recompute`。無 cron 全量。首次 backfill=對每個既有庫手動各呼一次(installer/腳本一行)。 > **2026-08-08 更正(matrix/arcrun CC)**:上面這條「ingest 尾端接鏈」的路徑本身沒錯(`arcrun-rag` > 那條管線也確實接了,`670f38a`),但它**只覆蓋接了鏈的那一條 ingest**——這個 repo 內(kbdb/ > cypher-executor/mcp)從沒有任何呼叫點會打 `/map/recompute`,導致沒手動 backfill 過的租戶 > (絕大多數)恆空,且拖了三週沒人發現/接上(見 `system-dev/wiki/mistakes.md` 08-08 段)。 > **改法**(leo 否決「降級成即時聚合、不維護快取」的提案,因為那會丟失 narrative 這類摘要 > 本體):`GET /map`/`GET /map/:library` 讀端自己核對即時三元組數,落差就地呼叫既有的 > `recomputeLibraryMap` 補算(`kbdb/src/actions/library-map.ts` `ensureFreshLibraryMaps`)。 > 聚合 SQL 仍只住 kbdb base(沒有違反 §2 的歸屬裁定),只是觸發時機從「等外部呼叫」改成 > 「讀的當下順手核對」——這條讀端機制本身就是「無 cron 全量」的自動 backfill,取代了 > 「首次 backfill=對每個既有庫手動各呼一次」這句手動步驟。細節見 > `system-dev/docs/3-specs/library-map/tasks.md` M3 段。 ## 4. 注入(leo spec §5,本功能重點) - **MCP instructions**:arcrun-mcp 啟動組 instructions 時拉 `GET /map` 嵌入(快取+TTL,或每次連線現拉——量數百 token,現拉可接受)。 - **`get_map` 工具**:薄殼呼 `/map`/`/map/:library`。與 #68 的 kbdb_graph_neighbors 同族(D17 KBDB MCP 面),可同 PR 或緊接。 - **GUI**:console/portal 首頁 render `GET /map`——庫卡片(narrative+top entities+規模)+跨庫 bridges 一覽;點庫進該庫搜尋。 ## 5. 與 D30 檢索治本的連動(本 SDD 新增,超出原 spec) leo 終景「補充的只 vectorize entities+一句話」與本件是同一素材的兩用:map block 的 narrative+top_entities=卡級/庫級摘要嵌入單位。落法:map block 的 content 欄寫成可嵌人話(`{library}:{narrative}。核心:{top_entities}`),標 embed → **semantic 第一跳打 map 層做庫路由**,第二跳才進庫內(D30「向量做路由、定稿給 LLM 讀」)。#58/#59 的殘影/模型問題在 map 層天然緩解(block 少、可整層重刷)。 ## 6. Retrieval 流程(改造後,leo spec §6 原文) session 啟動 → instructions 已含全館地圖(push 零查詢)→ 需細節 get_map(library) → graph query 找 entity/關係 → 依 index 取 repo wiki 定稿(真相源不變)。 ## 7. 歸屬 kbdb base 端點=B 類(PR);ingest 尾端接鏈=A 類(workflow 改版,arcrun-rag/個人庫同款);MCP=B 類;GUI=B 類(console 路由)。全框架件:寫一次,arcrun-rag/Mira/未來客戶全實例受益(bundle 更新分發)。