AI 證據自動分類 — 部署指引

適用版本:v1.2.1+ 狀態:experimental,適用 dev / STG;production 部署等 DB 化(v1)後再評估 相關文件:spec.md(行為規格)、design.md(設計決策)


1. 部署架構

┌─────────────────────────────────────────────────┐
│  BE host(STG 主機)                              │
│                                                 │
│  ┌──────────────┐   spawn      ┌──────────────┐│
│  │ BE process   │─────────────▶│ Docker daemon││
│  │ (Flask)      │              │              ││
│  │              │              │ ┌──────────┐ ││
│  │ ANTHROPIC_   │              │ │ classifier│││
│  │ API_KEY env  │              │ │ container│ ││
│  └──────┬───────┘              │ │ (-rm)    │ ││
│         │                      │ └──────────┘ ││
│         │ shell out            └──────────────┘│
│         │ libreoffice --convert                 │
│         ▼                                       │
│  ┌──────────────┐                              │
│  │ LibreOffice  │ (預覽 docx→pdf 用)            │
│  └──────────────┘                              │
└─────────────────────────────────────────────────┘
         │
         │ HTTPS
         ▼
┌──────────────────┐        ┌────────────────────┐
│ Google Drive API │        │ Anthropic Claude API│
└──────────────────┘        └────────────────────┘

重點:

  • Classifier docker container 跑在 BE host 本機 daemon(同機),不另外起 registry / orchestrator
  • BE 用 subprocess.run(["docker", "run", "--rm", ...]) 拉起 → 拿結果 → container 自動清掉
  • BE 跑的 user 必須在 docker group(可不用 sudo 起 container)

2. STG 主機一次性確認

2.1 Docker daemon

docker ps           # 不該噴 permission denied / cannot connect
docker info         # 應正常列出 daemon 資訊

如果 BE 跑的 user 沒權限:

sudo usermod -aG docker <BE-user>
# 重新登入該 user(group 變更需要新 session)

2.2 LibreOffice(預覽 docx → PDF 用)

which libreoffice || which soffice
# 應該回 /usr/bin/libreoffice 或類似
libreoffice --version    # 確認版本

如果未安裝:

  • Ubuntu/Debian:sudo apt install -y libreoffice --no-install-recommends
  • 與 SSP 匯出共用,通常 STG 已有

2.3 環境變數

BE process 必須能讀到:

ANTHROPIC_API_KEY=sk-ant-...    # Anthropic API key,classifier 用

放法(擇一):

  • systemd service 用 EnvironmentFile=/etc/guidant/.env
  • supervisord environment=ANTHROPIC_API_KEY=...
  • 直接寫進啟動 script 的 export

禁止 commit .env / API key 任何含憑證的檔到 git(per CLAUDE.md)。

2.4 磁碟空間

Container 跑時會在 BE host /tmp/cm-jobs/<job_uid>/ 放 catalog.json + _state.json + _report-original.json(每 job 約 1-2 MB)。

df -h /tmp     # 至少預留 1GB,長期堆積要加定期清理(下方 §6)

2.5 Drive OAuth Redirect URI

進 Google Cloud Console → 該 OAuth Client 的 Authorized redirect URIs,加上 STG 域名的 callback,例如:

https://stg.your-domain.com/api/1.0/integrations/google-drive/oauth-callback

否則 STG 連 Drive 時會被 Google 擋掉。


3. 每次發版

# 1. SSH 進 STG
ssh <user>@stg-host

# 2. Pull 最新 code(切到目標 tag / branch)
cd /path/to/compliance-manager-be
git fetch
git checkout v1.2.1     # 或對應 branch

# 3. 套件更新(若有改 pyproject.toml)
poetry update

# 4. 跑 DB migration(本期 v1.2.1 需要)
PGPASSWORD='<cmmgr-pwd>' psql \
    -h <stg-db-host> -p <port> -U cmmgr -d <db-name> \
    -f scripts/sql/2026-05-28-add-evidences-drive-folder-scope.sql

# 5. Build classifier image(就地 build,不推 registry)
cd scripts/evidence/classify/docker
docker build -t cmmc-classifier:latest .
# 約 1-3 分鐘
cd -

# 6. 重啟 BE
lsof -ti :8000 | xargs -r kill -9
nohup python main_socketio.py > log/app.log 2>&1 &
# 或用既有 systemd / supervisord 機制

# 7. 確認 BE 起來
sleep 3
curl -fsS http://localhost:8000/swagger-ui/ > /dev/null && echo OK
tail -20 log/app.log    # 確認沒 stack trace

4. 既有專案 backfill(本期需要)

v0.4 init folders 流程才會建 Evidences/ subfolder。v0.3 之前 init 的專案沒有,要重跑 init 補上。

由 PM 在 UI 操作:

  1. 進專案 → ProjectPlanning → Cloud Integrations tab
  2. 點「初始化專案資料夾」(POST /api/1.0/integrations/google-drive/projects/<project_uid>/init-folders)
  3. handler 是 idempotent — 既有的資料夾用 find-or-create 接管,只多建 Evidences/(以及對應的 EVIDENCES drive_folder_mappings 紀錄)

不重跑的話 → 該專案的「自動分類證據」按鈕不會顯示(drive_ready=false,因為 _derive_evidence_folder_id 找不到 Evidences 資料夾)。


5. Smoke test 流程

# 1. Classifier image 可正常起
docker run --rm cmmc-classifier:latest --help
# 預期:列出 service-classify 子命令的 help 訊息

