feat(collector): 結構先行——掃描完幾秒內就能問「資料夾裡有什麼」,不等 LLM 不等額度(InkStoneCo#43)

leo 08-15 量測:266 檔真正算的 ~20 分鐘/<$0.5,使用者卻等 ~4 天(多在等
Workers AI 每日額度);而「有哪些檔案/最近改了什麼」本地掃一遍就有答案
(1,335 檔實測 0.254s),卻因 part_of 只在雲端 parse_card(萃取之後)生成
而排同一條隊、撞同一面額度牆。

- collector/inventory.go:Scan() 後、萃取迴圈前(額度冷卻閘之外),把 manifest
  現況做成機械總覽卡(檔案清單/最近改動/目錄 part_of)POST 既有 rag_ingest_card
  ——零 LLM、零新 workflow、既有實例直接受益;自帶同頁名 upsert 防重複
- 冪等:內容 sha256 記 manifest.inventory_hash;失敗 10 分鐘退避(防 t195 同款
  每 5 秒重撞);額度失敗訊息人話化,不裸露 4006/502
- 清單 ≤200 逐檔、關聯 ≤10 條:守 Workers 免費層 50 subrequests 天花板
- 不寫本機檔案、不動使用者原稿

實測(youlin stage yuga3bse,刻意無金鑰=零萃取):總覽卡 200 送達、rag_chat
正確回答「有哪些檔案/最近改了什麼」;重掃不重複(blocks 恆 5、triplets 恆 2);
go test collector+supervisor+arcrun-app 全綠(新增 inventory_test.go 6 案)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 16:39:45 +08:00
parent 35b113f147
commit f233eec9c2
9 changed files with 662 additions and 37 deletions
+14 -2
View File
@@ -58,8 +58,18 @@ func TestDirect_QuotaExhausted_ShowsHumanMessageNoRawCode(t *testing.T) {
if exit != 1 {
t.Fatalf("兩份都該萃取失敗,exit 應為 1got %d", exit)
}
if len(results) != 2 {
t.Fatalf("results=%+v", results)
// 結構先行:本測試的假伺服器對**所有**端點回額度 502,總覽卡也會失敗——
// 它的錯誤同樣不准裸露錯誤碼(下方 banned 檢查涵蓋全部 results),
// 但三句話(成就/出口)是萃取路 quotaState 的契約,總覽卡只講自己的狀態。
inv, fileResults := splitInventory(results)
if len(inv) != 1 || inv[0].Status != "failed" {
t.Fatalf("inv=%+v", inv)
}
if !strings.Contains(inv[0].Error, "額度") || !strings.Contains(inv[0].Error, "不影響檔案同步") {
t.Errorf("總覽卡的額度失敗要說人話:%q", inv[0].Error)
}
if len(fileResults) != 2 {
t.Fatalf("results=%+v", fileResults)
}
banned := []string{"4006", "502", "HTTP", "neurons"}
@@ -69,6 +79,8 @@ func TestDirect_QuotaExhausted_ShowsHumanMessageNoRawCode(t *testing.T) {
t.Errorf("結果不該出現裸露的錯誤碼 %q%q", b, r.Error)
}
}
}
for _, r := range fileResults {
// arcrun-rag#59:這支測試裡兩份檔案全部失敗、dailyCount 全程是 0——
// 正是 leo 實查那個「一份都沒成功、額度卻用完」的情境,buildQuotaNotice
// 改口不再承諾「自動恢復」(那是做不到的承諾)、也不再建議換模型