Move PRD, design specs, architecture docs, and TDD from .autoflow/ (personal/per-branch layer) to docs/autoflow/ (shared layer that goes into git) per the new Autoflow workspace layout. Files moved: - 02-prd/PRD.md - 03-design/design-review.md - 03-design/user-flow-cross-system.md - 04-architecture/TDD.md - 04-architecture/design-doc.md - 04-architecture/security.md The originals were never tracked, so git mv reduced to a filesystem rename with no history to preserve. .autoflow/ remains for personal notes (progress.md, review reports, testing logs). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
18 KiB
18 KiB
跨系統使用者流程圖 — VisionA 終端使用者視角
視角:VisionA 終端使用者(Edge AI 應用開發者)在 VisionA 平台內完成模型轉檔的完整體驗。
重點:從使用者能「感知到什麼」切入,而非技術細節。Token 種類、錯誤情境等只標示關鍵分支,詳細 API 規格見
04-architecture/TDD.md。涵蓋情境:
- 情境 A:首次上傳模型並轉檔(主要 Happy Path)
- 情境 B:離開頁面後回來看未完成的 job(Recovery)
- 情境 C:Phase 2 使用者下載模型(阻塞中 — 僅供未來參考)
服務標記慣例:
- 🧑 = VisionA 終端使用者(人類)
- 🖥️ = VisionA 前端(瀏覽器)
- ⚙️ = visionA-backend(Go 服務)
- 🔐 = Member Center(Auth 中心)
- 📦 = File Access Agent(檔案閘道)
- 🏭 = Kneron Converter API(本專案)
- 🗄️ = Converter Bucket(MinIO 暫存)
- 💾 = NAS Bucket(模型庫長期儲存)
變更歷程
| 日期 | 變更內容 |
|---|---|
| 2026-04-25 | 原始模型上傳路徑改為 visionA-backend → Converter multipart 直連(不經過 File Access Agent)。POST /api/v1/jobs 改為 multipart/form-data,移除 input_object_key 欄位,user_id 改以 multipart field 帶入。Phase 1 Converter 不再從 File Access Agent 讀取原始模型,只在 promote 階段 PUT 結果檔。階段編號:原「階段 2(上傳 FAA)+ 階段 3(建 job)」合併為新「階段 2(建 job + 上傳)」,後續階段編號往前挪一格。 |
情境 A:使用者上傳新模型並轉檔(Happy Path)
A.1 使用者視角的流程摘要(非技術)
🧑 使用者在 VisionA 平台
│
├─ 1. 在「模型庫」按「新增模型」→ 選本機 ONNX 檔
│
├─ 2. 填寫 model_id / version / 選擇 Kneron 晶片平台(520/720/...)
│
├─ 3. 按「上傳並轉檔」
│ ↓
│ [系統顯示進度條:ONNX 優化 0% → 33% → BIE 量化 33% → 66% → NEF 編譯 66% → 100%]
│
├─ 4. 轉檔完成!顯示成功訊息 + 「加進我的模型庫」按鈕
│
└─ 5. 按「加進我的模型庫」→ 短暫 loading → 顯示「已加入模型庫」
↓
使用者在模型庫看到新的已轉檔模型
使用者感知到的總體體驗:從選檔到加進模型庫,一條流水線,使用者不需要知道背後有多個服務在協作(建 job 時涉及 visionA-backend / Member Center / Converter,promote 時才會再呼叫 File Access Agent)。
A.2 完整跨系統流程圖(Mermaid Sequence)
sequenceDiagram
autonumber
participant U as 🧑 使用者
participant FE as 🖥️ VisionA 前端
participant BE as ⚙️ visionA-backend
participant MC as 🔐 Member Center
participant FA as 📦 File Access Agent
participant CV as 🏭 Converter API
participant CB as 🗄️ Converter Bucket
participant NB as 💾 NAS Bucket
Note over U,FE: 階段 1:選檔 + 填表
U->>FE: 選本機 ONNX 檔
U->>FE: 填 model_id / version / platform
U->>FE: 按「上傳並轉檔」
FE->>BE: 送出 (multipart: 檔案 + 參數)
Note over BE,CB: 階段 2:visionA-backend 呼叫 Converter 建 job(multipart 同時帶原始模型)
BE->>MC: POST /oauth/token (client_credentials, scope=converter:job.write)
MC-->>BE: service token (aud=kneron_converter_api)
BE->>CV: POST /api/v1/jobs<br/>Authorization: Bearer token<br/>Content-Type: multipart/form-data<br/>files: model (必填, ≤500MB), ref_images[] (optional, maxCount 100)<br/>fields: user_id, model_id, version, platform, enable_evaluate, enable_sim_fp, enable_sim_fixed, enable_sim_hw
CV->>MC: 驗 token (JWKS)
MC-->>CV: 驗證通過
CV->>CV: 檢查:user_id 是否有 in-progress job?<br/>(US-11 同使用者一個轉檔限制)
alt 有 in-progress job
CV-->>BE: 409 Conflict + {code: user_has_active_job, active_job_id, active_job_status, active_job_stage}
BE-->>FE: 409 + active job 詳情
FE-->>U: 顯示「你有一個轉檔進行中,要切過去看嗎?」
Note over U: 走情境 B(Recovery)
else 沒有 in-progress job
CV->>CB: 寫入原始模型 (jobs/{job_id}/input/{filename})<br/>(含 ref_images[] 如有)
CV->>CV: 建 job 記錄,塞進 Redis<br/>Enqueue 到 onnx-worker
CV-->>BE: 201 Created + {job_id, status: created}
BE-->>FE: 201 + job_id
end
Note over FE,CV: 階段 3:Polling 進度(每 2-5 秒一次)
loop 直到 status=completed 或 failed
FE->>BE: GET /api/jobs/{job_id}/status
BE->>CV: GET /api/v1/jobs/{job_id}<br/>Authorization: Bearer token
CV-->>BE: {status, stage, progress, stage_timings, expires_at, ...}
BE-->>FE: 轉譯後的進度 JSON
FE->>U: 更新進度條 + 階段顯示
end
Note over CV: Worker 背景處理:ONNX → BIE → NEF<br/>(Crash 即 Reset 設計,不保證跨 Crash 存活)
CV->>CB: 儲存各階段結果(out.onnx / out.bie / out.nef)
Note over FE,U: 階段 4:轉檔完成,顯示「加進模型庫」按鈕
CV-->>BE: {status: completed, result_object_keys: [...], expires_at: "2026-05-02"}
BE-->>FE: 已完成 + result 資訊
FE->>U: 顯示「轉檔完成!」<br/>+「加進我的模型庫」按鈕<br/>+「檔案將於 7 天後自動清除」提示
Note over U,NB: 階段 5:使用者按「加進模型庫」→ promote
U->>FE: 按「加進我的模型庫」
FE->>BE: POST /api/models/{job_id}/add-to-library
BE->>CV: POST /api/v1/jobs/{job_id}/promote<br/>Authorization: Bearer token<br/>Body: {target_object_key: "visionA/models/{user_id}/{model_id}/v{version}/out.nef", ...}
CV->>MC: 取 files:upload.write token(若 cache 過期)
MC-->>CV: service token
CV->>CB: 讀結果檔
CV->>FA: PUT /files/{target_object_key}<br/>Authorization: Bearer token<br/>Body: out.nef
FA->>MC: 驗 token
MC-->>FA: 驗證通過
FA->>NB: 儲存到 NAS(使用者的模型庫區)
FA-->>CV: 200 OK
CV-->>BE: 200 OK + promoted_object_keys
BE-->>FE: 成功
FE->>U: 顯示「已加入模型庫」
Note over U: 使用者在模型庫看到新模型
A.3 使用者感知到什麼 vs 背後發生什麼
| 使用者看到 / 做的事 | 背後發生的事(簡述) |
|---|---|
| 選檔並按「上傳並轉檔」 | visionA-backend 以 multipart/form-data 直接把原始模型上傳到 Converter(經 Member Center auth 取 converter:job.write service token),Converter 收檔後暫存在自己的 Bucket |
| 進度條跳動 | visionA-backend 每 2-5 秒 polling Converter,拿到新進度就更新 UI |
| 「ONNX 優化 → BIE 量化 → NEF 編譯」階段切換 | Converter 的 Task Scheduler 在 Redis 中推進 job stage,Worker 消耗 stage queue |
| 「檔案將於 7 天後自動清除」 | Converter Bucket 有 7 天 lifecycle,促使使用者做 promote |
| 按「加進我的模型庫」 | visionA-backend 呼叫 Converter /promote,Converter 自己把檔案 PUT 到 File Access Agent,不經 visionA-backend(省流量) |
| 模型出現在使用者的模型庫 | NAS Bucket 已經收到檔案(在 VisionA 使用者模型庫的 objectKey path 下) |
A.4 關鍵時間點與可能的等待體驗
| 時間點 | 使用者感受 | 影響 UX 的因素 |
|---|---|---|
| 按「上傳並轉檔」後 | Loading / 上傳進度條 | 單一 multipart 上傳從 visionA-backend 到 Converter 的時間,與檔案尺寸相關(上限 500MB)。建議 UI 顯示 upload progress bar:multipart 上傳可追蹤 byte 進度(XHR upload progress event 或等效機制),不再像舊設計有「上傳 FAA + 拉檔入 Bucket」兩段式等待 |
| 進度條階段切換 | 期待每階段的時間均等,但實際上 NEF 通常最久 | 建議 UI 顯示各階段預估時間(可從 stage_timings 歷史推算) |
| 轉檔完成 → 按「加進模型庫」 | 短暫 loading | API p95 < 3s,使用者體感應該 OK |
| 轉檔失敗 | 希望看到具體原因 | 錯誤碼要結構化,error.details.stage + 人類可讀的 error.details.reason |
情境 B:使用者離開頁面後回來(Recovery)
B.1 使用者視角
🧑 使用者在轉檔進行中...
│
├─ 按上一頁 / 關閉瀏覽器 / 切到其他 app
│ [Converter 背景繼續轉檔,不受影響]
│
├─ 15 分鐘後回來 VisionA,進入「模型庫」或「轉檔中心」
│
├─ 系統自動偵測到「你有一個轉檔進行中」
│ └─ 顯示:「模型 XYZ 正在轉檔(BIE 階段 60%),要繼續追蹤嗎?」
│
└─ 使用者按「繼續追蹤」→ 回到進度頁,正常 polling 進度
B.2 跨系統流程(Mermaid Sequence)
sequenceDiagram
autonumber
participant U as 🧑 使用者
participant FE as 🖥️ VisionA 前端
participant BE as ⚙️ visionA-backend
participant MC as 🔐 Member Center
participant CV as 🏭 Converter API
Note over U,FE: 使用者重新打開 VisionA 模型庫頁
U->>FE: 進入頁面
FE->>BE: GET /api/models/in-progress?user_id=...
BE->>MC: 取 service token(若 cache 過期)<br/>scope=converter:job.read
MC-->>BE: service token
BE->>CV: GET /api/v1/jobs?user_id=...&status=in_progress<br/>Authorization: Bearer token
CV->>MC: 驗 token
MC-->>CV: 驗證通過
CV->>CV: 從 Redis 查 user_id 對應的所有 in-progress job
CV-->>BE: [{job_id, status, stage, progress, created_at}, ...]
alt 有 in-progress job
BE-->>FE: job 列表
FE->>U: 顯示「你有一個轉檔進行中<br/>模型 XYZ - BIE 階段 60%<br/>[繼續追蹤] [放著不管]」
U->>FE: 按「繼續追蹤」
FE->>U: 進入進度頁,開始 polling<br/>(同情境 A 階段 4)
else 沒有 in-progress job
BE-->>FE: 空陣列
FE->>U: 正常顯示模型庫頁,不打擾使用者
end
Note over CV: ⚠️ 特別情境:Converter 在使用者離開期間 Crash
Note over CV: Redis 被重置 → in-progress job 消失<br/>(符合「Crash 即 Reset」設計)
Note over U: 使用者回來看不到 job<br/>需要重新上傳(US-12 已明示此限制)
B.3 Recovery 的 UX 考量
| 考量點 | 處理建議 |
|---|---|
| 使用者不記得自己有 job 在跑 | 進入頁面時主動查詢並顯示提示 |
| 使用者有多個裝置(手機+電腦)都登入 | 每個裝置都會看到相同的 in-progress job(因為是 server-side 查詢) |
| Converter Crash 導致 job 消失 | VisionA 前端顯示「上次的轉檔記錄已遺失,建議重新上傳」(需 visionA-backend 決定是否保留本地 cache) |
| 回來時 job 已完成 | 可查 GET /api/v1/jobs?user_id=...&status=completed 看最近完成的 job(可選功能) |
情境 C:Phase 2 — 使用者直接下載模型庫檔案(阻塞中)
⚠️ 重要提示:本情境的
POST /file-access/download-tokensendpoint 在 Member Center 尚未實作,Phase 1 不做此情境。以下流程圖僅供未來 Phase 2 啟動時參考。Phase 1 上線時使用者如何處理下載需求,見
design-review.md第 6 節「議題 #1」。
C.1 Phase 2 目標使用者體驗
🧑 使用者在 VisionA 模型庫
│
├─ 看到已轉檔的模型
│
├─ 按「下載 .nef 檔」
│
└─ 瀏覽器直接下載(快速,因為直連 File Access Agent,不經 visionA-backend)
C.2 Phase 2 跨系統流程(Mermaid Sequence,尚未實作)
sequenceDiagram
autonumber
participant U as 🧑 使用者
participant FE as 🖥️ VisionA 前端
participant BE as ⚙️ visionA-backend
participant MC as 🔐 Member Center
participant FA as 📦 File Access Agent
participant NB as 💾 NAS Bucket
Note over U,FE: 使用者按下載按鈕
U->>FE: 按「下載 .nef」
FE->>BE: POST /api/models/{model_id}/download-token
Note over BE,MC: ⚠️ Phase 2 阻塞中:Member Center 尚未實作此 endpoint
BE->>MC: POST /file-access/download-tokens<br/>Body: {tenant_id, user_id, object_key, scope: "files:download.delegate"}
MC-->>BE: {delegated_token (opaque, exp <= 5min), expires_at}
BE-->>FE: delegated_token + File Access Agent 的 download URL
Note over FE,FA: 瀏覽器直接下載(不經 visionA-backend,省流量)
FE->>FA: GET /files/{object_key}?token=<delegated_token>
FA->>MC: POST /validate-delegated-token<br/>Body: {token}
MC-->>FA: {valid: true, user_id, object_key, tenant_id}
FA->>NB: 讀檔
FA-->>FE: 檔案(stream 下載)
FE->>U: 瀏覽器儲存檔案
C.3 Phase 2 體驗 vs Phase 1 折衷方案的 UX 落差
| 項目 | Phase 1 折衷方案(任一) | Phase 2 完整方案 |
|---|---|---|
| 下載速度 | 慢(大檔走 visionA-backend proxy)或無法下載(隱藏按鈕) | 快(瀏覽器直連 File Access Agent) |
| 對 visionA-backend 的負擔 | 高(若 proxy) | 零 |
| 檔案流量路徑 | 使用者 → visionA-backend → File Access Agent → 使用者 | 使用者 ↔ File Access Agent(直連) |
| 使用者 UX | 有 spinner,可能 timeout | 瀏覽器原生下載體驗 |
| 安全性 | 同 Phase 2(都走 Member Center auth) | 同 |
綜合:三個情境的狀態機
stateDiagram-v2
[*] --> 選擇模型: 使用者進入 VisionA
選擇模型 --> 上傳中: 選檔 + 填表 + 提交
上傳中 --> 建 job: multipart 直接上傳到 Converter(含原始模型 + ref_images)
建 job --> 有衝突: 檢查 user_id 已有 in-progress
有衝突 --> 繼續追蹤舊 job: 使用者選擇查看既有 job
建 job --> 轉檔中: 無衝突,Converter 收檔並建立 job
轉檔中 --> ONNX 階段
ONNX 階段 --> BIE 階段: 完成
BIE 階段 --> NEF 階段: 完成
NEF 階段 --> 轉檔完成: 完成
ONNX 階段 --> 轉檔失敗: 失敗
BIE 階段 --> 轉檔失敗: 失敗
NEF 階段 --> 轉檔失敗: 失敗
轉檔中 --> 使用者離開: 使用者關頁面
使用者離開 --> 繼續追蹤舊 job: 使用者回來(走情境 B Recovery)
使用者離開 --> Job 消失: Converter Crash(符合設計哲學)
Job 消失 --> [*]
轉檔完成 --> 已 promote: 使用者按「加進模型庫」(Converter PUT 到 File Access Agent)
已 promote --> 下載: 使用者要用模型
下載 --> Phase 1 折衷: Phase 1(下載功能缺口)
下載 --> Phase 2 直連: Phase 2(等 Member Center 補完)
轉檔失敗 --> [*]: 使用者看錯誤訊息
Phase 1 折衷 --> [*]
Phase 2 直連 --> [*]
繼續追蹤舊 job --> 轉檔中
附錄:Token 類型快速對照
| Token | 誰持有 | 誰簽發 | 誰驗 | 用途 |
|---|---|---|---|---|
| visionA-backend service token(aud=kneron_converter_api, scope=converter:job.write / converter:job.read) | visionA-backend | Member Center(client_credentials grant) | Converter API(JWKS 驗簽) | visionA-backend 呼叫 Converter API(建 job / 查詢 / promote) |
| Converter service token(aud=file_access_api, scope=files:upload.write) | Converter API | Member Center(client_credentials grant) | File Access Agent | Converter 在 promote 階段 PUT 結果檔到 NAS。Phase 1 不再需要 files:download.read / files:metadata.read,因為原始模型已改由 visionA-backend multipart 直接上傳到 Converter,Converter 不再從 FAA 拉檔 |
| Delegated download token(Phase 2) | 瀏覽器 | Member Center(代 user 簽) | File Access Agent(線上驗) | 使用者瀏覽器直連 File Access Agent 下載 |
附錄:錯誤情境快速對照
| 使用者看到的訊息 | 技術原因 | 對應 HTTP 狀態 + error code |
|---|---|---|
| 「你有一個轉檔進行中,要切過去看嗎?」 | 同使用者同時一個轉檔限制觸發 | POST /api/v1/jobs 回 409 user_has_active_job |
| 「轉檔失敗:BIE 量化階段 — [具體原因]」 | Worker 內部失敗 | Job 的 status=failed, error.details.stage=bie, error.details.reason=... |
| 「上傳失敗,請確認檔案格式與欄位」 | multipart body 格式錯 / model 欄位缺失 / 必填 field 缺失(例如 user_id、model_id) |
POST /api/v1/jobs 回 400 invalid_multipart |
| 「檔案過大,請確認模型不超過 500MB」 | 上傳檔案超過 Converter 的大小上限 | POST /api/v1/jobs 回 413 file_too_large |
| 「模型庫服務暫時不可用,請稍後再試」 | promote 時 File Access Agent 不可用 | POST /promote 回 502 file_gateway_unavailable |
| 「轉檔還沒完成,請等進度條到 100% 再加進模型庫」 | promote 時 job 還沒 completed |
POST /promote 回 409 job_not_ready_for_promote |
| 「你的登入已過期,請重新登入」 | visionA-backend 的 user session 過期(visionA 自己的 auth,不是 Converter 的問題) | visionA-backend 自己處理 |
| 「系統錯誤,請聯絡客服」 | Member Center 或 File Access Agent 完全無法連線 | POST /api/v1/jobs 回 503 auth_service_unavailable 或類似(Phase 1 建 job 本身不依賴 FAA,FAA 錯誤集中在 promote) |
結語
這份跨系統流程圖揭露的關鍵 UX 訊息:
- 使用者只看到「在 VisionA 平台內按幾下就轉完」,背後有多個服務協作(visionA-backend / Member Center / Converter / File Access Agent)— 這是架構要盡力維持的體驗。值得注意的是 Phase 1 建 job 階段只涉及 3 方(visionA-backend / Member Center / Converter),File Access Agent 僅在 promote 階段參與,簡化了上傳時的故障面
- Phase 1 的 UX 閉環卡在「下載」,需要 VisionA 產品團隊協調 messaging(見
design-review.md議題 #1) - Recovery 體驗是 VisionA 的 UX 優勢,但受限於「Crash 即 Reset」設計,不保證跨 Crash 存活 — 要誠實告知使用者
- 錯誤訊息的人類可讀性直接決定使用者對系統的信任,Architect 在 TDD 中要重視 error code 的結構化設計
- multipart 直連上傳的 UX 優勢:相較於舊設計「visionA-backend → FAA → Converter 拉檔」兩段式,新設計從使用者按下「上傳並轉檔」到 Converter 建 job 成功,只有一次網路傳輸;上傳時間可用 progress bar 精準呈現,不再有中間「看不見的 Converter 拉檔等待」