AI 證據自動分類 — 功能規格

版本:v1.2.1(2026-05-28)— 實驗階段(experimental) 狀態:dev demo-ready,正式環境不建議啟用 相關文件:

  • design.md — 設計決策與技術 trade-off
  • implementation-plan.md — 階段性實作計畫
  • ../../api/evidence-classification/api-spec.md — API 端點規格
  • ../../changelog/2026-05-28-* — 變更紀錄

本文目的:作為此功能的「行為規格 source of truth」,涵蓋功能範圍、使用流程、資料模型、權限、限制。供開發 / QA / 文件 / 未來維護參考。


1. 功能概述

1.1 解決的問題

合規稽核準備階段,user 需把幾百份證據檔(政策文件、稽核 log、權限清單等)對照到合規框架的每個評估項目(Assessment Objective, AO)資料夾。以 CMMC Level 1 為例:6 個 domain、17 個 control、59 個 AO,134 份證據檔人工整理需 1-3 天。

1.2 解決方式

使用 LLM(Claude Sonnet 4.6)分析每份檔案的內容與檔名,自動對應到一個或多個 AO,並把檔案 copy 到 Drive 上對應的三層分類資料夾。User 透過審閱介面逐檔複核與調整。

1.3 範圍(v1)

In scope:

  • CMMC Level 1(NIST SP 800-171A 對照)框架
  • 證據檔以 Google Drive 為唯一儲存
  • 單一專案、單一 AP 為觸發單位
  • 單一活動 job 限制(同 project 不能並行)
  • 預覽 docx / xlsx / pptx / pdf / 文字 / 圖片

Out of scope(列為 follow-up):

  • 其他合規框架(ISO 27001、NIST SP 800-53 等)
  • 跨 tenant / super admin 場景
  • 多 AP / 多 project 並行
  • 大檔(> 50MB)預覽
  • DB-backed job 紀錄(目前 in-memory + Drive scan 復原)

2. 使用者角色與權限

角色 觸發分類 編輯結果 儲存變更 查看結果
Project Manager
Project Auditor / 一般成員 ✓(read-only,v1 未實作)
Tenant Admin 視是否同時為 PM 視是否同時為 PM 視是否同時為 PM
跨 tenant Super Admin 未支援(v1)

權限檢查點:

  • 觸發(POST .../classify-evidence):service 層查 project_participant,role == "manager" 才放行,否則 EC_403001 Forbidden
  • 編輯 / 儲存(PUT .../state):v1 簡化,僅檢 JWT 不檢 project 角色(實驗階段可接受;正式版要補)
  • 預覽(GET .../file/.../preview):v1 同上

3. 前置條件

  1. Tenant 已連 Google Drive(tenant_integrations.status = 'CONNECTED')
  2. 專案已 Drive 初始化(drive_folder_mappings 有對應 project / AP / Evidences mapping)
    • v0.4 起 init folders 流程會自動建 Evidences/ 子資料夾並寫 EVIDENCES scope mapping
    • v0.4 之前 init 的專案需重跑 init 才有 Evidences mapping(handler 為 idempotent,安全)
  3. Evidences 資料夾有檔案(至少 1 份)
  4. 環境變數:BE 主機 / process env 含有效 ANTHROPIC_API_KEY
  5. 基礎建設:
    • Docker daemon 運行中,BE 跑的 user 具 docker group
    • 已 build cmmc-classifier:latest image(scripts/evidence/classify/docker/)
    • LibreOffice 安裝於 BE 主機(預覽 docx → pdf 用,與 SSP 匯出共用)

4. 功能流程

4.1 觸發分類

  1. PM 進入「專案總覽」(/project/projects/<uid>/ap/<ap_uid>/)
  2. 系統檢查 drive_ready(透過 GET .../classify-evidence/jobs?ap_uid=<...>)
    • 不 ready → 不顯示「自動分類證據」按鈕
    • ready → 顯示按鈕(已有 active job 時切換成「分類中...」+ spinner)
  3. PM 點按鈕 → 開 trigger Dialog,顯示信心閾值 slider(預設 0.80)+ 歷史執行清單
  4. PM 確認觸發 → 跳 confirm dialog 顯示「N 個檔案,預估 M 分鐘」
  5. PM 確認 → BE 收 POST .../classify-evidence,回傳 { job_uid, status: queued, input_file_count, estimated_minutes }
  6. UI 顯示 toast「{N} 個檔案,預估 {M} 分鐘。可關閉視窗」
  7. PM 可立即關閉 Dialog 繼續其他工作

