---
title: FR-107 定版交付 — 證據自動分類 2.0 的最終長相
brand: Guidant AI · **FR-107** 定版交付
eyebrow: 定版參考文件 · 只寫現況
h1: 分類完成之後，檔案真的掛進任務了
lede: 這頁是 FR-107 的**定版交付規格**——只回答「現在長什麼樣」。要看「當初為什麼這樣決定」去 design.md，要看「哪一棒做了什麼」去 handoff/LOG。**本頁不記沿革、不 append 進度**，內容變了就整段改寫。
chips: [{text: 兩條線並存, kind: ok}, {text: 25 支對外端點, kind: plain}, {text: 套件 1.2.0, kind: plain}, {text: 定版參考, kind: accent}]
footer: 本頁只描述現況。決策理由見 design.md，逐棒紀錄見 handoff/fr107-LOG.md 與 git log。
---

## 一句話 {#tldr nav="一句話"}

**證據自動分類不再需要 Google Drive，而且分完之後檔案會真的掛進對應的稽核任務。** 使用者把一批檔上傳到暫存批次 → AI 判斷每個檔對應哪些檢查點 → 人工逐項確認 → 按歸檔，檔案改綁任務並在任務證據表寫入一筆，帶「AI 分類」標籤。框架知識從程式碼搬進資料表，任何自匯框架都能跑。

---

## 使用者看到的流程 {#flow nav="使用者流程"}

入口在**專案總覽頁動作列的「自動分類證據」**（限專案管理者，輪次路徑才出現），點進去是該輪的歷史清單，再點一批進四步流程頁：

| 步 | 畫面 | 使用者做什麼 | 批次狀態 |
|---|---|---|---|
| ① | 上傳證據 | 拖拉多檔進來，可逐檔刪除；按「完成上傳」 | `uploading` → `ready` |
| ② | AI 分類中 | 按「開始 AI 分類」（先開設定視窗），之後可以離開頁面 | `ready` → `classifying` |
| ③ | 確認分類 | 逐檔看 AI 判到哪些檢查點，加一個／移除／標不適用／刪檔，儲存 | `classifying` → `review` |
| ④ | 歸檔完成 | 按「歸檔進任務」，看摘要；可再決定要不要清掉沒歸檔的暫存檔 | `review` → `archived` |

分類失敗落 `failed`，**已上傳的檔都還在、重新分類不必重傳**。分類進行中可以離開頁面，回清單按「繼續」接著做——AI 會跑很久，這是入口做成歷史清單而不是上傳畫面的原因。

**開始分類前的設定視窗**四個欄位：信心門檻（滑桿）、AI 模型（僅系統管理員可見）、思考深度（低／中／高，預設中）、歸檔方式（分類並歸檔／只分析不歸檔）。

**稽核員（auditor）進得去但按鈕整組隱藏**，並顯示一行說明——反灰會讓人一直想「怎樣才能按」。

---

## 兩條線並存，差別在哪 {#two-lines nav="兩條線"}

分類有兩條線，**批次線是現行主線，Drive 線保留是為了讓既有分類結果仍打得開**：

| | 批次線 | Drive 線 |
|---|---|---|
| 檔案來源 | 使用者上傳進暫存批次，落在租戶自己的儲存後端（本機／MinIO／SeaweedFS／遠端 agent 皆可） | Google Drive 的 `Evidences/` 資料夾 |
| 掛在誰身上 | 稽核輪次，每次新上傳開一個新批次 | 專案的 AP |
| 分完之後 | **歸檔進任務**——檔案改綁控制任務，`job_evidences` 真正寫入一筆 | 複製進 Drive 的 per-AO 資料夾，任務證據表一筆都不寫 |
| 定址 | `run_uid`（UUID） | `run_folder_id`（Drive 資料夾 ID） |
| 檢查點代號 | `part_id` ＋ `ao_letter`（如 `IA.L1-3.5.1_obj.1`） | `{ControlId}[{letter}]`（如 `AC.L1-3.1.1[a]`） |
| 目標集 | run 的 `catalog_snapshot`（分類當下凍結） | living SSP 的 in-scope 控制集 |
| 覆核權限 | 寫入鎖專案管理者、讀要是專案參與者 | 僅驗 JWT（未鎖專案角色） |

兩條線的審閱畫面是**同一支元件**，只換資料來源。

> **落地版客戶沒有 Google Drive**，批次線是讓分類功能對他們也能用的前提。

