手冊首頁 / 證據管理 / 證據分類覆核

證據分類覆核(Evidence Classification Review)

功能群:證據管理|分類生命週期、兩種證據概念、EC_ error code、核心資料模型先讀 功能群總覽

事實基準:2026-07-05 從 FE / BE code 掃出(FR-030 AI 證據自動分類 v1);state JSON 形狀對照 app/evidence_classification/service/evidence_classification_service.py,UI 對照 EvidenceClassificationReview.vue 全檔

1. 功能描述

證據分類覆核頁是 AI 證據自動分類的人工覆核工作區:一個分類 run(run_folder_id)跑完後,逐檔檢視 AI 建議的 placement(檔案 → AO 的對應),手動新增 / 移除 placement、標記 N/A、刪檔,最後儲存(同步回 Drive _state.json)並在需要時歸檔(把 placement 檔複製進 per-AO 資料夾)。編輯的真相是 Drive 上的 _state.json(見 _overview §2)。

主要使用者:觸發分類的專案 manager(覆核 / 存 / 歸檔 v1 僅 JWT、未鎖專案角色,見 §3)。

角色速覽(完整見 _overview §5):

角色 一句話
manager 專案經理 — 觸發分類(上游)並覆核修正
(覆核者) v1 覆核 / 存 / 歸檔僅需 JWT(未鎖專案角色,實驗階段)

1.1 功能總覽(本頁全部功能)

