# Kneron Model Converter - 產品需求文件 (PRD) > **文件性質**:此 PRD 為從既有程式碼反向整理,尚待產品負責人確認。 > 標記 `[推測]` 的部分為從程式碼推斷但無法確認的內容,請使用者審閱時特別留意。 > > **最後更新**:2026-04-25(原始模型上傳路徑變更:visionA-backend 直接 multipart 上傳到 Converter,不經過 File Access Agent) > > ## 變更歷程 > > ### 2026-04-25(第二次更新):原始模型上傳路徑變更 > > - **變更內容**:原始模型的上傳路徑由「visionA-backend → File Access Agent(使用者模型庫)→ Converter 用 S2S 從 File Access Agent 拉下來」改為「visionA-backend 直接把原始模型以 multipart 上傳到 Converter(跟現有 Web UI 一樣)」。 > - **變更原因**:先前 PRD 基於誤解,把使用者模型庫設計為「原始模型的來源」;使用者已澄清,正確設計是「模型庫只在使用者按『加進模型庫』後才存入轉檔結果」。原始模型上傳是從 visionA-backend 直接推給 Converter。 > - **影響章節**: > - §4.3(F-28 刪除、F-23 描述重寫) > - §4.4(US-08 前置條件與驗收標準重寫) > - §5.5(POST /api/v1/jobs 描述改為 multipart) > - §5.6(OAuth2 Client 讀檔相關 scope 移除) > - §9.2.1(POST /jobs 的 p95 目標調整,標註大檔上傳時間不計入) > - §10(路線圖移除「從 File Access Agent 取原始模型」) > - §14(Phase 1 對 File Access Agent 只剩 PUT(promote),不需要 GET、HEAD) > - §15(Phase 1 Scope 表移除「從 File Access Agent 取原始模型」) > - §12.2(大檔 promote 風險表述維持;新增 multipart 上傳相關風險) > - 附錄 A(補充本次變更造成的新問題) > - **不影響章節**: > - 既有 Persona A / B 定義 > - 既有 US-01 ~ US-07 > - 既有 `[推測]` 標記 > - US-13(promote 流程) > - US-14(Phase 2 delegated download) > - 既有「Crash 即 Reset」設計哲學 > > ### 2026-04-25(首次更新):新增 L 級功能(對外 API) > > 將 Kneron Model Converter 從「僅有 Web UI 的內部工具」擴展為「對外提供 OAuth2 保護 REST API 的服務」,讓 Innovedus 生態中的其他服務(首個消費者為 VisionA)能以程式化方式呼叫轉檔能力。 > > 新增章節: > - §1.2 新增 **Persona C — 服務消費者(visionA-backend)** > - §4.3 **API 消費者功能總覽**(Phase 1 / Phase 2 切分) > - §4.4 **新增的 User Stories(US-08 ~ US-14)** > - §5.5 **新增 API 端點預告(對外 API / v1)** > - §8.1 **RICE 新增項目:API 對外開放** > - §9.1 **新增成功指標(API SLA、採用度、錯誤率)** > - §14 **相依服務(新增章節)** > - §15 **Phase 1 / Phase 2 Scope 切分(新增章節)** > > 保留不變: > - 既有 Persona A / B、既有 User Stories US-01 ~ US-07 > - 既有 Web UI 行為(Phase 1 不改 Web UI 流程) > - 既有「Crash 即 Reset」設計哲學 > - 既有 `[推測]` 標記(本次不清除,留待使用者決定) --- ## 1. 產品策略 ### 1.1 產品願景 **一句話描述**:讓 AI 工程師能透過 Web 介面,輕鬆地將 AI 模型轉換為可部署到 Kneron AI 晶片的格式,並透過任務佇列機制解決高併發下的效能瓶頸。 **背景問題**:Kneron Toolchain 的模型轉換流程(ONNX → BIE → NEF)在單一 VM / 單一 Toolchain instance 運行時,高併發場景下會造成 CPU 爆滿與 Crash。現有的轉換方式需要工程師手動操作 CLI 或 Python API,缺乏視覺化介面與任務管理能力。 **解決方案**:建構一個 Web-based 的模型轉換服務,採用「Crash 即 Reset」的設計哲學,將轉換流程拆分為可水平擴充的 Worker Pool,由 Task Scheduler 統一派工與狀態管理,提供即時任務追蹤與結果下載。 ### 1.2 目標用戶(Persona) #### Persona A:AI 應用工程師 — 小明 - **角色**:AI 應用開發工程師,負責將訓練好的模型部署到 Kneron 晶片 - **背景**:熟悉 Python、ONNX 模型格式,但不一定熟悉 Kneron Toolchain 的底層細節 - **目標**:快速將 ONNX/TFLite 模型轉換為 NEF 格式,部署到 Kneron KDP 系列晶片 - **痛點**: - 手動使用 Toolchain CLI 操作繁瑣,容易出錯 - 多人同時轉換時系統容易當機 - 轉換過程耗時,無法即時掌握進度 - **技術素養**:高 - **一句話描述**:「我希望能一鍵上傳模型就自動完成轉換,這樣我就可以專注在模型調校而非部署工具的操作」 #### Persona B:[推測] 團隊主管 / 技術經理 — 大華 - **角色**:管理多位 AI 工程師的技術主管 - **目標**:提供團隊穩定的模型轉換服務,減少因工具問題導致的開發阻塞 - **痛點**: - 單人佔用 Toolchain 時其他人無法使用 - Crash 後需要 IT 介入重啟 - 缺乏任務可見性,無法掌握團隊的轉換排隊情況 - **技術素養**:高 - **一句話描述**:「我希望團隊能同時使用轉換服務不互相干擾,這樣就不會因為工具問題拖延專案進度」 #### Persona C:服務消費者 — visionA-backend(非人類 Persona) > **注意:這不是一個「使用者」,而是一個「服務」。** Persona C 代表所有以程式化方式呼叫 Kneron Model Converter API 的下游服務。首個消費者為 Innovedus 旗下的 **VisionA**(`visionA-backend`,Go 服務,路徑:`~/visionA/visionA-backend/`)。未來可擴展至 Innovedus 生態的其他 vertical 應用。 - **角色**:Innovedus 生態中需要「模型轉檔」能力的下游後端服務 - **當前代表**:visionA-backend(Go / Gin),目前處於 Phase 0 雛形,近期進入 Phase 1 將導入 OAuth2/OIDC - **目標**: - 讓 VisionA 的終端使用者(Edge AI 應用開發者)能在 VisionA 平台內直接觸發模型轉檔,無需離開平台 - 把轉檔結果自動納入使用者的 VisionA 模型庫(長期儲存於 NAS) - 不需要自己重新實作 Kneron Toolchain 的封裝 - **痛點**: - 目前 Kneron Model Converter 只有 Web UI,無法以程式化方式整合 - 沒有標準的 auth 機制,無法對齊 Innovedus 統一的 Member Center OAuth2 體系 - 沒有檔案搬運 API,下游服務無法把轉檔結果搬到自己的長期儲存 - 缺乏「同使用者同時一個轉檔」的保護,容易讓使用者搞混 - 沒有 job recovery 能力,使用者離開頁面後回來看不到未完成的任務 - **技術素養**:不適用(服務對服務) - **關鍵特性**: - 以 OAuth2 `client_credentials` 取得 service token 呼叫 Converter API - 在 request 中帶 `user_id`(VisionA 端的使用者 ID,Converter 信任不驗證;僅用於業務邏輯,非授權邊界);建 job 時 `user_id` 作為 multipart field,查詢 API 作為 query string - **直接將原始模型以 multipart 上傳給 Converter**(不經過任何中間檔案庫) - 透過 polling(非 webhook)取得轉檔進度 - 轉檔完成後呼叫 `promote` API 將結果搬進模型庫 - **一句話描述**:「作為服務,我希望能用標準的 OAuth2 service token 呼叫 Converter API,讓我的使用者能在我的平台內完成模型轉檔,同時不用自己處理 Kneron Toolchain 的複雜度」 #### 間接使用者:VisionA 終端使用者 雖然他們不直接接觸 Converter API,但他們是最終受益者: - **角色**:VisionA 平台上的 Edge AI 應用開發者 - **感受到的能力**:在 VisionA 模型庫內可以「把模型送去轉檔」、「看進度」、「把結果加進模型庫」 - **他們不會知道**:背後是另一個服務(Kneron Model Converter)在處理,也不會直接和它溝通 - **關鍵體驗要求**: - 同時只能有一個轉檔進行中(避免搞混) - 離開頁面再回來能看到未完成的任務 - 轉檔失敗時能看到具體原因 ### 1.3 核心問題與價值主張 | 核心問題 | 價值主張 | |---------|---------| | 單一 Toolchain instance 無法承受高併發 | Worker Pool 水平擴展,多任務平行處理 | | 模型轉換操作繁瑣(CLI / Python API) | Web UI 提供視覺化操作介面 | | 轉換進度不透明 | SSE 即時推送任務狀態 | | Crash 後恢復困難 | 「Crash 即 Reset」哲學,無狀態設計,重啟即恢復服務 | | 多人共用時互相干擾 | 任務佇列隔離,每個 Worker 獨立處理 | ### 1.4 OKR [推測] **Objective:提供穩定、高效的 AI 模型轉換服務** - KR1:系統可同時處理 N 個轉換任務(透過 Worker 水平擴展) - KR2:單一任務的轉換成功率 > 95%(排除模型本身問題) - KR3:系統 Crash 後 < 30 秒恢復服務能力 ### 1.5 北極星指標與指標體系 [推測] ``` 北極星指標:每日成功轉換任務數 ├── 驅動指標:任務提交數、轉換成功率、平均轉換時間 │ ├── 輸入指標:Web UI 使用率、API 呼叫次數 │ ├── 輸入指標:各階段(ONNX/BIE/NEF)失敗率 │ └── 輸入指標:Worker 利用率 └── 護欄指標:Worker Crash 率、Redis 記憶體使用量、佇列堆積長度 ``` --- ## 2. 市場分析 [推測] ### 2.1 市場定位 此產品為 **內部工具 / 企業內部服務**,主要服務 Kneron 內部或其客戶的 AI 工程團隊。非面向公開市場的 SaaS 產品。 ### 2.2 競品對照 | 方案 | 類型 | 優勢 | 劣勢 | 與本產品差異 | |------|------|------|------|------------| | Kneron Toolchain CLI | 原生 CLI 工具 | 功能完整、官方支援 | 無 GUI、不支援併發、操作門檻高 | 本產品是其 Web 化包裝 | | Kneron Toolchain Web GUI(原版) | 官方 Web GUI | 有介面 | 單一 instance、Crash 問題 | 本產品解決了其效能與穩定性瓶頸 | | Edge Impulse | 雲端 ML 平台 | 完整 MLOps 流程 | 不支援 Kneron 特定格式 | 本產品專注 Kneron 晶片生態 | | TensorFlow Lite Converter | Google 官方工具 | 生態完整 | 只支援 TFLite 格式 | 本產品支援 Kneron 特有的 BIE/NEF 格式 | ### 2.3 差異化策略 - **唯一性**:唯一支援 Kneron KDP 系列晶片完整轉換流程(ONNX → BIE → NEF)的 Web 服務 - **護城河**:與 Kneron Toolchain 深度整合,封裝其 Python API - **關鍵優勢**:Worker Pool 架構解決了原生 Toolchain 的併發瓶頸 --- ## 3. 用戶研究 ### 3.1 用戶旅程地圖 | 階段 | 用戶行為 | 想法/感受 | 痛點 | 機會點 | |------|----------|----------|------|--------| | 模型準備 | 訓練好模型,匯出 ONNX/TFLite | 「模型訓練完成,該部署了」 | 需要確認模型格式相容性 | [推測] 提供模型格式驗證 | | 上傳模型 | 透過 Web UI 上傳模型檔 + 參考圖片 | 「介面還算直覺」 | 大檔案上傳可能較慢(上限 500MB) | 上傳進度顯示 | | 設定參數 | 選擇平台、填寫 Model ID / Version | 「這些參數是什麼意思?」 | 參數含義不夠直覺 | [推測] 提供參數說明 / 預設值 | | 等待轉換 | 觀察 SSE 即時進度更新 | 「大概還要多久?」 | 只有階段進度,無時間預估 | [推測] 加入預估剩餘時間 | | 下載結果 | 下載 BIE/NEF 產出檔 | 「轉換完成,可以部署了」 | - | - | | 失敗處理 | 看到失敗訊息,需要重新提交 | 「為什麼失敗?該怎麼修?」 | 錯誤訊息可能不夠友善 | [推測] 提供更詳細的錯誤診斷 | --- ## 4. 功能需求 ### 4.1 功能總覽與狀態 | # | 功能 | 狀態 | 說明 | |---|------|------|------| | F-01 | 模型檔案上傳 | 已實作 | 支援 ONNX、TFLite 格式,上限 500MB | | F-02 | 參考圖片上傳 | 已實作 | BIE 量化用的參考圖片(最多 100 張) | | F-03 | 完整轉換流程(ONNX → BIE → NEF) | 已實作 | 固定三階段序列化流程 | | F-04 | 單階段轉換(ONNX 優化) | UI 已實作,後端部分實作 | Web UI 有獨立 ONNX 表單,但後端 API 僅有完整流程端點 | | F-05 | 單階段轉換(BIE 量化) | UI 已實作,後端部分實作 | 同上 | | F-06 | 單階段轉換(NEF 編譯) | UI 已實作,後端部分實作 | 同上 | | F-07 | 任務狀態即時更新(SSE) | 已實作 | Server-Sent Events + Polling 備援 | | F-08 | 任務狀態查詢(REST API) | 已實作 | GET /jobs/:jobId | | F-09 | 任務列表查詢 | 已實作 | GET /jobs | | F-10 | 結果檔案下載 | 已實作 | GET /jobs/:jobId/download/:filename | | F-11 | Worker 水平擴展 | 已實作 | docker-compose --scale | | F-12 | Stub Worker 開發模式 | 已實作 | WORKER_MODE=stub 模擬處理 | | F-13 | IP 評估(可選) | 已實作 | enable_evaluate 旗標 | | F-14 | 浮點 E2E 模擬 | 未完成 | enable_sim_fp 旗標已定義,邏輯未接線 | | F-15 | 定點 E2E 模擬 | 未完成 | enable_sim_fixed 旗標已定義,邏輯未接線 | | F-16 | 硬體 E2E 模擬 | 未完成 | enable_sim_hw 旗標已定義,邏輯未接線 | | F-17 | 健康檢查 API | 已實作 | GET /health | | F-18 | [推測] 任務取消 | 未實作 | 目前無法中途取消進行中的任務 | | F-19 | [推測] 任務重試 | 未實作 | 符合「Crash 即 Reset」哲學,失敗需手動重送 | | F-20 | [推測] 歷史任務持久化 | 未實作 | Redis 重啟即清空,無持久化 | ### 4.2 User Stories #### US-01:上傳模型並執行完整轉換 > **作為** AI 應用工程師, > **我希望** 上傳 ONNX/TFLite 模型檔和參考圖片,設定目標平台後一鍵啟動轉換, > **以便** 自動完成 ONNX 優化 → BIE 量化 → NEF 編譯的完整流程,取得可部署到 Kneron 晶片的 NEF 檔。 **驗收標準**: - 支援上傳 .onnx 和 .tflite 格式檔案 - 必填參數:model_id、version、platform - 支援平台:KDP520、KDP720、KDP530、KDP630、KDP730 - 上傳成功後自動進入 ONNX → BIE → NEF 流程 - 狀態:**已實作** #### US-02:即時追蹤轉換進度 > **作為** AI 應用工程師, > **我希望** 在提交任務後能即時看到目前處於哪個階段(ONNX/BIE/NEF)以及進度百分比, > **以便** 掌握任務狀態,不需要反覆手動刷新。 **驗收標準**: - 透過 SSE 即時推送狀態更新 - SSE 斷線時自動降級為每 3 秒 Polling - 顯示當前階段名稱與進度百分比 - 任務完成或失敗時顯示對應通知 - 狀態:**已實作** #### US-03:下載轉換結果 > **作為** AI 應用工程師, > **我希望** 轉換完成後能直接從 Web UI 下載產出的 NEF 檔(或中間產物 BIE), > **以便** 取得可部署的模型檔案。 **驗收標準**: - 任務 COMPLETED 後提供下載按鈕 - 支援下載 out.onnx、out.bie、out.nef - 狀態:**已實作** #### US-04:選擇單階段處理 > **作為** AI 應用工程師, > **我希望** 能選擇只執行某一個階段(例如只做 ONNX 優化,或只做 BIE 量化), > **以便** 在除錯時逐步確認每個階段的輸出正確性。 **驗收標準**: - Web UI 提供四種工作流程選擇:ONNX 優化 / BIE 量化 / NEF 編譯 / 完整流程 - 各流程有獨立的參數表單 - 狀態:**UI 已實作,後端 API 部分實作**(Web UI 的前端表單呼叫的 API 端點如 `/api/onnx/upload`、`/api/bie/process` 尚未在 Task Scheduler 中實作,後端目前僅支援 POST /jobs 建立完整流程任務) #### US-05:水平擴展 Worker > **作為** 系統管理員, > **我希望** 能透過簡單的指令增加 Worker 數量, > **以便** 在任務量大時提升系統處理能力。 **驗收標準**: - 透過 `docker-compose up --scale bie-worker=N` 即可擴展 - 新 Worker 自動加入 Consumer Group 分配任務 - 不需修改任何設定檔或重啟其他服務 - 狀態:**已實作** #### US-06:[推測] 查看失敗原因 > **作為** AI 應用工程師, > **我希望** 任務失敗時能看到具體的錯誤原因與失敗階段, > **以便** 判斷是模型問題還是系統問題,並採取對應措施。 **驗收標準**: - 失敗任務顯示失敗的階段(ONNX/BIE/NEF) - 顯示錯誤原因文字 - 狀態:**已實作**(job record 中有 error.step 和 error.reason) #### US-07:開發與測試用 Stub 模式 > **作為** 開發人員, > **我希望** 能在不安裝 Kneron Toolchain 的情況下啟動整套系統進行開發測試, > **以便** 快速迭代 Scheduler、Web UI 和佇列邏輯。 **驗收標準**: - 設定 WORKER_MODE=stub 即可啟用 - Stub Worker 模擬處理時間(2-3 秒)並產生假輸出檔 - 完整流程可走通,SSE 通知正常運作 - 狀態:**已實作** --- ### 4.3 API 消費者功能總覽(新增,2026-04-25) 本節說明本次 L 級更新的新功能範圍。所有新功能都是針對 **Persona C(服務消費者)** 設計,並以「讓 VisionA 終端使用者獲得完整模型轉檔體驗」為最終目標。 | # | 功能 | 狀態 | Phase | 說明 | |---|------|------|-------|------| | F-21 | OAuth2 Resource Server(驗 visionA-backend 的 service token) | 未實作 | Phase 1 | 以 Member Center JWKS 驗簽 JWT,檢查 `aud=kneron_converter_api` 與 scope | | F-22 | OAuth2 Client(Converter 自己取 service token) | 未實作 | Phase 1 | 用 `client_credentials` 向 Member Center 取 token,供 PUT 檔案到 File Access Agent(promote 時使用) | | F-23 | 對外 API: 建立轉檔 job(`POST /api/v1/jobs`) | 未實作 | Phase 1 | **multipart/form-data** 格式(跟現有 Web UI 一致),visionA-backend 直接把原始模型上傳到 Converter,body 帶 `user_id` + `model`(原始模型檔)+ `ref_images[]`(可選)+ 參數 | | F-24 | 對外 API: 查詢 job 進度(`GET /api/v1/jobs/:id`) | 未實作 | Phase 1 | Polling 模式,回傳 status、stage、progress、error | | F-25 | 對外 API: 查詢 job 列表(`GET /api/v1/jobs?user_id=...&status=...`) | 未實作 | Phase 1 | 支援 recovery 場景(使用者離開頁面回來) | | F-26 | 對外 API: 搬檔到模型庫(`POST /api/v1/jobs/:id/promote`) | 未實作 | Phase 1 | Converter 自己用 service token PUT 結果檔到 File Access Agent | | F-27 | 同使用者同時一個轉檔限制 | 未實作 | Phase 1 | 建 job 時檢查 `user_id` 是否有 in-progress job,有則拒絕 | | F-29 | OpenAPI 規格(對外 API 契約) | 未實作 | Phase 1 | 產出 OpenAPI 3.0 spec,方便下游服務整合與測試 | | F-30 | 使用者下載模型(delegated token 直連 File Access Agent) | 未實作 | **Phase 2** | 阻塞:Member Center `POST /file-access/download-tokens` 尚未實作 | > **註(2026-04-25 變更)**:原 F-28「從 File Access Agent 取 input 模型」已刪除。Phase 1 的原始模型上傳改為由 visionA-backend 直接 multipart 上傳到 Converter(見 F-23),不再經過 File Access Agent。 **Phase 1 / Phase 2 切分原因**:Phase 2 的「使用者下載」依賴 Member Center 的 delegated token endpoint,但該 endpoint 目前尚未實作。為避免阻塞 Phase 1 整體上線,Phase 1 先讓 VisionA 能完成「上傳 → 轉檔 → 搬進模型庫」閉環,下載則等 Member Center 補完後再做。詳見 §15。 ### 4.4 新增 User Stories(2026-04-25) #### US-08:visionA-backend 以 service token 建立轉檔 job > **作為** visionA-backend(服務消費者), > **我希望** 用 OAuth2 service token 呼叫 Converter API 建立轉檔 job, > **以便** 讓我的使用者能在 VisionA 平台內觸發模型轉檔,而不用跳到另一個系統。 **前置條件**: - visionA-backend 已取得終端使用者要轉檔的原始模型檔案(由 VisionA 自己的上傳流程收到,保存在 visionA-backend 自己可存取的位置) - visionA-backend 已向 Member Center 取得 service token(`aud=kneron_converter_api`, scope=`converter:job.write`) **驗收標準**: - `POST /api/v1/jobs`,Content-Type: `multipart/form-data`,Header: `Authorization: Bearer ` - **multipart files**: - `model`(必填,原始模型檔,支援 `.onnx` / `.tflite`,單檔 ≤ 500MB) - `ref_images[]`(可選,BIE 量化用參考圖片,最多 100 個) - **multipart fields**: - `user_id`(必填,VisionA 使用者 ID,字串) - `model_id`(必填,Integer,1–65535) - `version`(必填,字串,如 `"0001"`) - `platform`(必填,字串,`"520"` / `"720"` / `"530"` / `"630"` / `"730"`) - `enable_evaluate`(可選,Boolean,預設 false) - `enable_sim_fp`(可選,Boolean,預設 false,未接線) - `enable_sim_fixed`(可選,Boolean,預設 false,未接線) - `enable_sim_hw`(可選,Boolean,預設 false,未接線) - Converter 驗證 token 簽章、`aud`、`scope`,失敗則回 `401` / `403` - 驗證通過後,Converter 把原始模型存到 Converter Bucket(例如 `jobs/{job_id}/input/{filename}`),由既有 Worker 流程處理(與現有 Web UI 走 `POST /jobs` 的儲存路徑一致) - 建 job 成功回 `201 Created`,body 含 `job_id`、`status=created` - 若該 `user_id` 已有進行中 job(`status ∈ {created, running}`),回 `409 Conflict`,body 含現有 job_id(詳見 US-11) - 若 multipart 欄位缺漏、檔案超過 500MB、模型格式非 `.onnx` / `.tflite`、platform 不支援,回 `400 Bad Request` - **設計備註**:此路徑刻意與既有 Web UI 的 `POST /jobs`(Phase 1 內部路徑)走同一份 multipart 上傳行為,以最大化重用既有上傳處理邏輯(multer memory storage、檔案大小限制、Worker 派工)。差異僅在:`/api/v1/jobs` 多驗 OAuth token、必填 `user_id`、多檢查 US-11 的同使用者同時限制。 - 狀態:**未實作(Phase 1 必做)** #### US-09:追蹤轉檔進度(polling) > **作為** VisionA 終端使用者(透過 visionA-backend), > **我希望** 在觸發轉檔後能看到目前進度(哪個階段、百分比), > **以便** 知道還要多久、是否成功。 **驗收標準**: - visionA-backend 以固定間隔呼叫 `GET /api/v1/jobs/:id`(建議 2-5 秒一次) - 回傳 body 含:`status`(created / running / completed / failed)、`stage`(onnx / bie / nef)、`progress`(0-100)、`error`(失敗時)、`result_object_keys`(完成時,各輸出檔在 Converter MinIO 的暫存 key) - visionA-backend 將此資訊轉化為 VisionA UI 要顯示的進度條 - **不做** Webhook / SSE 對外推送(Phase 1 範圍內,polling 已足夠) - 若 `job_id` 不存在或不屬於呼叫者的 tenant,回 `404` - 狀態:**未實作(Phase 1 必做)** #### US-10:轉檔完成後加進模型庫 > **作為** VisionA 終端使用者(透過 visionA-backend), > **我希望** 轉檔完成後可以選擇「加進我的模型庫」, > **以便** 結果被長期保存到 NAS,而不是留在 Converter 的暫存區等 7 天後被清掉。 **驗收標準**: - visionA-backend 呼叫 `POST /api/v1/jobs/:id/promote`,body 帶目標 `object_key`(VisionA 端決定命名,例如 `visionA/models/{user_id}/{model_id}/v{version}/out.nef`) - Converter 自己用 service token(scope=`files:upload.write`)PUT 檔案到 File Access Agent - 檔案流只在 NAS 側流動(Converter → File Access Agent → NAS Bucket),不透過 visionA-backend 中轉 - 成功回 `200 OK`,body 含已搬檔的 object_key 列表 - 若 job 尚未 completed 或已 failed,回 `409 Conflict` - 若 promote 過程失敗(File Access Agent 不可用、auth 問題等),回 `502 Bad Gateway`,並可重試 - Converter Bucket 的暫存檔仍保留直到 7 天 lifecycle 到期(不立即刪除,以利失敗重試) - 狀態:**未實作(Phase 1 必做)** #### US-11:同使用者同時一個轉檔 > **作為** VisionA 終端使用者(透過 visionA-backend), > **我希望** 系統限制同一個使用者同時只能進行一個轉檔, > **以便** 我不會被多個同時進行的轉檔搞混,也避免意外消耗 Worker 資源。 **驗收標準**: - Converter 在建 job 時檢查該 `user_id` 是否已有 `status ∈ {created, running}` 的 job - 若有,回 `409 Conflict`,body 範例:`{"error":"user_has_active_job","active_job_id":"..."}` - 限制作用於 `user_id`(VisionA 使用者),**不是** `client_id`(同一個 VisionA backend 服務可以同時有多個不同使用者的轉檔) - 此為「產品面軟限制」,非「授權邊界」:Converter 只是信任 visionA-backend 傳來的 user_id - 狀態:**未實作(Phase 1 必做)** #### US-12:離開頁面 recovery > **作為** VisionA 終端使用者(透過 visionA-backend), > **我希望** 離開轉檔頁面再回來時,能看到我之前未完成的 job, > **以便** 不用重新送一次或擔心轉檔結果遺失。 **驗收標準**: - visionA-backend 進入轉檔頁面時呼叫 `GET /api/v1/jobs?user_id=...&status=in_progress`(`in_progress` 為 `created` + `running` 的總稱) - Converter 回傳該使用者所有進行中的 job 清單(Phase 1 同時至多 1 個,但 API 設計為 list 以保彈性) - visionA-backend 根據清單決定是否顯示「有未完成的轉檔,是否繼續追蹤?」的 UI - 同時支援 `status=completed` / `status=failed`(近期完成)、`limit`、`offset` 參數 - **注意**:Converter 本身仍遵循「Crash 即 Reset」哲學,Redis 重啟後 job 會消失。recovery 僅在 Converter 正常運行期間有效,不保證跨 Crash 可恢復 - 狀態:**未實作(Phase 1 必做)** #### US-13:Converter 自己搬檔避免大檔跨網段傳輸 > **作為** visionA-backend, > **我希望** 呼叫 Converter 搬檔時,Converter 能自己把檔案推到 File Access Agent, > **以便** 避免大檔(可能數百 MB)跨 AWS ↔ NAS 來回傳輸(Converter → visionA-backend → File Access Agent 會慢且貴)。 **驗收標準**: - `POST /api/v1/jobs/:id/promote` 的實作中,Converter 直接從自己的 MinIO 讀檔,然後 PUT 到 File Access Agent - visionA-backend 不接觸檔案實體,只接收 promote API 的成功/失敗結果 - Converter 以自己的 OAuth2 `client_credentials` 向 Member Center 取得 `files:upload.write` scope 的 token(non-user-bound service token) - File Access Agent 以 Member Center JWKS 驗該 token,檢查 `aud=file_access_api`、`scope=files:upload.write`、`tenant_id` 吻合 - 狀態:**未實作(Phase 1 必做)** #### US-14:【Phase 2】使用者下載模型走 delegated token > **作為** VisionA 終端使用者, > **我希望** 從 VisionA 模型庫下載已搬進 NAS 的模型檔時,瀏覽器能直連 File Access Agent(帶短效 delegated token), > **以便** 大檔不必經過 visionA-backend 中轉,下載快速且省流量。 **前置條件(阻塞)**: - Member Center 已實作 `POST /file-access/download-tokens`(目前狀態:**未實作**) **驗收標準(Phase 2)**: - visionA-backend 呼叫 Member Center 的 `POST /file-access/download-tokens`(scope=`files:download.delegate`),傳入 `tenant_id`、`user_id`、`object_key` - Member Center 回傳短效 opaque token(建議 `exp <= 5 分鐘`) - visionA-backend 把 token 回給瀏覽器,瀏覽器直連 `GET https:///files/{object_key}?token=` - File Access Agent 呼叫 Member Center validation endpoint 線上驗 token,驗過才回檔案 - **此 User Story 不涉及 Converter API 的修改**,只是 VisionA ↔ Member Center ↔ File Access Agent 的串接 - 狀態:**未實作,Phase 2(等 Member Center 補完後啟動)** **阻塞觸發條件**: - Member Center owner 確認 `POST /file-access/download-tokens` 的實作時程,並提供測試環境 - 或者 VisionA 團隊決定自己暫時 proxy 下載(折衷方案,需另議) --- ## 5. 系統架構摘要(從程式碼確認) ### 5.1 服務元件 | 元件 | 技術 | 職責 | |------|------|------| | Web UI | Vue 3 + Vite + Element Plus | 使用者操作介面 | | Nginx | Alpine | 反向代理、靜態檔案服務、SSE 轉發 | | Task Scheduler | Node.js + Express | REST API、Job 狀態管理、Queue 派工、SSE 推送 | | Redis | Redis 7 Alpine(不開 persistence) | Job 狀態儲存、任務佇列(Redis Stream) | | ONNX Worker | Python | ONNX 優化 / TFLite 轉換 | | BIE Worker | Python | 量化分析 | | NEF Worker | Python | NEF 編譯 | | Shared Volume | Docker named volume | Worker 間的檔案交換 | ### 5.2 API 端點(已實作) | 方法 | 路徑 | 說明 | |------|------|------| | GET | /health | 健康檢查 | | POST | /jobs | 建立新任務(上傳模型 + 參考圖片 + 參數) | | GET | /jobs | 列出所有任務 | | GET | /jobs/:jobId | 查詢單一任務狀態 | | GET | /jobs/:jobId/events | SSE 事件串流 | | GET | /jobs/:jobId/download/:filename | 下載結果檔案 | ### 5.3 任務參數 | 參數 | 必填 | 類型 | 說明 | |------|------|------|------| | model(檔案) | 是 | File | ONNX 或 TFLite 模型檔 | | ref_images(檔案) | 否 | File[] | 量化用參考圖片(最多 100 張) | | model_id | 是 | Integer | 模型 ID(1-65535) | | version | 是 | String | 模型版本(如 "0001") | | platform | 是 | String | 目標平台("520"/"720"/"530"/"630"/"730") | | enable_evaluate | 否 | Boolean | 是否執行 IP 評估(預設 false) | | enable_sim_fp | 否 | Boolean | 是否執行浮點 E2E 模擬(預設 false,未接線) | | enable_sim_fixed | 否 | Boolean | 是否執行定點 E2E 模擬(預設 false,未接線) | | enable_sim_hw | 否 | Boolean | 是否執行硬體 E2E 模擬(預設 false,未接線) | ### 5.4 任務狀態機 ``` 建立 → ONNX → BIE → NEF → COMPLETED ↓ ↓ ↓ FAILED FAILED FAILED ``` - 流程固定:ONNX → BIE → NEF,不支援跳過或只跑部分(後端限制) - 任何階段失敗即整體 FAILED,不自動重試 ### 5.5 對外 API 端點預告(新增,2026-04-25,待 Architect 產出詳細規格) 本節只列出 **PRD 層級** 的功能與驗收要點,詳細 API 規格(request / response schema、錯誤碼、範例)由 Architect Agent 在 `04-architecture/TDD.md` 或 `04-architecture/api/api-external.md` 中定義。 **Base Path**:`/api/v1`(與既有內部 API 區隔,讓 Web UI 繼續用舊的 `/jobs`,對外走新版) **Auth**:所有端點都需要 `Authorization: Bearer `,token 需滿足: - 簽發者:Innovedus Member Center(以 JWKS 驗簽) - `aud=kneron_converter_api` - 對應 scope(下表列出) | 方法 | 路徑 | Content-Type | 用途 | 必要 scope | 對應 US | |------|------|-------------|------|-----------|---------| | POST | `/api/v1/jobs` | `multipart/form-data` | 建立轉檔 job(直接上傳原始模型) | `converter:job.write` | US-08, US-11 | | GET | `/api/v1/jobs/:id` | — | 查詢單一 job(polling) | `converter:job.read` | US-09 | | GET | `/api/v1/jobs` | — | 查詢 job 列表(recovery) | `converter:job.read` | US-12 | | POST | `/api/v1/jobs/:id/promote` | `application/json` | 搬檔到模型庫 | `converter:job.write` | US-10, US-13 | > **註**:`POST /api/v1/jobs` 採 multipart/form-data 是刻意選擇,與既有 Web UI `POST /jobs` 一致,讓 visionA-backend 直接把原始模型推給 Converter。**不使用**「body 帶 object_key,Converter 再去 File Access Agent 拉檔」的設計(詳見 §4.4 US-08、§14.2)。 **Phase 1 明確不做的端點**: - Webhook 回呼(不做,polling 已足夠) - Cancel job(延後到未來,既有專案本來就沒有) - Delegated download 相關(Phase 2) **scope 命名**:上述命名為 PM 建議,Architect 需與 Member Center owner 對齊最終名稱。若 Member Center 已有命名慣例則採用其慣例。 ### 5.6 Auth / 身分識別模型(新增,2026-04-25) 本節定義 Converter 在新 API 中的雙重身分與 user_id 傳遞策略。 **Converter 作為 OAuth2 Resource Server**: - 驗證 visionA-backend(或其他未來消費者)送來的 service token - 驗證項目:JWT 簽章(Member Center JWKS)、`iss`、`aud=kneron_converter_api`、`exp`、必要 `scope` - **不驗證使用者身分**:token 是 service-to-service(`client_credentials`),claim 裡沒有 `user_id` **Converter 作為 OAuth2 Client**: - 使用自己的 `client_id=kneron_converter`(需在 Member Center 註冊) - 以 `client_credentials` grant 取得 token - 取得後用於: - PUT 結果檔到 File Access Agent(scope=`files:upload.write`,promote 時使用,見 US-10 / US-13) - **Phase 1 Converter 完全不從 File Access Agent「讀」任何東西**(原始模型改由 visionA-backend 直接 multipart 上傳,見 US-08)。因此 Phase 1 **不需要** `files:download.read` 或 `files:metadata.read` 等讀取類 scope。 **user_id 傳遞策略(方式 A:request payload)**: - visionA-backend 在對外 API 中以 **request payload**(非 JWT claim,非 HTTP header)傳遞 `user_id`: - `POST /api/v1/jobs`:`user_id` 作為 **multipart form field** - `POST /api/v1/jobs/:id/promote`:`user_id` 作為 JSON body 欄位(若 promote API body 需要;若不需要,由 `:id` 反查即可) - `GET /api/v1/jobs?user_id=...`(list)與 `GET /api/v1/jobs/:id`(single,Converter 會在內部比對 job 的 user_id):`user_id` 作為 **query string** - Converter **信任** visionA-backend 送的 user_id,**不驗證** - user_id 的用途: - 業務邏輯:同使用者同時一個轉檔的限制(US-11) - recovery 的 job 列表過濾(US-12) - user_id **不是授權邊界**:Converter 不負責判斷「某個 user 能不能做某件事」,授權邊界由 visionA-backend 自己管 - 選擇方式 A 而非「把 user_id 放 token claim」的原因:visionA-backend 是 service-to-service token(非 user-bound),硬塞 user_id 進 claim 會破壞 OAuth2 semantics - 選擇方式 A 而非「自訂 header(如 X-User-Id)」的原因:delegation model 跟 payload 一起走、API 審計可讀性較好,且 multipart 本來就要帶其他欄位,user_id 放在一起語意一致 **重要:不做的事**: - Converter **不**呼叫 Member Center 驗證 user_id 是否存在(相信 visionA-backend) - Converter **不**在 job 上做 user 層級的 ACL(例如「user A 不能看到 user B 的 job」),這由 visionA-backend 自己管,Converter 只是按 user_id 過濾查詢結果 --- ## 6. 非功能需求 ### 6.1 效能需求 | 項目 | 需求 | 現況 | |------|------|------| | 併發任務數 | 取決於 Worker 數量(每 Worker 一次處理一個任務) | 已實作(水平擴展) | | 檔案上傳上限 | 500 MB | 已實作 | | API Rate Limit | 每 15 分鐘 200 次請求 | 已實作 | | SSE 心跳 | 每 15 秒 | 已實作 | ### 6.2 安全性需求 | 項目 | 需求 | 現況 | |------|------|------| | HTTP 安全標頭 | X-Frame-Options, X-Content-Type-Options, X-XSS-Protection | 已實作(Nginx + Helmet) | | 容器安全 | 非 root 用戶運行 | Task Scheduler 已實作 | | API Key 認證 | [推測] 應有 API Key 或其他認證機制 | 未實作(開發環境硬編碼) | | Redis 認證 | Redis 應有密碼保護 | 未實作 | | HTTPS | 生產環境需 TLS | 未實作 | | 輸入驗證 | 檔案類型/大小驗證 | 部分實作(大小有限制,類型由前端限制) | ### 6.3 可用性需求 | 項目 | 需求 | 現況 | |------|------|------| | Crash Recovery | 服務重啟後自動恢復可用 | 已實作(Crash 即 Reset) | | 容器自動重啟 | 服務異常時自動重啟 | 已實作(restart: unless-stopped) | | 健康檢查 | Redis 連線狀態監控 | 已實作(GET /health) | | [推測] 監控告警 | 系統指標監控與告警 | 未實作 | | [推測] 日誌收集 | 集中式日誌管理 | 未實作(僅 console 輸出) | ### 6.4 可擴展性 | 項目 | 需求 | 現況 | |------|------|------| | Worker 水平擴展 | 動態增減 Worker 數量 | 已實作 | | [推測] 跨主機部署 | 多台主機分散 Worker | 未實作(目前用 Shared Volume,未來規劃 MinIO) | | [推測] Scheduler HA | 多個 Scheduler instance | 明確列為 Non-goal | --- ## 7. 系統限制與假設 ### 7.1 設計決策(Non-goals,從設計文件確認) 以下為刻意的設計取捨,**不是缺陷**: 1. **不做任務持久化** — Redis 重啟即清空所有任務 2. **不做 Crash 後恢復** — 進行中的任務在 Crash 後消失 3. **不做 exactly-once / at-least-once 保證** — 任務可能因 Crash 而遺失 4. **不做 Scheduler HA** — 單一 Scheduler instance 5. **不做自動重試** — 失敗即 FAILED,由使用者手動重送 6. **轉換流程固定** — 必須走完 ONNX → BIE → NEF,不支援跳過 ### 7.2 技術假設 1. 所有 Worker 需掛載相同的 Docker Volume 才能存取共享檔案 2. Worker 需要 Kneron Toolchain Python 環境(或使用 Stub 模式) 3. Redis 不開啟 persistence,符合「Crash 即 Reset」哲學 4. [推測] 目標部署環境為 Linux Docker 環境 ### 7.3 已知限制 1. 前端 UI 的單階段 API 端點(`/api/onnx/upload`、`/api/bie/process` 等)在後端尚未實作,前後端 API 契約不一致 2. Simulator 功能(浮點/定點/硬體模擬)旗標已定義但邏輯未接線 3. 任務列表使用 `redis.keys('job:*')` 掃描,任務量大時效能可能有問題 [推測] 4. 檔案上傳使用 memory storage(`multer.memoryStorage()`),大檔案可能導致記憶體壓力 --- ## 8. 功能優先排序(RICE) > 以下 RICE 評分為從程式碼推斷的優先級建議,Reach 與 Confidence 為推測值,Effort 需 Architect 確認。 ### 8.1 RICE 評分表(含 2026-04-25 新增項目) | # | 功能 | Reach | Impact | Confidence | Effort (人週) | RICE 分數 | 階段 | |---|------|-------|--------|------------|--------------|----------|------| | **F-21~29** | **API 對外開放 Phase 1(OAuth2 + 對外 API + promote + recovery)** | **100%** | **3** | **70%** | **4** | **53** | **本次 Phase 1** | | F-30 | Phase 2 使用者下載(delegated token) | 100% | 2 | 40% | 1 | 80 | Phase 2(阻塞中)| | F-14~16 | E2E 模擬(浮點/定點/硬體) | 80% | 2 | 60% | 3 | 32 | Phase 2 | | F-04~06 | 單階段轉換 API(後端) | 70% | 2 | 80% | 1.5 | 75 | Phase 2 | | - | 前後端 API 契約對齊 | 100% | 3 | 90% | 1 | 270 | 急迫 | | - | Redis 認證 + HTTPS | 100% | 2 | 90% | 1 | 180 | Phase 1 | | F-18 | 任務取消 [推測] | 50% | 1 | 50% | 2 | 13 | Phase 3 | | F-20 | 歷史任務持久化 [推測] | 60% | 1.5 | 40% | 3 | 12 | Phase 3 | | - | 跨主機部署(MinIO) | 30% | 2 | 70% | 4 | 11 | Later | | - | 監控與告警 | 80% | 1.5 | 70% | 2 | 42 | Phase 2 | | - | 錯誤訊息改善 [推測] | 80% | 1 | 60% | 0.5 | 96 | Phase 1 | | - | 轉換時間預估 [推測] | 70% | 0.5 | 40% | 1 | 14 | Phase 3 | ### 8.2 API 對外開放(Phase 1)RICE 推論說明 - **Reach = 100%**:100% 指「所有未來要整合 Converter 的下游服務」都需要此能力,不是終端使用者數。Phase 1 首個消費者是 visionA-backend,但架構設計為可擴展(未來 Innovedus 其他 vertical)。 - **Impact = 3(最高)**:沒有 API,VisionA 就無法整合,整個合作關係無法成立。屬於「關鍵路徑」。 - **Confidence = 70%**:OAuth2 / File Access Agent(僅 upload 方向)/ Member Center 三邊整合有不確定性(特別是 scope 命名、client 註冊流程、tenant_id 策略),實作細節待與兩個服務 owner 對齊。 - **Effort = 4 人週**:涵蓋 OAuth middleware、client 實作、multipart 上傳整合對外 API、promote API、user_id 相關業務邏輯、recovery API、OpenAPI 規格產出、整合測試。待 Architect Agent 在 TDD 中精確估算(**2026-04-25 變更後,Phase 1 不含「從 File Access Agent 取原始模型」的整合工作,Architect 可視情況調低 Effort**)。 - **RICE = 53**:分數看起來不高是因為 Effort 較大,但此功能屬於「解鎖型」——不做就無法開始整個商業合作,性質上是 P0,RICE 分數在此僅作排序參考,不應作為 go/no-go 的唯一依據。 ### 8.3 商業效益(預期) | 指標 | Phase 1 上線前 | Phase 1 上線後(預期) | 備註 | |------|---------------|---------------------|------| | 下游服務整合可能性 | 0(無 API)| 1+(VisionA)| 未來可擴展 | | VisionA 使用者轉檔路徑 | N/A(無法使用 Converter)| 平台內一站完成 | 降低切換成本 | | 手動操作步驟(從模型到 NAS)| 5+ 步(上傳 Web UI → 轉檔 → 下載 → 上傳到 VisionA → 搬到 NAS)| 2 步(在 VisionA 選模型 → 按轉檔)| 使用者體驗大幅改善 | | 服務耦合度 | 緊耦合(各服務自己做)| 鬆耦合(經 OAuth2 契約)| 可維護性 | **注意**:上述「預期」為 PM 推論,實際採用率與整合價值需 Phase 1 上線後用 §9 的成功指標追蹤。 ### RICE 公式 RICE = (Reach x Impact x Confidence) / Effort --- ## 9. 成功指標(KPI) > 9.1 既有指標 [推測] 為建議性質,需與產品負責人確認是否適用。 > 9.2 新增指標(2026-04-25)專門針對 API 對外開放功能。 ### 9.1 既有成功指標 [推測] | 類別 | 指標 | 建議目標值 | 追蹤方式 | |------|------|-----------|---------| | 穩定性 | 系統 uptime | > 99%(工作時段) | 健康檢查 API 監控 | | 效率 | 轉換成功率 | > 95% | Job COMPLETED / 總 Job | | 效率 | 平均轉換時間 | 視模型大小而定 | Job created_at → completed_at | | 容量 | 每日處理任務數 | 視團隊規模而定 | Job 計數 | | 體驗 | 任務佇列等待時間 | < 5 分鐘 | 入 Queue 到 Worker 開始處理 | | 可用性 | Crash 後恢復時間 | < 30 秒 | Docker restart 時間 | ### 9.2 API 對外開放 成功指標(新增,2026-04-25) #### 9.2.1 API SLA(品質指標) | 指標 | 目標值 | 護欄(不能低於) | 追蹤方式 | |------|--------|---------------|---------| | API 可用率(`/api/v1/*` 非 5xx 的比率) | > 99.5%(工作時段) | 99% | Nginx access log + 監控 | | API p95 回應時間(GET /jobs/:id) | < 200ms | 500ms | APM / 自建時間戳 | | API p95 server-side 處理時間(POST /jobs)| < 500ms(**不含 multipart 上傳時間**,計算從 Converter 收到整份 body 後的處理時間) | 2s | APM(server-side 埋點,從 body 完整接收到回 201 的時間)| | 端到端回應時間(POST /jobs,含上傳)| 取決於檔案大小與網路;**大檔上傳時間不計入 API p95 SLA** | — | 由 visionA-backend 側觀測,作為 UX 指標而非服務 SLA | | API p95 回應時間(POST /jobs/:id/promote) | < 3s(含 PUT 到 File Access Agent) | 10s | APM | | OAuth token 驗證失敗率(401 / 403) | < 1%(排除 token 過期正常情況) | 5% | Nginx log 分類統計 | > **註(2026-04-25 變更)**:原 `POST /jobs` p95 目標「< 2s(含從 File Access Agent 下載原始模型)」已調整。現行設計為 visionA-backend 直接 multipart 上傳原始模型,因此 API SLA 分兩層: > - **server-side 處理時間**:Converter 收完 multipart body 後的處理邏輯(驗 token、存檔到 Bucket、建 job record、派工到 Queue)— 這是 Converter 可控的 SLA。 > - **端到端上傳時間**:包含大檔上傳網路傳輸時間(可能數百 MB),受限於 visionA-backend ↔ Converter 網路頻寬與檔案大小,不納入 API p95 SLA,改由 visionA-backend 側追蹤作為使用者體驗指標。 #### 9.2.2 API 採用度指標(成長指標) | 指標 | 目標(Phase 1 上線後 3 個月)| 追蹤方式 | |------|----------------------------|---------| | 每月透過 API 建立的 job 數 | 由 visionA 的使用量決定,設 baseline | job 來源標記(API vs Web UI)| | API job 占總 job 的比例 | > 30%(Phase 1)→ > 60%(6 個月)| 同上 | | promote 成功率(搬進模型庫)| > 90% | `promote` API 成功/總呼叫 | | 離開頁面 recovery 使用率(有 in-progress 時回來查看)| 不設硬目標,但要能觀測 | `GET /api/v1/jobs?status=in_progress` 呼叫次數 | | 同使用者 409 Conflict 比率(US-11 觸發)| < 5%(表示大多數使用者理解限制)| Conflict 回應計數 / 總 POST /jobs | #### 9.2.3 錯誤率護欄 | 指標 | 護欄(不能超過) | 觸發動作 | |------|----------------|---------| | Auth failure rate | 5% | 檢查 Member Center JWKS 是否可達、scope 設定 | | Promote 失敗率(5xx 類)| 5% | 檢查 File Access Agent 連線、Converter service token 有效性 | | File Access Agent 連線失敗率 | 2% | 跨服務網路問題警報 | | Converter 自己的 service token 取用失敗率 | 1% | Member Center 連線問題 | #### 9.2.4 使用者體驗指標(透過 visionA 間接觀察) | 指標 | 目標 | 追蹤方式 | |------|------|---------| | 同使用者同時一個轉檔的限制被使用者理解率(不會重複嘗試)| 高 | 連續同 user_id 在短時間內的 409 次數 | | recovery 成功率(使用者離開頁面回來能看到 job)| > 95%(Converter 正常運行期間)| visionA 側埋點 | **追蹤工具建議(待 Architect 決定)**: - API layer 埋點:Nginx access log + 自訂結構化 log - 指標聚合:Prometheus + Grafana(與既有監控對齊,如有) - 失敗類別拆分:在 Scheduler 層記錄 error code(auth_failed / promote_failed / input_fetch_failed 等) **注意**:上述目標值為 PM 提案,具體數字需 Phase 1 上線前與 SRE / 監控負責人對齊。沒有歷史資料的指標(例如 API p95)需在 Phase 1 beta 期間收集 baseline 後再訂正式 SLA。 --- ## 10. 策略性路線圖 ### Now(當前 — 已實作 / 急需修復) - [x] 完整轉換流程(ONNX → BIE → NEF) - [x] Web UI 基本功能 - [x] SSE 即時狀態推送 - [x] Worker 水平擴展 - [x] Stub Worker 開發模式 - [x] Docker Compose 一鍵啟動 - [ ] **前後端 API 契約對齊**(Web UI 的單階段端點在後端未實作) ### Next(Phase 1 — 對外 API + 穩定化與安全性) 本次 L 級新功能主要工作: - [ ] **API 對外開放 Phase 1(本次重點)** - [ ] OAuth2 Resource Server(以 Member Center JWKS 驗 service token) - [ ] OAuth2 Client(Converter 自己取 service token,promote 時 PUT 到 File Access Agent) - [ ] 對外 API 路徑:`POST /api/v1/jobs`(multipart)、`GET /api/v1/jobs/:id`、`GET /api/v1/jobs`、`POST /api/v1/jobs/:id/promote` - [ ] user_id 業務邏輯(同使用者同時一個轉檔、recovery) - [ ] multipart 上傳流程(visionA-backend → Converter,與現有 Web UI 路徑重用儲存邏輯) - [ ] 搬結果檔到 File Access Agent(promote) - [ ] OpenAPI 3.0 規格產出與測試 - [ ] 安全性強化:Redis 認證、HTTPS(原 API Key 由 OAuth2 取代) - [ ] 錯誤訊息改善(更友善的錯誤描述) - [ ] 單階段轉換 API(後端實作 /api/onnx/process、/api/bie/process、/api/nef/process)— 延後至下一波 - [ ] 輸入驗證強化(後端檔案類型驗證) ### Later(Phase 2 — 功能擴展) - [ ] **使用者下載模型走 delegated token**(本次 Phase 2,阻塞於 Member Center) - [ ] E2E 模擬功能接線(浮點/定點/硬體) - [ ] 監控與告警(系統指標、Worker 狀態、API SLA) - [ ] 日誌收集與集中管理 - [ ] [推測] 批次轉換(多個模型一次提交) - [ ] Web UI 是否也改走新 OAuth 流程(待決) ### Future(Phase 3 — 進階功能) - [ ] [推測] 任務取消功能 - [ ] [推測] 歷史任務持久化(可選開啟) - [ ] 跨主機部署(MinIO 替換 Shared Volume) - [ ] [推測] 轉換時間預估 - [ ] [推測] 使用者認證與權限管理 --- ## 11. 支援平台清單(從程式碼確認) | 平台代號 | 平台名稱 | 備註 | |---------|---------|------| | 520 | KDP520 | - | | 720 | KDP720 | - | | 530 | KDP530 | - | | 630 | KDP630 | - | | 730 | KDP730 | - | --- ## 12. 風險與緩解 ### 12.1 既有風險(延續) | 風險 | 可能性 | 影響 | 緩解措施 | |------|--------|------|----------| | Redis Crash 導致所有任務遺失 | 中 | 中 | 符合設計哲學,使用者重送即可;未來可考慮 optional persistence | | 大檔案上傳耗盡 Node.js 記憶體 | 中 | 高 | 改用 disk storage(multer.diskStorage)替代 memory storage | | Worker 處理時間過長佔用資源 | 中 | 中 | [推測] 加入 timeout 機制 | | 前後端 API 不一致造成功能無法使用 | 高 | 高 | 優先對齊前後端 API 契約 | | 無認證機制導致未授權存取 | 中 | 高 | Phase 1 以 OAuth2 取代硬編碼 | | Redis keys 掃描在大量任務時效能劣化 | 低 | 中 | [推測] 改用 Redis Sorted Set 管理任務索引 | ### 12.2 本次 L 級新功能風險(新增,2026-04-25) | 風險 | 可能性 | 影響 | 緩解措施 | |------|--------|------|----------| | Member Center `POST /file-access/download-tokens` 長期不實作,Phase 2 無法啟動 | 中 | 中 | Phase 1 可獨立上線(不依賴此 endpoint);定期與 Member Center owner 同步;必要時 VisionA 做 proxy 下載作為 fallback | | File Access Agent 部署位置 / tenant_id 未定,promote 整合測試被阻塞 | 中 | 中 | 盡早與 File Access Agent owner 對齊部署計畫;若必要 Converter 開發環境先接 Stub 做 promote 契約驗證(讀檔已不再依賴 File Access Agent)| | Member Center OAuth client / audience 註冊延遲 | 中 | 高 | 盡早申請;開發初期可用 local Member Center 測試 | | scope 命名與 Member Center 既有命名慣例衝突 | 低 | 中 | Architect 在 TDD 提案後,走跨團隊 review | | visionA-backend 傳來的 user_id 被偽造(VisionA 被入侵或誤用)| 低 | 高 | 接受此風險:Converter 不做 user 層 ACL,責任邊界在 visionA-backend。可加日誌記錄 user_id 變更頻率作為監測 | | 大檔 promote 時 File Access Agent 不可用 | 中 | 中 | Converter Bucket 7 天 lifecycle 保留,使用者可重試 promote;API 回清楚的 502 | | **大檔 multipart 上傳失敗或 timeout**(visionA-backend ↔ Converter 之間) | 中 | 中 | 沿用既有 Web UI 的 500MB 上限;若未來需更大檔,與 Architect 討論改用 disk storage 或 chunked upload;API 回清楚的 `413 Payload Too Large` / `408 Request Timeout`;由 visionA-backend 做重試邏輯(Converter 無 resume upload 能力)| | **multipart 上傳占用記憶體**(既有 `multer.memoryStorage()` 問題)| 中 | 高 | 此為既有既有風險(見 §12.1),L 級新功能後因 API 消費者數量增加可能加劇。建議 Architect 評估是否 Phase 1 同步改 `multer.diskStorage()`;若不改,需明確在 Capacity Planning 中計算記憶體上限 | | Converter 的 service token 過期未正確刷新 | 低 | 中 | 實作 token cache + 主動 refresh(`exp-60s`)| | 同使用者同時一個轉檔對「急用者」造成不便 | 中 | 低 | 以 409 + 現有 job_id 回應,讓 VisionA 引導使用者「繼續追蹤」而非重送 | | 對外 API 穩定性承諾(OpenAPI)修改困難 | 低 | 中 | Phase 1 採保守設計,預留未來擴充欄位;API 走 `/api/v1` 版本化 | | OAuth middleware 阻塞既有 Web UI 走的 `/jobs` 路徑 | 低 | 高 | API 分兩套:Web UI 維持原路徑無 OAuth,對外 API 走 `/api/v1/*` 新路徑 | --- ## 14. 相依服務(新增,2026-04-25) 本次 L 級新功能使 Kneron Model Converter 從「獨立工具」變為「依賴兩個外部服務 + 被一個外部服務呼叫」的生態成員。本章節說明三個相依關係。 ### 14.1 Member Center(身分與權限中心) - **專案位置**:`/tmp/member_center/`(Innovedus 旗下,C# .NET + OpenIddict + PostgreSQL) - **負責範圍**:OAuth2 / OIDC authorization server、JWKS 發布、token 簽發、delegated token 簽發與驗證 - **Converter 對它的依賴**: - **驗 token(作為 Resource Server)**:從 `GET /.well-known/openid-configuration` 與 JWKS 端點拉取公鑰,驗證 visionA-backend 送來的 service token - **取 token(作為 Client)**:以 `client_credentials` 向 `POST /oauth/token` 取得 Converter 自己的 service token - **目前狀態**: - 核心 OAuth2 / OIDC 已完成(見 `/tmp/member_center/docs/DESIGN.md §7.1`) - `POST /file-access/download-tokens`(Phase 2 需要)**尚未實作** - **本次 Converter 需要 Member Center 做的事**(需跨團隊協調): - 註冊一個 resource audience:`kneron_converter_api`(暫定名) - 註冊一個 OAuth client:`kneron_converter`(暫定名,Converter 自己使用) - 在 visionA-backend 的 OAuth client 設定中加入 `kneron_converter_api` 的 scope 授權(例如 `converter:job.write`, `converter:job.read`) - 在 Converter 的 client 設定中加入 `file_access_api` 相關 scope 授權:**Phase 1 僅需 `files:upload.write`**(promote 時 PUT 檔案用);**不需要 `files:download.read` 或 `files:metadata.read`**(2026-04-25 變更後,Phase 1 Converter 完全不從 File Access Agent 讀取任何東西) - **風險**:任何註冊、scope 命名、client_secret 發放都需要 Member Center owner 配合 ### 14.2 File Access Agent(檔案閘道) - **專案位置**:`/tmp/file_access_agent/`(Innovedus 旗下,C# / ASP.NET Core) - **負責範圍**: - 代表「tenant 邊界內的所有檔案」的統一存取閘道 - 所有檔案進出都 proxy 經過 File Access Agent(**無 presigned URL 模式**) - 單一 tenant per instance(啟動時設定 `INSTANCE_TENANT_ID`) - 驗 upload 的 JWT(向 Member Center JWKS) - 驗 download 的 delegated token(向 Member Center 線上 validation endpoint) - **Converter 對它的依賴(Phase 1 簡化版,2026-04-25 變更)**: - **僅 PUT 結果到模型庫**:promote 時 Converter PUT 結果檔到 File Access Agent(帶 service token,scope=`files:upload.write`) - **Phase 1 Converter 完全不從 File Access Agent「讀」任何東西**(原始模型改由 visionA-backend 直接 multipart 上傳到 Converter) - Phase 1 **不需要** GET / HEAD / metadata 類的呼叫 - **目前狀態**: - 核心功能完成(見 `/tmp/file_access_agent/docs/API.md`):upload (PUT)、download (GET with delegated token)、metadata、delete - MinioFileStorage、MemberCenterDelegatedDownloadTokenValidator 已實作(約 1865 行 C#) - **本次 Converter 需要 File Access Agent 做的事(Phase 1)**: - 確認 deployment:哪個 instance 服務哪個 tenant、Converter 要連哪個 URL - 約定 objectKey 命名(建議:`kneron-converter/{user_id}/{model_id}/v{version}/{filename}` 或類似結構,需與 VisionA 對齊) - 只使用 PUT 端點(upload 類),不使用 GET / HEAD / metadata - **設計選擇(已確認)**:沒有 presigned URL 模式 — 所有 **promote 流量** 都經過 File Access Agent,Converter 接受此設計,不爭取改變 - **Phase 2 可能新增的依賴**:無(Phase 2 的 delegated download 不經過 Converter,見 US-14) ### 14.3 visionA-backend(服務消費者) - **專案位置**:`~/visionA/visionA-backend/`(Innovedus 旗下,Go + Gin) - **負責範圍**: - VisionA 平台的後端,管理其使用者、模型庫、應用設定 - Phase 0 雛形階段,近期進 Phase 1 導入 OAuth2/OIDC 與 JWT 認證 - **Converter 對它的關係**: - 是 Converter 的 **第一個 API 消費者(Persona C 實例)** - visionA-backend 會以 `client_credentials` 向 Member Center 取 service token(`aud=kneron_converter_api`),然後呼叫 Converter API - **職責邊界(明確區分)**: - **visionA-backend 的責任**:使用者認證、使用者 ACL(哪個使用者能看哪些模型)、模型庫 UX、將終端使用者的操作轉換為對 Converter API 的呼叫、處理 polling 與 UI 進度顯示、決定 promote 的目標 objectKey - **Converter 的責任**:Kneron Toolchain 轉檔執行、job 狀態管理、以自己的 service token 讀寫 File Access Agent、同使用者同時一個轉檔的限制、job 查詢過濾 - **不能混淆的邊界**:Converter **不**驗 user_id 真偽、**不**做使用者層級 ACL、**不**管 VisionA 的模型庫命名規則 - **目前狀態**:Phase 0 雛形(static auth,無 DB,LocalFS)。Phase 1 將導入 JWT/OIDC 與真實使用者系統 - **協作時間表假設**:VisionA 進 Phase 1(OAuth2 完成)的時間需與 Converter Phase 1 的整合測試時程對齊 ### 14.4 相依關係圖(PRD 層級,2026-04-25 更新) ``` [VisionA 終端使用者] │ │ (web UI 操作) ▼ ┌──────────────────┐ │ visionA-backend │ ← Persona C(Converter API 消費者) └───┬──────┬───┬───┘ │ │ │ service token │ │ │ ① multipart 上傳原始模型 (client_cred.) │ │ │ (POST /api/v1/jobs, Phase 1) │ │ │ ▼ │ │ delegated token(Phase 2) ┌────────────────┐ │ │ Member Center │ │ │ (OAuth2, JWKS, │ │ │ delegated tok.)│ │ └────┬──────┬────┘ │ │ │ │ 驗 token │ │ 驗 │ 取 token │ │ │ ▼ ▼ ▼ ┌────────────────────────┐ │ Converter (本專案) │ │ - 收 multipart 上傳 │ │ - 驗 OAuth token │ │ - Worker 轉檔流程 │ │ - Bucket 暫存 (7天) │ └──────────────┬─────────┘ │ │ ② promote 時 PUT 結果檔 │ (Phase 1, scope=files:upload.write) ▼ ┌──────────────────┐ │ File Access Agent │ ← Phase 1 只收寫入 │ (檔案閘道) │ (不處理 Converter 的讀檔) │ 無 presigned URL │ └─────────┬────────┘ │ │ NAS(長期儲存) ▼ [模型庫] ``` **Phase 1 檔案流(已確認)**: 1. 原始模型:`visionA-backend ──multipart──> Converter`(不經 File Access Agent) 2. 轉檔結果(promote 時):`Converter ──PUT──> File Access Agent ──> NAS` 3. 使用者下載結果(Phase 1 UX 由 visionA-backend 決定,可選方案:VisionA proxy、VisionA 存自己的 copy、或等 Phase 2) **Phase 2 新增的檔案流(阻塞於 Member Center)**: 4. 使用者下載:`瀏覽器 ──GET (delegated token)──> File Access Agent`(Converter 不參與) --- ## 15. Phase 1 / Phase 2 Scope 切分(新增,2026-04-25) 本章節明確界定本次 L 級功能的交付範圍,避免在實作時範圍蔓延或誤砍。 ### 15.1 Phase 1(本次必做) **目標**:讓 visionA-backend 能完成「上傳 → 轉檔 → 搬進模型庫」閉環,不需要使用者直接下載能力。 **必須交付**: | 項目 | 說明 | 對應 US / F | |------|------|------------| | OAuth2 Resource Server | 驗 visionA-backend 的 service token(JWKS 驗簽、aud、scope) | F-21, US-08 | | OAuth2 Client | Converter 自己取 service token(僅用於 promote 時呼叫 File Access Agent 的 PUT) | F-22, US-13 | | `POST /api/v1/jobs`(multipart) | 建 job,multipart files:`model` + `ref_images[]`;multipart fields:`user_id` + `model_id` + `version` + `platform` + 旗標 | F-23, US-08 | | `GET /api/v1/jobs/:id` | Polling 進度 | F-24, US-09 | | `GET /api/v1/jobs?user_id=...&status=...` | Recovery 的 job 列表查詢 | F-25, US-12 | | `POST /api/v1/jobs/:id/promote` | 搬結果到 File Access Agent(Converter 自己推) | F-26, US-10, US-13 | | 同使用者同時一個轉檔限制 | 建 job 時檢查 user_id 有無 in-progress job | F-27, US-11 | | OpenAPI 3.0 規格 | 對外 API 契約,visionA-backend 整合用 | F-29 | > **註(2026-04-25 變更)**:原 Phase 1 Scope 中的「從 File Access Agent 取原始模型(F-28)」已移除。Phase 1 原始模型來源改為「visionA-backend 直接 multipart 上傳到 Converter」(見 F-23 / US-08)。 **明確不做**(Phase 1 不在範圍): - ❌ 使用者下載模型的 delegated token 流程(移至 Phase 2) - ❌ Web UI 改走新 OAuth 流程(維持既有 multipart 路徑,另外評估) - ❌ Webhook / SSE 對外推送(polling 已足夠) - ❌ Job 取消功能(既有專案本來就沒有,不在本次新增) - ❌ Job 持久化 / Crash 恢復(維持「Crash 即 Reset」哲學) - ❌ 單階段轉換 API(F-04~06)— 這是既有 backlog 項目,和本次 L 級功能獨立 **Phase 1 上線完成的判定標準**: 1. visionA-backend 能成功以 service token 呼叫所有四個對外 API 2. 一個完整 E2E 流程跑通:VisionA 使用者上傳模型 → File Access Agent → 觸發轉檔 → polling 進度 → 完成後 promote 到模型庫 3. 同使用者同時轉檔限制生效(回 409) 4. Recovery 能查到 in-progress job 5. OpenAPI 規格通過 visionA-backend 整合測試 6. API SLA 基本指標可觀測(可用率、p95 回應時間) ### 15.2 Phase 2(延後做) **目標**:讓 VisionA 終端使用者能直接從瀏覽器下載 NAS 裡的模型檔,不經 visionA-backend 中轉。 **必須交付**: | 項目 | 說明 | 對應 US / F | |------|------|------------| | visionA-backend 呼叫 Member Center 取 delegated token | scope=`files:download.delegate`,帶 tenant_id + user_id + object_key | F-30, US-14 | | 瀏覽器直連 File Access Agent 下載 | `GET /files/{object_key}?token=` | US-14 | | Converter 無變更 | Phase 2 對 Converter 本身無直接改動 | — | **阻塞條件(必須先解除才能啟動 Phase 2)**: 1. Member Center 的 `POST /file-access/download-tokens` 已實作並部署(目前:**未實作**) 2. Member Center owner 提供測試環境與 scope 授權 3. File Access Agent 的 `MemberCenterDelegatedDownloadTokenValidator` 已通過整合測試(目前:程式碼已有,待驗證) **觸發 Phase 2 的條件**: - Member Center owner 確認時程,或 - VisionA 業務需求強烈要求,此時可考慮折衷方案(VisionA 自己做 proxy 下載),但這是另一個討論 **Phase 2 的 US-14 風險**: - 若 Member Center 長期不實作,VisionA 的終端使用者要下載自己的模型就只能「從 VisionA 下載介面(經 visionA-backend proxy)」或「連 Kneron Converter 舊的 Web UI(此時兩邊資料不同步,會混亂)」。需提前與 VisionA 產品團隊溝通 UX 取捨。 ### 15.3 Phase 1 / Phase 2 決策原則 - **Phase 1 必須能獨立上線**:不能因為 Phase 2 阻塞就拖累 Phase 1。 - **Phase 1 / Phase 2 之間不會互相破壞**:Phase 2 只新增功能,不修改 Phase 1 的 API 契約。 - **阻塞透明化**:Phase 2 的阻塞原因(Member Center endpoint 未實作)必須明確記錄在 PRD,讓使用者知道是上游依賴不是本專案問題。 --- ## 16. 附錄 > 註:本章原為 §13,因新增 §14(相依服務)與 §15(Phase 切分)而重編號為 §16。章節內容不變。 ### 16.1 技術堆疊完整列表 | 層級 | 技術 | 版本 | |------|------|------| | 前端框架 | Vue 3 + Vite | Vue 3.3.4, Vite 4.4.5 | | UI 元件庫 | Element Plus | - | | 狀態管理 | Pinia | - | | 後端(調度) | Node.js + Express | Node 18 | | Worker 框架 | Python(同步) | Python 3.9 | | 佇列 / 狀態 | Redis Stream | Redis 7 Alpine | | 模型處理 | ONNX 1.14.1, TensorFlow 2.16.2 | - | | 容器化 | Docker + docker-compose | - | | 反向代理 | Nginx | Alpine | | 測試框架 | pytest + pytest-asyncio | - | ### 16.2 後端介面層設計 程式碼採用 **Protocol + Factory 模式**,將 Kneron Toolchain 的呼叫封裝為可替換的後端介面: - `QuantizationBackend`(`services/backends/quantization.py`)— 量化分析 - `CompilerBackend`(`services/backends/compiler.py`)— NEF 編譯 - `EvaluatorBackend`(`services/backends/evaluator.py`)— IP 評估 - `SimulatorBackend`(`services/backends/simulator.py`)— E2E 模擬(未完成) 此設計使得未來可以替換為不同版本的 Toolchain 後端,或在測試時注入 Mock。 ### 16.3 程式碼品質觀察 - 模組化設計良好,Worker / Consumer / Backend 職責清晰 - 測試覆蓋:6 個測試檔涵蓋 ONNX/BIE/NEF 核心與 E2E 流程 - 使用 Helmet、Rate Limiting、Compression 等 Express 最佳實踐 - Graceful shutdown 處理(SIGTERM / SIGINT) --- ## 附錄 A:本次更新的 PM 疑問清單(2026-04-25) 撰寫本次 PRD 更新時遇到以下疑問,列出給 Orchestrator / 使用者裁決。**這些問題不阻塞三方交叉審閱,但需在使用者最終審核 PRD 前釐清**。 ### A.1 需使用者決定的產品問題 1. **Phase 1 的 Web UI 要不要也改走 OAuth 流程?** - 傾向不改(維持既有 multipart 行為),未來評估 - 若決定改,則 Phase 1 範圍會擴大(升級為 L+ 或拆兩個 L) 2. **同使用者同時一個轉檔**限制範圍確認 - 是否真的是「整個 Converter 服務」共用的 user_id 空間?還是不同 `client_id`(例如未來有 visionA-backend 以外的消費者)各自獨立? - 目前 PRD 以「整個服務內 user_id 唯一」設計(預期未來不會出現跨 client 的 user_id 衝突) 3. **tenant_id 策略** - Member Center 與 File Access Agent 都是多租戶設計,但 PRD 目前未處理「Converter 內部怎麼標記 tenant」 - 待 Architect Agent 評估:Converter 是否要在 job record 上記錄 tenant_id?還是只信任 File Access Agent 的 single-tenant-per-instance 保證? 4. **Phase 2 的 fallback 策略** - 若 Member Center 長期不實作 delegated token,VisionA 要不要做 proxy 下載作為折衷? - 還是完全等 Member Center? 5. **API 採用度的 baseline 如何取得?** - §9.2.2 列的目標值(例如「每月 X 個 job」)目前沒有歷史資料 - 建議:Phase 1 上線後跑 1 個月 beta 再訂正式目標 6. **既有 PRD 中的 `[推測]` 標記要不要在此次順便確認?** - 本次 PM 暫不動既有 `[推測]`,保留給使用者決定 - 建議在使用者最終審核本版 PRD 時一併確認 ### A.2 需跨團隊協調的問題(給 Orchestrator 記到 progress.md 未解決問題) 1. **Member Center owner 協調**:註冊 `kneron_converter_api` audience、`kneron_converter` client、設定 scope 授權 2. **File Access Agent owner 協調**:確認 Converter 要連的 instance URL、tenant_id、objectKey 命名約定 3. **VisionA 團隊協調**:確認 VisionA 的 Phase 1 OAuth 整合時程,對齊 Converter 整合測試時機 4. **Member Center `/file-access/download-tokens` 實作時程**:影響 Phase 2 啟動 ### A.3 給 Architect Agent 的待確認項 1. **scope 命名**:`converter:job.write` / `converter:job.read` 是否符合 Member Center 慣例?由 Architect 在 TDD 提案後跨團隊 review 2. **Effort 估算**:RICE 表中 Phase 1 Effort = 4 人週 為 PM 概估,需 Architect 在 TDD 後精確。**注意:2026-04-25 變更後,Phase 1 移除「從 File Access Agent 取原始模型」這條路徑,相對減少 OAuth Client 的讀檔 scope 與整合測試複雜度,Architect 可重新估 Effort** 3. **OpenAPI 規格的維護策略**:自動生成還是手寫?版本變更流程? 4. **user_id 在 job record 的索引設計**:Redis 現在用 `redis.keys('job:*')` 掃描,加 user_id 過濾後效能策略? 5. **(新增,2026-04-25)multipart 上傳的記憶體策略**:既有 Web UI 用 `multer.memoryStorage()`,對外 API 接受相同路徑後,同時多個 visionA-backend 呼叫可能放大記憶體問題。Architect 需評估 Phase 1 是否同步改 `diskStorage`,或是否需 Capacity Planning 加上明確的上限(例如同時 multipart 上傳數 × 500MB ≤ Node.js heap) 6. **(新增,2026-04-25)`POST /api/v1/jobs` 與既有 `POST /jobs` 的路徑實作關係**:兩者行為近似,Architect 需決定是「共享同一個 handler,前面加 OAuth middleware + user_id 驗證分支」還是「完全獨立兩個 handler」。這會影響未來維護性 ### A.4 給 Design Agent 的待確認項 1. 本次 Phase 1 主要影響「API 層 + visionA-backend 串接」,**Kneron Converter 自己的 Web UI 預期不改** 2. Design Agent 需確認:既有 Web UI 的「上傳模型」流程是否在 Phase 1 仍維持 multipart?若維持,則 Web UI 不受影響 3. visionA-backend 端的 UI 設計(進度條、recovery 提示、promote 按鈕等)**不在 Kneron Converter 的 Design Agent 範圍內**,由 VisionA 團隊負責 ### A.5 本次變更(2026-04-25 第二次更新)新產生的 Open Questions 1. **VisionA 終端使用者如何下載轉檔結果(Phase 1 期間)**: - 由於 Converter 不再從 File Access Agent「讀」原始模型,而使用者要看 / 下載 **轉檔結果**(BIE、NEF)的流程在 Phase 1 就尚未有 end-to-end 設計 - 選項:(a) 使用者先等到 promote 完成後從 VisionA UI 下載(VisionA 自己 proxy),(b) Phase 1 只讓 VisionA 平台顯示「已加進模型庫」,不提供下載鍵,等 Phase 2 delegated token 再開放,(c) VisionA 先不呼叫 promote,從 Converter 的 `/jobs/:jobId/download/:filename` 既有路徑下載(此路徑目前無 OAuth) - 需 PM + VisionA 產品團隊對齊 2. **visionA-backend 在上傳前是否也存一份原始模型到自己**: - 由於 Phase 1 原始模型不進 File Access Agent,若轉檔失敗或使用者要重試,visionA-backend 是否需要自己暫存一份? - 這是 visionA-backend 內部設計問題,不影響 Converter API 契約,但會影響 VisionA 使用者 UX(失敗後能不能一鍵重試) 3. **既有 `/jobs/:jobId/download/:filename` 端點的對外曝光問題**: - Phase 1 visionA-backend 若走 promote 路徑,下載由 VisionA ↔ File Access Agent 處理,Converter 的下載端點不需對外 - 但若 VisionA 決定「先不 promote、直接從 Converter 拉結果」,此端點就需要加 OAuth 保護 - 需在 TDD 中決定:既有下載端點是否要區分「Web UI 內部用」vs「對外 API /api/v1 用」兩套