6500d7fda4
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
96 lines
6.4 KiB
Markdown
96 lines
6.4 KiB
Markdown
# HANDOFF:cloud-worker 補跑 T-kb-skeleton(2026-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 SOP(JSON-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 .
|
||
|
||
# 卡1(Gitea 卡)— 應該 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'
|
||
```
|