# 功能 說明 位置 詳述
1 分類統計列 總檔數 / 已分類數+% / 未分類(可點篩選)/ placement 總數 / 覆蓋度(covered_aos/總 AO) Header 下方 ClassificationStatsRow §10
2 未歸檔警示 + 立即歸檔 archive_files=false(analyze-only)時顯示警示,可「立即歸檔」把 placement 檔複製進 Drive 警示條 UC-EC-02
3 篩選列 未分類收件匣 toggle / 搜尋(檔名)/ 領域下拉 / 狀態(all/unclassified/single/multi/heavy/na)/ 最低信心 slider / 重設 ClassificationFilterBar §5
4 檔案清單(左欄) 虛擬捲動表:檔名(+ N/A / 未分類 tag、AO 數)/ 信心(迷你進度條,色分 tier)/ placement 數;可排序 ClassificationFileList(34%) §5
5 檔案詳情(右欄) 選中檔的 header(Drive ID / AO 數 / placement 數)+ 前後鍵導覽(j/k);三態身:N/A / 未分類 / 已分類 ClassificationFileDetail(66%) UC-EC-01
6 placement 卡 已放置:領域→控制項→AO 麵包屑 + 信心 / MANUAL 徽章 + reasoning + 移除 PlacementCard UC-EC-01
7 AI 候選卡 未放置的 AI 建議(≥ minConf):麵包屑 + 信心 + reasoning +「加入」 CandidateCard UC-EC-01
8 加入 AO(AOPickerDialog) 從全 AO 清單勾選加/減 placement(勾=保留、取消=移除、新勾=新增) 存底列「Add AO」 UC-EC-01
9 標記 N/A / 復原 整檔標為不適用(不需分類)/ 復原 存底列 / N/A 態「Undo」 UC-EC-01
10 刪除檔案 把檔案移出證據池(is_deleted 存底列「Delete File」 UC-EC-01
11 儲存 / 捨棄 儲存 diff 回 Drive(copy/trash)+ 鏡像 DB;捨棄重載 存底列(未存變更 badge) UC-EC-01
12 檔案預覽 開 Drive 檔預覽(docx/xlsx/pptx/pdf/text/image,<50MB) 清單 / 詳情預覽鈕 → EvidenceFilePreviewDialog §6
13 開 Drive 資料夾 / 報表捷徑 開 run 資料夾;DEV_MODE 顯示驗證 / 誤判報表捷徑 Header 動作鈕 驗證 / 誤判
14 重跑分類 返回上頁重新觸發(本頁不觸發,router.back() Header §12

UC(§2)只展開 1(逐檔覆核與儲存)、2(歸檔)這些核心操作路徑;統計 / 篩選 / 預覽是輔助檢視。觸發分類本身不在本頁(在上游專案 / AP 頁,見 §12)。

2. Use Case

角色速覽(完整見 _overview §5):

角色 一句話
manager 專案經理 — 觸發分類(上游)並覆核修正
(覆核者) v1 覆核 / 存 / 歸檔僅需 JWT(未鎖專案角色,實驗階段)

流程圖視覺慣例:菱形 = 判斷、橘底 = 例外/擋下、紅框 = 錯誤(標 error code)、綠框 = 成功終點、虛線 = 可選路徑。

UC-EC-01 逐檔覆核分類並儲存

項目 內容
角色 覆核者(v1 JWT)
前置條件 分類 run 已 completed、_state.json 存在
產出 / 後置條件 placement 增減 / N/A / 刪檔在記憶體 → 儲存時 diff → Drive copy/trash + overwrite _state.json + DB 鏡像同步
ecrev_uc_01 start pick 選檔(清單) 看 AI 候選 / 現有 placement start->pick edit 記憶體編輯:加/減 placement(AOPicker) 標 N/A / 刪檔 pick->edit save 儲存 PUT /classification-run/{id}/state(整包) edit->save diff BE diff old vs new placements save->diff ops 新增 → Drive copy 進 per-AO 移除 → Drive trash 覆寫 _state.json + DB 鏡像 diff->ops ok operations_applied / drive_copied / drive_trashed 統計刷新 ops->ok
UC-EC-01 流程:選檔 → 加/減 placement / 標 N/A / 刪檔(記憶體)→ 儲存 diff → Drive copy/trash + _state.json + DB 鏡像

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
ecrev_uc_02 start gate archive_files = false? (analyze-only run) start->gate done 已歸檔,無警示 gate->done 否(已歸檔) warn 未歸檔警示 →「立即歸檔」 POST /classification-run/{id}/archive gate->warn copy 對無 placement_drive_id 者 Drive copy 進 per-AO 資料夾、填回 id warn->copy ok archive_files=true + archived_at drive_copied / already_archived copy->ok
UC-EC-02 流程:未歸檔警示 → 立即歸檔 → 對無 placement_drive_id 者 copy 進 per-AO 資料夾 → 標 archived

3. 權限矩陣

角色基調見 _overview §5。本頁檢查點:

操作 FE 判定 BE 強制 BE 檢查位置
觸發分類(上游) 專案 manager project_participant.role == "manager"EC_403001 app/evidence_classification/service/evidence_classification_service.py:99-150
讀 state / 存 / 歸檔 / 預覽 能到 URL 即可(無頁內角色判定) v1 僅 @jwt_required(),未檢專案角色 api/evidence_classification/routes/evidence_classification_route.py

⚠️ v1 覆核權限未鎖(收進 §12):觸發分類鎖 manager,但覆核 / 存 / 歸檔 / 預覽目前只驗 JWT——實驗階段(FR-030 v1 demo-ready)可接受,非資安定案。日後正式化須補專案角色檢查。

4. 狀態機與前置條件

分類 run 狀態機見 _overview §2。本頁相關 gate:

條件 效果 出處
run status = completed 才有可覆核的 _state.json evidence_classification_service(run state 讀取)
archive_files = false 顯示未歸檔警示 + 立即歸檔鈕;placement placement_drive_id 為 null ClassificationFileDetail / metadata
未存變更 存 / 捨棄鈕啟用,pending 數 badge(原始 snapshot vs 當前 placement diff) useEvidenceClassification.hasUnsavedChanges
檔案三態 placements 空且非 N/A → 未分類;N/A → 不適用;有 placements → 已分類(single/multi/heavy = 1/2–4/≥5) FE 分類邏輯

5. UI 設計

📸 實際畫面截圖待補(一律亮色模式拍攝):以 playwright 對 STG 擷取,預計補以下四張到 assets/img/: ① 整頁(統計列 + 篩選 + 左檔案清單 + 右詳情)ec-review-overview.png ② 檔案詳情 — 已分類(placement 卡 + AI 候選卡)ec-review-classified.png ③ 未分類 + AI 候選(低於門檻警示)ec-review-unclassified.png ④ AOPickerDialog + 未歸檔警示 ec-review-aopicker.png

版面骨架與區塊職責(2 欄 Splitter,狀態存 localStorage ec-review-splitter;彩色區塊可點跳本頁小節錨點,API 僅標代表性):

Header 動作:開 Drive 資料夾/報表捷徑(DEV_MODE)/重跑分類(router.back) ClassificationStatsRow:總檔數/已分類數+%/未分類(可點篩選)/placement 總數/覆蓋度 跳至 §2 Use Case(UC-EC-02 歸檔 run:analyze-only → 落檔) 未歸檔警示(archive_files=false)+「立即歸檔」 POST /classification-run/{run_folder_id}/archive  → §2 UC-EC-02 ClassificationFilterBar:未分類收件匣 toggle/搜尋/領域下拉/狀態/最低信心 slider/重設 左:檔案清單(34%) ClassificationFileList 虛擬捲動表:檔名(+N/A / 未分類 tag) 信心迷你進度條(色分 tier)/placement 數 可排序 policy_v3.pdf ・ 0.91 ・ 3 backup.txt ・ 未分類 ・ 0 readme.md ・ N/A GET …/state(metadata + files + placements) 跳至 §2 Use Case(UC-EC-01 逐檔覆核分類並儲存) 右:檔案詳情(66%)ClassificationFileDetail 選中檔 header(Drive ID/AO 數/placement 數)+前後鍵導覽 j/k;三態身:N/A /未分類 /已分類 PlacementCard(已放置) 領域→控制項→AO 麵包屑+信心/MANUAL+reasoning+移除 CandidateCard(AI 候選 ≥ minConf) 麵包屑+信心+reasoning+「加入」 存底列:Add AO /標 N/A /Delete File /儲存·捨棄(未存變更 badge) PUT /classification-run/{run_folder_id}/state(diff → Drive copy/trash + DB 鏡像) → 點我跳 §2 UC-EC-01 AOPickerDialog(存底列「Add AO」開) 全 AO 清單勾選加/減 placement 彩色=可點跳本頁小節錨點(框內標錨點+代表性 API)|灰=本頁描述|虛線框=AOPickerDialog 條件層|2 欄 Splitter 34/66 API 僅列代表性 2~3 條,完整規格見 §6
證據分類覆核頁版面 wireframe:Header 動作+統計列+未歸檔警示+篩選列;左檔案清單 34%/右詳情 66%(placement 卡·AI 候選卡·存底列)+AOPickerDialog

狀態呈現:信心 tier 色(高 ≥0.85 綠 / 中 ≥0.70 橘 / 低 紅);未分類 / N/A 列有底色標;存 / 捨棄 per-按鈕狀態;鍵盤 j/k / 上下鍵切檔。無 socket,覆核為前端記憶體操作、存時才寫 Drive。

6. API 規格

Envelope:成功 {"status": true, "data": …}、失敗 {"status": false, "error_code": "EC_…", "msg": "…"}

6.1 總清單(本頁呼叫的全部 endpoint)

分類 Method + Path 說明 完整規格
載入 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=truearchived_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/components/ClassificationStatsRow.vue 統計列
.../components/ClassificationFilterBar.vue 篩選列
.../components/ClassificationFileList.vue 左檔案清單(虛擬捲動)
.../components/ClassificationFileDetail.vue 右詳情(三態)
.../components/PlacementCard.vue / CandidateCard.vue / AOPickerDialog.vue placement / 候選 / 加 AO
.../components/EvidenceFilePreviewDialog.vue Drive 檔預覽
src/service/EvidenceClassificationService.js API 包裝(getState / putState / archiveRun / 報表)
src/composables/useEvidenceClassification.js state 管理(load / add/remove placement / markNa / 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. 頁面邏輯與資料對應

載入時序

ecrev_load_sequence mounted onMounted load ec.load() GET /classification-run/{runFolderId}/state mounted->load snap snapshot 原始 placements (未存變更偵測基準) load->snap sel 自動選第一個未分類檔 (全分類則選第一檔) snap->sel ready 頁面就緒(統計 + 清單 + 詳情) sel->ready
頁面載入時序:onMounted → getState → snapshot 原始 placement(未存偵測)→ 自動選第一個未分類檔

關鍵欄位對應(畫面 ↔︎ API):

畫面元素 FE state API 欄位
統計列 computed files 聚合(classified / unclassified / placements / coverage)
信心進度條 match.confidence matches[].confidence(tier 前端算)
placement 卡 file.placements placements[](source=manual → MANUAL 徽章)
AI 候選卡 未放置的 matches ≥ minConf matches[] \ placements[]
未存 badge pendingCount(原始 vs 當前 diff)
未歸檔警示 notArchived(metadata.archive_files=false) metadata.archive_files

儲存流程:前端整包 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 分類 觸發後 Docker 容器 cmmc-classifier 逐檔呼 Claude API(prompt caching);產 _state.json + _report-original.json(背景,非本頁即時)
Drive 覆核存檔 = Drive copy/trash + 覆寫 JSON(EvidenceDriveOps);歸檔複製 placement 檔進 per-AO 三層資料夾
Socket 本頁 socket;覆核為前端記憶體操作、存時才寫
jedi-* 套件 Drive 操作 / classifier 容器(模組自有 infra);jedi-auth(JWT)
系統參數 分類模型允許集、信心門檻預設 0.80;DEV_MODE 顯示報表捷徑

12. 邊界情況與已知坑

  1. 真相在 Drive _state.json 不是 DB:DB state 是鏡像;直接改 DB 會與 Drive 脫鉤(見 _overview §7 坑 2
  2. 覆核權限 v1 未鎖:觸發鎖 manager,覆核 / 存 / 歸檔僅 JWT(§3)
  3. 觸發分類不在本頁:本頁「重跑分類」是 router.back();實際觸發 POST /project/{uid}/ap/{apUid}/classify-evidence 在上游專案 / AP 頁(單 project 單 AP、無並行,同 project 一次一 job)
  4. analyze-only 模式 placement_drive_id 為 null:未歸檔前 placement 只在 JSON、檔案未複製;歸檔後才落檔
  5. 儲存是整包 diff 不是增量:前端送完整 files 陣列,BE 自行 diff;多人同時覆核同一 run 會互蓋(無鎖)
  6. 限 CMMC L1 + Drive(v1):非 CMMC L1 框架 / 非 Drive 來源 v1 不支援(見 _overview §7 坑 5
  7. 報表即時算、無確認狀態:驗證 / 誤判報表每次開重算(見 驗證報表 / 誤判報表
  8. 預覽限檔型 / 大小:docx/xlsx/pptx/pdf/text/image、<50MB

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
  • E2Ecompliance-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