證據分類覆核(Evidence Classification Review)¶
功能群:證據管理|分類生命週期、兩種證據概念、EC_ error code、核心資料模型先讀 功能群總覽
事實基準:兩條線並存——批次線 2026-09-17 從套件
jedi-evidence-classification與 FE 掃出(FR-107,以run_uid定址、檢查點用part_id+ao_letter、目標集吃 run 的catalog_snapshot);Drive 線 2026-07-05 掃出(FR-030 v1,以run_folder_id定址、檢查點用{ControlId}[{letter}]、真相在 Drive_state.json)。UI 兩條線共用同一支EvidenceClassificationReview.vue,只換資料來源變更紀錄: - 2026-09-30(FR-122):寫明「待人審」的兩種來源與各自呈現(沙箱判定藏字或注入分數高→不送 AI;AI 閘道標記→照送 AI、結果僅供參考)、被閘道擋下與額度用完的檔怎麼顯示、AI 配對與人工配對的區分;分類改經 AI 閘道與檔案沙箱(§11) - 2026-09-29(FR-122.6-D,CM-2336/2337):版面改「一條待辦線」——統計列、待人審黃區、「只看未分類」鈕與狀態下拉收成頁頭四分頁(要你處理/已完成/非證據/全部)+進度條;新增「下一步」卡與人工確認(寫入
reviewed_by/reviewed_at);自動配對門檻可在本頁調整並重挑 AI 配對(存review_threshold,不重跑 AI);原「AI 把握度 ≥」改名「只顯示 AI 分數 ≥」、僅在「全部」分頁;被安全檢查攔下的檔(needs_review)紅底標「已攔截」排最前;已配對表加「來源」欄;「標為非證據」「從證據池刪除」收進檔名旁 ⋮ 選單 - 2026-09-17(FR-107,CM-1888):右欄配對區由一筆一卡改為一筆一列的表格(已配對與 AI 候選共用PlacementTable,mode切換)——完整麵包屑收進代號 tooltip、AI 理由收進展開列、狀態改用左側色線,一屏可比對十筆以上;篩選列「最高信心 ≥」更名為「AI 把握度 ≥」、收件匣鈕「未分類審閱」更名為「只看未分類」,兩者都補上說明 tooltip(原文案看不出是篩選動作),批次線的領域下拉改讀該 run 的catalog_snapshot(原讀舊 Drive 線的靜態表,列出的領域與本次分類對不上) - 2026-09-17(FR-107):批次線上線——新增以run_uid定址的入口(嵌在批次流程頁第③步)、檢查點代號改part_id/ao_letter、目標集改吃 run 的catalog_snapshot(凍結快照)、權限鎖專案角色;原 Drive 線(run_folder_id)保留供既有分類結果讀取。批次四步與狀態機見 功能群總覽 §2
1. 功能描述¶
證據分類覆核頁是 AI 證據自動分類的人工覆核工作區:一個分類 run 跑完後,逐檔檢視 AI 建議的 placement(檔案 → 檢查點的對應),手動新增 / 移除 placement、標記 N/A、刪檔,最後儲存。
同一支元件服務兩條線(見 _overview §2),差別在資料來源與「存完之後去哪」:
| 批次線(現行主線) | Drive 線(既有資料) | |
|---|---|---|
| 入口 | 批次流程頁第③步嵌入(/project/projects/:id/evidence-batches/:batchUid/review/:runUid) |
獨立頁(.../ap/:apUid/classify-evidence/run/:runFolderId) |
| 定址 | 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 控制集 |
| 存完之後 | 回批次流程頁按「歸檔進任務」,檔案改綁控制任務、job_evidences 真正寫入一筆 |
同步回 Drive _state.json,需要時歸檔=把檔複製進 per-AO 資料夾 |
| 編輯真相 | run 的 state(沒有 Drive) |
Drive 上的 _state.json,DB state 是鏡像 |
主要使用者:批次線=專案 manager(寫)/專案參與者(讀,auditor 唯讀);Drive 線=v1 僅 JWT、未鎖專案角色。見 §3。
角色速覽(完整見 _overview §5):
| 角色 | 一句話 |
|---|---|
| manager | 專案經理 — 觸發分類(上游)並覆核修正;批次線的寫入類動作只有他能做 |
| 專案參與者 | 批次線可讀(auditor 進得去但按鈕整組隱藏,唯讀) |
| (覆核者) | Drive 線的覆核 / 存 / 歸檔僅需 JWT(未鎖專案角色,實驗階段遺留) |
1.1 功能總覽(本頁全部功能)¶
| # | 功能 | 說明 | 位置 | 詳述 |
|---|---|---|---|---|
| 1 | 分頁切換 | 要你處理/已完成/非證據/全部,各帶份數;「要你處理」旁紅色小計=其中被安全檢查攔下的份數。預設停「要你處理」,沒有待處理就轉「已完成」 | 頁頭 ClassificationReviewHeader |
§4、§10 |
| 2 | 進度與統計 | 進度條「已處理 N / M 份(非證據不計)· 涵蓋 X / Y 個檢查點」;「統計」展開列出總檔數/已分類數+%/未分類/配對總數+每檔平均/檢查點涵蓋 | 頁頭 | §10 |
| 3 | 自動配對門檻 | 顯示目前生效門檻(調過會附「原本 X」);點開滑桿 0.40~1.00、按「套用」用既有 AI 分數重挑自動配對 | 頁頭 | §10、§12 |
| 4 | 儲存/捨棄/歸檔 | 儲存(帶未存項數)、捨棄重載;批次線 embedded 時的「歸檔進任務」/「重新歸檔」(未存時停用、稽核員不顯示) | 頁頭右側 | UC-EC-01、UC-EC-02 |
| 5 | 篩選列(僅「全部」分頁) | 搜尋檔名/領域下拉(批次線列該 run catalog_snapshot 的領域)/配對數(全部/單一/≥2/≥5)/「只顯示 AI 分數 ≥」slider(預設 0,只影響顯示)/重設 |
ClassificationFilterBar |
§5 |
| 6 | 檔案清單(左欄) | 標題隨分頁;每列一個狀態點+一行原因(紅盾+「已攔截」=安全檢查攔下、橙=AI 沒把握、綠=已配對或已確認、灰=非證據);AI 分數欄、配對數欄可排序;預覽鈕 | ClassificationFileList(34%) |
§5 |
| 7 | 檔案詳情(右欄) | header(檔案編號/評估數/配對數、已確認者與時間、確認過的被攔檔顯示灰色「待人審」)+上下一份(j/k、↑↓)+ ⋮ 選單;兩態身:非證據/一般 | ClassificationFileDetail(66%) |
UC-EC-01 |
| 8 | 下一步卡 | 只在人必須動手時出現(被攔下、未配對、手動配了但未確認):原因+一句要做什麼+「確認這樣分類,下一份」「標為非證據」;被攔下的檔紅框 | 詳情最上方 | UC-EC-01 |
| 9 | 已配對表 | 一筆一列:檢查點代號(麵包屑在 tooltip)/說明/來源(AI/人工)/AI 分數/狀態(已配對/新加入/已移除)/移除或復原;AI 理由在展開列;標題旁「調整檢查點」 | PlacementTable(mode="placed") |
UC-EC-01 |
| 10 | AI 候選表 | 未放置的 AI 建議,預設只列 ≥ 生效門檻,可切「看全部」;操作為「加入」 | PlacementTable(mode="candidate") |
UC-EC-01 |
| 11 | 調整檢查點(AOPickerDialog) | 從全部目標集勾選加/減配對(勾=保留、取消=移除、新勾=新增),AI 評過的顯示分數 | 已配對表標題旁 | UC-EC-01 |
| 12 | 標記非證據/復原 | 整檔標為非證據(確認框)/復原 | ⋮ 選單、下一步卡;非證據態「還原」 | UC-EC-01 |
| 13 | 從證據池刪除 | 把檔案移出證據池(is_deleted,確認框) |
⋮ 選單 | UC-EC-01 |
| 14 | 檔案預覽 | 開檔預覽(docx/xlsx/pptx/pdf/text/image,<50MB) | 清單預覽鈕 → EvidenceFilePreviewDialog |
§6 |
| 15 | 重跑分類 | 獨立路由開啟時的 Header 鈕,返回上頁重新觸發(本頁不觸發,router.back()) |
Header(非 embedded) | §12 |
| 16 | 用量費用列 | 模型/token/粗估費用(僅 DEV_MODE) | 頁頭下方 | — |
UC(§2)只展開 1(逐檔覆核與儲存)、2(歸檔)這些核心操作路徑;統計 / 篩選 / 預覽是輔助檢視。觸發分類本身不在本頁——批次線在批次流程頁第②步,Drive 線在上游專案 / AP 頁(見 §12)。
🔴 批次線的歸檔(第 4 項「歸檔進任務」)是把檔改綁控制任務、寫
job_evidences;Drive 線的歸檔是把檔複製進 Drive 資料夾——兩者名字都叫「歸檔」但做的事不同,見 _overview §2。
2. Use Case¶
角色速覽(完整見 _overview §5):
| 角色 | 一句話 |
|---|---|
| manager | 專案經理 — 觸發分類(上游)並覆核修正;批次線的寫入類動作只有他能做 |
| 專案參與者 | 批次線可讀(auditor 進得去但按鈕整組隱藏,唯讀) |
| (覆核者) | Drive 線的覆核 / 存 / 歸檔僅需 JWT(未鎖專案角色,實驗階段遺留) |
流程圖視覺慣例:菱形 = 判斷、橘底 = 例外/擋下、紅框 = 錯誤(標 error code)、綠框 = 成功終點、虛線 = 可選路徑。
UC-EC-01 逐檔覆核分類並儲存¶
| 項目 | 內容 |
|---|---|
| 角色 | 覆核者(v1 JWT) |
| 前置條件 | 分類 run 已 completed、_state.json 存在 |
| 產出 / 後置條件 | placement 增減 / N/A / 刪檔在記憶體 → 儲存時 diff → Drive copy/trash + overwrite _state.json + DB 鏡像同步 |
批次線的主路徑:停在「要你處理」分頁,逐份看「下一步」卡 → 必要時「調整檢查點」或從 AI 候選加入 → 按「確認這樣分類,下一份」(寫 reviewed_by/reviewed_at,自動跳下一份)→ 份數歸零後按儲存 → 歸檔進任務。要整批放寬或收緊自動配對,先在頁頭調門檻再逐份處理。
UC-EC-02 歸檔 run(analyze-only → 落檔)¶
| 項目 | 內容 |
|---|---|
| 角色 | 覆核者(v1 JWT) |
| 前置條件 | run archive_files=false(analyze-only,placement 檔尚未複製) |
| 產出 / 後置條件 | 無 placement_drive_id 的 placement copy 進 per-AO 資料夾、填回 id;archive_files=true + archived_at |
3. 權限矩陣¶
角色基調見 _overview §5。本頁檢查點:
兩條線的守門不同(這是兩者最大的差異之一):
| 操作 | FE 判定 | BE 強制 | BE 檢查位置 |
|---|---|---|---|
| 批次線:建批次 / 上傳 / 刪檔 / 開始分類 / 歸檔 / 清理 | canWrite = 專案 manager 且已設定儲存位置 |
is_project_manager() → EC_403005 |
套件 app/service/evidence_batch_service.py _require_manager() |
| 批次線:看批次清單 / 流程頁 / 分類結果 | auditor 進得去,但按鈕整組隱藏+顯示一行說明 | is_project_participant() → EC_403002 |
同上 _require_participant() |
| Drive 線:觸發分類(上游) | 專案 manager | project_participant.role == "manager" → EC_403001 |
app/evidence_classification/service/evidence_classification_service.py:99-150 |
| Drive 線:讀 state / 存 / 歸檔 / 預覽 | 能到 URL 即可(無頁內角色判定) | 僅 @jwt_required(),未檢專案角色 |
套件 route(認證由宿主注入的 decorator 套上) |
⚠️ Drive 線的覆核權限未鎖(收進 §12):觸發分類鎖 manager,但覆核 / 存 / 歸檔 / 預覽只驗 JWT——FR-030 v1 實驗階段遺留,非資安定案。批次線已改鎖專案角色,這條只影響舊入口。
批次寫入類動作刻意用專用碼
EC_403005(「僅專案管理者可操作證據批次」)而非泛用的EC_403001:後者文案只講「觸發自動分類」,上傳或刪檔被擋時顯示那句對不上使用者當下做的事。
4. 狀態機與前置條件¶
分類 run 狀態機見 _overview §2。本頁相關 gate:
| 條件 | 效果 | 出處 |
|---|---|---|
批次線:批次 status = review |
才進得到第③步確認分類;classifying 時前端每 5 秒輪詢批次詳情,轉 review 自動跳頁 |
批次狀態機見 _overview §2.1 |
批次線:run 有 catalog_snapshot |
才有目標集可對照;缺快照回 EC_412007(「此 run 無目標集快照,請重新分類」) |
套件 run state 讀取 |
| 批次線:分類進行中封口 | 加檔 / 刪檔被擋回 EC_409003,前端顯示「分類進行中不能改檔」 |
SEALED_ACTIONS |
run status = completed |
才有可覆核的 _state.json(Drive 線) |
evidence_classification_service(run state 讀取) |
archive_files = false |
顯示未歸檔警示 + 立即歸檔鈕;placement placement_drive_id 為 null |
ClassificationFileDetail / metadata |
| 未存變更 | 儲存/捨棄鈕啟用,儲存鈕帶未存項數。比對每份檔的配對+非證據+刪除+確認,以及 review_threshold |
useEvidenceClassification.hasUnsavedChanges |
| 待人審來源①:沙箱判定 | 檔案沙箱拆檔時發現藏字(hidden_text),或注入分數 ≥ 0.8(injection)→ 不送 AI,needs_review=true、ai_suggested=false、無候選。清單列「{原因} · 沒送 AI」 |
套件 jedi_evidence_classification/app/service/gateway_classifier.py(INJECTION_SKIP_THRESHOLD,常數不可調) |
| 待人審來源②:AI 閘道標記 | 送 AI 時閘道命中標記級規則(疑似注入 injection、原文外洩比對 verbatim)→ 照送、照配對,needs_review=true、ai_suggested=true。清單列「{原因} · AI 結果僅供參考」 |
同上 |
| 被閘道擋下(例如檔內含私鑰) | 不送 AI、不設 needs_review;error 記擋下原因,無候選,清單列落在橙點「沒有候選」,歸「要你處理」 |
同上 |
| 執行中額度用完 | error=quota_exhausted,清單列橙點「額度用完未分類」,歸「要你處理」;額度恢復後可重跑 |
同上;額度見 AI 額度 |
| 分頁歸屬 | 非證據 → 非證據;已確認(reviewed_at)→ 已完成;needs_review → 要你處理;有配對 → 已完成;其餘 → 要你處理。AI 自動配好的檔不需人確認 |
reviewBucket.js::fileBucket |
| 確認失效 | 已確認的檔被加減配對或改非證據 → 清掉 reviewed_by/reviewed_at,回到要你處理 |
useEvidenceClassification.clearReview |
5. UI 設計¶
📸 實際畫面截圖待補(一律亮色模式拍攝):以 playwright 對 STG 擷取,預計補以下四張到
assets/img/: ① 整頁「要你處理」分頁(頁頭 + 左清單含被攔下的檔 + 右詳情下一步卡)ec-review-overview.png② 檔案詳情 — 已完成(已配對表含來源欄 + AI 候選表)ec-review-classified.png③ 頁頭門檻面板展開ec-review-threshold.png④ 調整檢查點對話框(AOPickerDialog)ec-review-aopicker.png
版面骨架與區塊職責(2 欄 Splitter,狀態存 localStorage ec-review-splitter;彩色區塊可點跳本頁小節錨點,虛線框=點開或特定分頁才出現):
狀態呈現:清單列狀態點(紅盾=被攔下、橙=AI 沒把握、綠=已配對或已確認、灰=非證據);被攔下的檔整列紅底+左側紅條+「已攔截」標籤;AI 分數 tier 色(高 ≥0.85 綠 / 中 ≥0.70 橘 / 低 紅);已配對表來源欄 AI 藍、人工灰。鍵盤 j/k / 上下鍵切檔。無 socket,覆核為前端記憶體操作、存時才寫。
6. API 規格¶
Envelope:成功 {"status": true, "data": …}、失敗 {"status": false, "error_code": "EC_…", "msg": "…"}。
6.1 總清單(本頁呼叫的全部 endpoint)¶
| 分類 | Method + Path | 說明 | 完整規格 |
|---|---|---|---|
| 批次線 載入 | GET /classification-run/{run_uid}/state |
讀 run 的 state(run_uid 為 UUID,掛載順序在 legacy 那條之前) |
§6.2 |
| 批次線 儲存 | PUT /classification-run/{run_uid}/state |
存編輯(沒有 Drive,run 的 state 即真相) |
§6.2 |
| 批次線 預覽 | GET /classification-run/{run_uid}/file/{file_uid}/preview |
走租戶儲存後端取檔 | GAI-SD-02 |
| 批次線 上游(非本頁) | POST /evidence-batches/{batch_uid}/classify・/archive・/purge |
開始分類 / 歸檔進任務 / 清理暫存檔(在批次流程頁) | _overview §2.1 |
| 載入 | GET /classification-run/{run_folder_id}/state |
讀 _state.json(metadata + files + placements) |
§6.2 |
| 儲存 | PUT /classification-run/{run_folder_id}/state |
存編輯(BE diff → Drive copy/trash → 覆寫 + DB 鏡像) | §6.2 |
| 歸檔 | POST /classification-run/{run_folder_id}/archive |
analyze-only run 落檔 | §6.2 |
| 預覽 | GET /classification-run/{run_folder_id}/file/{file_drive_id}/preview |
Drive 檔預覽 | GAI-SD-02 |
| 報表 | GET .../report/validation・.../report/adjudication |
DEV 捷徑(下游頁) | 驗證報表 / 誤判報表 |
| 上游(非本頁) | POST /project/{project_uid}/ap/{ap_uid}/classify-evidence、GET .../classify-evidence/jobs |
觸發分類 / job 清單(在專案/AP 頁) | §12 |
6.2 分類 run state 類¶
[GET] /classification-run/{run_folder_id}/state¶
Response data:{metadata, files[]}——
metadata:
| 欄位 | 型別 | 說明 |
|---|---|---|
| drive_run_folder_id | string | Drive run 資料夾 id |
| drive_run_folder_name | string | Drive run 資料夾名稱 |
| framework_id | string | 合規框架(v1 = cmmc-l1) |
| confidence_threshold | number | 信心門檻 |
| model | string | 使用模型 |
| archive_files | bool | 是否已複製 placement 檔進 per-AO 資料夾 |
| token_usage | object | token 用量:{total_tokens, cache_read_input_tokens, estimated_cost_usd} |
| last_edited_at | string | 最後編輯時間 |
| last_edited_by_user_id | int | 最後編輯者 |
| archived_at | string | 歸檔時間(歸檔後才有) |
| archived_by_user_id | int | 歸檔者(歸檔後才有) |
files[](每檔):
| 欄位 | 型別 | 說明 |
|---|---|---|
| file_drive_id / file_name | string | Drive 檔識別 / 名稱 |
| is_na / is_deleted | bool | 標記不適用 / 移出證據池 |
| matches[] | array | AI 候選:{ao_id(如 AC.L1-3.1.1[a]), confidence(0–1), reasoning} |
| placements[] | array | 確定放置:{ao_id, placement_drive_id(歸檔後才有), source(ai/manual), added_at?} |
出處:evidence_classification_service.py::get_state(讀 Drive _state.json)
[PUT] /classification-run/{run_folder_id}/state¶
Request:同 GET 的 {metadata, files[]} 完整結構(前端整包送回)。BE diff old vs new placements:新增 → copy_file 進 per-AO 資料夾填 placement_drive_id;移除 → trash_file;蓋章 last_edited_*;覆寫 _state.json;_sync_run_state_to_db(idempotent)。Response data:{operations_applied, drive_copied, drive_trashed, errors[]}。出處:evidence_classification_service.py::put_state
[POST] /classification-run/{run_folder_id}/archive¶
無 body。對每個無 placement_drive_id 的 placement copy 進 per-AO 資料夾;設 archive_files=true、archived_at。Response data:{drive_copied, already_archived, errors[]}。出處:evidence_classification_service.py::archive_run
7. 前端檔案地圖(compliance-manager-fe/)¶
| 檔案 | 角色 |
|---|---|
src/views/evidence-classification/EvidenceClassificationReview.vue |
主檢視(2 欄 Splitter、分頁、篩選、存 / 歸檔、確認後跳下一份) |
src/views/evidence-classification/reviewBucket.js |
分頁歸屬規則(fileBucket)與「要你處理」排序(被攔下的排前) |
.../components/ClassificationReviewHeader.vue |
頁頭:分頁、進度、統計展開、門檻調整、儲存/捨棄、歸檔 slot |
.../components/ClassificationFilterBar.vue |
篩選列(僅「全部」分頁) |
.../components/ClassificationFileList.vue |
左檔案清單(虛擬捲動) |
.../components/ClassificationFileDetail.vue |
右詳情(三態) |
.../components/PlacementTable.vue / AOPickerDialog.vue |
已配對/AI 候選共用表(mode 切換)/ 加 AO |
.../components/EvidenceFilePreviewDialog.vue |
Drive 檔預覽 |
src/service/EvidenceClassificationService.js |
API 包裝(getState / putState / archiveRun / 報表) |
src/composables/useEvidenceClassification.js |
state 管理(load / add/remove placement / markNa / markReviewed / applyThreshold / save / hasUnsavedChanges) |
src/config/router/index.js:859-862 |
route evidence-classification-review |
src/config/locales/i18n/{zh-tw,en}/evidence-classification.json |
i18n(namespace lang.evidence_classification.*) |
8. 後端檔案地圖(本頁核心鏈路)¶
| 鏈路 | Route | App Service | 底層 |
|---|---|---|---|
| 讀 / 存 state | api/evidence_classification/routes/evidence_classification_route.py::ClassificationRunStateRoute |
app/evidence_classification/service/evidence_classification_service.py::get_state / put_state(diff + Drive ops + DB 鏡像) |
EvidenceDriveOps(copy/trash/update_json)+ ClassificationRunDomainService.update_state |
| 歸檔 | 同檔 ClassificationRunArchiveRoute |
archive_run |
EvidenceDriveOps |
| 觸發分類(上游) | 同檔(classify-evidence route) | trigger_classify(_check manager)→ ClassifierContainerRunner(Docker)→ Drive + DB persist |
evidence_classification_runs repo |
DDD 提醒:本模組 route → app service → domain service / repo(ClassificationRunDomainService)+ infra 的 EvidenceDriveOps(Drive 操作限縮在 infra)。error code 前綴 EC_(common/code/evidence_classification_error_code.py)。
9. DB¶
9.1 資料表總清單(本頁讀寫的全部表)¶
| 表 | 讀/寫 | 說明 | 欄位詳述 |
|---|---|---|---|
| compliance.evidence_classification_runs | 讀/寫(鏡像同步 state) | 分類 run 記錄;真相在 Drive _state.json,本表 state/report_original JSONB 為鏡像 |
§9.3 |
(Drive _state.json / _report-original.json) |
讀/寫 | placement 編輯的真相來源(非 DB,見 _overview §2) | — |
本頁不直接寫
ground_truth(那是誤判報表 CANON 匯入);ground truth 表見 _overview §6 與 驗證報表。
9.3 核心表欄位(取自 DEV DB dump)¶
compliance.evidence_classification_runs — 證據分類 run 結果¶
| 欄位 | 型別 | 說明 |
|---|---|---|
| run_folder_id | varchar(128) | Drive run folder id(自然鍵) = 頁面 URL 的 runFolderId |
| tenant_id / project_uid / ap_uid | int / varchar | 租戶 / 專案 / AP |
| framework_id | varchar(64) | 合規框架(v1 = cmmc-l1) |
| model / confidence_threshold | varchar / numeric(4,2) | 使用模型 / 信心門檻 |
| status | varchar(32) | queued / processing / completed / failed |
| archive_files | boolean | 是否已複製 placement 檔進 per-AO 資料夾 |
| input_file_count / classified_count | int | 輸入 / 已分類檔數 |
| estimated_cost_usd | numeric(10,4) | 估算費用 |
| state | jsonb | _state.json 鏡像(User 修正後) |
| report_original | jsonb | _report-original.json 鏡像(AI 首次輸出,immutable;報表對照基準) |
| container_log | text | 容器日誌 |
| triggered_at / completed_at / last_edited_at / archived_at | timestamptz | 生命週期時間 |
| triggered_by / last_edited_by / archived_by _user_id | int | 各動作使用者 |
10. 頁面邏輯與資料對應¶
載入時序:
關鍵欄位對應(畫面 ↔ API):
| 畫面元素 | FE state | API 欄位 |
|---|---|---|
| 分頁份數 | bucketCounts |
files 依 fileBucket 分組 |
| 統計展開 | ec.stats |
files 聚合(classified / unclassified / placements / coverage) |
| 生效門檻 | metadata.review_threshold ?? metadata.confidence_threshold |
同左 |
| 待人審/原因 | file.needs_review、review_reason(hidden_text 疑似藏字/injection 疑似注入指令/verbatim 原文外洩比對命中/其他) |
沙箱或閘道寫入,前端唯讀 |
| 是否送過 AI | file.ai_suggested |
true=AI 有看過、結果僅供參考;false 且 needs_review=沒送 AI,需人工配對 |
| 未分類原因 | file.error |
quota_exhausted=額度用完;其他=閘道擋下或處理失敗 |
| 人工確認 | file.reviewed_by(login_name)、reviewed_at;清單列顯示「已人工確認」 |
files[] 同名欄位,隨 PUT state 整包存(無獨立 endpoint) |
| AI 分數條 | match.confidence |
matches[].confidence(tier 前端算) |
| 已配對表來源欄 | placement.source |
placements[].source(ai/manual;舊資料無此欄時有分數視為 AI) |
| AI 候選表 | 未放置的 matches ≥ 生效門檻(或「看全部」) |
matches[] \ placements[] |
| 未存項數 | pendingCount(配對 diff+每檔狀態變動) |
— |
調整門檻:applyThreshold(n) 對每份檔(跳過已刪除、非證據、已確認)保留人工配對,AI 配對只留 matches 分數 ≥ n 者,並補上 ≥ n 但尚未配上的 match(source: 'ai');metadata.review_threshold = n。不呼叫後端,儲存時隨 state 一起寫。
儲存流程:前端整包 PUT state → BE diff → Drive copy/trash → 覆寫 _state.json → DB 鏡像;捨棄 = 重載。歸檔另走 POST archive。
錯誤對應:BE EC_ envelope → toast;常見 EC_403001(觸發非 manager)、EC_404003(_state.json 不存在)、EC_404008(run 未存 DB 無法產報表)、EC_500003(Drive 檔操作失敗)、EC_500004(state JSON 解析失敗)。
11. 背景行為與外部依賴¶
| 類型 | 內容 |
|---|---|
| AI 分類(批次線) | 背景工作 guidant-worker 撿單;每份檔先交檔案沙箱 guidant-sandbox 拆檔、掃藏字、算注入分數,再經 AI 閘道送外部 AI(遮蔽機敏資料、檢查額度、寫 ai_call_log,feature classification)。沙箱不連外網、不持有 AI 金鑰 |
| AI 分類(Drive 線) | 既有資料,只讀;_state.json + _report-original.json |
| Drive | 覆核存檔 = Drive copy/trash + 覆寫 JSON(EvidenceDriveOps);歸檔複製 placement 檔進 per-AO 三層資料夾 |
| Socket | 本頁無 socket;覆核為前端記憶體操作、存時才寫 |
| jedi-* 套件 | Drive 操作 / classifier 容器(模組自有 infra);jedi-auth(JWT) |
| 把握度門檻 | 批次流程第②步設定(預設 0.70、可調 0.50~1.00),本頁可另調 0.40~1.00;沙箱的注入分數門檻 0.8 是套件常數,不在畫面上 |
| 守門規則 | 主機規則目錄,現場可調,見 落地版維運 4.12 |
| 送出前額度預估 | 在批次流程第②步:預估超過剩餘額度 1.5 倍擋整批(GRC_412058),本頁不會見到 |
12. 邊界情況與已知坑¶
- 真相在 Drive
_state.json不是 DB:DBstate是鏡像;直接改 DB 會與 Drive 脫鉤(見 _overview §7 坑 2) - 覆核權限 v1 未鎖:觸發鎖 manager,覆核 / 存 / 歸檔僅 JWT(§3)
- 觸發分類不在本頁:本頁「重跑分類」是
router.back();實際觸發POST /project/{uid}/ap/{apUid}/classify-evidence在上游專案 / AP 頁(單 project 單 AP、無並行,同 project 一次一 job) - analyze-only 模式 placement_drive_id 為 null:未歸檔前 placement 只在 JSON、檔案未複製;歸檔後才落檔
- 儲存是整包 diff 不是增量:前端送完整 files 陣列,BE 自行 diff;多人同時覆核同一 run 會互蓋(無鎖)
- 限 CMMC L1 + Drive(v1):非 CMMC L1 框架 / 非 Drive 來源 v1 不支援(見 _overview §7 坑 5)
- 報表即時算、無確認狀態:驗證 / 誤判報表每次開重算(見 驗證報表 / 誤判報表)
- 預覽限檔型 / 大小:docx/xlsx/pptx/pdf/text/image、<50MB
- 兩個分數別搞混:頁頭的「自動配對門檻」會改變配對(算不算分類成功);「全部」分頁的「只顯示 AI 分數 ≥」只決定清單顯示哪些檔,不改任何配對
- 門檻下限 0.40:AI 分類時只回報 ≥ 0.4 的候選,調更低也配不出新東西;調門檻只重挑 AI 配的,人工配對與已確認檔不動
review_threshold不覆寫confidence_threshold:驗證/誤判報表以confidence_threshold當「AI 原始判斷」基準線,覆寫會讓報表的 AI 準確率失真- 確認不動待人審欄位:
needs_review/review_reason是安全檢查的判定,人工確認只加reviewed_by/reviewed_at,事後仍查得到這份曾被攔下 - 「已攔截」涵蓋兩種來源:沒送 AI 的與 AI 結果僅供參考的都紅底標「已攔截」;要分辨看清單列那行原因與詳情下一步卡的待辦文字。
- 被閘道擋下的檔不算「已攔截」:它沒有
needs_review,落在橙點「沒有候選」;要知道是哪條規則擋的,到 AI 用量 呼叫明細找 feature=證據自動分類、狀態=被阻擋。
13. 開發與驗證¶
- 跑起來:BE
python main_socketio.py(port 8000,log 在log/app.log);FE 在compliance-manager-fe/起 Vite dev server。BE 改 service code 後必須重啟(無 hot reload) - 測試帳號:dev 環境專案 manager 帳號(密碼見
.env/ 部署文件) - 導航路徑:登入 → 專案 / AP 觸發分類 → 分類 job 完成 → 進覆核頁(
.../classify-evidence/run/{runFolderId}) - 前置資料:租戶已連 Google Drive(
CONNECTED)+ AP 已 init Drive 資料夾 + Evidences 有檔案 + 已跑過一個 completed 分類 run - E2E:
compliance-manager-test/repo(Cucumber + Playwright);以證據分類/classification搜尋 - 相關文件:FR-030(AI 證據分類,
docs/features/FR-030-2605-auto-evidence-classification/)、FR-031(報表);下游報表見 驗證報表 與 誤判報表;DB / API 全量見 GAI-SD-02/03