FR-039 分散式檔案 Agent — 交接文件(冷接用,自包含)

2026-06-18 · branch feature/FR-039-evidence-agent(BE + evidence-agent)· 均未 push 接手前必讀:① design.md2026-06-18-FR039-progress.md(進度表)③ 本檔 ④ 2026-06-18-FR039-next-prompt.md


🧭 §0 先懂需求(硬 gate,別跳)

WHY:Guidant AI 是雲端 SaaS,但客戶的證據檔案是機敏資料,部分客戶不接受上傳到我們雲端託管。所以把「檔案儲存」拉出去,落地在客戶自有設備;雲端依不同客戶把檔案轉發到客戶端的 agent,binary 不存我們雲端磁碟

目標不是「做一個檔案上傳服務」,「讓檔案物理上待在客戶機房、雲端只持有指標 + 業務 metadata」。任何改動若讓 binary 回流雲端磁碟,就是違背初衷。

冷接自檢(答得出才開工):

  1. 為什麼 binary 不能存雲端?→ 客戶機敏資料、不託管。
  2. 雲端怎麼知道要打哪台 agent?→ 該 tenant 的 STORAGE_CONFIG(per-tenant、走 RLS)。
  3. 預覽轉檔在哪做、為什麼?→ 客戶端 agent(否則 source 要回流雲端,違背初衷)。

§1 架構(誰做什麼)

雲端 BE(compliance-manager-be)                客戶 server(121 / 122)
─────────────────────────────────              ──────────────────────────
業務碼(不動)                                    evidence-agent(獨立 Flask + Docker)
 └ IUploadFileProvider(不動)                     └ jedi-file-upload 原廠(local/minio adapter + 轉檔)
     └ RemoteAgentAdapter(Phase 2 新)──HTTP──►       └ 既有 MinIO(bucket guidant-ai)
         · 寫雲端 upload_files 參照                     └ 本地 Postgres(upload_files:uid↔物件名)
         · 算 SHA-256 錨點(Phase 3)
STORAGE_CONFIG(per-tenant):remote_agent
  · inline base_url,或
  · agent_uid → file_agents registry(方案 B)

關鍵設計

  • 雲端只存「參照 row」(cloud uid、檔名、tenant),save_file_name 記 agent 回傳的 uid 當跨邊界 key;binary 全在客戶端。
  • agent 是「jedi-file-upload 包成獨立服務」,完整重用、不 fork;雲端側套件零改動。
  • 路由沿用既有 per-tenant STORAGE_CONFIGsystem_configs 走 RLS),不是另建 routing 機制。

§2 現在到哪了(進度表見 progress.md)

  • Phase 0/1/2/3 + registry 後端 + deploy 資產 全完成且 commit(hash 見 progress.md)。
  • Phase 4(demo 組裝)進行中:121 agent 已跑、dev DB 已 seed、102 已指向 121;就差「重啟 BE → 102 登入上傳新檔」走完整鏈

§3 怎麼繼續(下一步具體動作)

3.1 完成 Phase 4 e2e(最優先)

  1. 重啟本機 BE(新 registry 模組要重啟才載入;服務由 user 自己起,別替他起)。
  2. 102 帳號blsadmin / Billows@123!)登入前端,上傳一個新檔
  3. 預期:檔案經 BE → 解析 agent_uid=agent-cust-a-121 → registry 拿 http://192.168.50.121:8080RemoteAgentAdapter → 121 agent → 落 121 MinIO bucket guidant-ai
  4. 下載 / 預覽該檔 → 應正常。
  5. 測完還原 102 config(102 是共用 dev tenant):
    UPDATE public.system_configs SET value='{"minio_bucket":"guidant-ai-stg","storage_type":"minio","minio_endpoint":"192.168.50.171:9002","minio_access_key":"admin","minio_secret_key":"1qaz2wsx"}'
    WHERE "group"='STORAGE_CONFIG' AND key='CONFIG' AND tenant_id=102;
    ⚠️ 102 切成 remote_agent 期間,它舊的 171-minio 檔讀不到(read adapter 依當前 config,非每檔 storage_type);測試只用新上傳檔。

3.2 部署 122(另一客戶)

同 121,evidence-agent/deploy/:把 evidence-agent:0.1.0 image(amd64)docker load 到 122 → .envMINIO_ENDPOINT=192.168.50.122:9000docker compose up -d。要驗 122 就把 102 改 {"agent_uid":"agent-cust-b-122"}

3.3 讓防竄改真生效(選做)

jedi-file-upload 的 UploadFileMapper.to_entity 漏 map checksum,已在套件 source 補一行(未 commit)。BE 現 pin 0.0.16 → 要 dev path-dep 或發套件版 才 live;未 live 時整性 fail-open(不驗、不誤擋)。

3.4 registry 管理 UI(後補)

FE 已有 /system/storage-configStorageConfigForm.vue,支援 local/minio)。要加:storage_type 多 remote_agent 選項 + agent 下拉(讀 file_agents registry,需新增 BE 查詢 API)。


