t181 daemon 端:萃取預設走 Workers AI(免金鑰)+托盤可切換 AI 來源

【leo 08-04 列為最優先】「daemon 的 AI 改用 workers AI」
——「這是我的用戶**最大障礙**,造成首輪測試用戶的**好評或惡評**」。

【新增】collector/extract_workersai.go:打自己雲端實例的 /portal/daemon/extract,
那端用 env.AI binding ⇒ **完全不需要任何金鑰**。與 gemma 路架構完全對稱
(leo 的判斷:「不論用 Claude/Gemini/Workers AI…應該是同一件事」——是,
只有「送到哪」不同,讀檔/轉檔/淨化/落卡全共用)。
舊實例回 404 時講人話:「你的知識庫還是舊版 ⇒ 請到 portal 按『立即更新』」。

【預設改為一律 Workers AI】leo 特別交代:
  「default 用 Workers AI,你要用 Gemini 要**特別去選取**,**不管你現在是否有填金鑰**」
  「只要更新版本,就已經 default workers AI 了,除非去一個地方切換」
  「不然我會有很多質疑,**花在解釋為什麼 Gemini 不管用上**」
⇒ 判準是新欄位 ExtractorExplicit(使用者主動選過),**不是「有沒有金鑰」**。
  舊 config 沒這欄=false ⇒ 更新版本後自動走 Workers AI;金鑰留著不動,改選 Gemini 立刻可用。

【托盤=唯一切換處】「AI 設定…」改成引擎選擇:雲端 AI(預設)/Gemini(選配)。
選 Gemini 才顯示金鑰欄,並附「Billing Tier: Unavailable ⇒ 換 Google 帳號」的排難提示
(今天 oscar 撞的那題)。Gemini 不推廣(leo:「特定人告訴他怎麼做就好」),文案只說明不慫恿。

【標籤】leo:「用 workers AI 就**不顯示**,用 Gemini 會顯示 Gemini」
⇒ 預設路徑不佔版面;只有主動選了別的才標出來(延續 t178「標籤必須與實際行為一致」)。

【測試】新增 TestT181DefaultsToWorkersAI 六則,含最關鍵的
**「有金鑰但沒主動選 ⇒ 仍走 workers-ai」**;既有測試補 ExtractorExplicit 以隔離變因
(它們測的是萃取管線,不是預設邏輯)。兩模組全綠。

⚠️ 未送達:要打包 v0.15.5 並部署雲端端點才會到用戶手上。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-04 15:00:48 +08:00
parent 1a3e974941
commit 1a8906c0b7
7 changed files with 302 additions and 31 deletions
+33 -3
View File
@@ -70,7 +70,12 @@ type DirectConfig struct {
IngestWF string `json:"ingest_workflow"` // 直送萃取 workflow 名(空=rag_ingest_direct
RemovedWF string `json:"removed_workflow"` // 下架 workflow 名(空=rag_takedown_direct
// —— 四步定稿(daemon-beta t3/t4/t6):本地萃卡模式 ——
Extractor string `json:"extractor,omitempty"` // "claude""gemma";空=舊制(內容直送雲端萃
Extractor string `json:"extractor,omitempty"` // "workers-ai"(預設,免金鑰)|"gemma""claude"(已停用
// t181:使用者**主動在托盤選過**萃取引擎才為 true。false=一律走 workers-ai。
// 判準刻意不是「有沒有金鑰」——leo 08-04:「不管你現在是否有填金鑰」都要先 default
// Workers AI,否則他得「花在解釋為什麼 Gemini 不管用上」。
// 有金鑰但沒主動選 ⇒ 金鑰留著不動,之後選 Gemini 立刻可用。
ExtractorExplicit bool `json:"extractor_explicit,omitempty"`
ClaudeBin string `json:"claude_bin,omitempty"` // claude 執行檔(空=PATH 找 claude
GeminiAPIKey string `json:"gemini_api_key,omitempty"` // gemma 路的用戶 key
LLMModel string `json:"llm_model,omitempty"` // gemma 路模型(空=gemma-4-31b-it
@@ -439,6 +444,27 @@ func RunDirectOnce(cfg *DirectConfig, dryRun bool) ([]DirectResult, int, *Trigge
if cfg.Extractor == "claude" {
cfg.Extractor = "gemma"
}
// 🔴 t181leo 08-04 最優先):**所有人一律預設 Workers AI(免金鑰)**。
// leo:「daemon 的 AI 改用 workers AI」——「這是我的用戶最大障礙,
// 造成首輪測試用戶的好評或惡評」。
//
// ⚠️ 包含**已經填過金鑰的老用戶**(leo 08-04 特別交代):
// 「default 用 Workers AI,你要用 Gemini 要**特別去選取**
// 不管你現在是否有填金鑰……不然我會有很多質疑,
// **花在解釋為什麼 Gemini 不管用上**」
// ⇒ 判準是 `extractor_explicit`(使用者在托盤主動選過)而不是「有沒有金鑰」。
// 有金鑰但沒主動選 ⇒ 仍走 Workers AI,金鑰留著不動、之後選 Gemini 立刻可用。
// Gemini 仍是選配(leo:「不需要推廣,特定人告訴他怎麼做就好」)。
if !cfg.ExtractorExplicit {
cfg.Extractor = "workers-ai"
}
if cfg.Extractor == "workers-ai" {
// 這條路要的不是金鑰,是「連得上自己的知識庫」——沒連線才是真的沒法做事。
if strings.TrimSpace(cfg.CypherURL) == "" || strings.TrimSpace(cfg.APIKey) == "" {
extractorOK = false
extractorError = "還沒連上知識庫 ⇒ 點托盤選單的「+ 新增帳號…」輸入網址與帳密"
}
}
if cfg.Extractor == "gemma" {
if strings.TrimSpace(cfg.GeminiAPIKey) == "" {
extractorOK = false
@@ -643,12 +669,16 @@ func runDirectOnceRoot(cfg *DirectConfig, root string, dryRun bool) ([]DirectRes
var cards []string
var xerr error
switch cfg.Extractor {
case "workers-ai":
// t181leo 08-04 最優先):走自己雲端實例的 Workers AI ⇒ **免金鑰**。
// 用戶不必去 Google 申請、也不受 Google 帳號被 flag 影響。
cards, xerr = ExtractWithWorkersAI(cfg.CypherURL, cfg.APIKey, absRoot, ev.Path)
case "gemma":
cards, xerr = ExtractWithGemma(cfg.GeminiAPIKey, cfg.LLMModel, absRoot, ev.Path)
default:
// t176claude 路先不支援(RunDirectOnce 開頭已把 "claude" 正規化為 "gemma")。
// t176claude 路先不支援(RunDirectOnce 開頭已正規化)。
// 走到這裡代表 config 有沒見過的值——誠實報錯,不要靜默跳過(禁假綠)。
xerr = fmt.Errorf("不支援的萃取方式 %q目前只支援 Gemini", cfg.Extractor)
xerr = fmt.Errorf("不支援的萃取方式 %q支援:workers-aigemma", cfg.Extractor)
}
if xerr != nil {
res.Status, res.Error = "failed", "本地萃取失敗:"+xerr.Error()