---

## 歸檔做了什麼（整案的重點）{#archive nav="歸檔"}

這是 FR-107 相對於前一代最實質的差別：**以前分完只是把檔搬到另一個資料夾，系統的任務證據表一筆都沒寫；現在會真的寫入。**

- **一檔多掛、不複製**：一個檔命中幾個檢查點，就在那幾個任務底下各掛一筆證據，但實體檔只有一份、多筆證據指向同一個檔案 id。
- **重複不重掛**：歸檔是冪等的，已經在任務底下的證據會被略過。摘要分開報「新掛幾筆／已存在幾筆」。
- **標了不適用或已刪除的檔不會掛上**：審閱頁標這兩個旗標時不會一併清掉判定結果，只看判定結果的話，使用者標了「不適用」的檔還是會被掛上任務——而畫面上它顯示的是「不適用」。
- **沒有對應任務的檔不會憑空掛上**：AI 判到的檢查點若這一輪沒人被指派任務，那些檔留在批次裡標「無對應任務」，摘要會講還有幾份是這個狀況。
- **可以重按**：已歸檔批次的按鈕降級為「重新歸檔」，只補上次歸檔後才新建的任務。擋掉第二次的話，歸檔後才建出來的任務永遠拿不到證據，而使用者看到的是一個無法補救的錯誤。

歸檔回傳四個數字：掛進任務的檔數、新產生的證據筆數、已存在所以略過的筆數、沒有對應任務的檔數。

在任務那一側，證據列依 `job_evidences.source` 顯示標籤：`AI_CLASSIFIED` →「AI 分類」（綠）、`DRIVE_SYNC` →「Google Drive」（藍）、`SYSTEM_UPLOAD`（手動上傳）**不掛標籤**（每個檔都掛等於沒有資訊）。`AI_CLASSIFIED` 另有 `classification_run_id` 記得是哪一次分類來的。

> 🔴 **清理（purge）永遠不動已歸檔的檔**：那些檔背後有任務證據指著同一個檔案 id，實刪後任務上的證據會變成打不開的死連結。清理只動沒歸檔的暫存檔，且只對已歸檔的批次開放。

---

## 框架知識搬進資料表 {#profiles nav="框架去硬編碼"}

「換一個合規框架就換一組 AI 講法」那組講法存在 `compliance.classification_profiles`，**支援新框架是新增一筆設定，不是改程式**。維護介面在框架版本管理頁的「AI 分類設定」分頁。

欄位：設定名稱、適用範圍（通用／某個框架版本）、AI 角色（必填）、判斷指引、證據提示（檢查點代號 → 提示陣列）、預設模型、預設信心門檻、預設思考深度、啟用狀態。

**四層退路**（由精確到通用，找到第一筆啟用的就用）：這個租戶 × 這個框架版本 → 出廠針對此版本 → 這個租戶的通用設定 → 出廠通用設定；全落空才用內建最小版。**內建那份刻意不含任何框架名**——寫死 CMMC 等於把框架名焊回分類引擎。

**停用一筆設定不是關掉分類，是讓它改用上一層。** 出廠那份（ROOT 租戶）對業務租戶唯讀，但清單上一起回是刻意的：使用者要看得到「我沒設定時系統實際會用什麼」，看不到的話「為什麼 AI 這樣講話」無從查起。

**守門軸與框架版本寫入不同**：框架版本是全域共用資源（只有平台管理員能寫），分類設定是**租戶級**的（走 `storage-config.update` 能力點）。把它也鎖成平台管理員，等於所有客戶都不能自己調 AI 講法。

---

## 可用的 AI 模型怎麼決定 {#models nav="AI 模型"}

**該租戶已設定金鑰的供應商 × 系統支援的型號表，取交集**，由 `GET /api/1.0/classification/available-models` 動態算出。

- 型號表（套件 `domain/llm_models.py`）是**資料不是邏輯**——支援新型號＝加一列，不改程式。
- 沒設定任何金鑰時回空清單，畫面提示去填金鑰，**不列出選了才會 401 的型號**。
- 不支援讀圖的型號在選單上標明，且遇到圖片證據會明確標「此模型不支援圖片，未分類」——**不靜默跳過**（靜默跳過會讓使用者以為那張截圖被看過且判定為無關）。

