---
title: FR-122 arc 收口 SUMMARY（2026-09-30）
doc_class: handoff
---

# FR-122 AI 閘道與檔案沙箱——arc 收口 SUMMARY

> 收口日期 2026-09-30。材料來自 `FR-122-LOG.md` 七棒 block 濃縮，不憑記憶重建。
> 出貨版本 **1.22.0**，STG（188）／POC（189）／e2e 測試機（190）三環境同一份包（sha `0f788947…`）。

## 1. 一句話結論

三支 AI 功能（小幫手、動態儀表板、證據自動分類）全部改走同一道 AI 閘道（`jedi-ai-gateway`）；證據檔改在無金鑰、不出網的沙箱容器拆解；分類長工作搬進獨立背景工作行程 `guidant-worker`；每次 AI 呼叫留 `ai_call_log` 稽核紀錄；租戶與個人可設美金額度與每分鐘次數。從 09-28 需求討論到 09-30 三環境上線，共三天。

## 2. 規模

| 項目 | 數字 |
|---|---|
| Notion 卡 | 母卡 1＋子需求 6＋子任務／補強／文件／出包卡 64（CM-2293～CM-2363），全部 Done |
| BE commits（`712fb5b64`..`e62c3afcb`，含出包鏈修正） | 133 |
| FE commits（`50bda00`..`3826339`） | 31 |
| jedi commits（`18b52e50`..`017d12cb`） | 35 |
| jedi 套件 | 新增 jedi-ai-gateway 0.1.0；升版 jedi-evidence-classification 1.5.0、jedi-ai-bot 1.4.0、jedi-ai-dashboard 1.5.0、jedi-license-runtime 1.1.3、jedi-remote-agent 1.2.3 |
| 主線 migration | 5 支（ai-call-log、background-jobs、ai-call-log-page、ai-quota-page、ai-usage-menu-rename），皆已進出貨基線 |
| 新服務 | `guidant-worker`、`guidant-sandbox`；退役 `guidant-classifier` |

## 3. 行為差異（升級後使用者有感的）

- 送給 AI 前先遮機敏（Presidio 規則類）；含私鑰、索取密碼憑證、代下 SQL 一律擋下並說明原因；離題回固定說明不耗額度；疑似注入（Prompt Guard 2 ≥0.8）標警告、≥0.95 擋下。
- 證據分類送出後立刻回「已受理」，由 worker 背景跑；沒起 worker 單子停在排隊。藏字（白字、極小字、零寬）與高分注入的檔案不送 AI、進待人審清單。
- 系統設定新增「AI 用量」（呼叫紀錄＋用量兩分頁，可匯出 xlsx）與「AI 額度」頁（租戶 5 USD／日、每人 1 USD／日、20 次／分為預設，80% 畫面黃條）。
- 守門規則與三段 prompt 外置到主機 `/srv/guidant-ai/ai-gateway-rules/`，客戶端可調、升級不覆蓋，改完重啟 api＋worker 即生效。
- 正式模式缺 AI 金鑰加密鑰或守門模型檔會拒絕啟動；主機記憶體建議 16 GB 起（閘道常駐實測 1.5 GB，靠 gunicorn `preload_app` 共用）。
- Azure OpenAI／Ollama 兩張供應商卡先藏（後端契約不動）。

## 4. 關鍵決策（全文 design.md §2；此處只列會影響後續維護的）

| 決策 | 一句理由 |
|---|---|
| 閘道做 library 不做 proxy | 守門與稽核都自己做，多一個服務只是重疊；LiteLLM proxy 部分功能企業授權 |
| 沙箱純拆檔、無金鑰、不出網 | 解析不可信檔案的箱子不該握鑰匙又能打電話 |
| 稽核落自家 `ai_call_log`，預設只存 hash＋長度 | 稽核紀錄不能變成新的外洩點；Langfuse 只 DEV |
| 成本用美金不用點數（推翻 09-28） | 點數換算表要另外維護且與實際花費脫鉤 |
| Prompt Guard 2 只量化詞彙表（513 MB） | 全量化 int8 帶 20 句內文時注入分數從 0.998 掉到 0.04 |
| 稽核寫入獨立交易 | 掛在業務交易上時「最該留紀錄的失敗呼叫」正是消失的那批 |
| 模型盲區用格式規則補，不調門檻 | Prompt Guard 對「索取密碼」分數 0.004，調門檻只會誤殺正常句 |
| 金鑰合併以既有列為底、刪除只認顯式 `clear: true` | 只走 payload 的鍵時，前端「不送沒改的」最佳化就把沿用變成刪除 |

## 5. 驗收狀態

