依 autoflow-agent workspace v2 設計把 PRD / 設計 / 架構 / 交付類 共享文件從個人層 .autoflow/(ignored)搬到 docs/autoflow/(進 git), 讓團隊可共享產品與架構文件,個人層只留 progress / review / testing 等 per-branch 筆記。 - 02-prd/ 21 個檔(PRD、features、market-analysis 等) - 03-design/ 18 個檔(design-spec、wireframes、flows 等) - 04-architecture/ 31 個檔(TDD、design-doc、ADR×14、API 規格等) - 07-delivery/ 3 個檔(project-summary、phase-0.6-handover、stage-deployment-setup) 合計 73 檔。原檔已從 .autoflow/ 移除(migration 工具執行 git mv, 但因 .autoflow/ 在 .gitignore 中、git 將此操作視為新增、無 rename history)。
247 lines
15 KiB
Markdown
247 lines
15 KiB
Markdown
# Feature:轉檔功能整合(P0 — Phase 0.8 MVP)
|
||
|
||
> 父文件:[PRD.md](../PRD.md) | 對應 User Stories:US-24、US-TODO-05
|
||
>
|
||
> **狀態變更(2026-05-02)**:Phase 0 規劃為「P2、只定 API 契約」;Phase 0.8 提升為 P0 MVP 並落地實作。
|
||
> 本檔保留 Phase 0 的設計決策脈絡(API 契約、ConverterClient 介面),新增 Phase 0.8 的 MVP 範圍與整合決策。
|
||
|
||
---
|
||
|
||
## 1. 概要
|
||
|
||
Kneron 的 `.nef` 是 Kneron 晶片專用格式,使用者手上常是 ONNX / TFLite,需要轉檔才能在 KL520 / KL720 / KL630 / KL730 上跑。`kneron_model_converter` 已有完整轉檔 service(Phase 1 已完成 `POST /api/v1/jobs` 提交、`GET` 查狀態、`POST /promote` 把結果搬上 File Access Agent)。
|
||
|
||
Phase 0.8 的目標是在 visionA Cloud **接入 converter**,讓使用者**不離開 visionA**就能完成「上傳原始模型 → 轉檔 → 進模型庫推論」的完整流程,把原本斷裂的兩個網站合併成一條動線。
|
||
|
||
跟「模型管理(P0)」的關係:轉檔產生的 `.nef` 是模型庫的一個**新來源**(與「使用者直接上傳 nef」並列)。轉檔成功後使用者**自己決定**是否要把 NEF 加進模型庫;加進去後走的是模型管理既有的 `/api/models/init+finalize` 流程,並標記 `Source="converted"` + `SourceJobID=<converter job_id>`。
|
||
|
||
---
|
||
|
||
## 2. User Stories(Phase 0.8 MVP 範圍)
|
||
|
||
| ID | Story | 優先級 |
|
||
|----|-------|--------|
|
||
| US-24a | 作為 AI 應用開發者,**我有一個 ONNX 模型**,想轉成 NEF 放到我的 KL720 device 上跑,希望全程在 visionA Cloud 完成 | P0 |
|
||
| US-24b | 作為使用者,**轉檔完成後**我想決定是「加到模型庫直接推論」還是「下載 NEF 回去自己用」,或者兩個都做 | P0 |
|
||
| US-24c | 作為使用者,**轉檔失敗時**我想看到清楚的錯誤原因(不是只有 `failed`),知道是檔案格式不支援、量化失敗、還是其他問題 | P0 |
|
||
|
||
> Phase 0 規劃中 US-24 的「自動把 NEF 推進模型庫」(端到端全自動)改為 Phase 0.8 的**半自動**設計(user 顯式選擇)。理由:避免「使用者試了個轉檔但其實只想下載結果」也被自動塞進模型庫。
|
||
|
||
---
|
||
|
||
## 3. 功能需求(Phase 0.8 MVP)
|
||
|
||
### F1:「轉檔」進入點
|
||
|
||
- 左側 sidebar 新增「轉檔」tab,與既有的 Devices / Models / Inference 並列
|
||
- 進入後顯示**轉檔頁面**:上傳區 + 設定區 + 開始按鈕
|
||
- 不在 `/models` 頁面內混合(避免「上傳模型」按鈕同時要處理 nef / onnx / tflite 兩種流程,UX 變複雜)
|
||
|
||
### F2:上傳與設定
|
||
|
||
- **支援檔案格式**:`.onnx`、`.tflite`(Phase 0.8 不支援 `.pt` / `.h5`,由 converter 能力決定)
|
||
- **必填欄位**:
|
||
- 來源檔案(拖拽或選擇)
|
||
- 目標 chip:KL520 / KL630 / KL720 / KL730(單選)
|
||
- **可選欄位**:
|
||
- Reference images(多張,給 converter 做精度校準用)
|
||
- 任務名稱(顯示用,預設用檔名)
|
||
- **檔案大小限制**:
|
||
- 模型檔:≤ 500 MB(converter 端的限制;應透過 config 對齊,建議 `CONVERTER_MAX_MODEL_SIZE_MB`)
|
||
- Reference images:每張 ≤ 10 MB、總數 ≤ 100 張
|
||
- **上傳行為**:upload 走 **visionA backend streaming proxy**(見 §6 整合決策 D1),browser → backend → converter,使用者看到的是「上傳到 visionA」的單一進度條(XHR upload event)
|
||
|
||
### F3:轉檔執行與進度
|
||
|
||
- 上傳完成後自動切到「轉檔進度頁」(同一個 tab,不開新分頁)
|
||
- 進度顯示用 **polling**(前端每 5–10 秒打一次 `GET /api/conversion/{job_id}`)
|
||
- status 機械化的四個狀態:`queued` / `running` / `succeeded` / `failed`
|
||
- `running` 時若 converter 提供 progress 比例則顯示百分比,無提供則顯示「轉檔中⋯」+ 跑馬燈
|
||
- **同 user 同時只能跑一個 active job**:converter 端會回 409 `user_has_active_job`;前端拿到 409 時 UI 提示「你已有一個轉檔任務正在進行,請等待完成或重新整理」並禁用「開始轉檔」
|
||
|
||
### F4:完成後的半自動結果處理
|
||
|
||
轉檔狀態變為 `succeeded` 後,頁面顯示:
|
||
|
||
- 任務摘要(來源檔名、目標 chip、輸出 NEF 大小、checksum)
|
||
- **兩個並列按鈕**:
|
||
- **「加到模型庫」**:走 F6 流程
|
||
- **「下載」**:走 F7 流程
|
||
- 兩個按鈕**互不互斥**(使用者可以兩個都按),按鈕點擊後不消失,可重複觸發
|
||
- 提醒:「7 天後 converter 會自動清除這個任務,請在期限內完成處理」(converter Phase 1 已有 7 天 GC 機制)
|
||
|
||
### F5:失敗錯誤訊息
|
||
|
||
- 狀態變為 `failed` 時顯示**翻譯後**的 user-friendly 錯誤訊息
|
||
- 對照表(visionA backend 維護,避免暴露 converter 內部訊息):
|
||
|
||
| converter error code | 顯示給使用者的訊息 |
|
||
|---|---|
|
||
| `UNSUPPORTED_FORMAT` | 此模型格式目前不支援,請改用 ONNX / TFLite |
|
||
| `INVALID_CHECKSUM` | 檔案傳輸過程毀損,請重新上傳 |
|
||
| `QUANTIZATION_FAILED` | 模型內含不支援的運算子,無法量化到目標晶片 |
|
||
| `MODEL_TOO_LARGE` | 模型超過 500 MB 上限 |
|
||
| `QUOTA_EXCEEDED` | 系統暫時繁忙,請稍後再試 |
|
||
| 其他 / unknown | 轉檔失敗,請稍後重試。若持續發生請聯絡支援團隊(顯示 job_id 供回報) |
|
||
|
||
- 失敗任務也顯示 job_id(縮短前 8 碼)供使用者報修參考
|
||
|
||
### F6:「加到模型庫」流程
|
||
|
||
- 前端 → visionA backend `POST /api/conversion/{job_id}/promote-to-models`
|
||
- visionA backend:
|
||
1. 確認 job 屬於該 user 且狀態為 `succeeded`
|
||
2. 從 converter 取得 `target_object_key`(promote 階段已上 FAA)
|
||
3. **server-to-server** 從 FAA pull NEF(用 `files:download.read` scope,不走 delegated token)
|
||
4. 走既有 `/api/models/init` + `/api/models/finalize` 三段式流程進模型庫
|
||
5. 在 `model.Source = "converted"`、`model.SourceJobID = <converter job_id>`(`internal/model.Model` 已預埋此兩欄位,無需擴 schema)
|
||
- 完成後前端 toast「已加入模型庫」+ 提供連結跳到 `/models/{model_id}`
|
||
- 若同一 job 已被加入過模型庫,回 409 並顯示「此任務已加入過,請至模型庫查看」
|
||
|
||
### F7:「下載」流程
|
||
|
||
- 前端 → visionA backend `GET /api/conversion/{job_id}/download`(server-side 302 redirect → FAA)
|
||
- visionA backend:
|
||
1. 確認 job 屬於該 user 且狀態為 `succeeded`
|
||
2. 跟 Member Center 換 **delegated download token**(scope `files:download.delegate`,TTL 5 分鐘)
|
||
3. 直接回 HTTP 302 Redirect,`Location: <FAA-URL>/files/{key}?access_token=<token>`(token 不暴露給 frontend JS)
|
||
- 前端觸發方式:
|
||
- 用 anchor tag(`<a href="/api/conversion/{job_id}/download" download>`)或 `window.location.href = '/api/conversion/{job_id}/download'`
|
||
- Browser 跟著 302 redirect 到 FAA,瀏覽器內建下載管理器接手
|
||
- 觸發後 browser 內建下載管理器接手(無自訂進度條 — 但因為大檔下載 browser 自己有 UI,是合理 trade-off)
|
||
- **不需要 FAA 加 CORS**:server-side 302 redirect + browser navigation 完全不適用 CORS(FAA owner 2026-05-02 確認 + FAA TestSite `DownloadFileDirect` 範例驗證)
|
||
- Browser **直連 FAA**(透過 302 跳轉),不經 visionA backend 中轉檔案內容,避免 N 次跨 internet 流量
|
||
|
||
---
|
||
|
||
## 4. 非功能需求
|
||
|
||
| 類別 | 需求 |
|
||
|---|---|
|
||
| 大小上限 | 模型 ≤ 500 MB;ref image 每張 ≤ 10 MB、總計 ≤ 100 張 |
|
||
| 上傳體驗 | 上傳進度條(XHR `upload.progress` 事件);上傳期間禁止離開頁面(`beforeunload` warning) |
|
||
| 並行限制 | 同 user 同時最多 1 個 active job(converter enforce 409 `user_has_active_job`) |
|
||
| 任務保留 | 7 天後 converter 自動 GC;UI 在結果頁顯示倒數提醒 |
|
||
| 安全 | 所有 visionA → converter 的呼叫帶 service account JWT;使用者不直接接觸 converter 認證 |
|
||
| 可觀測性 | visionA backend log 每個 job 的 lifecycle(submit、poll status change、import、download token issued) |
|
||
|
||
---
|
||
|
||
## 5. Non-Goals(Phase 0.8 不做)
|
||
|
||
| # | 不做的事 | 原因 / 後續 |
|
||
|---|---------|-----------|
|
||
| N1 | 轉檔歷史清單(`/converter/jobs`)| Phase 0.8 只支援「眼前這個 job」,不做 list;converter Phase 1 GC 7 天,做歷史也只能看 7 天的,CP 值低 |
|
||
| N2 | 取消正在跑的 job | converter 已支援 `POST /jobs/{id}/cancel`,但 UX flow 與錯誤狀態複雜,留待 Phase 1 |
|
||
| N3 | 多 chip 同時轉檔(一次轉成多個目標)| converter 端尚不支援;user 可重複跑 |
|
||
| N4 | SSE / WebSocket 進度推送 | polling 已足夠,前端複雜度低;Phase 1 量大時再評估 |
|
||
| N5 | 進階轉檔參數(FP16、自訂量化)| 預設 INT8,足以涵蓋 80% case |
|
||
| N6 | 模型版本管理(同來源轉多版)/ A/B 比較 | 與「模型管理 Phase 2」共同規劃 |
|
||
| N7 | 轉檔配額計費 | Phase 2 Billing 一併處理 |
|
||
| N8 | Webhook push 模式(converter → visionA)| Phase 0.8 純 polling;webhook 在 converter Phase 1 已實作但 visionA 暫不接,避免在 stage 環境管理 webhook URL / 簽章 |
|
||
|
||
---
|
||
|
||
## 6. 整合決策(Phase 0.8 確認)
|
||
|
||
| # | 議題 | 決策 | 理由 |
|
||
|---|------|------|------|
|
||
| D1 | Upload 流量路徑 | Browser → visionA backend → converter(streaming proxy)| 一次性上傳;不需 converter 改 endpoint;保持「user 只認 visionA」單一信任邊界 |
|
||
| D2 | Download 流量路徑 | Browser 直連 FAA(用 delegated token)| 同一 NEF 可能被多 user / 多次下載到 device;經 backend 中轉會 N 次跨 internet 燒流量 |
|
||
| D3 | 結果處理 | 半自動(user 顯式選擇 import / download / 都做)| 避免「user 只是試試」的 NEF 被自動推進模型庫 |
|
||
| D4 | 進度更新 | Polling 5–10 秒一次 | 簡單可靠;轉檔本身耗時 1–10 分鐘,polling 開銷可忽略 |
|
||
| D5 | 通訊協定 | converter API 採既有 REST `/api/v1/jobs`,不新增 endpoint | converter 完全不用動 |
|
||
| D6 | 進入點 | Sidebar 獨立 tab,不混進 `/models` | UX 流程線性、避免「上傳模型」按鈕承載過多分支 |
|
||
|
||
---
|
||
|
||
## 7. 整合 Dependency 一覽
|
||
|
||
| 系統 | Phase 0.8 是否需要動? | 細節 |
|
||
|---|---|---|
|
||
| **kneron_model_converter** | ❌ 完全不用動 | `POST /api/v1/jobs`、`GET /api/v1/jobs/{id}`、`POST /api/v1/jobs/{id}/promote` 全部已實作(Phase 1 完成) |
|
||
| **File Access Agent (FAA)** | ❌ 完全不用動 | server-side 302 redirect 模式不需要 CORS(FAA owner 2026-05-02 確認 + TestSite 範例驗證);既有 `PUT /files/{key}`、`GET /files/{key}?access_token=`、delegated download token validation 都已實作 |
|
||
| **Member Center (MC)** | ⚠️ 確認 visionA service client 4 個 scope 已授權 | `converter:job.write`、`converter:job.read`、`files:download.read`、`files:download.delegate` |
|
||
| **visionA-backend** | ✅ 新增 `/api/conversion/*` 路由群 + ConverterClient HTTP 實作 | `internal/model.Model` 已預埋 `Source`、`SourceJobID`,無需 schema migration |
|
||
| **visionA-frontend** | ✅ 新增「轉檔」tab + upload / progress / result 三個畫面 | UI 設計依現有設計系統 |
|
||
|
||
> 跨團隊 P0/P1 工作項目詳見 `kneron_model_converter/docs/TODO-visionA-integration.md`。
|
||
|
||
---
|
||
|
||
## 8. 成功指標(KPI)
|
||
|
||
| 指標 | 目標(Phase 0.8) | 量測方式 |
|
||
|---|---|---|
|
||
| 第一個內部使用者轉檔成功率 | > 80% | converter job status 統計(succeeded / total) |
|
||
| 從上傳到拿到 NEF 的 P95 時間 | < 10 分鐘(含上傳 + 轉檔 + promote)| visionA backend 在 import / download 觸發點 log timestamp |
|
||
| 「加到模型庫」按鈕點擊率 | > 50%(驗證半自動設計合理)| 前端事件埋點 / backend `/promote-to-models` 呼叫次數 |
|
||
| 轉檔失敗錯誤訊息可理解率 | 100% 失敗 case 都對應到 §F5 表內訊息 | 失敗 log review |
|
||
| Stage 環境每週至少 5 次成功 e2e | — | converter job log 統計 |
|
||
|
||
---
|
||
|
||
## 9. 驗收條件(Phase 0.8)
|
||
|
||
### 功能驗收
|
||
|
||
- [ ] 左側 sidebar 顯示「轉檔」tab
|
||
- [ ] 可上傳 `.onnx` 模型 + 選 KL720 chip + 0 張 ref image,跑通 e2e
|
||
- [ ] 可上傳 `.onnx` + KL720 + 5 張 ref images,e2e 成功
|
||
- [ ] 上傳進度條正確顯示(0% → 100%)
|
||
- [ ] 轉檔中頁面 polling 正確顯示 `queued` / `running` / `succeeded` 狀態變化
|
||
- [ ] 完成頁顯示「加到模型庫」與「下載」兩個按鈕
|
||
- [ ] 點「加到模型庫」後 `/models` 頁可看到新模型,標記為「轉檔來源」
|
||
- [ ] 點「下載」後 browser 開始下載 NEF(檔名合理)
|
||
- [ ] 同一個 job 可重複按「加到模型庫」(第二次顯示 409 已加入過)
|
||
- [ ] 同一個 job 可重複按「下載」拿到新 token
|
||
- [ ] 同 user 已有 active job 時,submit 第二個 job 顯示 409 提示
|
||
- [ ] 上傳 600 MB 檔案被拒(前端先擋 + 後端兜底)
|
||
- [ ] 上傳 `.pb`(不支援格式)顯示明確錯誤
|
||
- [ ] 轉檔失敗時顯示翻譯後的錯誤訊息 + job_id
|
||
|
||
### 整合驗收
|
||
|
||
- [ ] visionA service client 在 MC 已有 4 個 scope(人工確認)
|
||
- [ ] 端到端:browser → visionA backend → converter → FAA → browser,stage 環境跑通
|
||
- [ ] `model.Source="converted"` + `SourceJobID=<job_id>` 正確寫入 DB
|
||
- [ ] FAA delegated token TTL 5 分鐘正確;過期後再次點下載拿到新 token
|
||
|
||
---
|
||
|
||
## 10. 後續 Phase 規劃(Non-Goals 升級路線)
|
||
|
||
| Phase | 項目 | 說明 |
|
||
|---|---|---|
|
||
| Phase 1 | 轉檔歷史清單 | 列出該 user 過去 7 天的 jobs;配合 converter 提供 `GET /api/v1/jobs?user_id=` |
|
||
| Phase 1 | 取消 job | UI 加「取消」按鈕,呼叫 `POST /jobs/{id}/cancel` |
|
||
| Phase 1 | 自訂下載進度條 / 暫停恢復 | 改成 visionA backend stream proxy 模式(多一跳但有完整 UI 控制);只在使用者要求時做,目前 browser 內建下載管理器足夠 |
|
||
| Phase 1 | Webhook push 進度 | converter → visionA backend webhook,用於精確進度與避免 polling 浪費 |
|
||
| Phase 2 | 進階參數(FP16 / 自訂量化)| converter 暴露更多 knob 後接入 |
|
||
| Phase 2 | 多 chip 同時轉 | 一次提交產出多個 NEF |
|
||
| Phase 2 | 模型版本管理 | 同來源 ONNX 不同 chip 的多版 NEF 視為同一邏輯模型的 variant |
|
||
| Phase 2 | 轉檔配額 / Billing | 與 Billing feature 一併處理 |
|
||
|
||
---
|
||
|
||
## 11. 給 Architect / Design 的注意事項
|
||
|
||
- **Architect**:
|
||
- visionA backend 需新增 `internal/converter/` 套件實作 `HTTPConverterClient`(取代 Phase 0 的 Stub)
|
||
- 上傳要走 streaming proxy(`io.Copy` + `multipart.Reader`),**不可 buffer 全 RAM、不可寫 disk**
|
||
- polling 端點 `/api/conversion/{job_id}` 要做 user-scoped 授權檢查
|
||
- promote-to-models 流程要 idempotent(同 job 重複呼叫不重複建模型)
|
||
- **Design**:
|
||
- 轉檔 tab 的 wireframe(upload → progress → result)需獨立設計
|
||
- 失敗狀態的視覺處理(顏色 / icon)參考既有錯誤模式
|
||
- 「加到模型庫」與「下載」兩個按鈕的視覺平衡(不要讓使用者覺得有預設答案)
|
||
- 進度條設計要區分「上傳階段」(0–100% 精確)與「轉檔階段」(不確定百分比)
|
||
|
||
---
|
||
|
||
## 12. 連結
|
||
|
||
- 回:[PRD 索引](../PRD.md)
|
||
- 相關:[模型管理](feature-model-management.md)、[介面契約](../interface-contracts.md)
|
||
- 跨專案:`kneron_model_converter/docs/TODO-visionA-integration.md`、`kneron_model_converter/apps/task-scheduler/docs/openapi.yaml`
|