**金鑰不在分類設定頁**：只有 ROOT 平台管理員能填，讀取時遮成「有沒有設定」的旗標、不回明文；加密後才落地。第一版不開放客戶自填。三個 AI 功能（AI 小幫手／AI Dashboard／證據分類）共用同一套解析順序：租戶自己的設定 → ROOT 原廠鑰 → 環境變數。

> 本頁**刻意不列型號名**。型號隨供應商發版換代，寫進手冊等於保證下次換代時這頁是錯的——要看當下有哪些，打上面那支端點。

---

## 交付物現況 {#deliverables nav="交付物"}

| 項目 | 現況 |
|---|---|
| 套件 `jedi-evidence-classification` | 1.2.0（主專案已 pin，離開 path dependency） |
| 套件 `jedi-file-upload` | 1.2.0（同上） |
| 對外端點 | 25 支（套件 `FROZEN_URLS` 凍結清單，測試對實際掛載結果做集合比對） |
| 套件內 migration | 5 支（`001`～`005`；不寫 `schema_migrations`，屬套件自己的管線） |
| 新增資料表 | `compliance.evidence_batches`、`evidence_batch_files`、`classification_profiles` |
| 任務證據表擴充 | `job_evidences.source` 增加 `AI_CLASSIFIED`；新增 `classification_run_id` 追溯欄 |
| 分類容器 image | `evidence-classifier:dev`（宿主不覆寫名稱，固定用套件預設） |
| SPEC 頁 | 五頁已更新（專案總覽／審閱頁／框架版本管理／我的任務／證據管理總覽） |

**端點分組**：暫存批次 8 支、AI 分類設定 3 支、run 新定址 4 支、Drive 線 legacy 與報表 10 支。

---

## 這一輪沒做完的 {#remaining nav="沒做完的"}

| 沒做完的事 | 白話說明 | 誰接 |
|---|---|---|
| 分類容器 image 進出貨包 | 現在全新裝機要手動拉 image 才跑得動分類，還沒進安裝包 | T-6.4（CM-1871） |
| AI 模型碼表換代 ＋ 思考深度落地 | 型號表要換成當代型號；「思考深度」三檔要從前端一路穿到容器（批次表與設定表各加一欄）。**SPEC 兩處已標「CM-1885 落地中」** | CM-1885 |
| 端到端回歸測試 | 走完「上傳→分類→確認→歸檔→任務看到證據」的自動化測試。**前置是含 FR-107 的 image 進 190 測試機**——目前該機所有 image 都不含本案端點，測試寫了也會在第二步就停 | T-6.3b（CM-1886） |
| 舊 Drive 線退場 | 舊入口已隱藏、舊路徑標 legacy 保留一版，尚未真正移除 | 下一版另議 |

> 子任務卡 25 張：**22 張已驗收完成**，未完成的就是上表前三項（另有一張前端設計稿卡不計入）。**全案未 push**，等決策者明示。

---

## 延伸出去的 FR {#downstream nav="延伸 FR"}

| FR | 在做什麼 | 為什麼會長出來 | 狀態 |
|---|---|---|---|
| [FR-030](../FR-030-2605-auto-evidence-classification/) | 第一代分類（Drive 線） | 本案取代的前身；它的分類結果仍要打得開，故舊線保留一版 | 已上線，退場中 |
| [FR-031](../FR-031-2605-evidence-classify-reports/) | 分類成效報表（驗證／誤判） | 報表要算「歸檔後的正解」，靠本案寫入的 `classification_run_id` 追溯 | 已上線 |
| [FR-069](../FR-069-2608-jedi-module-extraction/) | 套件化與插件契約 | 本案的新功能一律進套件、主專案只留宿主接線，遵循的是那一案定下的插件形狀 | 已上線 |

閱讀順序：先看本頁 → 要懂分類報表看 FR-031 → 要懂套件為什麼長這樣看 FR-069。

---

## 這份文件的定位 {#scope nav="本頁定位"}

| 你想知道 | 看哪份 |
|---|---|
| 最後長什麼樣、規則是什麼 | **本頁** |
| 當初為什麼這樣決定、哪些方案被排除 | [design.md](design.md)（D1–D13） |
| 需求怎麼來的、決策者表態過什麼 | [discussion.md](discussion.md)、[README](README.md) 的「需求討論紀錄」 |
| 哪一棒做了什麼、踩過什麼坑 | [handoff/fr107-LOG.md](handoff/fr107-LOG.md) 與 git log |
| 這些頁面實際怎麼運作（給工程師查） | SPEC 手冊的五頁（見上方「交付物現況」） |