§4 部署 runbook(客戶 server)

image 必須 build 成 linux/amd64(客戶 server 是 x86_64,Mac 是 arm64):

# 我們這邊(Mac)
cd ~/Projects/Billows/Audit-Manager/evidence-agent
docker build --platform linux/amd64 --secret id=nexus_auth,src=.secrets/nexus.env -t evidence-agent:0.1.0 .
docker save evidence-agent:0.1.0 | gzip > /tmp/evidence-agent-0.1.0.tar.gz   # 傳到 121/122

# 客戶 server(121/122,x86_64)
docker load < evidence-agent-0.1.0.tar.gz
# 放 deploy/docker-compose.yml + .env(填 MINIO_ENDPOINT=本機IP:9000 / keys / bucket / DB_PASSWORD)
docker compose up -d && curl http://localhost:8080/health
  • .secrets/nexus.env(Nexus 帳密 admin/1qaz2wsx)只在 build 用、gitignored。
  • agent ↔︎ 既有 minio:分開 compose = 不同 network → 用 server LAN IP192.168.50.121:9000),非 minio:9000
  • agent ↔︎ agent-db:同 compose → service 名 agent-db

§5 踩過的雷(這 arc 學到的,別重踩)

exec format error image 架構不對:Mac=arm64、server=amd64 → build 要 --platform linux/amd64
分開 compose 連不到 minio 不同 compose 不同 network → 用 host LAN IP,非 service 名
DB_PASSWORD@ 打爆 DATABASE_URL compose 傳 DB_PASSWORD 各段、agent config.py quote_plus 組 URL(已改)
container name already in use 移除 container_name(已改);或 docker rm -f 舊 container
Postgres 密碼改了沒生效 Postgres 只在 pgdata 首次初始化套密碼;第一次就設好別後改
整性驗證被靜默跳過 jedi-file-upload mapper to_entity 漏 map checksum(write-only)→ 補一行才讀得回
切 tenant 到 remote_agent 後舊檔讀不到 read adapter 依「當前 STORAGE_CONFIG」非每檔 storage_type;mixed backend 不支援
flask-restful 非 debug 吞 500 app_factory 設 PROPAGATE_EXCEPTIONS=True(evidence-agent 已設)
tenant_id 欄沒出現 TenantScopedMixinModelENABLE_MULTI_TENANT=true env 才加;產 DDL 要帶此 env
source .env 漏讀後段 DB_SECRET JSON 那行會中斷 source;取值用 python parse .env
minio 預覽 500 os.replace EXDEV PDF_CACHE_DIR 不可指 bind-mount(/data);要容器內 /tmp(同 os.replace 目標裝置)。套件已 os.replaceshutil.move 根治(待發版)
轉檔後中文變空白/豆腐字 LibreOffice 缺 CJK 字型 → Dockerfile 加 fonts-noto-cjk要重 build image
刪程序書留 minio 孤兒 各刪除路徑要經 file_upload_service.delete_file→adapter;已修 delete_from_pool,其他刪除路徑(如 job_evidence)若有同問題比照

§6 關鍵檔案地圖

BE(雲端)

  • infra/upload_file/remote_agent_adapter.py — RemoteAgentAdapter(HTTP 轉發 + 寫參照 row + SHA-256)
  • infra/upload_file/remote_agent_config_dto.py / common/code/file_storage_type.py(REMOTE_AGENT 常數)
  • app/upload_file/service/managed_file_upload_service.py_load_config / _build_adapter / _resolve_agent_base_url
  • domain/file_agent/ + infra/file_agent/ + di_containers/file_agent/ — registry 模組
  • api/uploadfile/routes/uploadfile_route.py — download/preview 的 REMOTE_AGENT 分支
  • scripts/sql/2026-06-18-fr039-file-agents-table.sql — registry 表 migration

evidence-agent(客戶端)

  • core/app_factory.py(極小)/ config/config.py(env→DTO)/ api/blob/(5 endpoint)/ di_containers/containers.py
  • deploy/docker-compose.yml + .env.example(客戶部署)/ Dockerfile(含 LibreOffice)
  • CLAUDE.md(專案規範與硬規則)

§7 測試座標

dev DB 192.168.50.188:25432 / guidant_ai_dev / cmmgr(密碼查 .env DB_SECRET
測試 tenant 102(登入 blsadmin / Billows@123!
121 agent http://192.168.50.121:8080,minio bucket guidant-ai
registry uids agent-cust-a-121(→121)、agent-cust-b-122(→122)

§8 不變式(改動別違反)

  • 業務碼 / IUploadFileProvider / 既有 local/minio adapter 不得改
  • 雲端任何路徑不得把 binary 寫進雲端磁碟(relay 用串流)。
  • agent 不連雲端 DB;雲端↔︎agent 只走 REST。
  • agent 完整重用 jedi-file-upload,不得 fork
  • 整性信任根是雲端算的 sha256;驗證一律對實際 bytes 重算
  • agent DB(pgdata)是關鍵狀態,bind-mount、勿刪。