design §7.1 十一條：1～7、11 在 DEV 由決策者親手驗過；8（沙箱無金鑰不出網）、10（classifier-jobs 清空）在 STG 與 190 驗過。**第 9 條「分類中 restart guidant-api 不中斷」三環境皆未實跑**——要先有分類工作在跑才驗得到，決策者裁收尾時接受此打折，留待下次 STG 有分類批次時順手驗。

「先存 A 家金鑰、再只填 B 家儲存，A 要保留」的修正（BE `62a864e62`／FE `3826339`）已在三環境部署，決策者手測未逐項回報，隨收尾一併視為接受。

## 6. 規範文件（✓ 已更新）

- SPEC 站：collab/ai-assistant、reporting/ai-dashboard、evidence/evidence-classification-review、system-admin/ai-usage、system-admin/ai-quota、onprem 手冊 04 §4.12（CM-2361）
- 使用手冊：`ai-guard-tuning-guide.md`（客戶端維運調守門，CM-2360）
- release note `docs/release_notes/v1.22.0.md`（CM-2362）；spec-site 快照 `docs/spec-site/current/legacy/v1.22.0/`
- design.md：§5.19 DB 總覽、§5.5.3 規則目錄、§7.1 驗收狀態（本次補 8／10 ✅、9 未驗）
- analysis：`docs/analysis/2026-09-30-fr122-nuitka-hidden-deps-and-nofollow-closure.md`（出包鏈根因）
- `scripts/build/README.md`：補「smoke 用臨時庫」SOP（本次）
- memory：`project_fr122_ai_gateway_shipped`、`followup_fr122_leftovers`、`feedback_fr122_push_delegated`（失效）、新增三條教訓（見 §8）

## 7. 部署 handover

| 環境 | 狀態 | 備份 |
|---|---|---|
| 188 STG | 1.22.0／`62a864e62`，8 服務健康零重啟 | `/srv/guidant-ai/backup/guidant_ai-pre-1.21.0-20260930-213121.sql.gz` |
| 189 POC | 同上（先停 api／socketio 再升，避開 Agent 心跳 deadlock） | `/srv/guidant-ai/backup/guidant_ai-pre-1.21.0-20260930-225529.sql.gz` |
| 190 e2e | 全新安裝後再 `--upgrade --force` 對齊 | `~/backups/pre-1.22.0-reinstall/`（license_raw、guidant.env、.env、pki/agent） |

出貨包：188 `/opt/guidant-ai-be/.build/bundle/guidant-ai-1.22.0.tar.gz`（2.4 GB，sha256 `0f788947424d66b83d3370bd38b2e3fc42d6d7dd9054d0979bd543c547ea3a55`）。**這是 `--skip-prod-key-check` 內部測試包，套件內只有 DEV／STG／POC 三把簽章鑰，不可交付客戶。**

## 8. 教訓（濃縮自 LOG，已入 memory 的標 ★）

- ★ 上游 import 消失會讓一整串隱性相依掉出 Nuitka 編譯圖；nofollow 清單與複製範圍必須同一組封閉集合。每輪 smoke 只暴露一個坑，正確做法是整份「該在／實在」diff 一次列完。
- ★ installer 健康檢查對 socketio／worker 只看「執行中」，無限重啟照報綠；驗收看 `RestartCount` 與 log。
- ★ 「空值＝沿用」的合併若只走 payload 的鍵，前端一個最佳化就把它變成刪除；沿用要以既有列為底。
- 討論任何 FR 前必查開源輪子（第 1 棒，已入 memory）。
- 派工 prompt 只給卡號不夠，大檔多的卡要給座標並拆卡（第 2 棒）。
- 設計寫「可覆寫」不等於落地，收尾前要對 design 每條「運維可調」逐一查出貨鏈（第 4 棒）。
- 出包 SOP 的 smoke 預設連 STG stack，與「出包前不准套 STG」結構性矛盾——已寫進 build README 的臨時庫 SOP。
- `poetry.lock` 在本 repo 入版控，舊 memory 寫反了（本次已改）。

## 9. Follow-ups（不擋收口，座標 memory `followup_fr122_leftovers`）

1. **PROD 簽章鑰**：客戶交付前必補（LC 生鑰→公鑰進 jedi-license-runtime 發版→重 build BE image→重封包）。
2. 190 license 重匯（備份可直接用）與 Agent stack 重 enroll（目前 401 迴圈，無害）。
3. design §7.1-9 補驗。
4. e2e 補測另棒（test repo）；arc-review 另棒。
5. spec-site 快照 `legacy/` 巢狀肥大，1.23.0 切版前必解。
6. 188:25432 臨時庫 `guidant_ai_smoke_1220` 留刪待裁；`.build/bundle/` 舊包 1.20／1.21 系列約 8 GB。
7. 沿用：license-runtime 16 項測試舊債、用量頁 `key_source` 篩選小卡、schema 歸屬規則、小幫手 FE 未接 `needs_review`。
