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>
354 lines
18 KiB
Markdown
354 lines
18 KiB
Markdown
# 跨系統使用者流程圖 — 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)
|
||
|
||
```mermaid
|
||
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)
|
||
|
||
```mermaid
|
||
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-tokens` endpoint 在 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,尚未實作)
|
||
|
||
```mermaid
|
||
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)| 同 |
|
||
|
||
---
|
||
|
||
## 綜合:三個情境的狀態機
|
||
|
||
```mermaid
|
||
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 訊息:
|
||
|
||
1. **使用者只看到「在 VisionA 平台內按幾下就轉完」**,背後有多個服務協作(visionA-backend / Member Center / Converter / File Access Agent)— 這是架構要盡力維持的體驗。值得注意的是 Phase 1 **建 job 階段只涉及 3 方**(visionA-backend / Member Center / Converter),File Access Agent 僅在 promote 階段參與,簡化了上傳時的故障面
|
||
2. **Phase 1 的 UX 閉環卡在「下載」**,需要 VisionA 產品團隊協調 messaging(見 `design-review.md` 議題 #1)
|
||
3. **Recovery 體驗是 VisionA 的 UX 優勢**,但受限於「Crash 即 Reset」設計,不保證跨 Crash 存活 — 要誠實告知使用者
|
||
4. **錯誤訊息的人類可讀性直接決定使用者對系統的信任**,Architect 在 TDD 中要重視 error code 的結構化設計
|
||
5. **multipart 直連上傳的 UX 優勢**:相較於舊設計「visionA-backend → FAA → Converter 拉檔」兩段式,新設計從使用者按下「上傳並轉檔」到 Converter 建 job 成功,只有一次網路傳輸;上傳時間可用 progress bar 精準呈現,不再有中間「看不見的 Converter 拉檔等待」
|