⚠ STG demo 期間例外:從 commit c6e9de1drive_ready gate 暫時拿掉(ProjectAuditorOverview.vue v-if 沒檢查 driveReady),按鈕一律顯示。原因:STG 部署期間既有專案的 EVIDENCES mapping 尚未 backfill 完成。STG 部署到位後要加回(見 §9 限制 / §12 roadmap)。

4.2 後台執行

  1. BE worker thread 起 docker container cmmc-classifier:latest,傳入 tenant OAuth 憑證 + AP Evidences folder ID + framework catalog
  2. Container:
    • 從 Drive 下載每個檔案的內容
    • 對每檔呼叫 Claude API(prompt caching,catalog block ~2300 tokens cached)
    • 產出 per-file:matches 陣列(候選 AOs + confidence + reasoning)+ placements 陣列(confidence >= threshold 的 AOs)
    • _state.json + _report-original.json 到 host /tmp/cm-jobs/<job_uid>/
  3. BE worker:
    • 在 Drive 建 自動分類_YYYY-MM-DD_HH-MM/ run folder(Evidences/ 內)
    • 為每個 placement copy 原檔到 <run_folder>/[domain]/[control]/[ao]/ 三層資料夾
    • 上傳 _state.json + _report-original.json 到 run folder
    • 更新 JobRegistry status = completed
  4. FE polling(5s 一次,page-level)偵測 job 完成 → 跳 toast「{N}/{M} 已分類,可進入審閱頁」

4.3 審閱與編輯

  1. PM 進審閱頁(/evidence-classification-review/...)
  2. 載入 _state.json 顯示:
    • 統計列:處理檔案 / 已分類 / 未分類 / AO 配對總數 / AO 涵蓋率
    • Filter bar:
      • 未分類審閱 toggle(進入時自動把 minConf 拉到 0,離開回 0.80,避免 user 進 inbox 還看不到目標檔的矛盾)
      • 搜尋 / 領域 / 狀態
      • 最高信心 slider 範圍 [0, 1],預設 0.80(原本 [0.5, 1],改成 0 起以利顯示 AI 沒抓到的 0 分檔)
    • 左欄檔案清單(占 1/3 寬)
    • 右欄 Detail panel(占 2/3 寬)
  3. 左欄檔案清單每列顯示:
    • 檔名(換行,不截斷)+ 副檔名 + AO 配對數
    • 最高信心 ProgressBar + 數值
    • 預覽 icon button
    • 是否「未分類」/「已標為非證據」tag
  4. 右欄 Detail panel:
    • 上方:檔名 + Drive ID + AO 評估數 + 上下篇導覽
    • 已配對 section(實心卡 + primary border):每張卡顯示分類路徑 / AO ID / 信心 / reasoning / 移除按鈕
      • 新加入未儲存:綠色左邊 + 「新加入」chip + 綠色提示
      • 標記移除未儲存:虛線 + 透明 + 「已標記移除」紅字提示 + 復原按鈕
    • AI 推薦 section(虛線卡):AI 評估但尚未配對的 AO(自動過濾 < minConf)
      • 一鍵「加入此 評估項目」直接配對
    • Save bar:標為非證據 / 從證據池刪除 / 加入其他 評估項目 配對 / 捨棄變更 / 儲存變更(N)
  5. PM 編輯動作:
    • 從候選加入 / 從已配對移除
    • 從 AOPicker dialog 樹狀選 / 取消(diff 後 apply)
    • 標為非證據(可還原)
    • 從證據池刪除(Drive 上原檔 + 副本全 trash,不可復原)
  6. PM 點「儲存變更」→ PUT .../state → BE diff 舊新 state → 跑 Drive copy / trash → 覆寫 _state.json

