From dffefdcdc20796b07f1dd76e2cb12a8dbe21b128 Mon Sep 17 00:00:00 2001 From: richblack Date: Fri, 26 Jun 2026 12:43:26 +0800 Subject: [PATCH] =?UTF-8?q?init:=20kbdb-ingest-plugin=20=E9=AA=A8=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KBDB 堆疊的餵食器(最薄一層):拉 GitHub repo → 採取/萃取三元組 → 餵 graph 寫入端 → 織跨 vault 的圖。 - CLAUDE.md:上游指針 + ingest 鐵律(純餵食器/不碰儲存/不算向量/ 不帶查詢 MCP/部署繞 Actions/三守則) - README:定位 + 護城河命題(跨 vault 的圖) - contracts/ingest-candidate.json:凍結 envelope 契約(與 graph repo 同份) - docs/3-specs/ingest-pipeline/:SDD 三件式骨架(藍圖指向 InkStoneCo mira-dissolve) Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 9 ++ CLAUDE.md | 53 ++++++++++ README.md | 37 +++++++ contracts/ingest-candidate.json | 104 +++++++++++++++++++ docs/3-specs/ingest-pipeline/design.md | 38 +++++++ docs/3-specs/ingest-pipeline/requirements.md | 27 +++++ docs/3-specs/ingest-pipeline/tasks.md | 45 ++++++++ 7 files changed, 313 insertions(+) create mode 100644 .gitignore create mode 100644 CLAUDE.md create mode 100644 README.md create mode 100644 contracts/ingest-candidate.json create mode 100644 docs/3-specs/ingest-pipeline/design.md create mode 100644 docs/3-specs/ingest-pipeline/requirements.md create mode 100644 docs/3-specs/ingest-pipeline/tasks.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..98e3e72 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +node_modules/ +dist/ +.wrangler/ +.env +.dev.vars +*.bak +*.db +.DS_Store +/tmp/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..623da3b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,53 @@ +# CLAUDE.md — KBDB-ingest 插件開發規範 + +> **上游約束(InkStoneCo 總管)**:此 repo 是 InkStoneCo 子專案,受頂層知識庫約束。 +> 動工前讀 `github.com/uncle6me-web/InkStoneCo` 的 CLAUDE.md + `docs/3-specs/mira-dissolve/`(本插件的跨專案藍圖)。 +> **定位(leo 2026-06-26)**:此 repo = **KBDB-ingest 插件**(餵食器),堆疊裡最薄一層。掛在 [kbdb-graph-plugin](https://github.com/uncle6me-web/kbdb-graph-plugin) 上,graph 又掛在基本盤 `arcrun/kbdb` 上。 +> 關鍵鐵律:禁跨 repo 自動同步 Actions / 部署繞開 GitHub / API-as-Wall(不碰儲存)。 + +> 本檔由 Claude Code 自動讀取。所有此目錄下的開發必須遵守以下規則。 + +--- + +## 🔒 ingest 鐵律(leo 2026-06-26 拍板) + +1. **純餵食器,不碰儲存** — ingest 只 POST 候選 envelope 給 graph 的寫入 API,**不直連 base、不碰 D1/Vectorize、不碰任何表**。牆是「儲存」不是「運算」:准做萃取(LLM 呼叫),不准碰儲存。 +2. **不算向量 normalize** — 詞+gloss 的語義 normalize 歸 base optional embed 模組,不在 ingest(單檔視角算不準跨庫同義)。 +3. **不帶查詢 MCP** — ingest 是 ambient 餵食器,沒人互動「問」它。只帶**薄 ops CLI**(人手動觸發重萃)。觸發由 KBDB MCP 的 `refresh` 代理。 +4. **三守則(北極星 §5.2)** — 無環(graph 永不回呼 ingest)、純 API(走 graph HTTP 寫入端,不碰 graph 內部)、凍結契約(`contracts/ingest-candidate.json` 是唯一耦合面)。 +5. **部署繞開 GitHub Actions**(被 flag 教訓)— 走 wrangler;runtime 用 GitHub API/webhook 拉 repo 是 runtime 行為,不衝突。 + +--- + +## 職責(採取為主,extract 為 fallback,跨庫織網是主職) + +``` +路徑 A(用了 system-dev-template,優先): + GitHub 拉 → 本地 CC 已建好的三元組 + gloss → ingest 採取(拉)→ POST envelope +路徑 B(裸原文,fallback): + GitHub 拉 → 本地無三元組 → ingest extract(模型用戶可選)→ 候選 (s,p,o)+gloss → POST envelope +共同:跨 repo 匯總 → 織跨庫三元組關聯(ingest 主職,本地做不到) +``` + +- **採取 > 萃取**:本地萃成效更好(知識連結長在生產當下,LLM Wiki 指引);ingest 優先採取本地已建的。 +- **extract 模型用戶可選**(像 `/model`,選意圖非型號)+ 品質門檻(萃得出深層暗示 + 中文過關才入白名單);預設 Haiku;深萃 Claude via CC(走月費,不走 API)。 +- **跨 repo 織網**:匯總多 repo 三元組(單 repo 本地看不到的橋/異見)=ingest 不可取代的價值。 + +## 掛載架構 + +``` +基本盤 arcrun/kbdb(不動) + ↑ API +kbdb-graph-plugin(三元組領域 + 查詢 + POST /triplets/ingest 寫入端) + ↑ API(ingest 餵候選 envelope) +kbdb-ingest-plugin(本 repo,純餵食器) + · SourceAdapter:GitHub 拉 + content-hash(source.uri = github:owner/repo@path) + · 採取本地三元組 / extract(text, model) fallback + · POST envelope → graph 寫入 API +``` + +凍結契約:`contracts/ingest-candidate.json`(與 graph repo 同一份)。SDD:`docs/3-specs/ingest-pipeline/`(待立)。 + +## 命名 + +只養一個品牌 Arcrun;本元件功能名當水管:`ingest`。 diff --git a/README.md b/README.md new file mode 100644 index 0000000..a070016 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# kbdb-ingest-plugin + +KBDB 堆疊的**餵食器**——從 GitHub repo 拉知識庫,採取/萃取成三元組候選,餵給 [kbdb-graph-plugin](https://github.com/uncle6me-web/kbdb-graph-plugin),織成跨 vault 的圖。 + +堆疊裡最薄的一層: + +``` +base KBDB(arcrun/kbdb) ← 儲存(D1 三表 + CRUD API) + ↑ API +kbdb-graph-plugin ← 三元組領域 + 圖查詢 + ↑ API(ingest 餵候選) +kbdb-ingest-plugin(本 repo) ← 純餵食器:拉 + 採取/萃取 + 跨庫織網 +``` + +## 它做什麼 + +- **採取(優先)**:拉本地 CC 已建好的三元組 + gloss(用了精耕模板的 repo)。 +- **萃取(fallback)**:裸原文時自己萃成 (subject, predicate, object) + gloss;模型可選(預設 Haiku)。 +- **跨 repo 織網**:匯總多 repo 的三元組,織出單一 repo 看不到的跨庫橋與異見。 + +## 它不做什麼 + +- ❌ 不碰儲存(不直連 D1/Vectorize/表)——只 POST 候選給 graph 的寫入 API。 +- ❌ 不算向量 normalize(歸 base embed 模組)。 +- ❌ 不帶查詢 MCP(ambient 餵食器,只帶 ops CLI)。 + +## 命題 + +不是「更好的 notes RAG」(紅海),而是**跨所有 vault 的圖**——Obsidian/Logseq 結構上做不到的那層。護城河是你累積的圖,不是這份程式碼。 + +## 契約 + +`contracts/ingest-candidate.json` — ingest→graph 的凍結 envelope 契約(與 graph repo 同一份)。 + +--- + +InkStoneCo 子專案。開發規範見 `CLAUDE.md`,跨專案藍圖見 InkStoneCo `docs/3-specs/mira-dissolve/`。 diff --git a/contracts/ingest-candidate.json b/contracts/ingest-candidate.json new file mode 100644 index 0000000..0eb9d22 --- /dev/null +++ b/contracts/ingest-candidate.json @@ -0,0 +1,104 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "IngestCandidateEnvelope", + "description": "ingest 插件 → graph 插件 的唯一邊界契約。一個 envelope = 一個來源檔(canonical MD)一次萃取的產物。graph 收下後負責正規化/clusters/bridge_score/embed,並以『翻 triplet template 實例的 status slot』做取代:同 source.uri 出新 content_hash 時,graph 把舊 active 實例 PATCH 成 deprecated、append 新批 active(可查、可 rollback、可清),ingest 對這些一無所知。注意:這是【輸入候選】,不是【已存三元組】(後者見 triplet.json)。", + "type": "object", + "required": ["source", "extractor", "triplets"], + "additionalProperties": false, + "properties": { + "source": { + "type": "object", + "description": "這批候選來自哪個 canonical MD。同時是『指回原文的指標』與『append-only 快照鍵』。", + "required": ["uri", "content_hash"], + "additionalProperties": false, + "properties": { + "uri": { + "type": "string", + "minLength": 1, + "description": "來源的穩定識別 = 快照鍵 + get_source 指標。格式: 'github:/@' 例如 'github:uncle6me-web/LLM-Wiki-for-n8n@.claude/wiki/graph-rag.md'。同一 uri 的後續 envelope 會【取代】(latest-wins)前一批,而非疊加。" + }, + "content_hash": { + "type": "string", + "minLength": 1, + "description": "來源檔內容的 hash(快照鍵)。graph 比對: 與該 uri 現存快照同 hash → no-op 跳過;不同 → 寫新快照。" + }, + "anchor": { + "type": "string", + "description": "檔內定位(heading slug / block id),供 get_source 精準回跳。選填。" + }, + "commit": { + "type": "string", + "description": "git commit sha(可追溯)。選填。" + }, + "block_id": { + "type": "string", + "description": "向後相容: Logseq Block ID(= 既有 triplet.json 的 source_block_id)。非 git 來源時用。選填。" + } + } + }, + "extractor": { + "type": "object", + "description": "萃取出處。供『升級率』觀測與『要不要重萃』判斷;不影響圖結構。", + "required": ["model", "tier"], + "additionalProperties": false, + "properties": { + "model": { + "type": "string", + "minLength": 1, + "description": "產生這批的模型,例如 'workers-ai/@cf/...' 或 'claude-sonnet-4-6'。" + }, + "tier": { + "type": "string", + "enum": ["shallow", "deep"], + "description": "shallow = Workers AI 淺萃;deep = Claude API 深萃(淺萃 JSON-fail/過稀時升級)。" + }, + "extracted_at": { + "type": "integer", + "description": "萃取的 unix 時間(秒)。快照排序用。選填(graph 收件時可補)。" + } + } + }, + "nodes": { + "type": "array", + "description": "節點層附帶資訊(選填)。entity_type 與 gloss 是【節點】屬性,不是【邊】屬性 → 放這裡,不放 triplets。graph 用 gloss 去 embed(每節點一句,不是裸詞)、用 entity_type 去 typing。", + "items": { + "type": "object", + "required": ["name"], + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "minLength": 1, + "description": "節點名(須對應某 triplet 的 subject 或 object 原字面)。" + }, + "gloss": { + "type": "string", + "description": "一句話描述,供 embedding。例如 'Graph RAG — 用關係遍歷檢索、保住異見的 RAG 變體'。選填(建議 deep tier 產出)。" + }, + "entity_type": { + "type": "string", + "enum": ["person", "event", "product", "market", "org"], + "description": "節點類型提示。graph 最終決定;ingest 只提示。選填。" + } + } + } + }, + "triplets": { + "type": "array", + "minItems": 1, + "description": "邊(關係)。ingest 只產原始 (s,p,o) + confidence。", + "items": { + "type": "object", + "required": ["subject", "predicate", "object"], + "additionalProperties": false, + "properties": { + "subject": { "type": "string", "minLength": 1, "description": "主詞(實體名,須與 nodes[].name 對得上若有提供)" }, + "predicate": { "type": "string", "minLength": 1, "description": "謂詞(關係)" }, + "object": { "type": "string", "minLength": 1, "description": "受詞(目標實體或值)" }, + "confidence":{ "type": "number", "minimum": 0, "maximum": 1, "default": 1.0, "description": "萃取可信度。淺萃可附自評;graph 不據此過濾,只記錄。" } + } + } + } + }, + "$comment": "禁止欄位(graph 領域,ingest 絕不可送): id / clusters / bridge_score / created_at / updated_at / 以及 triplet 上的 subject_entity_type|object_entity_type(類型只走 nodes[])。送了即違反 ingest=純餵食器的邊界,graph 應拒收或忽略。" +} diff --git a/docs/3-specs/ingest-pipeline/design.md b/docs/3-specs/ingest-pipeline/design.md new file mode 100644 index 0000000..68f7faf --- /dev/null +++ b/docs/3-specs/ingest-pipeline/design.md @@ -0,0 +1,38 @@ +# ingest pipeline — Design + +> 對應 requirements.md。**架構設計(envelope 契約、職責切割、normalize 歸屬、MCP 邊界、模型策略)在 InkStoneCo `docs/3-specs/mira-dissolve/design.md`。本檔只放 ingest 內部設計。** + +## 1. 資料流 + +``` +GitHub repo MD(精耕 LLM Wiki) + │ commit / content-hash 變動 + ▼ +SourceAdapter(拉 + content-hash) + │ + ├─ 路徑 A:本地已建三元組+gloss → 採取(拉) + └─ 路徑 B:裸原文 → extract(text, model) → 候選 (s,p,o)+gloss + │ + ▼ 跨 repo 匯總(織跨庫關聯) + ▼ POST envelope(contracts/ingest-candidate.json) +graph 寫入端 POST /triplets/ingest +``` + +## 2. envelope(= contracts/ingest-candidate.json) + +形狀與職責切割見 InkStoneCo design §3。ingest 給:原始 s/p/o + source.* + extractor.* + nodes[].gloss/type + confidence。**禁送**:id / clusters / bridge_score / 時間戳 / 邊上 type(graph 算)。 + +## 3. extract 模型策略 + +見 InkStoneCo design §6.6:用戶選意圖非型號、品質門檻白名單(深層暗示+中文)、預設 Haiku、深萃 Claude via CC、難度隨來源分層(AI 報告弱模型夠/人類 vault 須好模型)。 + +## 4. 觸發 + +被 KBDB MCP `refresh` 代轉(人發起,非自動 fan-out)。flag 紅線見 InkStoneCo design §6.7:禁 Actions/webhook 自動觸發。 + +## 5. cherry-pick 來源 + +- `polaris/mira/tools/_kbdb_client.py` → 純餵食器 client。 +- `polaris/mira/arcrun/wiki_synthesis.yaml` classify 節點 → extract。 +- 兩個 KBDB skill block(精耕規則 prompt)。 +詳 InkStoneCo requirements §6。 diff --git a/docs/3-specs/ingest-pipeline/requirements.md b/docs/3-specs/ingest-pipeline/requirements.md new file mode 100644 index 0000000..2eb07f9 --- /dev/null +++ b/docs/3-specs/ingest-pipeline/requirements.md @@ -0,0 +1,27 @@ +# ingest pipeline — Requirements + +> 建立:2026-06-26 +> **跨專案藍圖在 InkStoneCo `docs/3-specs/mira-dissolve/`(R7 攝入分工、§6.6 模型策略、§6.7 MCP 邊界)。本 SDD 只放 ingest 內部實作細節。** +> 凍結契約:`contracts/ingest-candidate.json`(與 graph repo 同一份,唯一耦合面)。 + +## 範圍 + +ingest 插件內部:SourceAdapter(拉)+ 採取/extract + 跨庫織網匯總 + POST envelope 給 graph。**不涉**graph 內部圖演算法、不涉 base 儲存。 + +## 需求 + +- **R1 SourceAdapter**:GitHub 拉 repo + content-hash(`source.uri = github:owner/repo@path`)。webhook/排程觸發,或被 KBDB MCP `refresh` 代轉觸發。 +- **R2 採取(路徑 A,優先)**:用了 system-dev-template 的 repo → 本地 CC 已建三元組 + gloss → ingest 拉取,不重萃。 +- **R3 extract(路徑 B,fallback)**:裸原文 → 萃 (s,p,o)+gloss。模型用戶可選(意圖非型號)+ 品質門檻白名單(深層暗示 + 中文);預設 Haiku;深萃 Claude via CC;JSON-fail 升級閘。 +- **R4 跨 repo 織網**:匯總多 repo 三元組(單 repo 看不到的橋/異見)=主職。 +- **R5 輸出**:POST envelope(嚴格符合 contract)給 graph `POST /triplets/ingest`,不直連 base。 +- **R6 ops CLI**:手動觸發重萃。不帶查詢 MCP。 + +## 約束(沿用 ingest 鐵律,見 CLAUDE.md) + +- 純餵食器、不碰儲存、不算向量、不帶查詢 MCP、部署繞 Actions、三守則(無環/純 API/凍結契約)。 + +## 待對端 + +- graph 的 `POST /triplets/ingest` 寫入端(InkStoneCo T3.3,graph repo 實作)。 +- 模型品質門檻測試集(含中文 + 人類暗示樣本,InkStoneCo §6.6)。 diff --git a/docs/3-specs/ingest-pipeline/tasks.md b/docs/3-specs/ingest-pipeline/tasks.md new file mode 100644 index 0000000..52dbaac --- /dev/null +++ b/docs/3-specs/ingest-pipeline/tasks.md @@ -0,0 +1,45 @@ +# ingest pipeline — Tasks + +> 唯一進度來源。狀態:[ ] 未開始 [🔄] 進行中 [x] 完成 [⏸] 卡住 +> 跨專案藍圖:InkStoneCo `docs/3-specs/mira-dissolve/`。 + +## T0 repo 骨架(本輪) + +- [x] 0.1 建 public repo `uncle6me-web/kbdb-ingest-plugin` +- [x] 0.2 CLAUDE.md(上游指針 + ingest 鐵律)+ README + .gitignore +- [x] 0.3 `contracts/ingest-candidate.json`(從頂層 SDD 複製,凍結契約) +- [x] 0.4 SDD 三件式骨架 +- [ ] 0.5 package.json / tsconfig / wrangler.toml(參考 kbdb-graph-plugin) + +## T1 SourceAdapter(R1) + +- [ ] 1.1 GitHub 拉 repo(runtime API/clone,非 Actions) +- [ ] 1.2 content-hash(per-file,source.uri = github:owner/repo@path) +- [ ] 1.3 被 KBDB MCP `refresh` 代轉觸發的接口 + +## T2 採取(R2,路徑 A 優先) + +- [ ] 2.1 拉本地 CC 已建三元組 + gloss(用了 system-dev-template 的 repo) +- [ ] 2.2 cherry-pick `polaris/mira/tools/_kbdb_client.py` → 改純餵食器(POST envelope,不寫 KBDB) + +## T3 extract(R3,路徑 B fallback) + +- [ ] 3.1 cherry-pick `wiki_synthesis.yaml` classify / 兩 skill block +- [ ] 3.2 模型用戶可選 + 品質門檻白名單(預設 Haiku,深萃 Claude via CC) +- [ ] 3.3 模型測試集(中文 + 人類暗示樣本,轉回歸測試)— deferred,先跑預設 +- [ ] 3.4 JSON-fail 升級閘(淺萃失敗升 deep) +- [ ] 3.5 第一版不 embed(embed 等 base vectorize,InkStoneCo T2.4) + +## T4 跨 repo 織網(R4,主職) + +- [ ] 4.1 匯總多 repo 三元組 + +## T5 輸出 + CLI(R5/R6) + +- [ ] 5.1 POST envelope 給 graph `POST /triplets/ingest`(嚴格符合 contract)⏸ 待 graph 寫入端(InkStoneCo T3.3) +- [ ] 5.2 薄 ops CLI(手動重萃);不帶查詢 MCP + +## 阻擋項 + +1. ⏸ T5.1 依賴 graph `POST /triplets/ingest`(InkStoneCo T3,待 graph repo 實作)。 +2. ⏸ embed 依賴 base vectorize(InkStoneCo T2.4)。第一版不 embed 可先動。