當我們設計 JieGou 的 LLM 層時,我們有一個大多數平台沒有的限制:客戶應該能夠使用自己的 API 金鑰,而我們不應該在靜態狀態下看到明文金鑰。本文將介紹我們的自帶金鑰(BYOK)系統如何運作、加密方案、供應商路由架構,以及保持一切可靠運行的保護機制。
為什麼 BYOK 很重要
大多數 AI 平台會透過他們自己的 API 金鑰代理您的呼叫。這意味著您的資料流經他們的帳戶,您的使用量受限於他們的速率限制,而且您無法控制使用哪些模型或端點。
使用 BYOK,每個客戶都連接自己的 Anthropic、OpenAI 或 Google API 金鑰。呼叫使用客戶的憑證直接傳送到供應商。JieGou 編排工作流程,但在使用 BYOK 金鑰時不會看到請求或回應的有效負載。
金鑰加密:AES-256-GCM
API 金鑰在靜態時使用 AES-256-GCM 加密,具有 12 位元組的初始化向量和 16 位元組的驗證標籤。加密金鑰是從儲存為環境變數的 64 字元十六進位字串衍生的 256 位元值 — 它永遠不會接觸資料庫。
儲存格式是 IV + authTag + ciphertext 的 Base64 編碼串接。我們將 8 字元的安全前綴(“sk-proj…”)一同儲存用於顯示,讓使用者無需解密即可識別儲存的金鑰。
金鑰儲存在 Firestore 的 account_api_keys 集合中,包含帳戶 ID、供應商名稱、加密金鑰 blob 和有效性標誌等欄位。
金鑰解析流程
當工作流程步驟需要呼叫 LLM 時,金鑰解析器會執行以下序列:
- 方案閘控 — 檢查帳戶的訂閱是否允許 BYOK(在 Redis 中快取,TTL 為 10 分鐘)。
- Redis 快取查找 — 解密的金鑰快取 5 分鐘。哨兵值(
__none__)表示之前查找過該金鑰但不存在,避免重複的 Firestore 讀取。 - Firestore 查找 — 如果快取未命中,從
account_api_keys擷取。 - 有效性檢查 — 跳過已被自動失效系統標記為無效的金鑰。
- 解密 — AES-256-GCM 解密在記憶體中即時進行。
- 故障開放 — 如果任何步驟出現問題,回退到平台自己的 API 金鑰。永遠不降低使用者體驗。
故障開放設計是刻意為之。如果 Redis 當機、解密失敗、Firestore 讀取錯誤 — 工作流程仍然執行,只是使用平台金鑰。使用者看到他們的工作完成,而不是收到難以理解的錯誤。
供應商路由
LLM 層建立在 Vercel AI SDK 之上,具有支援 Anthropic、OpenAI 和 Google 的供應商抽象。每個供應商有兩種實例化路徑:
平台金鑰(單例) — 在啟動時創建的共用實例,用於免費方案帳戶或作為 BYOK 的回退。
BYOK(臨時) — 每次呼叫使用客戶解密的金鑰創建新的供應商實例。此實例不會快取 — 它用於一次請求後就被丟棄,因此解密的金鑰不會在記憶體中滯留。
工作流程可以為每個步驟指定不同的模型。內容管道可能在步驟 1 使用 Claude 進行細緻的寫作,在步驟 2 使用 GPT 進行結構化資料擷取。供應商路由會透明地處理這一點。
自動失效
當 LLM 呼叫返回驗證錯誤(HTTP 401、402 或 403,或回應主體符合「invalid api key」或「unauthorized」等模式)時,系統會自動在 Firestore 中將該金鑰標記為無效,並從 Redis 快取中移除。
使用者會收到清楚的訊息:「您的 {Provider} API 金鑰無效或已被撤銷。請在帳戶設定中更新您的 API 金鑰。」後續呼叫會回退到平台金鑰,直到使用者提供新金鑰。
我們檢查跨供應商的 11 種已知錯誤模式。這可以捕獲輪換的金鑰、撤銷的金鑰和已超過支出限額的金鑰。
斷路器
每個 LLM 供應商都有一個按供應商的斷路器(不是按帳戶 — 單一供應商當機會影響所有人)。
在 60 秒窗口內出現 5 次錯誤後,斷路器會跳閘。一旦打開,它會保持開啟狀態 30 秒,然後允許單個探測請求(半開狀態)。如果探測成功,斷路器關閉。
只有伺服器端故障會計數:5xx 回應、逾時和連接錯誤。客戶端錯誤(4xx)如無效 API 金鑰不會觸發斷路器 — 這些由自動失效處理。
斷路器本身是故障開放的。如果 Redis 無法用於狀態追蹤,所有請求都會被允許通過。這與我們的整體理念一致:當基礎設施降級時,讓使用者的工作繼續進行。
並發控制
每個帳戶透過基於 Redis 的信號量限制為 10 個並發 LLM 呼叫。這可以防止單個帳戶的大批次執行消耗所有可用的供應商連接。
信號量使用 INCR/DECR,具有 5 分鐘的 TTL 安全網(如果處理程序崩潰而未遞減,計數器會自動過期)。當容量超出時,計數器會立即遞減以避免洩漏。與其他所有內容一樣,在 Redis 錯誤時它是故障開放的。
經驗教訓
故障開放是可選功能的正確預設值。 BYOK 是一種增強功能,而非必需功能。如果加密管道、快取層或金鑰解析失敗,使用者仍應該能夠執行他們的工作流程。我們記錄故障以供調查,但永遠不會阻止執行。
快取哨兵值可防止驚群效應。 如果沒有 __none__ 哨兵,沒有 BYOK 金鑰的帳戶在每次 LLM 呼叫時都會訪問 Firestore。快取負面結果 5 分鐘可以保持 Firestore 讀取的可預測性。
臨時供應商實例可防止金鑰洩漏。 透過每次呼叫創建新的供應商實例並讓它被垃圾回收,我們最小化了解密金鑰存在於記憶體中的時間窗口。雖然不如硬體安全模組強大,但對於 SaaS 應用程式來說,這是對攻擊面的有意義的減少。
按供應商的斷路器配合按帳戶的並發是正確的粒度。 供應商中斷是全局事件;並發是按租戶的問題。混合它們要麼太激進(一個帳戶中斷會為所有人觸發斷路器),要麼太寬鬆(無法防止供應商範圍的故障)。