Files
kbdb-ingest-plugin/system-dev/docs/HANDOFF-cloud-worker-2026-07-03.md
T

96 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HANDOFFcloud-worker 補跑 T-kb-skeleton2026-07-03
補跑今天 06:30 沒跑成的 cloud-worker routine,代做 sprint P3「T-kb-skeleton」(walking skeleton
notes → wiki 卡 → triples → kbdb-graph-plugin → 可查)。這份記錄只講**本 repo(ingest)份內**
撞的坑;graph 插件那邊的坑記在 `kbdb-graph-plugin` repo 自己的 commit message 裡。
## 現況起點(撞牆①):這個 repo 之前是空殼
clone 下來只有 `CLAUDE.md` / `README.md` / `contracts/ingest-candidate.json` / SDD 三件式
`docs/3-specs/ingest-pipeline/`),`tasks.md` 的 T0.5 到 T5 全部未打勾 —— **沒有任何程式碼**
不是「文件缺漏」而是「還沒開始寫」。任務指示裡「讀部署/使用文件,CLI 形態跑一次」的前提
(已有 CLI 可跑)不成立,只能自己把最小可行的 CLI 生出來才能往下走。
## 撞牆②:沒有 ANTHROPIC_API_KEY
沙盒環境變數裡沒有 `ANTHROPIC_API_KEY`(也搜過 `~/.arcrun/config.yaml` 等常見位置,
沒找到)。CLAUDE.md 說預設用 Haiku,但沒有直打 Anthropic API 的憑證。
**繞法(誠實記錄,非硬繞)**:這個沙盒本身跑在已登入的 Claude Code session 裡,
`claude` CLI 二進位可用且已授權。改用 `claude -p <prompt> --model haiku` 子行程呼叫,
這**是**真的 Haiku 推論(同一套 Anthropic 模型),只是呼叫路徑是「經 CC session 授權的
CLI 子行程」而非「直打 Anthropic API + API key」。正式版本上線前應該換回直打 API
(需要 leo 補 `ANTHROPIC_API_KEY` credential),因為:
- CLI 子行程呼叫有 session/交互開銷,不適合大量批次跑。
- 依賴一個已登入的 CC session 存在,不是獨立、可無人值守跑的服務憑證。
## 撞牆③:Haiku 偶爾吐 enum 外的 entity_type,會被 graph 端 422 擋整批
`contracts/ingest-candidate.json``nodes[].entity_type` 是 strict enum
person/event/product/market/org)。Haiku 萃取時偶爾猜出 enum 外的值(例如 "skill"),
graph 端 Zod `strict()` 驗證會直接拒收,422 打回整個 envelope(不是只丟該欄位)。
`scripts/ingest-cli.mjs` 加了送出前的過濾:非白名單值直接刪掉該欄位(寧可欄位缺,不要
整批被拒)。這是 ingest 端的責任(契約寫的很清楚「entity_type 沒把握就不要填」,
但沒堵住模型亂填的可能)。
## 撞牆④(比較重要):POST /triplets/ingest 不是原子的,client 中斷會留半殘資料
第一輪批次跑(3 張卡)用 `timeout 180` 包整支 CLI,結果卡 2`Prompt能力即拆解自己邏輯的能力.md`
萃取完 7 個 triplets 後,POST 到一半整支 node 行程被 `timeout` 砍掉 —— 但 graph 端已經
把其中 2 個 triplet 寫進去了(因為 `ingestEnvelope` 是 for-loop 逐條 `createTriplet`
不是一次性交易)。事後查 `GET /triplets` 證實:這張卡的 source_uri 底下只有 2/7 條,
不是 0 條也不是 7 條,卡在中間。
**更麻烦的是幂等性设计跟这个情境对不上**`ingestEnvelope` 的 dedup 邏輯是
「同 `source.uri` 下若已有 `content_hash` 相同的 active 記錄 → 整批 skip」。
因為檔案內容沒變(同一份卡片重跑),`content_hash` 一定相同 —— 意味著**這 2/7 的半殘狀態
會被後續重跑永久當成「已處理過」直接跳過,不會自動補完**。目前唯一的修復方式是人工介入
(改內容強制 hash 變化,或直接呼叫 graph 的單條 `POST /triplets` 補寫缺的 5 條)。
這次沒有為了掩蓋而重送假造一致的資料——是老實留著,另外用**沒有 timeout 限制**重新跑了
第 3 張卡(乾淨的 5/5),第 2 張的半殘狀態原樣留在 base 裡當作真實證據,可用下面的
curl 驗證。
建議記入正式設計(不是這次補跑範圍,留給 leo/graph 端評估):
- `POST /triplets/ingest` 對於「同 hash 但實際 triplet 數量對不上已寫入數量」的情況,
應該要能偵測並允許補完,而不是無條件 skip。
- 或者 ingest 端自己在 POST 前後做一次「數量核對」,不一致就重試/告警,而不是默默放過。
## 這次做了什麼(scripts/ingest-cli.mjs
最小可行 CLI,只走「路徑 A 簡化版」:不是拉「已存在的裸三元組」(notes 裡沒有這種東西),
而是拉**已經被之前一輪 cloud-worker 精耕過的 wiki 卡**`Leo/notes`
`system-dev/wiki/cards/notes/*.md`2026-07-02 產出的 3 張),對每張卡用 Haiku 萃取
triples + node gloss,組 `contracts/ingest-candidate.json` 規定的 envelope
POST 給 graph 插件的 `/triplets/ingest`
`source.uri` 格式從契約範例的 `github:<owner>/<repo>@<path>` 改成
`gitea:Leo/notes@<path>`(因為整個堆疊都在 Gitea 不是 GitHub,契約本身沒有嚴格要求
一定要 `github:` 前綴,只要求非空字串 + 穩定識別)。
## 未做(老實列出,不是這次範圍)
- T1 SourceAdapter 自動化(GitHub/Gitea API 拉 repo + per-file content-hash 自動判斷變動)— 這次是手動 clone + 手動指路徑。
- T2.2 cherry-pick `polaris/mira/tools/_kbdb_client.py` — 沒做,`ingest-cli.mjs` 是重新寫的最小版本,不是 cherry-pick 移植。
- T3.1/3.4 完整 extract SOPJSON-fail 升級 deep tier 等)— 這次沒做失敗重試/升級邏輯,Haiku JSON 解析失敗就直接跳過該卡。
- T4 跨 repo 織網 — 完全沒碰,這次只餵了 `Leo/notes` 一庫。
- 沒建 `package.json`/`wrangler.toml`T0.5)— `ingest-cli.mjs` 是純 Node script,不是 Worker,跟原規劃的「插件也是 CF Worker」形態不同,值得之後討論薄 CLI 到底要不要是 Worker。
## 驗證用 curl(供複驗)
```bash
BASE=https://kbdb-graph-plugin.leo21c.workers.dev
# 完整批次總覽
curl -sS "$BASE/triplets/stats" | jq .
# 卡1Gitea 卡)— 應該 7/7 乾淨
curl -sS "$BASE/triplets?subject=Gitea" | jq '.count'
# 卡2(Prompt能力卡)— 應該只有 2/7(半殘證據,見撞牆④)
curl -sS "$BASE/triplets?limit=100" | jq '[.triplets[] | select(.source_uri == "gitea:Leo/notes@system-dev/wiki/cards/notes/Prompt能力即拆解自己邏輯的能力.md")] | length'
# 卡3(程式化邏輯卡)— 應該 5/5 乾淨(第二輪無 timeout 補跑)
curl -sS "$BASE/triplets?limit=100" | jq '[.triplets[] | select(.source_uri == "gitea:Leo/notes@system-dev/wiki/cards/notes/程式化邏輯可圖解任何主題不限AI.md")] | length'
```