feat(ingest): 機械式 wiki 卡片→KBDB ingest(Arcrun#8 Phase A,dry-run 驗證)
新 ingest 模型(無 LLM,取代舊 raw→Haiku 路): - 來源 = system-dev/wiki/cards/**/*.md(精耕卡)+ ## 實體/## 關聯 typed-edge + [[wikilink]] - 卡片→base entry(metadata.embed=true);typed-edge/wikilink→graph triplet - 純機械決定性,零 token 形式 = Arcrun workflow(cron drain + Gitea webhook delta)+ 新自訂零件 km_wiki_card_parse 不撞頂設計:一卡一 tick + 超大卡以 source_uri anchor 自動分段; graph fan-out 精確 = 7+4N+M+D,dry-run 對 notes 三卡上限 33 subrequest(<50)。 Phase A:不部署、不寫 live。含純核心 + dry-run 證據(3 entries/15 triplets/16 nodes)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HJiLCRUU2o3aSpPEzVCt2o
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
name: km_wiki_ingest_drain
|
||||
description: >
|
||||
Phase 0 限速 drain:cron 每 tick 只處理「一張卡」→ 機械解析成 entry + triplet envelope
|
||||
→ 冪等寫 KBDB(base entry / graph triplet)。反覆跑直到全庫 drain 完。
|
||||
來源=repo 的 system-dev/wiki/cards/**/*.md(人工精耕卡,非裸筆記,無 LLM)。
|
||||
穩態(Gitea push webhook 只處理 delta)見檔尾 §穩態變體。
|
||||
|
||||
# ── 為什麼「一 tick 一卡」=根治 07_01 的 Too many subrequests ──
|
||||
# graph worker 處理一次 POST /triplets/ingest 的 subrequest = 7 + 4*N_triplets + M_nodes + D_deprecated。
|
||||
# 07_01 炸點:單一 envelope 吞整檔 N=11,M=10 → 61 > 50(CF bundled 上限)→ 半殘。
|
||||
# 對策:① 一卡一 tick(天然小批,notes 卡 ~N4/M5 → est 28~33,穩壓 50 下)
|
||||
# ② km_wiki_card_parse 會自動把超大卡以 source_uri anchor 分段(每段獨立冪等)。
|
||||
# ⟹ 任何單一 graph 呼叫都不會再破頂。
|
||||
|
||||
flow:
|
||||
- "watch_cron >> ON_SUCCESS >> pick_next_card"
|
||||
- "pick_next_card >> ON_SUCCESS >> fetch_card"
|
||||
- "fetch_card >> ON_SUCCESS >> parse_card"
|
||||
- "parse_card >> ON_SUCCESS >> upsert_entry" # 卡片 → base entry(embed=true),冪等
|
||||
- "upsert_entry >> ON_SUCCESS >> post_envelopes" # wikilink/typed-edge → graph triplet
|
||||
- "post_envelopes >> 對每個 envelope >> post_one_envelope" # 分段時多段,各段獨立冪等
|
||||
|
||||
config:
|
||||
# 1) 排程 tick:慢推。每 2 分鐘一張卡=限速(Phase 0 唯一需要 rate-limit 之處)。
|
||||
watch_cron:
|
||||
component: cron
|
||||
cron_expr: "*/2 * * * *"
|
||||
description: "每 2 分鐘 drain 一張卡(限速慢推,避免 CF 額度與 subrequest 壓力)"
|
||||
|
||||
# 2) 取下一張待處理卡(cursor drain)。用 Gitea contents API 列 cards 目錄 + 一個游標 block
|
||||
# 記「處理到哪」。回傳單一 { rel_path, download_url, content_hash?(git blob sha) }。
|
||||
# 註:list + cursor 的細節可用 http_request(Gitea API) + set/string_ops 組;此處給語意佔位。
|
||||
pick_next_card:
|
||||
component: http_request
|
||||
method: GET
|
||||
url: "https://git.uncle6.me/api/v1/repos/{{repo}}/contents/system-dev/wiki/cards?ref={{ref}}"
|
||||
headers:
|
||||
Authorization: "token {{gitea_token}}"
|
||||
Accept: "application/json"
|
||||
# 下游用 filter/set 取「游標之後第一張、且 .md、且非 00-INDEX」的一張。
|
||||
|
||||
# 3) 抓卡片全文(Gitea raw)。
|
||||
fetch_card:
|
||||
component: http_request
|
||||
method: GET
|
||||
url: "{{pick_next_card.next.download_url}}"
|
||||
headers:
|
||||
Authorization: "token {{gitea_token}}"
|
||||
|
||||
# 4) ★ 機械解析(新自訂零件,無 LLM、無 fs/網路,stdin→stdout JSON)。
|
||||
# input:卡片全文 + 相對路徑 + repo;output:{ entry, envelopes[] }(envelope 已分段、已估 subrequest)。
|
||||
# 參考實作見 lib/card-to-envelope.mjs(可移植成 Go/WASM 元件)。
|
||||
parse_card:
|
||||
component: km_wiki_card_parse
|
||||
content: "{{fetch_card.data.body}}"
|
||||
rel_path: "{{pick_next_card.next.rel_path}}"
|
||||
repo: "{{repo}}"
|
||||
budget: 40 # subrequest 目標上限(留 10 給 D_deprecated)
|
||||
|
||||
# 5) 卡片 → base entry,冪等 upsert(page_name 當鍵;找到 PATCH、沒有 POST)。
|
||||
# metadata.embed=true → base embed 模組會補嵌 → 語意可搜。
|
||||
upsert_entry:
|
||||
component: kbdb_upsert_block
|
||||
api_key: "{{kbdb_api_key}}"
|
||||
kbdb_url: "{{kbdb_url}}"
|
||||
page_name: "{{parse_card.entry.page_name}}"
|
||||
type: "{{parse_card.entry.entry_type}}"
|
||||
content: "{{parse_card.entry.content}}"
|
||||
source: "{{parse_card.entry.metadata.source}}"
|
||||
tags_json: "{{parse_card.entry.tags_json}}"
|
||||
# ⚠️ metadata.embed=true / content_hash 需經 base /entries 帶 metadata_json 落地;
|
||||
# 若 kbdb_upsert_block 尚未透傳 metadata_json,改用 http_request 直打 base POST/PATCH /entries
|
||||
# 帶 body_json.metadata_json(見 description.md §entry 冪等)。
|
||||
|
||||
# 6) triplet envelope(可能多段)→ 逐段 POST graph /triplets/ingest。
|
||||
# graph 端 per-source(uri+content_hash) 冪等:同 hash 整包 no-op;分段各段 uri 不同 → 各自獨立冪等。
|
||||
post_envelopes:
|
||||
component: foreach_control
|
||||
items: "{{parse_card.envelopes}}"
|
||||
item_key: envelope
|
||||
|
||||
post_one_envelope:
|
||||
component: http_request
|
||||
method: POST
|
||||
url: "{{graph_url}}/triplets/ingest"
|
||||
headers:
|
||||
Content-Type: "application/json"
|
||||
X-Arcrun-API-Key: "{{graph_api_key}}"
|
||||
body_json: "{{envelope}}" # envelope 已符合 ingest-candidate.json 契約(禁止欄位已排除)
|
||||
|
||||
# ── 執行環境變數(部署時注入;此檔不放密鑰)──
|
||||
# repo=Leo/notes ref=main gitea_token=<GITEA_TOKEN>
|
||||
# kbdb_url=https://arcrun-kbdb.leo21c.workers.dev kbdb_api_key=<partner key>
|
||||
# graph_url=<graph plugin base url on leo21c> graph_api_key=leo
|
||||
#
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# §穩態變體(km_wiki_ingest_delta):Gitea push webhook → 只處理 delta 檔
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# 觸發 = Gitea repo Settings → Webhooks → 指向 arcrun(cypher-executor) 的 workflow webhook URL。
|
||||
# ⚠️ 這是 Gitea → Cloudflare(arcrun),非 GitHub Actions → 不觸 GitHub flag 紅線(D4/D20)。
|
||||
# 只把上面 flow 的 watch_cron/pick_next_card 換成:
|
||||
# input(webhook payload)>> ON_SUCCESS >> collect_changed
|
||||
# collect_changed = 從 payload.commits[].{added,modified} 濾出 system-dev/wiki/cards/**/*.md
|
||||
# >> foreach 檔 >> fetch_card >> parse_card >> upsert_entry >> post_envelopes(同上)
|
||||
# 量小、天生不撞頂、不需限速;靠 graph/entry 冪等自動 skip 未變檔。
|
||||
Reference in New Issue
Block a user