依 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)。
15 KiB
Feature:轉檔功能整合(P0 — Phase 0.8 MVP)
父文件: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 張
- 模型檔:≤ 500 MB(converter 端的限制;應透過 config 對齊,建議
- 上傳行為: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 比例則顯示百分比,無提供則顯示「轉檔中⋯」+ 跑馬燈
- status 機械化的四個狀態:
- 同 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:
- 確認 job 屬於該 user 且狀態為
succeeded - 從 converter 取得
target_object_key(promote 階段已上 FAA) - server-to-server 從 FAA pull NEF(用
files:download.readscope,不走 delegated token) - 走既有
/api/models/init+/api/models/finalize三段式流程進模型庫 - 在
model.Source = "converted"、model.SourceJobID = <converter job_id>(internal/model.Model已預埋此兩欄位,無需擴 schema)
- 確認 job 屬於該 user 且狀態為
- 完成後前端 toast「已加入模型庫」+ 提供連結跳到
/models/{model_id} - 若同一 job 已被加入過模型庫,回 409 並顯示「此任務已加入過,請至模型庫查看」
F7:「下載」流程
- 前端 → visionA backend
GET /api/conversion/{job_id}/download(server-side 302 redirect → FAA) - visionA backend:
- 確認 job 屬於該 user 且狀態為
succeeded - 跟 Member Center 換 delegated download token(scope
files:download.delegate,TTL 5 分鐘) - 直接回 HTTP 302 Redirect,
Location: <FAA-URL>/files/{key}?access_token=<token>(token 不暴露給 frontend JS)
- 確認 job 屬於該 user 且狀態為
- 前端觸發方式:
- 用 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範例驗證)
- 用 anchor tag(
- 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 重複呼叫不重複建模型)
- visionA backend 需新增
- Design:
- 轉檔 tab 的 wireframe(upload → progress → result)需獨立設計
- 失敗狀態的視覺處理(顏色 / icon)參考既有錯誤模式
- 「加到模型庫」與「下載」兩個按鈕的視覺平衡(不要讓使用者覺得有預設答案)
- 進度條設計要區分「上傳階段」(0–100% 精確)與「轉檔階段」(不確定百分比)