4.4 預覽證據檔

  1. PM 點檔案列右側 👁 → 開 Preview Dialog(95vw × 95vh)
  2. BE GET .../file/<file_drive_id>/preview:
    • 用 tenant OAuth 從 Drive 抓 bytes
    • Office 格式(docx/doc/odt/rtf/xlsx/xls/ods/pptx/ppt/odp):LibreOffice headless 轉成 PDF
    • 其他格式:原樣串流
    • 回 binary,含正確 mime-type
  3. FE 依 response Content-Type 決定渲染器:
    • application/pdf → iframe(browser 原生 PDF viewer)
    • image/*<img>
    • text/* / json / xml / yaml<pre> 純文字
    • 其他 → fallback「不支援內嵌預覽」+「在 Drive 開啟」+「下載」

4.5 歷史復原(BE 重啟後)

  1. BE in-memory JobRegistry 重啟後清空
  2. 下次 GET .../classify-evidence/jobs?ap_uid=<...>:
    • 取空 registry
    • _derive_evidence_folder_id(tenant_id, ap_uid) 找該 AP 的 Evidences folder
    • 列 children 過濾 自動分類_* 命名規則的 subfolder
    • 為每個 reconstructed entry 設 status: completedrun_folder_idstarted_at / completed_at(folder modifiedTime)
    • Dedup 用 run_folder_id,registry 紀錄優先
  3. PM 仍可從歷史清單點「查看分類結果」進舊的審閱頁

5. 資料模型

5.1 In-memory(BE process)

JobRegistry(app/evidence_classification/service/job_registry.py):

{
    "<job_uid>": {
        "job_uid": str (UUID4),
        "project_uid": str,
        "project_id": int,
        "tenant_id": int,
        "evidence_folder_id": str,   # Drive folder ID
        "framework_id": str,         # "cmmc-l1"
        "confidence_threshold": float,
        "status": "queued" | "running" | "completed" | "failed",
        "started_at": ISO datetime,
        "completed_at": ISO datetime | None,
        "run_folder_id": str | None,
        "run_folder_name": str | None,    # "自動分類_2026-05-28_09-26"
        "input_file_count": int | None,
        "classified_count": int | None,
        "started_by_user_id": int,
        "error": str | None,
    }
}

5.2 Persistent(DB)

compliance.drive_folder_mappings — 新增 EVIDENCES scope(本期 migration):

scope_type    scope_uid       parent_drive_folder_id    drive_folder_id    說明
EVIDENCES     AP.uid          AP folder.id              Drive folder.id    AI 證據池
ARCHIVE       AP.uid          AP folder.id              Drive folder.id    (既有)
...           ...             ...                       ...                (其他 scope)

無其他 DB schema 變動。

5.3 Drive(_state.json schema)

{
  "metadata": {
    "framework_id": "cmmc-l1",
    "confidence_threshold": 0.80,
    "model": "claude-sonnet-4-6",
    "triggered_at": "ISO datetime",
    "completed_at": "ISO datetime",
    "drive_run_folder_id": "...",
    "drive_run_folder_name": "自動分類_..."
  },
  "files": [
    {
      "file_drive_id": "...",
      "file_name": "...",
      "is_na": false,
      "is_deleted": false,
      "matches": [
        { "ao_id": "AC.L1-3.1.1[c]", "confidence": 0.97, "reasoning": "..." }
      ],
      "placements": [
        { "ao_id": "AC.L1-3.1.1[c]", "placement_drive_id": "...",
          "source": "ai" | "manual", "added_at": "ISO datetime" }
      ]
    }
  ]
}

5.4 Drive 資料夾結構(以一個 AP 為例)

GuidantAI/
└── <Project>/
    └── <AP>/
        ├── Evidences/                        ← 證據池(user 丟檔處)
        │   ├── policy-doc.docx
        │   ├── access-log.csv
        │   ├── ...
        │   ├── 自動分類_2026-05-28_09-26/    ← run folder 1
        │   │   ├── _state.json
        │   │   ├── _report-original.json
        │   │   ├── [AC] Access Control/
        │   │   │   ├── [AC.L1-3.1.1] Authorized Access Control/
        │   │   │   │   ├── [a] authorized users are identified/
        │   │   │   │   │   └── policy-doc.docx          ← copy from Evidences
        │   │   │   │   ├── [b] processes acting .../
        │   │   │   │   └── ...
        │   │   │   └── ...
        │   │   ├── [IA] Identification and Authentication/
        │   │   └── ...
        │   └── 自動分類_2026-05-28_14-30/    ← run folder 2 (re-run)
        ├── _Archive/                         ← (既有,軟刪除堆積區)
        └── [AC] / [IA] / ...                 ← (既有,控制項分類資料夾,給人工配對用)

6. API 規格摘要

完整定義見 docs/api/evidence-classification/api-spec.md

Method Path 用途
POST /api/1.0/project/<project_uid>/ap/<ap_uid>/classify-evidence 觸發分類
GET /api/1.0/project/<project_uid>/classify-evidence/jobs?ap_uid=<...> 列 jobs + drive_ready flag
GET /api/1.0/project/<project_uid>/classify-evidence/jobs/<job_uid> 單 job 狀態(polling 用)
GET /api/1.0/classification-run/<run_folder_id>/state 讀 _state.json
PUT /api/1.0/classification-run/<run_folder_id>/state 儲存編輯後 state
GET /api/1.0/classification-run/<run_folder_id>/file/<file_drive_id>/preview 預覽證據檔(office→pdf)

7. Error code 對照

完整見 common/code/evidence_classification_error_code.py

Code HTTP 說明
EC_403001 403 僅專案管理者可觸發
EC_404001 404 專案不存在
EC_404002 404 Run folder 不存在
EC_404003 404 _state.json 不存在
EC_404004 404 Job 不存在
EC_404005 404 Evidences 資料夾不存在 / 無法存取
EC_404006 404 AP 尚未與 Drive 連結
EC_404007 404 AP 資料夾下找不到 Evidences 子資料夾
EC_409001 409 Job 已在執行(同 project 限一個 active)
EC_412001 412 Tenant 未連 Drive
EC_412002 412 Evidences 資料夾無檔案
EC_412003 412 Catalog 建立失敗
EC_500001 500 Container 執行失敗
EC_500002 500 Container 逾時
EC_500003 500 Drive ops 失敗
EC_500004 500 _state.json 解析失敗

8. 非功能性需求(NFR)

8.1 效能

項目 目標 實測(134 檔)
AI 分類 latency < 15 分鐘 ~10 分鐘
BE Drive 收尾 < 20 分鐘 ~17 分鐘(270+ copy 操作)
預覽 — text / pdf / image < 3 秒 通常 < 1 秒
預覽 — docx 轉 PDF < 10 秒 1-3 秒(LibreOffice 啟動成本)
Polling 間隔 5 秒 5 秒

8.2 並發 & 限制

  • 同 project 同時間最多 1 個 active job(EC_409001)
  • 不同 project / tenant 可並行(各自獨立 container)
  • 全機資源無上限(v0 限制,正式版要加 max concurrent + queue)
  • 預覽單檔大小 cap 50MB
  • 同 run folder 兩個 user 同時 PUT 採 last-write-wins(無 ETag,v1 未實作)

8.3 可靠性

  • Container 失敗 → JobRegistry 標 failed + 紀錄 error,user 可重觸發
  • BE worker thread 失敗 → 同上(但 BE process crash 會丟失 in-memory 紀錄)
  • Drive copy 個別失敗 → 紀錄到 placement.copy_error 欄位,其他 placement 繼續處理
  • BE 重啟 → 跑中 job 丟失;完成的歷史可從 Drive scan 復原

8.4 安全

  • API 全要求 JWT(@jwt_required)
  • 觸發限定 project manager
  • Tenant OAuth token 加密儲存(jedi-common FernetCrypto)
  • 預覽 endpoint 為 BE proxy,user 不直接拿 Drive token
  • RLS:drive_folder_mappings / tenant_integrations 都吃 RLS,跨 tenant 隔離由 PG 保證
  • v0 caveat:state get/put/preview 僅檢 JWT 不檢 project 角色

9. 限制與已知問題

項目 影響 解法 / Workaround
JobRegistry 是 in-memory BE 重啟後跑中 job 丟失 完成的可 Drive scan 復原(list_jobsap_uid 時 BE 掃 Evidences folder 自動 reconstruct);跑中的需重觸發
drive_ready gate 暫時拿掉 未連 Drive 的專案也顯示「自動分類證據」按鈕,點下去才 toast STG 部署 + 既有 project 重跑 init folders 補 EVIDENCES mapping 後,把 v-if 加回 && ecJobs.driveReady.value(commit c6e9de1 的反向)
run folder 命名時區 UTC 跟 FE 本地時間差 8h v1.2.1 b7fa0e9f 起硬綁 Asia/Taipei;舊紀錄留原樣
同 active job 顯示兩條歷史 (已修)BE 建好 run folder 才把 run_folder_id 寫 registry → 中間期 Drive scan recovery dedup 比不到 v1.2.1 543b2e1d 改為 create_folder 後立刻 update registry
Framework 硬綁 cmmc-l1 其他框架不支援 等 v1 把 catalog 搬 DB
無 cross-tenant 支援 super admin 跨 tenant 不能用 詳見 docs/analysis/2026-05-28-worker-thread-session-scope.md
預覽大檔(>50MB)失敗 超大 spreadsheet / 掃描 PDF FE 自動顯示「在 Drive 開啟」fallback
同 run 並發儲存 last-write-wins 兩 user 編輯衝突 v1 加 ETag
無 audit log 只能從 Drive _state.json revisions 反查 v1 加 audit table

10. 依賴

10.1 內部

  • jedi-common(FernetCrypto / session_scope / auth_context)
  • jedi-project(ProjectEntity / ProjectDomainService)
  • jedi-oscal(間接,review page 顯示用)
  • app/oscal/service/export/ssp_libreoffice_converter.py(預覽 docx→pdf 共用)
  • app/cloud_integration/(Drive OAuth + folder mappings)

10.2 外部

  • Anthropic API(claude-sonnet-4-6 + prompt caching)
  • Google Drive API v3
  • Docker daemon(host 端,跑 classifier container)
  • LibreOffice headless(host 端,預覽 docx 轉 PDF)

10.3 設定

  • 環境變數 ANTHROPIC_API_KEY(放 BE process env,不入版控)
  • cmmc-classifier:latest Docker image(host 端 build)

11. 接受條件(Acceptance Criteria)

以 Gherkin 風格陳述,供測試 repo 對應 Cucumber feature file:

Feature: AI Evidence Auto-Classification

Background:
  Given my tenant is connected to Google Drive
  And the project's AP has been Drive-initialized with an Evidences folder
  And the Evidences folder contains evidence files

Scenario: Manager can trigger classification
  Given I am a project manager on the ProjectAuditorOverview
  When I click "自動分類證據"
  And I confirm with threshold 0.80
  Then a job_uid is returned and a toast confirms queueing
  And the main button switches to "分類中..." with a spinner

Scenario: Non-manager cannot see trigger button
  Given I am a project auditor (not manager)
  Then the "自動分類證據" button is not visible

Scenario: Drive not ready hides the trigger button
  Given the AP has no Drive folder mapping
  Then the "自動分類證據" button is not visible

Scenario: Classification completes and triggers toast notification
  Given an active classification job
  When the BE worker finishes drive operations
  Then a success toast appears with "{classified}/{total} 已分類"
  And the main button reverts to "自動分類證據"

Scenario: Reviewer adjusts placements then saves
  Given a completed classification with run_folder_id "..."
  When I open the review page
  And I remove a placement
  Then it reappears in "AI 推薦" section with original confidence
  When I click "套用變更" in AOPicker after toggling AOs
  Then placements are updated locally with proper "新加入" / "已標記移除" indicators
  When I click "儲存變更"
  Then a success toast confirms the operation count
  And Drive folder contents reflect the changes (copies created / trashed)

Scenario: Docx preview inline
  Given the file list shows a .docx evidence file
  When I click the 👁 icon
  Then a preview dialog opens
  And the document is rendered as PDF inline (BE converted via LibreOffice)

Scenario: Unsupported preview falls back to Drive link
  Given the file is an obscure format with no inline renderer
  When I click the 👁 icon
  Then a fallback message appears with "在 Drive 開啟" and "下載" buttons

Scenario: History survives BE restart
  Given a completed classification exists in Drive
  And the BE process has been restarted
  When I open the trigger dialog
  Then the history list shows the past run reconstructed from Drive scan
  And clicking "查看分類結果" loads the review page successfully

12. 後續演進(roadmap)

詳見 docs/release_notes/v1.2.1.md §8。重點:

優先級 項目 觸發時機
drive_ready gate 加回:STG 部署完整 + 既有 project 重跑 init folders 補 EVIDENCES mapping 後,把 && ecJobs.driveReady.value 加回 ProjectAuditorOverview.vue v-if STG 跑完 backfill 之後立刻
DB 化 JobRegistry + run folder reverse-lookup 上 staging 之前
Cross-tenant super admin 支援 當第一個多 tenant 客戶需求出現
Framework catalog 搬 DB(支援多框架) 確認第二個目標框架時
並發鎖(ETag)+ audit log 多 user 場景
AOPicker 虛擬列表 評估項目 > 200 的框架時
預覽快取 + 大檔串流 預覽使用頻繁時

13. 變更歷史

日期 版本 主要變更
2026-05-29 v1.2.1 (未 push) minConf slider 範圍 0.5→0 / 「未分類審閱」toggle 自動 minConf=0 / drive_ready gate 暫時拿掉(STG demo workaround,要加回)/ MF template edit jump-to-control tab 切換修正
2026-05-28 v1.2.1 Drive scan 歷史復原 / 預覽(含 LibreOffice docx→pdf)/ EVIDENCES scope mapping(init folders 自動建)/ worker thread session_scope 修正 / run folder 時區改 Asia/Taipei / dedup 修(run folder 建好立即 update registry)/ verify-and-repair user 屬性 typo 修
2026-05-27 v1.2.0 ~ v1.2.1 過渡 docker container 收口(S1)/ BE module(S2)/ FE review page(S3)/ trigger dialog(S4)
2026-05-26 POC 純 script,以 docs/reference/CMMC-Level 1-Evidences 為測試集