# 2. LibreOffice headless 可用(預覽 docx)
echo "test" > /tmp/test.txt
libreoffice --headless --convert-to pdf --outdir /tmp /tmp/test.txt
ls /tmp/test.pdf && rm -f /tmp/test.txt /tmp/test.pdf

# 3. BE API 健康
curl -fsS http://localhost:8000/swagger-ui/ > /dev/null && echo "BE up"

# 4. UI 流程
#   - 登入 STG → 進已 init 的專案總覽
#   - 確認「自動分類證據」按鈕出現
#   - 用小測試集(< 10 檔)跑一次完整流程:觸發 → 等完成 → 進審閱頁 → 預覽 docx → 編輯 → 儲存

6. 維運注意

6.1 Container 殘留清理

--rm 旗標讓 container 跑完自動刪,但偶發 daemon crash 可能留 dead container:

docker container prune -f     # 清掉所有 stopped containers

6.2 /tmp/cm-jobs/ 清理

每跑一次分類會留 catalog + state + report,長期堆積。建議排程清理:

# 例:每週清 7 天前的 job dir
find /tmp/cm-jobs -mindepth 1 -maxdepth 1 -type d -mtime +7 -exec rm -rf {} +

放 cron 或 systemd timer。

6.3 BE log 監控

關鍵 log pattern(log/app.log):

  • [classifier] job=<uid> container done — 容器成功跑完
  • [classifier] job=<uid> completed; run_folder=<id> classified=N — 整個 job 收尾完成
  • [classifier] job=<uid> container failed / unexpected failure — 失敗

grep 查最近狀態:

grep "\[classifier\]" log/app.log | tail -20

6.4 並發監控

目前無全機資源上限。多 user 同時觸發不同 project 會同時起多個 container:

docker ps --filter "ancestor=cmmc-classifier:latest"

每個 container 跑時用 ~500MB RAM + 1 vCPU。若 STG 規格小,建議目前限 demo 用戶,觀察行為再考慮加 queue。

6.5 API quota

  • Anthropic API:跟 user 自己的 plan 走;134 檔 ~ $0.5-1 USD(含 prompt caching)
  • Google Drive API:每 tenant 一個 OAuth,daily quota 充足
  • 監控:Anthropic console / Google Cloud Console

7. 回滾

若 v1.2.1 上 STG 出問題:

# 1. 切回上一版 tag
git checkout v1.2.0
poetry update

# 2. DB migration 是 ADD ONLY(只擴 CHECK constraint 允許值),無需 rollback
#    但若要乾淨 rollback CHECK:
PGPASSWORD='...' psql -h ... -U cmmgr -d ... -c "
ALTER TABLE compliance.drive_folder_mappings
    DROP CONSTRAINT IF EXISTS chk_drive_folder_mappings_scope;
ALTER TABLE compliance.drive_folder_mappings
    ADD CONSTRAINT chk_drive_folder_mappings_scope
    CHECK (scope_type IN ('ROOT','PROJECT','AP','CONTROL_GROUP','CONTROL','AO','TASK','ARCHIVE'));
"
# (若已有 EVIDENCES scope row,DROP CHECK 不會擋,但加回去會;需先 DELETE row)
DELETE FROM compliance.drive_folder_mappings WHERE scope_type = 'EVIDENCES';

# 3. 重啟 BE
lsof -ti :8000 | xargs -r kill -9 && nohup python main_socketio.py > log/app.log 2>&1 &

# 4. 既有 Drive 上的 Evidences 資料夾不會自動刪,人工處理(或保留)

8. AWS STG 場景(若未來移植)

目前 STG 是 premise(BE 跑在 VM),docker 在本機。若改用 STAGING_AWS_BILLOWS 之類:

部分 premise STG AWS STG
Classifier 跑哪 host docker daemon 改用 ECS Fargate Task / k8s Job / AWS Batch
BE → 觸發 subprocess docker run 改用 boto3 ecs.run_task 或 k8s API
Image 來源 local build 推到 ECR,Task 從 ECR pull
環境變數注入 host env Task definition env / Secrets Manager

這是非平凡的改動,正式遷移時要規劃 infra/evidence_classification/classifier_container_runner.py 抽象化(目前只有 subprocess 一條路徑)。先列在 follow-up。


9. 已知部署陷阱

問題 症狀
BE user 沒 docker group 權限 trigger 後 BE log 噴 permission denied... docker.sock usermod -aG docker <user>,重新登入
ANTHROPIC_API_KEY 沒讀到 container 跑出來 _state.json 為空 / container exit code != 0 檢查 BE process env,確認 systemd / supervisord 注入
LibreOffice 沒裝 預覽 docx 顯示「不支援內嵌預覽」 fallback 裝 libreoffice 或 soffice
Drive OAuth redirect URI 未加 連結 Drive 跳轉時被 Google 擋 redirect_uri_mismatch Cloud Console 加 STG callback URL
cmmc-classifier:latest image 缺 trigger 後 BE log:Unable to find image 'cmmc-classifier:latest' 跑 build 步驟
DB migration 沒跑 trigger 後 chk_drive_folder_mappings_scope violation(寫 EVIDENCES row 時) 2026-05-28-add-evidences-drive-folder-scope.sql
既有專案沒重跑 init 「自動分類證據」按鈕不顯示 PM 在 UI 重跑「初始化專案資料夾」
並發太多 container 撞 RAM container 起不來 / OOM kill 限 demo user 範圍,v1 加 queue