diff --git a/docs/autoflow/02-prd/PRD.md b/docs/autoflow/02-prd/PRD.md new file mode 100644 index 0000000..0780e0e --- /dev/null +++ b/docs/autoflow/02-prd/PRD.md @@ -0,0 +1,151 @@ +# visionA Cloud — 產品需求文件(PRD)索引 + +| 項目 | 內容 | +|------|------| +| 產品名稱 | visionA Cloud | +| 產品代號 | visionA-frontend / visionA-backend | +| 文件版本 | v0.1(Phase 0 雛形規劃) | +| 最後更新 | 2026-04-21 | +| 狀態 | 三方聯合討論中(PM / Design / Architect 平行) | +| 主要負責人 | PM Agent | +| 相關專案 | `local-tool/`(離線版,不動)、`edge-ai-platform`(POC,要轉正) | + +--- + +## 文件結構 + +本 PRD 採用模組化結構,PRD.md 為索引檔,各章節拆成獨立子檔案。 + +``` +.autoflow/02-prd/ +├── PRD.md ← 本檔(索引 + 各章節一句話摘要) +├── strategy.md ← 第 1 章:產品策略 +├── product-positioning.md ← 第 2 章:產品定位(visionA Cloud vs local-tool vs POC) +├── market-analysis.md ← 第 3 章:市場分析 +├── user-research.md ← 第 4 章:用戶研究(Persona、旅程) +├── user-stories.md ← 第 5 章:User Stories(RICE 排序) +├── features/ ← 第 6 章:功能規格(按功能拆分) +│ ├── feature-device-management.md +│ ├── feature-model-management.md +│ ├── feature-inference.md +│ ├── feature-pairing.md ← 新增:Pairing 流程 +│ ├── feature-cluster-inference.md +│ ├── feature-dashboard.md +│ ├── feature-workspace.md +│ ├── feature-auth.md ← 雛形 TODO +│ ├── feature-converter-integration.md ← 轉檔整合 TODO +│ └── feature-billing.md ← 未來 TODO +├── nonfunctional.md ← 第 7 章:非功能性需求 +├── interface-contracts.md ← 第 8 章:介面契約(重要,給 converter 團隊等) +├── success-metrics.md ← 第 9 章:成功指標 +├── roadmap.md ← 第 10 章:開發範圍與階段(Phase 0 / 1 / 2) +└── risks.md ← 第 11 章:風險與相依 +``` + +--- + +## 章節摘要 + +### [1. 產品策略](strategy.md) + +visionA Cloud 是把 edge-ai-platform POC 升格的正式雲端產品,定位為「**Kneron 邊緣 AI 裝置的雲端操作平台**」,讓開發者透過瀏覽器遠端操作自己筆電 / 現場機上的 Kneron 裝置(KL520 / KL720),支援單裝置與多裝置叢集推論。與 local-tool(離線版)共享相同 UI 與核心功能,差別在前端連的是雲端 API,而非 localhost。 + +**核心價值主張**:「不用打開筆電,也能管你的 Kneron 裝置。」 + +**目標受眾**:Kneron FAE(內部)、Kneron 生態系開發者(外部)、做 PoC 的系統整合商。 + +### [2. 產品定位](product-positioning.md) + +釐清 visionA Cloud 在 Innovedus 產品線中的位置: + +- **local-tool**(離線版):現場 demo、網路鎖死的客戶場景 → **完全不動** +- **visionA Cloud**(本專案):遠端協作、多人共用、長駐叢集 → **Phase 0 雛形** +- **edge-ai-platform**(POC):已完成歷史使命,逐步 deprecate + +兩種模式用「同一套前端」加上不同 API base URL 決定,不做模式切換 UI。 + +### [3. 市場分析](market-analysis.md) + +邊緣 AI 開發工具市場簡要分析,競品如 NVIDIA Triton Inference Server、Edge Impulse Studio、SenseCraft AI、AWS IoT Greengrass。差異化聚焦「**Kneron 專用 + 雲端遠端存取 + 低延遲 tunnel + 叢集推論**」的組合,這是現有競品都沒有單一提供的。 + +### [4. 用戶研究](user-research.md) + +主要 Persona: + +1. **阿哲 — Kneron 客戶 FAE**:常出差做 demo,需要遠端查看現場客戶機台的推論狀態。 +2. **Sarah — SI 系統整合商**:在多個客戶端佈署 Kneron 裝置,需要統一介面管理。 +3. **Mike — AI 應用開發者**:寫應用 A/B test,要叢集跑多裝置比對效能。 + +用戶旅程涵蓋「認知 → 註冊 → Pairing → 首次推論 → 日常使用」。 + +### [5. User Stories(RICE 排序)](user-stories.md) + +所有 User Story 按 RICE 評分排序,P0(雛形必做)、P1(Phase 1)、P2(未來)。重點 stories: + +- **作為開發者**,我要在瀏覽器註冊登入,看到我名下所有裝置。 +- **作為開發者**,我要把我筆電上的 local agent 配對到 visionA Cloud 帳號。 +- **作為 FAE**,我要選一個遠端裝置 + 模型 + 來源,按「開始」就看到即時推論結果。 +- **作為 SI**,我要把多個裝置組叢集,做加權 round-robin 推論。 + +### [6. 功能規格](features/) + +按功能拆成獨立檔,每個功能含描述、使用者行為、驗收條件: + +| 功能 | 優先級 | 檔案 | 狀態 | +|------|--------|------|------| +| 裝置管理 | P0 | [feature-device-management.md](features/feature-device-management.md) | 搬自 local-tool,走 remote-proxy | +| 模型管理 | P0 | [feature-model-management.md](features/feature-model-management.md) | 7 預設 + 上傳,走 S3 介面 | +| 推論操作 | P0 | [feature-inference.md](features/feature-inference.md) | Camera / Image / Video / Batch | +| Pairing 流程 | P0 | [feature-pairing.md](features/feature-pairing.md) | 新增,取代 POC 的 MAC 寫死 | +| 工作區 | P0 | [feature-workspace.md](features/feature-workspace.md) | 裝置 → 模型 → 來源 | +| 叢集推論 | P1 | [feature-cluster-inference.md](features/feature-cluster-inference.md) | 從 POC 搬,加權 RR | +| 儀表板 | P1 | [feature-dashboard.md](features/feature-dashboard.md) | 搬自 local-tool | +| 會員系統 | P2(TODO) | [feature-auth.md](features/feature-auth.md) | 雛形只定介面 | +| 轉檔整合 | P0(Phase 0.8 MVP) | [feature-converter-integration.md](features/feature-converter-integration.md) | upload→轉檔→半自動處理;converter / FAA / MC 已就緒 | +| Billing | P2(TODO) | [feature-billing.md](features/feature-billing.md) | 未來 | + +### [7. 非功能性需求](nonfunctional.md) + +效能(推論延遲、API RT、tunnel throughput)、安全(pairing token 生命週期、傳輸加密、隔離)、可擴展性(Session 狀態管理、無狀態 API Server)、可用性(降級策略、離線覆蓋)、可觀測性(metrics / log)。 + +### [8. 介面契約(Interface Contracts)](interface-contracts.md) + +**本章節特別重要**,因為 Phase 0 有很多 TODO,介面契約是跨團隊合作的合約: + +- **AuthProvider 介面**(給未來會員系統填血) +- **kneron_model_converter API 契約**(visionA-backend 作為 client,反向定義) +- **ObjectStorage 介面**(S3-compatible,雛形用 local filesystem 實作) +- **SessionStore 介面**(雛形 in-memory,未來 Redis) +- **BillingProvider 介面**(未來) + +### [9. 成功指標](success-metrics.md) + +Phase 0 雛形指標(跑得動、Pairing 成功率、推論端到端延遲)、Phase 1 MVP 指標(WAU、Pairing conversion、首次推論時間)、長期北極星指標(每週推論次數 per user)。 + +### [10. 開發範圍與階段](roadmap.md) + +- **Phase 0(本次雛形)**:建骨架、跑得動、介面清楚、Auth/DB/Storage 用 stub +- **Phase 1**(未來 1-2 季):接真 Auth、接真 DB、接真 Storage +- **Phase 2**(未來 2-4 季):轉檔整合、Billing、多區域部署 + +### [11. 風險與相依](risks.md) + +- 和 kneron_model_converter 團隊的協作節奏 +- 不能改壞 local-tool 的風險 +- tunnel 在企業網路 NAT / Proxy / 防火牆的穿透風險 +- Pairing token 洩漏的安全風險 + +--- + +## 使用說明(給其他 Agent) + +**Design Agent、Architect Agent**: + +- 需要全貌時讀本索引檔 +- 需要細節時讀對應子檔案 +- 不要所有檔案一次讀,會爆 context + +**Orchestrator**: + +- 追蹤 TODO 清單時讀 `roadmap.md` 和 `interface-contracts.md` +- 追蹤 Phase 0 驗收時讀各 `feature-*.md` 的「驗收條件」段落 diff --git a/docs/autoflow/02-prd/features/feature-auth.md b/docs/autoflow/02-prd/features/feature-auth.md new file mode 100644 index 0000000..5c37d3a --- /dev/null +++ b/docs/autoflow/02-prd/features/feature-auth.md @@ -0,0 +1,200 @@ +# Feature:會員系統(P0 介面;實作 TODO → Phase 1) + +> 父文件:[PRD.md](../PRD.md) | 對應 User Stories:US-01、US-02、US-13、US-22、US-TODO-01、US-TODO-02、US-TODO-03 +> +> **⚠️ 重要**:本功能**只在 Phase 0 定義介面**,**不實作真實 Auth**。 +> Phase 0 的目標是讓雛形「看起來像有 Auth」:前端頁面有、後端 API 有、存個假的 session cookie,但 token 不驗簽、user 不落 DB。 +> Phase 1 再換成真實 Auth(JWT / OAuth + DB)。 + +--- + +## 範圍說明 + +| Phase 0 做 | Phase 1 做 | +|-----------|-----------| +| 登入頁 / 註冊頁 UI | 真實 Auth 後端 | +| POST /api/auth/login / register 的 stub handler | JWT 簽發 / 驗證 | +| 簡易 session cookie(in-memory) | Refresh token rotation | +| 個人設定頁 UI 骨架 | 密碼重設流程 | +| 登出功能(清 cookie) | Email 驗證 | +| — | OAuth(Google / GitHub) | +| — | 2FA | +| — | 權限 / Role 系統 | + +--- + +## 使用者行為(Phase 0) + +### 登入頁(`/login`) + +- Email + 密碼輸入 +- 「登入」按鈕 +- 「還沒有帳號?註冊」連結 +- (Phase 1):忘記密碼連結、OAuth 按鈕 + +### 註冊頁(`/register`) + +- Email + 密碼 + 確認密碼 +- 「建立帳號」按鈕 +- Phase 0:submit 後直接登入(in-memory 記一個 user) +- Phase 1:寄 email 驗證 + 存 DB + +### 個人設定頁(`/account`) + +Phase 0:只做 UI 骨架,顯示 user email + 登出按鈕。 + +Phase 1 加: +- 密碼變更 +- Email 變更 +- 頭像上傳 +- 刪除帳號 +- 2FA 設定 +- 已連結的 OAuth 提供者 +- API Key 管理 + +### 登出 + +- 清除前端 token / cookie +- 導回 `/login` + +--- + +## 技術細節(給 Architect 參考) + +### AuthProvider 介面 + +Phase 0 要定義清楚介面,Phase 1 直接換實作。 + +```go +// internal/auth/provider.go +type AuthProvider interface { + Register(ctx context.Context, email, password string) (*User, error) + Login(ctx context.Context, email, password string) (*Session, error) + ValidateToken(ctx context.Context, token string) (*User, error) + Logout(ctx context.Context, token string) error + // Phase 1: + // RefreshToken(ctx context.Context, refreshToken string) (*Session, error) + // RequestPasswordReset(ctx context.Context, email string) error + // ConfirmPasswordReset(ctx context.Context, token, newPassword string) error +} + +type User struct { + ID string + Email string + // Phase 1:更多欄位(Name、Avatar、Created、...) +} + +type Session struct { + Token string + UserID string + ExpiresAt time.Time + // Phase 1:RefreshToken string +} +``` + +**Phase 0 實作:`StubAuthProvider`** +- 記憶體 `map[string]*User`(email → user) +- Session token = 隨機 32 字元(不簽、不驗) +- 註冊與登入不做任何安全檢查(接受任何密碼) +- 目的只是**讓前端能測整個流程**,不是真安全 + +**Phase 1 實作:`JWTAuthProvider`** +- bcrypt 存密碼 +- JWT 簽發(有 secret) +- User 存 PostgreSQL +- Refresh Token rotation +- Rate limiting + +### Auth Middleware + +api-server 的所有需要登入的 endpoint 都過 middleware: + +```go +func RequireAuth(provider AuthProvider) gin.HandlerFunc { + return func(c *gin.Context) { + token := extractToken(c) // from cookie or Authorization header + user, err := provider.ValidateToken(c.Request.Context(), token) + if err != nil { + c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"}) + return + } + c.Set("user", user) + c.Next() + } +} +``` + +Phase 0 的 middleware 一樣跑,只是 `provider` 是 stub。 + +--- + +## API 端點(Phase 0) + +| Method | Path | 說明 | +|--------|------|------| +| POST | `/api/auth/register` | 註冊(stub)| +| POST | `/api/auth/login` | 登入(stub)| +| POST | `/api/auth/logout` | 登出 | +| GET | `/api/auth/me` | 取得當前 user 資訊 | + +Phase 1 加: + +| Method | Path | 說明 | +|--------|------|------| +| POST | `/api/auth/refresh` | Refresh token | +| POST | `/api/auth/password/reset` | 請求密碼重設 | +| POST | `/api/auth/password/confirm` | 確認密碼重設 | +| POST | `/api/auth/oauth/{provider}` | OAuth callback | +| DELETE | `/api/account` | 刪除帳號 | + +--- + +## 驗收條件(Phase 0) + +- [ ] `/login`、`/register`、`/account` 三個頁面可打開 +- [ ] 註冊 → 自動登入 → 跳轉到 `/` +- [ ] 已登入狀態下訪問 `/login` → 自動跳 `/` +- [ ] 未登入狀態下訪問需登入頁面(`/`、`/devices` 等)→ 跳 `/login` +- [ ] 登出後 cookie 清除 +- [ ] 兩個不同 user 的資料互相隔離(裝置列表、模型等) +- [ ] AuthProvider 介面定義完整 +- [ ] StubAuthProvider 實作能通過基本 flow +- [ ] `/account` 頁面顯示當前 email + 登出按鈕(其他欄位灰掉或 TODO 標記) + +--- + +## Phase 0 的 TODO 清單(明確追蹤) + +- **TODO-AUTH-01**:換 JWTAuthProvider(Phase 1 核心) +- **TODO-AUTH-02**:DB 層(PostgreSQL schema for users、sessions) +- **TODO-AUTH-03**:Email 驗證流程(需 email 服務,SendGrid / SES) +- **TODO-AUTH-04**:密碼重設流程 +- **TODO-AUTH-05**:OAuth(Google、GitHub) +- **TODO-AUTH-06**:2FA(TOTP) +- **TODO-AUTH-07**:密碼強度規則 +- **TODO-AUTH-08**:Rate limiting(防暴力) +- **TODO-AUTH-09**:Account 刪除(含所有裝置、模型、叢集清理) +- **TODO-AUTH-10**:個人設定頁完整功能 +- **TODO-AUTH-11**:Role / Permission 系統(Phase 2,for 企業版) +- **TODO-AUTH-12**:API Key 管理(Phase 2) + +--- + +## 安全警示(Phase 0 限制) + +⚠️ **Phase 0 的 Auth 不是真的 Auth。絕對不能上線給真用戶。** + +限制: +- 密碼明文比對(in-memory) +- Token 不簽、任何人偽造任何 token 都能通過 +- 無 rate limiting +- 無 HTTPS 強制(Phase 0 dev 環境可能 http://) + +**Phase 0 的 visionA Cloud 只給內部 FAE 測試**,不開放給外部。 + +--- + +## 連結 + +- 回:[PRD 索引](../PRD.md) +- 相關:[介面契約 — AuthProvider](../interface-contracts.md) diff --git a/docs/autoflow/02-prd/features/feature-billing.md b/docs/autoflow/02-prd/features/feature-billing.md new file mode 100644 index 0000000..a4d3321 --- /dev/null +++ b/docs/autoflow/02-prd/features/feature-billing.md @@ -0,0 +1,77 @@ +# Feature:Billing(P2;Phase 0 不做) + +> 父文件:[PRD.md](../PRD.md) | 對應 User Stories:US-27 +> +> **⚠️ Phase 0 完全不做**。本文件只留介面定義與未來規劃。 + +--- + +## 範圍 + +Phase 0 / Phase 1 全部跳過。Phase 2 再規劃。 + +--- + +## 為什麼 Phase 0 不做 + +- Phase 0 是雛形,只給內部測試 +- 商業模式尚未定案(訂閱 / usage-based / freemium 都有可能) +- 接金流(Stripe 等)會拖慢雛形進度 + +--- + +## 可能的商業模式(Phase 2 待討論) + +### 模式 A:訂閱制 + +| 方案 | 月費 | 裝置數上限 | 模型儲存 | 叢集 | +|------|------|----------|---------|------| +| Free | $0 | 1 | 100MB | ❌ | +| Pro | $19 | 5 | 10GB | ✅ | +| Team | $99 | 無限 | 100GB | ✅ | +| Enterprise | 客製 | — | — | + SSO, SLA | + +### 模式 B:按推論次數 + +- $X / 1M inferences +- 前 100K 免費 + +### 模式 C:Freemium + 轉檔計費 + +- 本體免費使用 +- 轉檔服務按次計費($Y / 轉檔) + +--- + +## BillingProvider 介面(Phase 2) + +```go +// internal/billing/provider.go +type BillingProvider interface { + CreateCustomer(ctx context.Context, user *User) (*Customer, error) + CreateSubscription(ctx context.Context, customerID, planID string) (*Subscription, error) + CancelSubscription(ctx context.Context, subscriptionID string) error + ReportUsage(ctx context.Context, customerID, meterID string, quantity int64) error + GetInvoices(ctx context.Context, customerID string) ([]Invoice, error) +} +``` + +Phase 2 實作候選: +- Stripe(最成熟,SaaS 標配) +- Paddle(全球稅務處理較好) +- Lemon Squeezy(小團隊友善) + +--- + +## 其他未定案 + +- 企業合約 / PO 流程 +- 退款政策 +- 多幣別 +- 發票系統(特別是台灣統一發票) + +--- + +## 連結 + +- 回:[PRD ��引](../PRD.md) diff --git a/docs/autoflow/02-prd/features/feature-cluster-inference.md b/docs/autoflow/02-prd/features/feature-cluster-inference.md new file mode 100644 index 0000000..10b315e --- /dev/null +++ b/docs/autoflow/02-prd/features/feature-cluster-inference.md @@ -0,0 +1,102 @@ +# Feature:叢集推論(P1) + +> 父文件:[PRD.md](../PRD.md) | 對應 User Stories:US-16、US-17 +> +> **Phase 0 不實作**。Phase 0 只留模組目錄(`internal/cluster/`),從 POC 搬過來但不接入 API。Phase 1 正式產品化。 + +--- + +## 概述 + +叢集推論是 edge-ai-platform POC 驗證過的功能:把多台 Kneron 裝置組成叢集,按**加權 Round-Robin** 分派推論任務。 + +這是 visionA Cloud 相對於 local-tool 的**核心差異化功能**。 + +--- + +## 使用者行為(Phase 1) + +### 叢集列表頁(`/clusters`) + +- 使用者能看到自己建立的叢集 +- 每個叢集顯示:名稱、裝置數、活躍狀態、最近推論量 +- 能建立新叢集、刪除叢集、編輯叢集 + +### 建立叢集流程 + +1. 點「新建叢集」 +2. 輸入叢集名稱 +3. 從已配對裝置挑選 2+ 台裝置加入 +4. 為每台裝置設權重(1-10) +5. 確認建立 + +### 叢集推論操作 + +- 進入某叢集的 workspace:`/clusters/[id]/workspace` +- 選模型(必須叢集內所有裝置都相容) +- 選來源(Camera / Image / Video / Batch) +- 啟動後,dispatcher 按加權 RR 分派任務到各裝置 +- UI 顯示: + - 每台裝置的即時負載(%) + - 每台裝置的推論 FPS + - 整體吞吐量 + - Bounding box overlay 仍然顯示(但標注是哪台裝置推論的) + +### 故障降級 + +- 某台裝置 tunnel 斷線 → 自動從 dispatcher 移除 +- 叢集內剩餘裝置繼續服務 +- 裝置重新上線 → 自動回加入叢集 + +--- + +## 技術細節(從 POC 搬) + +從 POC `cluster/` 模組搬: + +- `Dispatcher`:加權 RR 邏輯 +- `Manager`:叢集 CRUD +- `Pipeline`:單幀 `RunInference` 或連續 `StartContinuous` + +**要做的產品化改造**: + +- 加 user_id 隔離(POC 只有一個用戶) +- 叢集 metadata 存 DB(Phase 1 接 DB 後) +- MaxClusterSize 可配置 +- 健康度監控與告警 + +--- + +## 驗收條件(Phase 1,不在 Phase 0 範圍) + +- [ ] 能建立、編輯、刪除叢集 +- [ ] 叢集內至少能加 5 台裝置(上限可配置) +- [ ] 加權 RR 分派正確(實測權重 3:1 的裝置,流量比約 3:1) +- [ ] 某裝置離線時,叢集自動降級,剩餘裝置繼續服務 +- [ ] 裝置重新上線自動加回 +- [ ] 每台裝置的即時負載 UI 更新 +- [ ] 不同使用者的叢集互相隔離 + +--- + +## Phase 0 要做的準備 + +- [ ] `internal/cluster/` 模組從 POC 搬到 visionA-backend,但**不接入 API handlers** +- [ ] Phase 0 的前端 `/clusters` 頁面:可留空白或顯示「即將推出」 +- [ ] 介面層面預留:API Server router 保留 `/api/clusters/...` 路徑的預定義,先回 501 Not Implemented + +--- + +## Phase 2 的 TODO + +- **TODO 1**:跨使用者的叢集共享(多租戶團隊功能) +- **TODO 2**:動態權重調整(根據實測效能自動調) +- **TODO 3**:叢集級別的 Model A/B Testing +- **TODO 4**:叢集級別的 fallback policy(不只 RR,還能 failover) + +--- + +## 連結 + +- 回:[PRD 索引](../PRD.md) +- 相關:[推論操作](feature-inference.md) diff --git a/docs/autoflow/02-prd/features/feature-converter-integration.md b/docs/autoflow/02-prd/features/feature-converter-integration.md new file mode 100644 index 0000000..063f8af --- /dev/null +++ b/docs/autoflow/02-prd/features/feature-converter-integration.md @@ -0,0 +1,246 @@ +# 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=`。 + +--- + +## 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 = `(`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: /files/{key}?access_token=`(token 不暴露給 frontend JS) +- 前端觸發方式: + - 用 anchor tag(``)或 `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=` 正確寫入 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` diff --git a/docs/autoflow/02-prd/features/feature-dashboard.md b/docs/autoflow/02-prd/features/feature-dashboard.md new file mode 100644 index 0000000..e8d2bd3 --- /dev/null +++ b/docs/autoflow/02-prd/features/feature-dashboard.md @@ -0,0 +1,61 @@ +# Feature:儀表板(P1) + +> 父文件:[PRD.md](../PRD.md) | 對應 User Stories:US-14 +> +> **Phase 0 基本版**:對應 local-tool 的首頁 `/`,顯示裝置列表 + 簡易統計。 +> **Phase 1 完整版**:加上活動時間軸、詳細統計卡片。 + +--- + +## 概述 + +`/` 根目錄頁面,使用者登入後看到的第一個畫面。 + +對比 local-tool:local-tool 首頁顯示本機裝置、本機活動。visionA Cloud 顯示雲端配對的裝置、跨時段的活動紀錄。 + +--- + +## 使用者行為 + +### Phase 0 最小版 + +- Header 問候語:「Hi {user_name}」 +- 「快速開始」卡片: + - 「配對新裝置」→ `/devices` + - 「開始推論」→ `/workspace` + - 「瀏覽模型」→ `/models` +- 裝置狀態摘要:「你有 X 台裝置,其中 Y 台在線」 +- 空狀態(0 裝置):顯示引導文案 + 配對 CTA + +### Phase 1 完整版(沿用 local-tool) + +- 活動時間軸:近 N 小時的推論紀錄、裝置事件 +- 統計卡片: + - 今日推論次數 + - 活躍裝置數 + - 上傳的模型數 +- 裝置列表(縮略版) +- 最近使用的模型 + +--- + +## 驗收條件(Phase 0) + +- [ ] 登入後自動跳轉到 `/` +- [ ] 顯示問候語 + 快速開始卡片 +- [ ] 裝置狀態摘要數字正確 +- [ ] 空狀態引導文案顯示正確(新用戶首次登入) + +--- + +## Phase 0 的 TODO + +- **TODO**:活動時間軸(需 DB,Phase 1) +- **TODO**:詳細統計卡片(需 DB 埋點,Phase 1) +- **TODO**:自訂儀表板佈局(Phase 2) + +--- + +## 連結 + +- 回:[PRD 索引](../PRD.md) diff --git a/docs/autoflow/02-prd/features/feature-device-management.md b/docs/autoflow/02-prd/features/feature-device-management.md new file mode 100644 index 0000000..ac24987 --- /dev/null +++ b/docs/autoflow/02-prd/features/feature-device-management.md @@ -0,0 +1,120 @@ +# Feature:裝置管理(P0) + +> 父文件:[PRD.md](../PRD.md) | 對應 User Stories:US-03、US-06、US-07 + +--- + +## 概述 + +使用者能在雲端瀏覽自己所有已配對的 Kneron 裝置、查看狀態、連接 / 斷開、查看韌體資訊。 + +對比 local-tool:UI 幾乎一樣,差別在「裝置來源」— local-tool 掃本機 USB,visionA Cloud 列表來自 pairing 的 local agent(透過 tunnel 掃 agent 端的 USB)。 + +--- + +## 使用者行為 + +### 裝置列表頁(`/devices`) + +顯示當前使用者名下所有已配對的裝置: + +| 欄位 | 內容 | 來源 | +|------|------|------| +| 裝置名稱 | 使用者自訂或預設(例如「KL520-001」)| Phase 0:預設;Phase 1:可編輯 | +| 型號 | KL520 / KL720 | 從 local agent 回報 | +| 連線狀態 | 🟢 在線 / 🟡 Tunnel 重連中 / ⚪ 離線 | Remote-proxy session 狀態 | +| Tunnel 延遲 | RTT ms | 定期 ping | +| 韌體版本 | 例如 `KL520_FW_v1.2.3` | 從 local agent 回報 | +| 所屬 agent | 哪台筆電 / 機台 | Pairing 時填(Phase 1)| +| 最後活動 | 時間戳記 | 推論紀錄 | +| 操作 | 連接 / 斷開 / 詳細 / 撤銷配對(Phase 1)| — | + +### 裝置詳細頁(`/devices/[id]`) + +沿用 local-tool 的版型,新增: + +- **Tunnel 資訊區塊**:當前 session ID、連線時長、延遲圖、斷線重連次數 +- **Agent 資訊**:這台裝置來自哪個 local agent、agent 版本 +- **撤銷配對按鈕**(Phase 1) + +### 操作 + +- **連接 / 斷開**:和 local-tool 一樣(透過 tunnel 轉發 `/api/devices/{id}/connect`) +- **掃描新裝置**:觸發 agent 端的 USB 掃描(tunnel 轉發 `/api/devices/scan`) +- **撤銷配對**(Phase 1):撤銷該裝置的 Session Token,強制斷線 + +--- + +## 技術細節(給 Architect 參考) + +### 裝置狀態來源 + +``` +┌─────────────────────────────────────────────────────────┐ +│ Phase 0 資料流(in-memory) │ +│ │ +│ Browser ──GET /api/devices──> api-server │ +│ │ │ +│ │ 查 session 狀態 │ +│ ▼ │ +│ SessionStore (in-mem) │ +│ │ │ +│ │ 對每個在線 session │ +│ ▼ │ +│ remote-proxy (tunnel) │ +│ │ │ +│ │ yamux stream │ +│ ▼ │ +│ local agent (:3721) │ +│ │ │ +│ │ 本機 KneronPLUS │ +│ ▼ │ +│ Kneron KL520/KL720 │ +└─────────────────────────────────────────────────────────┘ +``` + +### 快取策略 + +Phase 0:不快取,每次 GET 都即時查 local agent。 + +Phase 1:考慮前端 SWR 快取 + 後端 5 秒 memory cache,減少 tunnel 流量。 + +### 即時事件 + +沿用 local-tool 的 `/ws/devices/events` WebSocket,但改由 visionA-backend 的 api-server 廣播: + +- Agent 上線 / 下線 +- 裝置連接 / 斷開 +- 裝置健康度變化 + +api-server 透過 remote-proxy 的 events 通道接收 agent 事件,再廣播給對應的前端 WebSocket client。 + +--- + +## 驗收條件(Phase 0) + +- [ ] 登入後 `/devices` 頁面顯示當前使用者的裝置列表 +- [ ] 未配對時顯示空狀態 + 「配對新裝置」CTA +- [ ] 裝置在線 / 離線狀態正確(agent 下線 5 秒內反映) +- [ ] 能打開裝置詳細頁,顯示韌體版本等資訊 +- [ ] 連接 / 斷開操作能成功執行 +- [ ] 掃描 USB 裝置能觸發 agent 端動作 +- [ ] Tunnel 延遲顯示正確(單位 ms) +- [ ] 兩個不同使用者的裝置列表互相隔離(multi-tenant 基本) +- [ ] UI 與 local-tool 一致性達到 90%+ + +--- + +## Phase 0 的 TODO + +- **TODO**:裝置名稱自訂編輯(Phase 1) +- **TODO**:裝置分組 / 標籤(Phase 2) +- **TODO**:裝置健康度告警規則(Phase 2) +- **TODO**:撤銷配對按鈕(Phase 0 顯示 disabled,Phase 1 實作) + +--- + +## 連結 + +- 回:[PRD 索引](../PRD.md) +- 相關:[Pairing 流程](feature-pairing.md)、[推論操作](feature-inference.md) diff --git a/docs/autoflow/02-prd/features/feature-inference.md b/docs/autoflow/02-prd/features/feature-inference.md new file mode 100644 index 0000000..d9bdecf --- /dev/null +++ b/docs/autoflow/02-prd/features/feature-inference.md @@ -0,0 +1,133 @@ +# Feature:推論操作(P0:Camera;P1:Image / Video / Batch) + +> 父文件:[PRD.md](../PRD.md) | 對應 User Stories:US-10、US-11、US-15 + +--- + +## 概述 + +使用者選裝置 + 模型 + 來源後,能啟動推論並即時看到結果(bounding box / 分類結果)。 + +這是**visionA Cloud 最核心的價值體驗**。推論結果要即時、低延遲、UI 和 local-tool 一致。 + +--- + +## 使用者行為 + +### Workspace 進入點(`/workspace` 或 `/workspace/[deviceId]`) + +三段式選擇: + +1. **選裝置**:從已配對的裝置列表挑一個 +2. **選模型**:從模型庫挑一個(需和裝置型號相容) +3. **選來源**: + - **Camera(Phase 0)**:使用者 agent 端的 USB / IP camera + - **Image(Phase 1)**:從瀏覽器上傳單張圖片 + - **Video(Phase 1)**:從瀏覽器上傳影片(MP4/AVI/MOV 等) + - **Batch Images(Phase 1)**:批次上傳多張圖片 + +選完後進推論工作區。 + +### 推論工作區 + +**UI 沿用 local-tool 的設計**: + +- 左上:即時 MJPEG 畫面 + overlay(bounding box + label + confidence) +- 右側:控制面板 + - 信心度門檻 slider + - 推論 FPS / 延遲顯示 + - 開始 / 停止按鈕 +- 底下:分類結果 / 偵測框列表 +- 右上 header:Tunnel 延遲顯示(visionA Cloud 獨有) + +### Camera 推論流程(Phase 0 重點) + +``` +┌────────────────────────────────────────────────────────────┐ +│ Camera 推論資料流 │ +│ │ +│ 1. Browser 呼叫 api-server:POST /workspace/start │ +│ { deviceId, modelId, source: "camera", cameraId } │ +│ ▼ │ +│ 2. api-server 找到對應的 tunnel session │ +│ 透過 remote-proxy 轉發到 local agent │ +│ ▼ │ +│ 3. local agent 啟動 KneronPLUS inference pipeline │ +│ Camera → 幀捕捉 → Kneron → 推論結果 │ +│ ▼ │ +│ 4. local agent 把 MJPEG stream 推給 tunnel │ +│ tunnel ─yamux stream─> remote-proxy ─> api-server │ +│ ▼ │ +│ 5. Browser 透過 /api/camera/stream 拿到 MJPEG │ +│ (遠端 stream 經過 api-server proxy 到瀏覽器) │ +│ ▼ │ +│ 6. Browser 透過 WebSocket /ws/inference 拿到推論結果 │ +│ Canvas overlay bounding box │ +└────────────────────────────────────────────────────────────┘ +``` + +### 關鍵技術挑戰(給 Architect 參考) + +1. **MJPEG binary stream 要能過 tunnel**:yamux 支援,但需要測試吞吐量 +2. **WebSocket(推論結果)也要過 tunnel**:雲端前端的 WebSocket 連到 api-server,api-server 的對應連線再過 tunnel 到 local agent +3. **多 client 存取同一個 stream**:local-tool 支援多 client multipart MJPEG。visionA Cloud 可能一個使用者開多個 tab,要處理並發 +4. **Tunnel 斷線處理**:推論中 tunnel 斷線,UI 要明確提示並自動重連 + +### 延遲預算 + +| 路徑段 | 延遲 | +|--------|-----| +| Camera → local agent 幀捕捉 | ~10ms | +| local agent → Kneron 推論 | ~30ms(model 而異)| +| local agent → remote-proxy(tunnel,經 WAN)| ~50-200ms RTT | +| remote-proxy → api-server | ~5ms(同機房)| +| api-server → Browser | ~10-50ms | +| **端到端總延遲(P95 目標)** | **< 500ms** | + +(local-tool 端到端 ~150-250ms,多的部分主要是 tunnel 的 WAN RTT) + +--- + +## 驗收條件(Phase 0,Camera only) + +- [ ] 能從 Workspace 頁選裝置 + 模型 + Camera 來源 +- [ ] 點「開始」後,MJPEG 畫面在 2 秒內出現 +- [ ] Bounding box / 分類結果 overlay 正確即時更新 +- [ ] 信心度門檻調整立即反映在畫面 +- [ ] 推論 FPS 顯示正確(大約 10-30 fps) +- [ ] 端到端延遲 P95 < 500ms(內網測試) +- [ ] Tunnel 延遲顯示正確(header) +- [ ] Tunnel 斷線時立即提示使用者並自動嘗試重連 +- [ ] 重連成功後推論自動恢復 +- [ ] 停止推論後,畫面停在最後一幀 + 有明確「已停止」狀態 +- [ ] 同一使用者開兩個 tab 看同一裝置推論 → 都能看(多 client multipart) +- [ ] 不同使用者之間互相隔離(不會看到別人的推論畫面) + +--- + +## Phase 1 擴充(Image / Video / Batch) + +Image / Video / Batch 推論**不需要即時 stream**,使用者上傳檔案到 api-server,透過 tunnel 把檔案送到 local agent,agent 做完回傳結果。 + +技術挑戰: +- 大檔案傳輸的 tunnel 效率 +- 檔案臨時儲存策略(api-server 記憶體 or 暫存磁碟) +- 影片 seek 操作的 tunnel 往返 + +--- + +## Phase 0 的 TODO + +- **TODO 1**:Image / Video / Batch 推論(Phase 1) +- **TODO 2**:推論結果的 history / 回放(Phase 1,需 DB) +- **TODO 3**:多 client 同時看同一推論的效能優化(Phase 1) +- **TODO 4**:推論結果下載 / export(Phase 1) +- **TODO 5**:Camera 來源的使用者選擇 UI — local agent 可能有多個 camera,要讓使用者選 +- **TODO 6**:推論 logging / analytics(Phase 2,用於叢集指標追蹤) + +--- + +## 連結 + +- 回:[PRD 索引](../PRD.md) +- 相關:[叢集推論](feature-cluster-inference.md)、[工作區](feature-workspace.md)、[非功能性需求 — 效能](../nonfunctional.md) diff --git a/docs/autoflow/02-prd/features/feature-model-management.md b/docs/autoflow/02-prd/features/feature-model-management.md new file mode 100644 index 0000000..b6e2226 --- /dev/null +++ b/docs/autoflow/02-prd/features/feature-model-management.md @@ -0,0 +1,145 @@ +# Feature:模型管理(P0) + +> 父文件:[PRD.md](../PRD.md) | 對應 User Stories:US-08、US-09、US-23 + +--- + +## 概述 + +使用者能瀏覽系統預設的 7 個 Kneron `.nef` 模型,上傳自己的模型,管理模型庫。 + +對比 local-tool: +- local-tool 的模型存在使用者電腦 `~/Library/Application Support/visiona-local/custom-models/` +- visionA Cloud 的模型**存在雲端**(Phase 0 用 local filesystem 實作 ObjectStorage 介面,Phase 1 換 S3/MinIO) + +--- + +## 使用者行為 + +### 模型庫頁(`/models`) + +UI 沿用 local-tool,但資料來源改為 visionA-backend。 + +**三個區塊**: + +1. **預設模型**(系統共用,所有使用者都能用) + - 7 個預置模型(KL520 × 4 + KL720 × 3) + - Phase 0:seed 到 backend 的 local fs + - Phase 1:seed 到 S3 的 `public/` prefix + +2. **我的模型**(使用者上傳的) + - 空狀態顯示 CTA「上傳模型」 + - 每個模型顯示:名稱、大小、上傳時間、支援硬體、上傳者 + +3. **組織模型**(Phase 2,留介面) + - 如果使用者在團隊 workspace,能看到團隊共享模型 + +**篩選**:按任務類型(classification / object_detection)、硬體(KL520 / KL720)、關鍵字。 + +### 上傳模型 + +1. 點「上傳模型」→ 拖拽 or 選檔 +2. 檔案類型:`.nef` +3. 檔案大小限制:**Phase 0 上限 100MB;Phase 1 上限 500MB(後續依付費方案調整)** + - 此限制必須落在 config(env var 或 config file),**不可硬編碼**,方便跨 Phase 調整 + - 建議 config key:`MODEL_UPLOAD_MAX_SIZE_MB`(預設 100) + - 前端與後端都要同步使用這個值(前端顯示錯誤提示、後端做實際拒絕) +4. 上傳進度 bar +5. 後端儲存到 ObjectStorage 介面實作 +6. 上傳完成後解析 metadata(檔案 header) +7. Phase 0:metadata 存 in-memory map;Phase 1:存 DB + +### 模型詳細頁(`/models/[id]`) + +- 基本資訊:名稱、ID、大小、MD5 +- 支援硬體(KL520 / KL720) +- Metadata(輸入尺寸、任務類型) +- 效能數據(推論 FPS、延遲)— Phase 0 預設模型有,自上傳的沒有 +- 下載按鈕(重新下載到本機) +- 刪除按鈕(只對自上傳模型) + +--- + +## 技術細節(給 Architect 參考) + +### ObjectStorage 介面(重點) + +Phase 0 要定義清楚介面,讓 Phase 1 可直接換 S3/MinIO 不動業務邏輯。 + +```go +// internal/storage/storage.go +type ObjectStorage interface { + Upload(ctx context.Context, key string, reader io.Reader, size int64) error + Download(ctx context.Context, key string) (io.ReadCloser, error) + Delete(ctx context.Context, key string) error + List(ctx context.Context, prefix string) ([]ObjectInfo, error) + // Phase 1:presigned URL + GetDownloadURL(ctx context.Context, key string, expiresIn time.Duration) (string, error) + GetUploadURL(ctx context.Context, key string, expiresIn time.Duration) (string, error) +} + +type ObjectInfo struct { + Key string + Size int64 + LastModified time.Time + ETag string +} +``` + +**Phase 0 實作:`LocalFSStorage`** +- 存在 `./data/models/{user_id}/{model_id}.nef` +- `GetDownloadURL` 回傳 `/api/models/{id}/download`(走 api-server 串流) + +**Phase 1 實作:`S3Storage`** +- 用 AWS SDK 或 minio-go +- `GetDownloadURL` / `GetUploadURL` 回傳真的 presigned URL + +### 上傳大檔案策略 + +- Phase 0:Multipart form upload 直接進 api-server,api-server 再寫進 local fs + - 限制:100MB 以下 + - api-server 的記憶體與磁碟 I/O 壓力 +- Phase 1:前端直接用 presigned URL 上傳到 S3,不過 api-server + - 需要 CORS 設定 + - 上傳完成後通知 api-server 更新 metadata + +### 預設模型 seed + +Phase 0:backend 啟動時,檢查 `data/models/system/` 是否有預設模型,沒有就從 bundled resources 複製過去。 + +Phase 1:預設模型在 `s3://visiona-models/system/` 共享給所有 user。 + +--- + +## 驗收條件(Phase 0) + +- [ ] `/models` 頁面顯示 7 個預設模型 +- [ ] 不同使用者看到自己的「我的模型」,互相隔離 +- [ ] 上傳 `.nef` 檔成功,出現在列表 +- [ ] 上傳進度 bar 顯示正確 +- [ ] 上傳 > 100MB 被拒絕,回傳明確錯誤(Phase 0 限制值) +- [ ] 上傳大小限制從 config(`MODEL_UPLOAD_MAX_SIZE_MB`,預設 100)讀取,非硬編碼 +- [ ] 前端顯示的大小限制與後端實際拒絕的值一致 +- [ ] 上傳非 `.nef` 檔被拒絕 +- [ ] 能下載已上傳的模型 +- [ ] 能刪除自上傳的模型,不能刪預設模型 +- [ ] ObjectStorage 介面定義完整(有 interface + LocalFSStorage 實作) +- [ ] 介面層面預留 S3 實作的 hook,切換時不動業務邏輯 + +--- + +## Phase 0 的 TODO + +- **TODO 1**:presigned URL 上傳(Phase 1) +- **TODO 2**:模型版本管理(Phase 2) +- **TODO 3**:模型標籤 / 搜尋增強(Phase 2) +- **TODO 4**:團隊共享模型(Phase 2) +- **TODO 5**:模型來源追蹤(是轉檔來的還是使用者上傳的,Phase 2) +- **TODO 6**:效能數據收集(推論時自動紀錄 FPS / 延遲,Phase 1) + +--- + +## 連結 + +- 回:[PRD 索引](../PRD.md) +- 相關:[介面契約 — ObjectStorage](../interface-contracts.md)、[轉檔整合](feature-converter-integration.md) diff --git a/docs/autoflow/02-prd/features/feature-pairing.md b/docs/autoflow/02-prd/features/feature-pairing.md new file mode 100644 index 0000000..150ebe6 --- /dev/null +++ b/docs/autoflow/02-prd/features/feature-pairing.md @@ -0,0 +1,181 @@ +# Feature:Pairing 流程(P0,雛形必做) + +> 父文件:[PRD.md](../PRD.md) | 對應 User Stories:US-04、US-05、US-06、US-18、US-19 + +--- + +## 概述 + +Pairing 是 visionA Cloud 獨有的流程(local-tool 沒有):把使用者筆電上的 local agent 連到 visionA Cloud 的 remote-proxy,讓雲端能代管該裝置。 + +這個流程替代了 POC 中「Token = SHA256(MAC)[:16]」的硬編碼方式,改為**雲端發 token、綁 user + device、有 expiry、可撤銷**的產品化版本。 + +--- + +## 使用者行為 + +### 正向流程(Happy Path) + +1. 使用者在瀏覽器登入 visionA Cloud +2. 在「裝置」頁面點「配對新裝置」 +3. 彈出對話框,顯示: + - 一組 Pairing Token(例如 `vAc_7f3c8e2a9b1d0f5e...`) + - QR code(方便手機掃或直接貼) + - 說明文字:「在你的 local agent 貼上這個 token」 + - 倒數計時:「15 分鐘內要完成配對」 +4. 使用者打開筆電上的 local-tool +5. (Phase 0 妥協)手動編輯 local-tool 的設定檔,填入: + - `VISIONA_RELAY_URL=wss://relay.visiona.cloud` + - `VISIONA_PAIRING_TOKEN=vAc_7f3c8e2a9b1d0f5e...` + - 重啟 local-tool +6. local-tool 的 tunnel client 啟動,連到 remote-proxy +7. remote-proxy 驗證 token → 找到對應 user → 記錄 device 已配對 +8. 雲端前端透過 WebSocket 收到「裝置上線」事件 → 即時更新裝置列表 +9. 使用者看到新裝置出現,完成配對 + +### 異常流程 + +**A. Token 過期** +- 超過 15 分鐘未完成配對 → local-tool 連線時 remote-proxy 回 403 +- local-tool 顯示「Pairing Token 已過期,請重新取得」 +- 使用者回雲端重新產生 + +**B. Token 已被使用** +- Token 預設為一次性。配對成功後不能再用 +- 再次用同一 token → 回 409 Conflict +- 雲端 UI 提示「此 token 已使用」 + +**C. Token 無效** +- 亂打 token → 401 Unauthorized + +**D. 網路中斷後重連** +- local-tool 已配對的裝置,tunnel 斷線後自動重連 +- 重連時用的是 **Session Token(長期)**,不是 Pairing Token(一次性) +- Session Token 在首次配對成功後由 remote-proxy 發給 local-tool,local-tool 存到本機 config + +--- + +## Token 設計 + +### Pairing Token(一次性、短期) + +| 屬性 | 值 | +|------|---| +| 格式 | `vAc_` + 32 字元 hex(範例:`vAc_7f3c8e2a9b1d0f5e...`)| +| 產生者 | api-server | +| 儲存 | Phase 0:in-memory map;Phase 1:DB | +| TTL | 預設 15 分鐘(可調整)| +| 使用次數 | 1 次 | +| 綁定 | user_id(產生者)| + +### Session Token(長期、可撤銷) + +| 屬性 | 值 | +|------|---| +| 格式 | `vAs_` + 64 字元 hex | +| 產生者 | remote-proxy(首次 pairing 成功後發)| +| 儲存 | Phase 0:in-memory map;Phase 1:DB
local-tool 端:寫入 config 檔 | +| TTL | **90 天**(到期或使用者主動撤銷時失效)| +| 使用次數 | 無限(TTL 內每次重連共用)| +| 綁定 | user_id + device_id | + +> **兩階段 TTL 設計**: +> - **Pairing Token 階段**:使用者在雲端產 token、貼到 local agent,15 分鐘內要完成首次連線,一次性使用 +> - **Session Token 階段**:local agent 首次連上 remote-proxy 後,由 remote-proxy 發 Session Token 寫入 local config;之後 tunnel 斷線重連、跨 session 維持長連線都用這把 token,90 天內有效 +> +> Phase 0 先做固定 90 天 TTL;Phase 1 加 rotation(到期前一段時間自動換新 token,避免使用者介入)。 + +### 為什麼分兩種 Token + +- Pairing Token 一次性、短期 → 防止 token 洩漏的危害被放大(最壞情況只影響 15 分鐘) +- Session Token 長期 → 避免每次重連都要使用者介入,但 90 天 TTL 限制洩漏風險 +- 兩階段模式類似 OAuth 的 authorization code + refresh token + +--- + +## 介面契約(API 層面) + +完整 API spec 由 Architect Agent 寫入 TDD,本文件只列出關鍵端點: + +### POST `/api/pairing/generate` + +**Request** (需登入): +```json +{} +``` + +**Response 200**: +```json +{ + "pairing_token": "vAc_7f3c8e2a9b1d0f5e...", + "expires_at": "2026-04-21T10:30:00Z", + "relay_url": "wss://relay.visiona.cloud/tunnel/connect" +} +``` + +### WebSocket `wss://relay.visiona.cloud/tunnel/connect?token={token}` + +**流程**: +1. local-tool 發起 WebSocket 連線,帶 token(可能是 Pairing Token 或 Session Token) +2. remote-proxy 驗證: + - 若是 Pairing Token:檢查未過期、未使用 → 發 Session Token(透過 WebSocket 首個 message 傳給 local-tool)→ 標記 Pairing Token 已使用 + - 若是 Session Token:檢查未撤銷 → 接受連線 +3. 建立 yamux session(沿用 POC 的設計) +4. 後續所有 HTTP/WebSocket 請求透過 yamux stream 轉發到 local agent + +### POST `/api/devices/{device_id}/revoke` (Phase 1) + +**功能**:撤銷某 Session Token,強制該裝置斷線。 + +--- + +## 驗收條件(Phase 0) + +- [ ] 登入後,裝置頁面的「配對新裝置」按鈕可點 +- [ ] 點擊後彈出對話框,顯示 Pairing Token(明文 + QR code) +- [ ] Pairing Token 格式符合規範(`vAc_` + 32 hex) +- [ ] Pairing Token TTL = 15 分鐘,過期後 local-tool 連不上 +- [ ] local-tool 用正確 token 連線 → 成功建立 tunnel +- [ ] local-tool 用錯誤 / 過期 / 已使用的 token 連線 → 收到明確錯誤 +- [ ] 配對成功後,雲端裝置列表 5 秒內顯示新裝置 +- [ ] local-tool 收到 Session Token 並寫入本機 config +- [ ] Session Token 格式符合規範(`vAs_` + 64 hex) +- [ ] Session Token TTL = 90 天,到期後 local-tool 連線被拒,需重新 pairing +- [ ] tunnel 斷線後,local-tool 用 Session Token 自動重連(TTL 內) +- [ ] 全流程繁體中文 UI +- [ ] TODO:Phase 0 不實作「撤銷」功能,但要在 UI 留按鈕(disabled) + +--- + +## Phase 0 的 TODO + +- **TODO 1**:local-tool 端的整合 — Phase 0 先讓使用者手動編輯 config 檔;Phase 1 要在 local-tool 內建 UI「配對到 visionA Cloud」 +- **TODO 2**:QR code 產生 — 可用前端套件(例如 `qrcode.react`),但優先級低,可先只顯示明文 token +- **TODO 3**:Token 撤銷 — 介面留著,實作留到 Phase 1 +- **TODO 4**:多 local agent 同時配對 — Phase 0 每個 user 最多 1 台 agent 同時連線,Phase 1 支援多台 +- **TODO 5**:Session Token rotation — Phase 0 採固定 90 天 TTL(到期需重新 pairing);Phase 1 做到期前自動 rotation,無需使用者介入 +- **TODO 6**:Pairing 流程的使用者引導(tutorial) — Phase 0 不做,Phase 1 加 onboarding + +--- + +## 安全考量 + +**Phase 0 最小要求**: + +- Token 必須透過 TLS(`wss://`)傳輸,不能走明文 +- Token 儲存在記憶體不要 log 出來 +- Pairing Token 產生後不要再可讀取(只有產生當下顯示給使用者) + +**Phase 1 要加的**: + +- Token 在 DB 以 hash 儲存(不存明文) +- Rate limiting(防暴力破解) +- IP 地址記錄(配對時、連線時) +- 異常偵測(同一 token 從不同 IP 嘗試連線) + +--- + +## 連結 + +- 回:[PRD 索引](../PRD.md) +- 相關:[介面契約](../interface-contracts.md)、[非功能性需求](../nonfunctional.md) diff --git a/docs/autoflow/02-prd/features/feature-workspace.md b/docs/autoflow/02-prd/features/feature-workspace.md new file mode 100644 index 0000000..e3d599f --- /dev/null +++ b/docs/autoflow/02-prd/features/feature-workspace.md @@ -0,0 +1,50 @@ +# Feature:工作區(P0) + +> 父文件:[PRD.md](../PRD.md) | 對應 User Stories:US-10 + +--- + +## 概述 + +工作區是使用者做推論的主要介面,對應 `/workspace` 和 `/workspace/[deviceId]` 頁面。 + +對比 local-tool:UI 幾乎完全一致。 + +--- + +## 使用者行為 + +### `/workspace`(選擇頁) + +- 三段式流程:選裝置 → 選相容模型 → 選推論來源 +- Phase 0:只支援 Camera 來源;Phase 1 加 Image / Video / Batch +- 選定後跳轉到 `/workspace/[deviceId]` + +### `/workspace/[deviceId]`(推論操作頁) + +- 沿用 local-tool 的工作區版型 +- 詳見 [feature-inference.md](feature-inference.md) + +--- + +## 驗收條件(Phase 0) + +- [ ] `/workspace` 可從裝置列表進入 +- [ ] 選裝置 + 選模型 + 選來源的三段式流程能完成 +- [ ] 相容性檢查:KL520 不能選 KL720 模型,反之亦然 +- [ ] 跳轉到 `/workspace/[deviceId]` 後推論操作可用 +- [ ] 離開頁面時自動停止推論(不留尾巴) + +--- + +## Phase 0 的 TODO + +- **TODO**:工作區「偏好設定」記憶(上次用哪個模型、哪個 camera) +- **TODO**:從 `/workspace/[deviceId]` 切換其他裝置的快捷操作 + +--- + +## 連結 + +- 回:[PRD 索引](../PRD.md) +- 相關:[推論操作](feature-inference.md)、[裝置管理](feature-device-management.md)、[模型管理](feature-model-management.md) diff --git a/docs/autoflow/02-prd/interface-contracts.md b/docs/autoflow/02-prd/interface-contracts.md new file mode 100644 index 0000000..bafdc09 --- /dev/null +++ b/docs/autoflow/02-prd/interface-contracts.md @@ -0,0 +1,330 @@ +# 8. 介面契約(Interface Contracts) — visionA Cloud + +> 父文件:[PRD.md](PRD.md) +> +> **本章節是 Phase 0 的核心產出之一**。Phase 0 有很多 TODO,介面契約讓未來能「無痛換實作」。 + +--- + +## 8.1 為什麼介面這麼重要 + +Phase 0 是雛形階段,很多子系統用 **stub 實作**,但: + +- **雛形不實作 ≠ 雛形不設計** +- 介面(interface / contract)現在就要定義清楚 +- 未來只換實作,不動業務邏輯 + +五大關鍵介面: + +1. **AuthProvider** — 會員系統(Phase 0 stub → Phase 1 JWT) +2. **SessionStore** — Tunnel session 狀態(Phase 0 in-memory → Phase 1 Redis) +3. **ObjectStorage** — 模型檔儲存(Phase 0 local fs → Phase 1 S3/MinIO) +4. **ConverterClient** — 轉檔服務(Phase 0 stub → Phase 2 真實 API) +5. **BillingProvider** — 計費(Phase 0 / 1 都不做 → Phase 2+) + +> 實際 Go interface 定義由 Architect Agent 寫進 TDD。本文件定義的是**需求與合約**。 + +--- + +## 8.2 AuthProvider 介面 + +### 目的 + +讓 visionA-backend 的所有 Auth 相關功能(登入、註冊、驗 token、登出)透過一個 interface 抽象,Phase 0 接 stub,Phase 1 接真實 Auth。 + +### 必須提供的能力 + +| 能力 | 說明 | Phase 0 | Phase 1 | +|------|------|---------|---------| +| Register | 建立新 user(email + password)| stub(僅接受固定 demo-user)| DB | +| Login | 驗證 user,發 session token | **接受任何帳密,一律回 demo-user + stub token** | JWT | +| ValidateToken | 驗證 token,回傳 user | 查 in-memory map | 驗簽 + 查 DB | +| Logout | 撤銷 token | 刪 in-memory entry | 加 blacklist | +| GetUser | 查 user 資訊 | in-memory(固定 demo-user)| DB | +| — 以下為 **Phase 1** 才做 — | | | | +| RefreshToken | Refresh token rotation | ❌(Phase 1) | ✅ | +| RequestPasswordReset | 寄 email 重設 | ❌(Phase 1) | ✅ | +| ConfirmPasswordReset | 確認重設 | ❌(Phase 1) | ✅ | +| Delete | 刪除帳號 + 所有相關資源 | ❌(Phase 1) | ✅ | + +### Phase 0 雛形實作:`StaticAuthProvider` + +Phase 0 不做真實會員系統,改用 `StaticAuthProvider`: + +- **Login**:不驗證 email/password,一律接受並回 `demo-user` 的 stub token +- **ValidateToken**:只認得 `StaticAuthProvider` 自己發的 stub token,驗過回 `demo-user` +- **Logout**:從 in-memory map 刪 token +- **GetUser**:固定回 `demo-user`(email: `demo@visiona.cloud`) +- **Register**:Phase 0 可回 `ErrNotImplemented` 或直接回 demo-user(Architect 決定) +- **Phase 1 方法**(RefreshToken / RequestPasswordReset / ConfirmPasswordReset / Delete):stub,直接回 `ErrNotImplemented` + +這個設計讓前端登入流程能跑通(任何帳密都能登入),但不涉及真實會員資料。Phase 1 換成 `JWTAuthProvider`(綁 DB + JWT 簽章)時,所有業務邏輯不用動。 + +### 錯誤類型 + +必須定義:`ErrUserNotFound`、`ErrInvalidCredentials`、`ErrTokenExpired`、`ErrTokenInvalid`、`ErrUserAlreadyExists`、`ErrNotImplemented`(Phase 0 stub 用)。 + +### 使用方式 + +api-server 啟動時透過 DI 注入 `AuthProvider` 實作: + +```go +// Phase 0 +authProvider := stub.NewStaticAuthProvider() + +// Phase 1 +authProvider := jwt.NewJWTAuthProvider(db, jwtSecret) + +router := api.NewRouter(authProvider, ...) +``` + +**Middleware 設計**: +```go +router.Use(auth.RequireAuth(authProvider)) // 需登入的路由 +``` + +### 詳細介面定義 + +見 [features/feature-auth.md § 技術細節](features/feature-auth.md#技術細節給-architect-參考)。 + +--- + +## 8.3 SessionStore 介面 + +### 目的 + +Tunnel session 狀態管理。api-server 和 remote-proxy 都需要查詢「某 user/pairing token 對應哪個 tunnel session」,Phase 0 兩個 binary 共用記憶體(同一個 process 或 shared memory);Phase 1 用 Redis 跨節點共享。 + +### 必須提供的能力 + +| 能力 | 說明 | +|------|------| +| CreatePairingToken | 建立一次性 pairing token,綁 user_id,15 min TTL | +| ConsumePairingToken | 驗證並消耗 pairing token,回傳對應 user_id | +| CreateSession | 建立 tunnel session,發 session token | +| GetSession | 從 session token 查 session(回 user_id、device_id、created_at 等)| +| RevokeSession | 撤銷 session(登出或使用者要求撤銷)| +| ListSessionsByUser | 查使用者所有活躍 session | +| UpdateSessionHeartbeat | 更新 session 最後心跳時間 | +| CleanupExpired | 清過期 session | + +### Phase 0 實作:`MemorySessionStore` + +- 單一 process 內用 sync.Map / mutex +- api-server 和 remote-proxy **必須跑在同一個 process**(用 goroutine 起兩個 server)OR +- **共享 memory store 透過 gRPC / HTTP**(remote-proxy 呼叫 api-server 的 internal API 查 session) + +> **Architect 需決策**:Phase 0 的 api-server 和 remote-proxy 是一個 process 還是兩個? +> 兩個 binary 就是兩個 process,不能共享記憶體。 +> 方案 A:兩個 process + internal gRPC 同步 session;方案 B:單 binary 啟動時透過參數選擇角色,Phase 0 都跑在一起。 +> **建議 Phase 0 用方案 A**(符合最終架構),即便兩個 process 都在本機跑,介面也走 HTTP/gRPC。 + +### Phase 1 實作:`RedisSessionStore` + +- 所有狀態存 Redis +- api-server 和 remote-proxy 都連同一個 Redis +- 支援多節點部署 + +### 資料模型(邏輯) + +``` +PairingToken: + - token (string, primary) + - user_id (string) + - created_at (timestamp) + - expires_at (timestamp) + - consumed (bool) + - consumed_at (timestamp, nullable) + +Session: + - session_token (string, primary) + - user_id (string) + - device_id (string) + - pairing_token_used (string, FK) + - created_at (timestamp) + - last_heartbeat_at (timestamp) + - revoked (bool) + - revoked_at (timestamp, nullable) + - proxy_node_id (string, Phase 1 for routing) +``` + +--- + +## 8.4 ObjectStorage 介面 + +### 目的 + +統一模型檔(`.nef`)、未來使用者上傳檔(影片、圖片)、推論結果等二進位資料的儲存。 + +### 必須提供的能力 + +| 能力 | 說明 | +|------|------| +| Upload | 上傳檔案到指定 key | +| Download | 下載檔案 | +| Delete | 刪除檔案 | +| List | 列出某 prefix 下的所有 key | +| Exists | 檢查 key 是否存在 | +| GetSize | 取得檔案大小 | +| GetDownloadURL | 取得下載 URL(Phase 0 走自己的 API,Phase 1 用 presigned)| +| GetUploadURL | (Phase 1)presigned upload URL | + +### Key 規範 + +採統一的 key schema,讓未來換 S3 不需要 migrate: + +``` +models/{user_id}/{model_id}.nef ← 使用者上傳 +models/system/{model_id}.nef ← 預設模型(所有 user 共享) +uploads/{user_id}/{upload_id}.{ext} ← 使用者臨時上傳(影片、圖片等) +converter-inputs/{job_id}/{filename} ← 轉檔的來源檔(臨時) +converter-outputs/{job_id}/model.nef ← 轉檔結果 +``` + +### Phase 0 實作:`LocalFSStorage` + +- 存在 `./data/storage/{key}` +- `GetDownloadURL` 回傳 `/api/storage/download?key=...`,api-server 自己 stream 出去 +- 需做 key 合法性檢查(不能 path traversal) + +### Phase 1 實作:`S3Storage` / `MinIOStorage` + +- 標準 AWS S3 SDK 或 minio-go +- 原生 presigned URL +- Bucket 策略: + - `visiona-models`:公開讀權限 for `system/` prefix,其他要 presigned + - `visiona-uploads`:全私有 + +### 檔案大小限制 + +- Phase 0:100 MB(記憶體限制) +- Phase 1:500 MB(model)、5 GB(video upload) + +--- + +## 8.5 ConverterClient 介面 + +### 目的 + +呼叫 kneron_model_converter 服務做格式轉檔。Phase 0 / 1 用 stub,Phase 2 接真實 API。 + +### 介面能力 + +見 [features/feature-converter-integration.md](features/feature-converter-integration.md) 的完整 API 合約。 + +**核心能力**: + +- `Submit(ConvertRequest) → ConvertJob` +- `GetStatus(jobID) → ConvertJob` +- `Download(jobID) → io.ReadCloser` +- `Cancel(jobID) → error` + +### 現況:converter API 尚未存在 + +**visionA-backend 先定義介面,對 converter 團隊提出 spec 需求**。流程: + +1. Phase 0:visionA-backend 定義 `ConverterClient` interface + `StubConverterClient` 實作 +2. Phase 0:PM Agent 把 API spec(`feature-converter-integration.md`)正式交付給 converter 團隊 +3. Phase 0-1:converter 團隊依 spec 實作 API +4. Phase 2:visionA-backend 實作 `HTTPConverterClient`,換掉 stub + +### Stub 行為 + +`StubConverterClient`: +- Submit 回 fake job_id + status = queued +- GetStatus 第一次回 queued,第二次回 processing,第三次之後回 completed(給 fake download URL) +- Download 回一個內建的 fake `.nef`(就是某個預設模型) +- 讓前端能跑完整流程 UI 開發 + +--- + +## 8.6 BillingProvider 介面(Phase 2) + +### 目的 + +抽象計費與訂閱管理,方便 Phase 2 接 Stripe / Paddle / 其他。 + +### 介面能力 + +| 能力 | 說明 | +|------|------| +| CreateCustomer | 建立計費客戶 | +| CreateSubscription | 建立訂閱 | +| CancelSubscription | 取消訂閱 | +| UpdateSubscription | 升降級 | +| ReportUsage | 上報使用量(usage-based pricing) | +| CreateCheckoutSession | 建立結帳 session(hosted checkout)| +| HandleWebhook | 處理 provider webhook(付款成功、失敗等)| +| GetInvoices | 列出 invoice | + +### Phase 0 / 1 都不做 + +Phase 0 / 1 visionA-backend 不存在任何 billing 相關程式碼。Phase 2 再從頭加。 + +--- + +## 8.7 給外部團隊的 API 合約清單 + +Phase 0 期間,需要對外溝通的契約: + +### 對 kneron_model_converter 團隊 + +**交付文件**:[features/feature-converter-integration.md](features/feature-converter-integration.md) + +**對方 Action Items**: + +- 審閱 API spec +- 評估實作時程 +- 提供 dev 環境與 test API key +- 決定 webhook 簽章格式 +- 決定 rate limit 數字 + +### 對 local-tool 團隊(同 repo) + +Phase 0 期間 local-tool **完全不動**。Phase 1 才考慮整合: + +- local-tool 新增「配對到 visionA Cloud」功能 +- 要共享哪些 Go 套件(tunnel client 可能可共用) + +### 對 Innovedus IT / DevOps + +Phase 0 後期,若要部署 staging 環境,需要: + +- 一個子域名(例如 `cloud.visiona.dev`) +- TLS 憑證(Let's Encrypt) +- Docker host 或 AWS account +- DNS 管理權 + +--- + +## 8.8 API 版本策略 + +### URL 版本 + +所有 visionA Cloud API 採 URL 版本策略: + +``` +/api/v1/... ← Phase 0 從 v1 開始 +/api/v2/... ← 未來 breaking change 時 +``` + +### 相容性 + +- 同一 major version 內必須向後相容 +- 遇到必須 breaking 時開新 major version,舊版保留至少 6 個月 +- Phase 0 內部使用,允許 v1 小幅變動(不視為 breaking) + +### WebSocket URL + +``` +wss://api.visiona.cloud/api/v1/ws/... +wss://relay.visiona.cloud/v1/tunnel/connect +``` + +--- + +## 連結 + +- 上一章:[非功能性需求](nonfunctional.md) +- 下一章:[成功指標](success-metrics.md) +- 跳回:[PRD 索引](PRD.md) diff --git a/docs/autoflow/02-prd/market-analysis.md b/docs/autoflow/02-prd/market-analysis.md new file mode 100644 index 0000000..c7465ee --- /dev/null +++ b/docs/autoflow/02-prd/market-analysis.md @@ -0,0 +1,166 @@ +# 3. 市場分析 — visionA Cloud + +> 父文件:[PRD.md](PRD.md) +> +> 註:Phase 0 雛形階段,市場分析以「**為產品定位提供參照**」為主,不做完整 TAM/SAM/SOM 數字推估。待 Phase 1 MVP 前會做更詳細的市場驗證。 + +--- + +## 3.1 市場概述 + +**邊緣 AI 推論裝置管理市場**可以分成三層: + +1. **硬體層**:Kneron、NVIDIA Jetson、Google Coral、Hailo、NXP i.MX +2. **模型訓練層**:Edge Impulse、SenseCraft AI、Roboflow、kneron_model_converter +3. **推論與部署管理層**(本產品所在):NVIDIA Triton、AWS IoT Greengrass、Azure IoT Edge、本地自建工具 + +**市場規模參考數字**(產業報告,非精準值): + +- 全球邊緣 AI 晶片市場 2025 約 $10B,2030 CAGR ~20% +- 邊緣推論軟體市場 2025 約 $2B,成長率略高於硬體 +- Kneron 在 NPU 市場屬於中小型玩家,主戰場在亞洲(台灣、日本、中國)+ 美國西岸 + +**visionA Cloud 的 SAM**:Kneron 裝置使用者,保守估算 Kneron 生態系全球開發者 5,000-10,000 人,加上內部 FAE 50 人。Phase 0 目標觸及 ~50 人(內部 + 早期採用者)。 + +--- + +## 3.2 競品分析 + +### 3.2.1 競品清單 + +| 競品 | 類型 | 和 visionA Cloud 的重疊度 | 威脅等級 | +|------|------|------------------------|---------| +| NVIDIA Triton Inference Server | 推論 Server | 中(不針對邊緣,鎖 NVIDIA 硬體)| 低 | +| Edge Impulse Studio | 訓練 + 部署 | 中(重 training,不重遠端操作)| 中 | +| SenseCraft AI | 訓練 + 部署 | 中(鎖 Seeed 硬體)| 低 | +| AWS IoT Greengrass | 邊緣設備管理 | 高(設備管理理念相近)| 中 | +| Azure IoT Edge | 邊緣設備管理 | 高(同上)| 中 | +| Kneron 自家工具(KneronPLUS SDK)| SDK / CLI | 低(命令列工具,沒有 UI)| 無 | +| edge-ai-platform POC(自家)| — | 100%(就是前身)| — | +| local-tool(自家)| 離線桌面 | 高(同一批用戶)| 互補不衝突 | + +### 3.2.2 重點競品深度分析 + +#### NVIDIA Triton Inference Server + +| 維度 | 內容 | +|------|------| +| 定位 | 企業級推論 server,跑在資料中心 / 雲端 GPU | +| 優勢 | 極高效能、多框架(TF/PyTorch/ONNX)、成熟 | +| 劣勢 | 鎖 NVIDIA 硬體、不是為「邊緣裝置遠端管理」設計、學習曲線陡峭 | +| 對我們的啟發 | 他們的 Model Repository、HTTP/gRPC 雙 API 可參考 | +| 威脅 | 不直接 — 不同硬體生態、不同場景 | + +#### Edge Impulse Studio + +| 維度 | 內容 | +|------|------| +| 定位 | 端到端邊緣 AI MLOps 平台(資料收集 → 訓練 → 部署) | +| 優勢 | 250K+ 開發者、40+ 硬體支援、完整 MLOps | +| 劣勢 | 訓練導向、部署後管理薄弱、Kneron 支援有限 | +| 對我們的啟發 | 他們的 Live Classification(類似我們的 workspace)體驗很好 | +| 威脅 | 中 — 如果他們加強 Kneron 支援和遠端管理,會直接競爭 | + +#### AWS IoT Greengrass + +| 維度 | 內容 | +|------|------| +| 定位 | AWS 生態系邊緣運算平台 | +| 優勢 | 企業級、與 AWS IoT Core 整合、大規模 | +| 劣勢 | 綁定 AWS、配置複雜、不是 AI 專用 | +| 對我們的啟發 | 他們的 device pairing / shadow 機制可參考 | +| 威脅 | 中 — 企業客戶可能偏好 AWS 生態系 | + +#### edge-ai-platform POC(前身) + +| 維度 | 內容 | +|------|------| +| 定位 | Kneron 內部 POC,驗證 relay + cluster 可行性 | +| 優勢 | 已驗證核心技術(tunnel、叢集推論)| +| 劣勢 | 無 auth、token hardcode、沒有產品化包裝、文件分散 | +| 對我們的繼承 | 搬核心模組(relay / tunnel / cluster / wsconn)| + +--- + +## 3.3 差異化策略 + +### 3.3.1 visionA Cloud 的獨特定位(UVP) + +> **「專為 Kneron 邊緣 AI 裝置打造的雲端遠端操作平台 — 不做訓練,不綁雲,就是要讓你打開瀏覽器就能操作自己的 Kneron 裝置。」** + +四個核心差異化: + +1. **Kneron 專用** → 不分心做 40+ 硬體,深度優化 KL520 / KL720 +2. **遠端 tunnel** → 不需要 VPN、公開 IP,local agent 主動連雲端 WebSocket +3. **叢集推論** → 加權 RR,多裝置並行,Edge Impulse / SenseCraft 都沒有 +4. **和 local-tool 互補** → 同一個使用者可同時使用,UI 一致 + +### 3.3.2 護城河分析 + +| 護城河 | 強度 | 可維持多久 | 說明 | +|-------|------|-----------|------| +| Kneron 生態系整合 | 高 | 2-3 年 | 我們是 Innovedus(Kneron 自家),官方渠道優勢 | +| Tunnel 技術(yamux over WebSocket) | 低 | 6 個月 | 競品可抄 | +| local-tool + cloud 雙模式 | 中 | 1-2 年 | 技術不難,但做到 UI 完全一致需要長期累積 | +| 叢集推論 | 低 | 6 個月 | POC 已展示,競品可抄 | +| 和 kneron_model_converter 整合 | 中 | 1-2 年 | 取決於 converter 團隊的護城河 | + +**結論**:最強的護城河是**「Kneron 官方身份 + local-tool 熟客基礎」**,技術護城河薄。需要靠快速迭代 + 生態系整合維持優勢。 + +### 3.3.3 沒做這個會怎樣(反面論證) + +如果不做 visionA Cloud: + +- FAE 繼續每次出差帶筆電 → 疲勞 + 出錯 +- SI 客戶管不了多裝置 → Kneron 採購天花板被限制 +- POC 技術被閒置 → 沈沒成本 +- 用戶被 Edge Impulse / SenseCraft 吸走 → 生態系流失 + +--- + +## 3.4 市場進入策略(簡版) + +> 註:Phase 0 雛形階段只做內部使用,完整 GTM 策略在 Phase 1 前規劃。 + +### Phase 0(2026 Q2) + +- 對象:Kneron 內部 FAE + Innovedus 團隊 +- 通路:內部公告、直接拉人測試 +- 目標:技術驗證,拿到 5-10 位深度回饋 + +### Phase 1 MVP(2026 Q3) + +- 對象:Kneron 外部生態系中**已知的早期採用者**(從 local-tool / POC 用戶名單找) +- 通路:Email 邀請、1:1 onboarding +- 目標:100 個 Pairing,50 個 WAD + +### Phase 2(2026 Q4+) + +- 對象:Kneron 晶片採購新客戶、Kneron 官網訪客 +- 通路:Kneron 官網首頁、developer portal 整合、技術部落格 +- 目標:北極星指標穩定成長 + +**在地化策略**: + +- Phase 0-1:繁體中文 + English(沿用 local-tool 的 i18n) +- Phase 2:加入簡體中文、日文(主要亞洲市場需求) + +--- + +## 3.5 關鍵假設與驗證 + +| 假設 | 驗證方式(Phase 0) | 成功標準 | +|------|-------------------|---------| +| 使用者願意把 local agent 連上雲端 | 內部 FAE 測試 Pairing 流程 | 5/5 FAE 完成 Pairing | +| 企業網路能穿透(NAT / Proxy / Firewall) | 在不同客戶網路做 tunnel 連線測試 | 至少在 3 種企業網路成功 | +| 推論端到端延遲可接受 | 實測 P95 延遲 | < 500ms(比 local 多 ~300ms tunnel)| +| UI 體驗一致性(local vs cloud) | 讓用過 local 的 FAE 試 cloud | 無需額外學習 | +| 叢集推論對 SI 有價值 | 展示給目標 SI 客戶 | 至少 2 家表達興趣 | + +--- + +## 連結 + +- 上一章:[產品定位](product-positioning.md) +- 下一章:[用戶研究](user-research.md) +- 跳回:[PRD 索引](PRD.md) diff --git a/docs/autoflow/02-prd/nonfunctional.md b/docs/autoflow/02-prd/nonfunctional.md new file mode 100644 index 0000000..4876618 --- /dev/null +++ b/docs/autoflow/02-prd/nonfunctional.md @@ -0,0 +1,284 @@ +# 7. 非功能性需求 — visionA Cloud + +> 父文件:[PRD.md](PRD.md) + +--- + +## 7.1 效能 + +### 7.1.1 API 回應時間 + +| 端點類型 | Phase 0 目標 | Phase 1 目標 | 備註 | +|---------|-------------|-------------|------| +| 簡單 API(如 `/api/auth/me`、`/api/devices` 列表) | P95 < 200ms | P95 < 100ms | 不過 tunnel 的 | +| 裝置操作(透過 tunnel) | P95 < 500ms + tunnel RTT | P95 < 300ms + tunnel RTT | 含 tunnel 往返 | +| 模型上傳(100MB) | P95 < 60 秒 | P95 < 30 秒(presigned)| 網路頻寬限制 | +| Pairing 產生 token | P95 < 100ms | P95 < 50ms | — | + +### 7.1.2 推論效能 + +| 指標 | Phase 0 目標 | Phase 1 目標 | +|------|-------------|-------------| +| Camera 端到端延遲 P95 | < 500ms | < 350ms | +| Camera FPS | >= 10 fps | >= 20 fps(視裝置 + 模型)| +| 首次推論啟動時間 | < 3 秒 | < 2 秒 | + +參考值:local-tool(本機)的 Camera 端到端延遲 ~150-250ms,visionA Cloud 多出的部分主要是 tunnel WAN RTT。 + +### 7.1.3 Tunnel 效能 + +| 指標 | Phase 0 目標 | +|------|-------------| +| Tunnel 建立時間 | < 2 秒(含 TLS handshake + yamux handshake)| +| Tunnel 重連時間 | < 5 秒(斷線偵測到重連成功)| +| Tunnel throughput | >= 5 Mbps(至少能跑 MJPEG 480p)| +| 並發 yamux streams per session | >= 32 | + +### 7.1.4 前端效能 + +| 指標 | Phase 0 目標 | +|------|-------------| +| 首頁 FCP | < 2 秒(cold load)| +| 頁面切換 | < 300ms(App Router prefetch)| +| WebSocket 訊息處理 | < 50ms | +| MJPEG 畫面渲染 | 維持 >= 20 fps | + +--- + +## 7.2 安全性 + +### 7.2.1 傳輸加密 + +- **必須使用 TLS**(Phase 1 起強制,Phase 0 dev 環境可暫用 http) +- Tunnel 必須用 `wss://`(不能走明文 WebSocket) +- TLS 版本:>= 1.2,建議 1.3 +- HSTS header(Phase 1 起) + +### 7.2.2 認證與授權 + +| 需求 | Phase 0 | Phase 1 | +|------|---------|---------| +| 使用者登入 | Stub(不安全)| JWT + bcrypt | +| Session 管理 | 記憶體 token | Redis + rotation | +| Pairing Token 安全 | 一次性 + 15min TTL | + DB hash 儲存 + rate limit | +| Session Token 安全 | 純字串存記憶體 | + DB hash + 撤銷機制 + rotation | +| API Rate Limiting | ❌ 不做 | ✅ 必做(Auth 相關 endpoint)| +| CSRF 防護 | ❌(cookie-only)| SameSite=Strict cookies | +| XSS 防護 | React 自動轉義 | + CSP header | + +### 7.2.3 資料隔離 + +**Phase 0 即須滿足**: + +- 使用者 A 不能看到使用者 B 的裝置、模型、叢集 +- 所有 API endpoint 必須檢查 resource 的 `owner_user_id` vs current user +- WebSocket 連線也要做同樣檢查 + +### 7.2.4 敏感資料保護 + +| 資料 | 處理方式 | +|------|---------| +| 密碼 | Phase 0 不存(stub 只記 in-memory);Phase 1 bcrypt | +| Session Token | 不 log 出來 | +| Pairing Token 產生後 | 只顯示一次,後端不可再查明文(Phase 1 存 hash)| +| 使用者模型檔 | 存在 ObjectStorage,需 user 授權才能下載 | +| 推論影像 / 結果 | Phase 0 不儲存;Phase 1 如有儲存需加密 | + +### 7.2.5 依賴套件安全 + +- Go:`go mod audit`(或 Snyk)每月掃一次 +- Node:`npm audit` 每週掃一次 +- Docker 映像:每月重 build 套新 base image +- 漏洞回應 SLA:Critical 1 週修,High 2 週修 + +--- + +## 7.3 可擴展性(Scalability) + +### 7.3.1 Phase 0 定位:可跑得動就好 + +- 單機部署 OK +- 支援 ~10 同時使用者、~20 裝置同時在線 +- 狀態 in-memory,重啟就沒了 + +### 7.3.2 Phase 1 定位:支援 100 人同時使用 + +- 接 PostgreSQL 作 persistent store +- 接 Redis 作 session store / tunnel session map(跨 api-server instance 共享) +- api-server 可水平擴展(stateless) +- remote-proxy 仍然有狀態(tunnel session 在記憶體),但可用 consistent hashing 分散 + +### 7.3.3 Phase 2+ 定位:支援 1000+ 使用者 + +- Multi-region 部署 +- CDN 前置 +- S3 / MinIO 大檔儲存 +- remote-proxy 支援 session migration(裝置換一個 proxy 節點不會斷) + +### 7.3.4 架構預留 + +**Phase 0 就要預留的擴展點**: + +- 狀態抽象(SessionStore 介面)→ 換 Redis 不動業務邏輯 +- AuthProvider 介面 → 換真實 Auth 不動業務邏輯 +- ObjectStorage 介面 → 換 S3 不動業務邏輯 +- api-server 儘量 stateless(除了 Auth 快取) +- config-driven(環境變數 / YAML) + +--- + +## 7.4 可用性(Availability) + +### 7.4.1 Phase 0 SLO(內部使用) + +| 指標 | 目標 | +|------|------| +| api-server uptime | 95%(有 bug 接受)| +| remote-proxy uptime | 99%(影響面大,較嚴)| +| 前端可訪問 | 99%(CDN)| + +### 7.4.2 Phase 1 SLO(外部早期採用者) + +| 指標 | 目標 | +|------|------| +| Overall uptime | 99%(月最多 7 小時 downtime)| +| Tunnel session 存活率 | 99.5% | +| API 成功率 | 99.5% | + +### 7.4.3 降級策略 + +**Tunnel 斷線時**: +- 前端顯示「裝置離線」狀態,推論頁面明確提示 +- 自動重連(指數退避) +- 不要讓整個 session 失效 + +**Backend 崩潰時**: +- 前端顯示「服務暫時無法使用」Offline Overlay(沿用 local-tool 的 pattern) +- WebSocket 自動重連 +- 使用者重新整理頁面即可恢復 + +**Converter API 不可用時**(Phase 2): +- 轉檔功能暫時不可用,但其他功能不受影響 +- UI 顯示「轉檔服務暫時中斷」 + +### 7.4.4 Graceful Shutdown + +api-server / remote-proxy 關機時: +- 不再接受新連線 +- 已有連線完成後才關 +- 廣播 `system.shutdown-imminent` WebSocket 事件讓前端做準備 +- (沿用 local-tool 的 `/api/system/shutdown-notify` pattern) + +--- + +## 7.5 可觀測性(Observability) + +### 7.5.1 Phase 0 最小要求 + +- **結構化 log**:JSON 格式,含 user_id / request_id / device_id +- **log level**:INFO / WARN / ERROR / DEBUG +- 記錄每個 API 請求(method、path、status、duration、user_id) +- 記錄每個 tunnel session 生命週期事件(建立、斷線、重連) + +### 7.5.2 Phase 1 加強 + +- **Metrics**:Prometheus 格式 + - HTTP 請求數 / 延遲分佈 by endpoint + - Tunnel session 數 by status + - 推論請求數 by user / device + - 錯誤率 by endpoint +- **Tracing**:OpenTelemetry,跨 api-server ↔ remote-proxy ↔ agent 的 trace +- **Dashboards**:Grafana,關鍵指標視覺化 +- **Alerting**:錯誤率異常、tunnel 大量斷線等 + +### 7.5.3 Phase 2 加強 + +- 使用者層級的 analytics(需尊重 privacy) +- Business metrics:WAD、Pairing conversion、推論量 + +--- + +## 7.6 相容性與支援 + +### 7.6.1 瀏覽器支援 + +| 瀏覽器 | 支援版本 | +|--------|---------| +| Chrome | 最新 3 個版本 | +| Firefox | 最新 3 個版本 | +| Safari | 最新 2 個版本 | +| Edge | 最新 3 個版本 | +| IE | ❌ 不支援 | + +### 7.6.2 Local Agent 支援 + +| 平台 | 支援版本 | +|------|---------| +| macOS | 14+ (x86_64) | +| Windows | 10+ (x64) | +| Linux | Ubuntu 22.04+ (x86_64) | +| ARM | ❌ Phase 0 不支援(local-tool 限制)| + +### 7.6.3 多語系(i18n) + +- Phase 0:繁體中文 + English(沿用 local-tool) +- Phase 2:加簡體中文、日文 + +### 7.6.4 無障礙(a11y) + +- Phase 0:沿用 local-tool 的 a11y 水準(Radix UI 內建 a11y) +- 基本要求:鍵盤操作可行、重要元素有 aria-label +- Phase 1:做 WCAG 2.1 AA 自動化測試(axe-core) + +--- + +## 7.7 部署與維運 + +### 7.7.1 部署方式 + +**Phase 0**: +- Docker Compose 本機啟動 +- 兩個 container:`api-server` + `remote-proxy` +- 前端:靜態部署(Vercel / Netlify / S3+CloudFront) +- 儲存:local filesystem + +**Phase 1**: +- 仍以 Docker 為主 +- 加上 PostgreSQL + Redis +- Cloud-agnostic(AWS / GCP / self-hosted 皆可) +- 加 ALB / Cloud LB + +**Phase 2**: +- Kubernetes(如有需要多節點) +- Multi-region + +### 7.7.2 CI/CD + +- Phase 0:手動 build + deploy(GitHub Actions on main push 可選) +- Phase 1:GitHub Actions,每 PR 跑測試,merge 自動 deploy 到 staging +- Phase 2:Blue/green deployment + +### 7.7.3 Backup + +- Phase 1 起:DB 每日 backup,保留 30 天 +- 模型檔:S3 有 versioning + +--- + +## 7.8 法規與合規(Phase 1+ 才認真處理) + +Phase 0 內部使用,不需要。Phase 1 外部開放前要完成: + +- [ ] Terms of Service +- [ ] Privacy Policy(明列哪些資料被收集、如何使用) +- [ ] Cookie Policy(有用 cookie) +- [ ] GDPR(如果要對歐洲用戶開放) +- [ ] 台灣個資法(主場市場) + +--- + +## 連結 + +- 上一章:[User Stories](user-stories.md) +- 下一章:[介面契約](interface-contracts.md) +- 跳回:[PRD 索引](PRD.md) diff --git a/docs/autoflow/02-prd/product-positioning.md b/docs/autoflow/02-prd/product-positioning.md new file mode 100644 index 0000000..6745a82 --- /dev/null +++ b/docs/autoflow/02-prd/product-positioning.md @@ -0,0 +1,167 @@ +# 2. 產品定位 — visionA Cloud vs local-tool vs edge-ai-platform POC + +> 父文件:[PRD.md](PRD.md) + +--- + +## 2.1 三個產品的關係圖 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Innovedus 產品線關係 │ +│ │ +│ edge-ai-platform (POC) ─── Deprecate ────> 2026 Q3 封存 │ +│ │ │ +│ │ 把核心模組搬過來(relay / tunnel / cluster) │ +│ ▼ │ +│ visionA Cloud(本專案,Phase 0 雛形中) │ +│ │ │ +│ │ │ +│ │ 共用同一套前端 UI(不同 API base URL) │ +│ │ │ +│ ▼ │ +│ local-tool(離線版,繼續維護,不動) │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 2.2 三者的定位比較 + +| 維度 | local-tool | edge-ai-platform POC | visionA Cloud | +|------|-----------|---------------------|---------------| +| 產品形態 | 桌面 App(Wails 打包)| 桌面 App + Relay Server | Web SaaS | +| 網路需求 | 完全離線 | 有/無網路皆可 | 必須連網 | +| 目標場景 | 現場 demo、鎖網環境 | POC 實驗 | 遠端操作、多裝置管理 | +| 使用者數 | 單人(本機)| 單人 / POC 小隊 | 多用戶、多租戶 | +| Auth | 無 | 無(token hardcode)| 有(雛形 stub)| +| 模型儲存 | 本機 filesystem | 本機 filesystem | S3-compat 介面 | +| 叢集推論 | ❌ 不支援 | ✅ 有(POC 版)| ✅ 有(產品化)| +| Tunnel 遠端 | ❌ 不支援 | ✅ 有(token = MAC hash)| ✅ 有(Pairing Token)| +| 維護策略 | **持續維護、不變動** | **停止功能開發、2026 Q3 封存** | **主力產品,長期投入** | + +--- + +## 2.3 核心原則:兩種模式,同一套前端 + +**使用者不需要選「模式」。** 前端程式碼只有一份,差別在: + +```typescript +// local-tool: 前端跑在 Wails WebView,API base URL 是 localhost +const API_BASE = "http://127.0.0.1:3721" + +// visionA Cloud: 前端跑在瀏覽器,API base URL 是雲端 +const API_BASE = "https://api.visiona.cloud" +``` + +實作上: + +- visionA-frontend 編譯成兩個產物: + - `build/local/` → 餵給 local-tool 的 Wails embed(`API_BASE=localhost`) + - `build/cloud/` → 部署到 CDN / 雲端 hosting(`API_BASE=api.visiona.cloud`) +- 兩者共用 95% 的元件、頁面、store。只有: + - API base URL 不同 + - Auth 相關頁面(登入 / 註冊 / Pairing)**只在雲端版出現** + - local-tool 特有的 Wails 控制台事件**只在離線版處理**(透過 feature flag / build flag) + +### 為什麼這樣做 + +1. **不用維護兩份 UI**:改一次 bug 兩邊都好 +2. **local-tool 不被動到**:Phase 0 階段 local-tool 的前端原始碼不動,等 Phase 1 再評估是否把 visionA-frontend 反向 sync 回 local-tool +3. **使用者體驗一致**:用過 local-tool 的人轉到 cloud 不用重學 + +### 什麼情況下共享程式碼會破功 + +- local-tool 的 WebSocket URL 是 `ws://127.0.0.1:3721/ws/...`,cloud 是 `wss://api.visiona.cloud/ws/...` — 必須抽到 config +- local-tool 沒有 Auth,cloud 有 Auth — Auth 相關元件用 feature flag 隱藏 +- local-tool 顯示 ServerStatusDashboard(Wails 控制台),cloud 不該顯示 — 用 feature flag + +**Design Agent 需留意**:設計規格需要標注哪些元件「只在 cloud 顯示」、哪些「只在 local 顯示」。 + +--- + +## 2.4 使用情境對照 + +### 情境 A:FAE 阿哲做客戶 demo + +| 步驟 | 傳統做法(local-tool) | visionA Cloud 做法 | +|------|---------------------|-------------------| +| 準備 | 帶筆電 + Kneron 裝置到客戶現場 | 客戶現場先寄一台筆電 + 裝置過去(或客戶自備),裝 local agent 並 pairing | +| Demo 中 | 筆電螢幕分享給客戶 | 阿哲自己筆電開 visionA Cloud,畫面分享給客戶(或客戶自己用瀏覽器打開)| +| Demo 後 | 收回筆電 | 裝置留在客戶那,阿哲之後繼續用 cloud 管理 | + +### 情境 B:SI Sarah 管多個客戶現場 + +| 步驟 | 傳統做法 | visionA Cloud 做法 | +|------|---------|-------------------| +| 佈署 | 每個客戶一台筆電跑 local-tool,Sarah 要飛去每家 | 每個客戶佈署一台 local agent + pairing,Sarah 在辦公室集中管理 | +| 監控 | 打電話問客戶「現在裝置狀態?」 | 打開儀表板看所有裝置即時狀態 | +| 叢集 | 做不到 | 把多客戶的裝置組叢集,做加權 RR | + +### 情境 C:開發者 Mike 做模型 A/B Test + +| 步驟 | 傳統做法 | visionA Cloud 做法 | +|------|---------|-------------------| +| 對比 | 手動切換模型一個一個跑 | 叢集推論,同時在 3 台裝置跑不同模型,一個畫面看結果 | +| 轉檔 | 跑去 kneron_model_converter 網站手動轉 | 從 visionA Cloud 點「轉檔」,後端打 converter API | + +--- + +## 2.5 產品邊界(做什麼、不做什麼) + +### Phase 0 做: + +- ✅ 裝置管理(遠端) +- ✅ 模型管理(上傳、瀏覽) +- ✅ 推論操作(Camera / Image / Video / Batch) +- ✅ Pairing 流程 +- ✅ 基本儀表板 +- ✅ 會員系統介面(stub,雛形不接真的 auth) +- ✅ 叢集推論(從 POC 搬) +- ✅ 轉檔整合的 API 契約(等 converter 團隊實作) + +### Phase 0 不做: + +- ❌ 模型訓練(那是 Edge Impulse / converter 的事) +- ❌ 真實 Auth 系統(JWT、OAuth、2FA 等)— 留介面,用 stub +- ❌ 真實資料庫接入 — 用 in-memory +- ❌ 真實 S3 / MinIO — 用 local filesystem 實作 ObjectStorage 介面 +- ❌ Billing、使用量計費 +- ❌ Multi-region / HA 部署 +- ❌ 真實 Converter 整合(等 API 建好再接) +- ❌ Observability(Prometheus / Grafana / Tracing) +- ❌ Mobile App + +### 永遠不做: + +- ❌ 取代 local-tool — local-tool 的離線場景是真實需求,會一直存在 +- ❌ 模型訓練功能 — 超出產品邊界 +- ❌ 非 Kneron 硬體支援 — 違反產品定位 + +--- + +## 2.6 對 local-tool 的影響(必須控制) + +visionA Cloud 開發過程中,**local-tool 必須保持 0 regression**。 + +| 項目 | 處理方式 | +|------|---------| +| local-tool 的前端程式碼 | Phase 0 完全不動 | +| local-tool 的後端程式碼 | Phase 0 完全不動 | +| local-tool 的 Go module (`visiona-local`) | 和 visionA-backend 的 module (`visiona-backend` 暫定) **完全獨立** | +| 共用程式碼 | Phase 0 不共用,各自維護。Phase 1 再評估是否抽共用 pkg | +| local-tool 測試 | 繼續在 CI 跑,不能因為新專案而停擺 | + +**未來(Phase 1+)可能的整合方向**(不在本 PRD 範圍): + +- 讓 local-tool 同時具備「pairing 上雲」的功能 → 變成同時是離線工具 + cloud agent +- 前端程式碼抽成 monorepo 共用 package + +--- + +## 連結 + +- 上一章:[產品策略](strategy.md) +- 下一章:[市場分析](market-analysis.md) +- 跳回:[PRD 索引](PRD.md) diff --git a/docs/autoflow/02-prd/risks.md b/docs/autoflow/02-prd/risks.md new file mode 100644 index 0000000..f2dc41d --- /dev/null +++ b/docs/autoflow/02-prd/risks.md @@ -0,0 +1,118 @@ +# 11. 風險與相依 — visionA Cloud + +> 父文件:[PRD.md](PRD.md) + +--- + +## 11.1 風險分析 + +| # | 風險 | 可能性 | 影響 | 緩解措施 | 負責人 | +|---|------|-------|------|---------|--------| +| R01 | **破壞 local-tool** — visionA Cloud 開發過程中意外改到 local-tool | 中 | 高 | Monorepo 分離目錄、local-tool 完全獨立的 Go module、CI 持續跑 local-tool 測試、code review 嚴格檢查 | Orchestrator | +| R02 | **Tunnel 在企業網路穿透失敗** — NAT / Proxy / Firewall 擋住 WebSocket | 中 | 高 | Phase 0 在至少 3 種企業網路測試、使用標準 443 port + TLS、提供 HTTP CONNECT proxy 支援(Phase 1) | Architect | +| R03 | **Pairing Token 洩漏** — token 被截獲,別人接管使用者裝置 | 低 | 高 | 強制 TLS、一次性 + 15min TTL、Phase 1 加 hash 儲存、IP 紀錄、rate limit | Architect | +| R04 | **converter 團隊延遲交付** — 我們等 converter 團隊實作 API | 高 | 中 | Phase 0 用 stub 讓前端流程能走完;轉檔整合 list 在 Phase 2(不卡 Phase 1 MVP) | Orchestrator / PM | +| R05 | **推論延遲過大** — tunnel 的 WAN RTT 導致推論體驗不如 local-tool | 中 | 中 | 前端延遲透明化、Phase 1 支援就近 region 部署、衡量後如果 P95 > 500ms 考慮降級(只給高速網路用) | Architect | +| R06 | **Scope Creep** — 雛形做著做著功能就暴增 | 高 | 中 | PRD 明確列 Phase 0 Scope、PM 嚴格守門、超過 Scope 的功能一律往 Phase 1 / 2 推 | PM | +| R07 | **in-memory state 讓雛形不能 restart** — Phase 0 的 in-memory session / auth 一 restart 就全部沒 | 高 | 低 | 這是 Phase 0 預期行為;Phase 1 換 Redis + DB 解決;使用者教育(內部 FAE 知道雛形就是這樣) | Architect | +| R08 | **資料隔離 bug** — 使用者 A 看到 B 的裝置 / 模型 | 低 | 高 | 所有 API handler 必做 ownership 檢查、Reviewer 特別關注、Phase 1 加整合測試 | Reviewer | +| R09 | **MJPEG over tunnel 效能不足** — yamux 吞吐不夠跑 camera stream | 中 | 中 | Phase 0 早期做 spike test、若不行改用 WebRTC(但複雜度高)、降低 MJPEG 解析度與 fps | Architect | +| R10 | **UI 雙模式不一致** — local-tool 和 cloud 版前端漸漸 diverge | 中 | 中 | Phase 0 先不共用程式碼;Phase 1 規劃 monorepo 共用 package、UI 測試截圖比對 | Frontend | +| R11 | **內部 FAE 缺乏動機測試雛形** — 大家都很忙 | 中 | 中 | 提前協調資源、提供簡單的測試腳本、給測試者獎勵(公開表揚)、主管背書 | PM | +| R12 | **Auth stub 不小心上線給外部** — Phase 0 安全性極弱 | 低 | 極高 | 部署環境用網段限制(只內部 IP 可連)、環境變數明確標 `DEV_MODE=true`、Banner 顯示「雛形」 | DevOps | +| R13 | **converter 團隊改 API spec** — 我們定義的 spec 對方做不出來或要改 | 高 | 低 | Spec review session、版本化(v1 / v2)、stub 先跑前端 UX 不綁死 | PM | +| R14 | **Kneron USB 驅動在 local agent 跑不穩** — agent 端的 Kneron SDK 掛掉 | 中 | 中 | 這是 local-tool 既有風險,不新增;agent 端加健康檢查 + 自動重啟 | Local-tool team | +| R15 | **Legal / 合規未處理** — Phase 1 對外開放但沒有 Terms / Privacy | 中 | 高 | Phase 1 前 1 個月開始準備法律文件、用樣板 + 律師審 | PM / 法務 | + +--- + +## 11.2 技術相依 + +### 11.2.1 對外部團隊的相依 + +| 相依對象 | 相依項目 | 時間敏感度 | 備案 | +|---------|---------|-----------|------| +| kneron_model_converter 團隊 | 轉檔 API 實作 | Phase 2 才需要 | 用 stub 撐住 Phase 0-1 | +| Innovedus IT | Staging 環境(子域名、TLS)| Phase 1 前 | Phase 0 在本機跑即可 | +| Kneron 官方 | KneronPLUS SDK 穩定性 | 持續 | 既有依賴,不新增 | + +### 11.2.2 對內部專案的相依 + +| 相依對象 | 相依項目 | 備註 | +|---------|---------|------| +| local-tool | 作為 local agent 使用 | Phase 0 用**未修改版** local-tool 測試;Phase 1 才考慮整合新功能 | +| edge-ai-platform POC | 從中搬 relay / tunnel / cluster / wsconn 模組 | Phase 0 完成後 POC 就封存 | + +### 11.2.3 對第三方套件的相依 + +**前端**(沿用 local-tool 版本): + +- Next.js 16(App Router) +- React 19 +- Tailwind 4 +- Radix UI +- Zustand +- **新增**:無(都沿用 local-tool 的 stack) + +**後端**(沿用 POC 架構): + +- Go 1.26 +- Gin +- gorilla/websocket +- hashicorp/yamux +- **新增(Phase 0 用 stub,但介面要預留)**:無實作層面的新依賴 + +**Phase 1 會新增**: + +- PostgreSQL / pgx +- Redis / go-redis +- JWT library(例如 `golang-jwt/jwt`) +- AWS SDK 或 minio-go +- bcrypt +- email library(sendgrid 或 SES) + +--- + +## 11.3 Phase 0 的已知限制(透明告知) + +使用者(內部 FAE)在使用 Phase 0 雛形時需知道: + +- **Auth 是 stub**:重啟 backend 後所有 user / session 消失,需重新註冊 +- **資料不持久**:上傳的模型在 backend 重啟後會消失(local fs 存的話仍在,但 in-memory metadata 會消失) +- **Pairing Token 不撤銷**:Phase 0 一旦發出就無法主動撤銷,只能等 15min TTL +- **不支援大規模**:同時 10+ user 可能遇到效能問題 +- **安全性極弱**:絕對不可用真實密碼,也不可佈署到公開網際網路 + +以上限制會在 visionA Cloud 前端明顯位置顯示 Banner(「⚠️ 雛形版本,僅供內部測試」)。 + +--- + +## 11.4 不可跨越的紅線 + +以下情況必須立即停下專案重新討論: + +1. **local-tool regression** — 任何新改動導致 local-tool 既有測試 fail +2. **Auth stub 意外對外** — 任何真實使用者能訪問 Phase 0 環境 +3. **資料隔離穿透** — 使用者 A 能看到 B 的資料 +4. **Pairing 流程無法在任何企業網路跑通** — 代表核心技術路線需重新評估 +5. **Phase 0 超時 2 個月以上** — 代表 Scope 有問題,需重新定義 + +--- + +## 11.5 開放問題(待使用者決定) + +以下問題目前沒有明確答案,建議在 Phase 0 進行中釐清: + +- [ ] **Q1**:雛形環境要部署到哪?本機 / 公司內部 VM / 外部 cloud?(影響安全設計) +- [ ] **Q2**:`visionA` 是正式產品名嗎?還是只是代號?(影響 domain 選擇與對外命名) +- [ ] **Q3**:Pairing Token 的 TTL 15 分鐘合適嗎?(影響使用者體驗) +- [ ] **Q4**:Phase 0 是否允許「一個 user 只能 pairing 一台 agent」的限制?(簡化設計但影響測試覆蓋) +- [ ] **Q5**:內部 FAE 測試後,哪些回饋算是 Phase 0 bug(要修),哪些推到 Phase 1?(判準) +- [ ] **Q6**:轉檔 API spec 要交給 converter 團隊的正式時機?(影響他們的排程) + +--- + +## 連結 + +- 上一章:[開發範圍與階段](roadmap.md) +- 跳回:[PRD 索引](PRD.md) diff --git a/docs/autoflow/02-prd/roadmap.md b/docs/autoflow/02-prd/roadmap.md new file mode 100644 index 0000000..519ba66 --- /dev/null +++ b/docs/autoflow/02-prd/roadmap.md @@ -0,0 +1,230 @@ +# 10. 開發範圍與階段 — visionA Cloud + +> 父文件:[PRD.md](PRD.md) + +--- + +## 10.1 三階段總覽 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Phase 0(本次) Phase 1 Phase 2+ │ +│ 雛形 / 骨架 MVP(外部早期採用) 產品化(商業化) │ +│ 2026 Q2 2026 Q3 2026 Q4+ │ +│ │ +│ • 跑得動 • 接真 Auth • Billing │ +│ • 介面清楚 • 接真 DB • 轉檔整合(真) │ +│ • 雙模式共存 • 接真 Storage • 多租戶 / 團隊 │ +│ • 內部 FAE 測試 • 叢集功能 • 多區域部署 │ +│ • local-tool 不動 • 圖片/影片推論 • 公開 API │ +│ • Auth 是 stub • 外部 ~100 用戶 • 正式上線 │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 10.2 Phase 0(本次雛形,2026 Q2) + +### 10.2.1 目標 + +**一句話**:visionA Cloud 的架構骨架 + 基本頁面 + API Server + Remote Proxy 跑得動,使用者能在瀏覽器完成「註冊 → 配對 → 推論」流程,但 Auth / DB / Storage 都是 stub。 + +### 10.2.2 範圍(Scope) + +**必做(P0)**: + +- visionA-frontend 骨架 + P0 頁面(登入、註冊、首頁、裝置、模型、工作區、設定) +- visionA-backend `cmd/api-server`(對前端的 REST + WebSocket) +- visionA-backend `cmd/remote-proxy`(從 POC 搬 relay,升級 Pairing Token) +- Pairing 流程端到端(至少手動編輯 local-tool config 能跑通) +- Camera 推論端到端(透過 tunnel) +- 模型上傳(local fs 實作 ObjectStorage 介面) +- 會員系統 **stub**(in-memory AuthProvider) +- SessionStore **in-memory 實作** +- ConverterClient **stub 實作**(讓前端流程可走完) +- 前端 `/clusters` 頁面預留(可顯示「即將推出」) +- i18n 繁中 + English(沿用 local-tool) +- 基本的 CI(至少 unit test 跑得過) + +**明確不做**: + +- 真 Auth(JWT / OAuth / DB) +- 真 DB(PostgreSQL) +- 真 S3 / MinIO +- 叢集推論 API(只搬 `internal/cluster/` 模組) +- 儀表板時間軸與統計(只做快速開始版) +- 圖片 / 影片 / Batch 推論(只做 Camera) +- 轉檔 API 真實對接 +- Billing +- Observability(Prometheus / Grafana / Tracing) +- 正式部署到雲端(dev 環境能跑就好) + +### 10.2.3 里程碑 + +| 週 | 里程碑 | 產出 | +|---|-------|------| +| W1-2 | 三方文件完成 | PRD、Design Spec、TDD 全部通過三方審閱 | +| W3 | 骨架搭起來 | monorepo 結構建好、兩個 binary 能啟動、前端能打開登入頁 | +| W4 | Auth stub + 前端 P0 頁面切換 | 能註冊 → 登入 → 看到空 dashboard | +| W5-6 | Pairing 流程 + Tunnel 重搬 | 用 test local-tool(修改版 config)能 pairing 成功 | +| W7 | Camera 推論跑通 | 至少一位 FAE 能遠端看到 Camera + overlay | +| W8 | 模型管理 + 上傳 | ObjectStorage 介面完成,上傳 `.nef` 可用 | +| W9 | 內部 FAE 測試 | 5+ FAE 完成端到端推論 | +| W10 | 修 bug + Phase 0 驗收 | 達成所有驗收條件,進入 Phase 1 規劃 | + +### 10.2.4 Phase 0 TODO 總清單(匯整) + +所有 Phase 0 留下的 TODO,統一列在此處,交給 Orchestrator 追蹤: + +**Auth 相關**: + +- TODO-AUTH-01:換 JWTAuthProvider +- TODO-AUTH-02:DB schema(users / sessions) +- TODO-AUTH-03:Email 驗證流程 +- TODO-AUTH-04:密碼重設流程 +- TODO-AUTH-05:OAuth(Google / GitHub) +- TODO-AUTH-06:2FA +- TODO-AUTH-07:密碼強度規則 +- TODO-AUTH-08:Rate limiting +- TODO-AUTH-09:Account 刪除 +- TODO-AUTH-10:個人設定頁完整功能 +- TODO-AUTH-11:Role / Permission +- TODO-AUTH-12:API Key 管理 + +**Storage 相關**: + +- TODO-STO-01:S3/MinIO 實作 +- TODO-STO-02:Presigned URL 上傳 +- TODO-STO-03:模型版本管理 +- TODO-STO-04:檔案掃毒 + +**Session / Tunnel 相關**: + +- TODO-SESS-01:Redis SessionStore 實作 +- TODO-SESS-02:Session Token rotation +- TODO-SESS-03:Session 撤銷功能 +- TODO-SESS-04:多節點 remote-proxy + consistent hashing +- TODO-SESS-05:Tunnel 斷線事件通知優化 + +**Pairing 相關**: + +- TODO-PAIR-01:local-tool 內建 Pairing UI +- TODO-PAIR-02:QR code 生成 +- TODO-PAIR-03:Pairing 成功後的使用者引導 + +**Converter 相關**: + +- TODO-CONV-01:正式對接 converter API +- TODO-CONV-02:Webhook 簽章驗證 +- TODO-CONV-03:轉檔進度 UX 優化 +- TODO-CONV-04:支援格式擴充(初期只支援 ONNX) + +**功能擴充**: + +- TODO-FEAT-01:圖片 / 影片 / Batch 推論 +- TODO-FEAT-02:叢集推論 API 實作與 UI +- TODO-FEAT-03:儀表板時間軸與統計 +- TODO-FEAT-04:多 client 看同一推論的效能優化 + +**可觀測性**: + +- TODO-OBS-01:Prometheus metrics +- TODO-OBS-02:Grafana dashboard +- TODO-OBS-03:OpenTelemetry tracing +- TODO-OBS-04:Log 聚合(ELK / Loki) + +**部署**: + +- TODO-DEP-01:真的雲端部署(staging + production) +- TODO-DEP-02:TLS 憑證 +- TODO-DEP-03:DNS / Subdomain +- TODO-DEP-04:CDN(前端) +- TODO-DEP-05:CI/CD pipeline + +**商業化(Phase 2)**: + +- TODO-BIZ-01:Billing 介面 + Stripe 整合 +- TODO-BIZ-02:定價方案設計 +- TODO-BIZ-03:Terms / Privacy Policy +- TODO-BIZ-04:GDPR / 台灣個資法合規 + +--- + +## 10.3 Phase 1 MVP(2026 Q3,外部早期採用者) + +### 10.3.1 目標 + +- 接真 Auth(JWT + DB) +- 接真 Storage(S3 或 MinIO) +- 完成 Pairing、叢集、儀表板功能 +- 部署到真的雲端環境 +- 對外開放給 ~100 位早期採用者 + +### 10.3.2 主要交付 + +- 替換所有 Phase 0 stub 為真實作 +- 叢集推論 UI + API +- 圖片 / 影片 / Batch 推論 +- 真實部署(staging + production) +- CI/CD +- 基本 Observability +- User Story US-14 ~ US-23 全部完成 + +### 10.3.3 成功標準 + +- WAD >= 30 +- Pairing 轉換率 > 60% +- 推論延遲 P95 < 400ms +- 系統 uptime > 99% + +--- + +## 10.4 Phase 2+(2026 Q4 起,產品化) + +### 10.4.1 範圍 + +- 轉檔整合完成(converter API 真實對接) +- Billing(Stripe) +- 多租戶 / 團隊功能 +- Multi-region 部署 +- 公開 API(for Mike 這類獨立開發者) +- Mobile app(read-only,看裝置狀態) +- 合規文件(Terms / Privacy / GDPR) +- Marketing site 整合 + +### 10.4.2 成功標準 + +- MRR $5K+ +- 付費用戶 50+ +- NPS > 40 + +--- + +## 10.5 策略性路線圖(Now / Next / Later) + +### Now(本季,Phase 0) + +- 雛形架構跑得動 +- 內部 FAE 測試通過 +- 介面契約穩定 + +### Next(下季,Phase 1 MVP) + +- 真實 Auth / DB / Storage +- 叢集推論產品化 +- 部署到雲端 + +### Later(6-12 個月,Phase 2+) + +- 商業化 Billing +- 轉檔整合 +- Mobile + 公開 API +- 多區域 + +--- + +## 連結 + +- 上一章:[成功指標](success-metrics.md) +- 下一章:[風險與相依](risks.md) +- 跳回:[PRD 索引](PRD.md) diff --git a/docs/autoflow/02-prd/strategy.md b/docs/autoflow/02-prd/strategy.md new file mode 100644 index 0000000..e4817ce --- /dev/null +++ b/docs/autoflow/02-prd/strategy.md @@ -0,0 +1,174 @@ +# 1. 產品策略 — visionA Cloud + +> 父文件:[PRD.md](PRD.md) + +--- + +## 1.1 產品願景(模擬新聞稿) + +### 標題:visionA Cloud 正式推出 — 讓 Kneron 邊緣 AI 裝置「人在家中坐,推論千里來」 + +### 副標題:為 Kneron FAE 與生態系開發者打造的雲端操作平台,瀏覽器即能遠端管理、監控、推論自己的 Kneron 裝置 + +**問題背景** + +Kneron KL520 / KL720 是強大的邊緣 AI 推論晶片,但開發者與 FAE 在實際使用時有三個痛點: + +1. **demo 現場得帶筆電**:目前唯一穩定的開發工具是 local-tool 離線版,demo 必須把整台筆電搬去客戶那。 +2. **多裝置難以集中管理**:一個 SI 客戶可能同時在不同分店佈署 5 台 Kneron,沒有統一的遠端介面。 +3. **叢集推論只有 POC**:edge-ai-platform POC 展示了多裝置加權 round-robin 的潛力,但沒有正式產品化。 + +**visionA Cloud 的解法** + +visionA Cloud 是一個雲端 SaaS。使用者在自己的筆電、工廠機台、店頭機裝上「local agent」(即現有 local-tool),透過 Pairing Token 把裝置配對到雲端帳號。之後在任何瀏覽器開啟 visionA Cloud,就能看到自己所有裝置、上傳模型、啟動推論,跟操作本機一樣流暢 — 但**人不用在裝置旁邊**。 + +**典型用戶體驗引述** + +> 「以前去客戶做 POC,要帶一整個後背包裝筆電。現在我筆電放辦公室,裝置留在客戶現場,我在咖啡廳開瀏覽器就能跑推論給客戶看 Teams 螢幕分享。體驗完全不同。」 +> +> — 阿哲,Kneron FAE + +**如何開始使用** + +1. 在 visionA Cloud 註冊帳號 +2. 筆電上安裝 visionA local agent(即 local-tool,未來會內建 Pairing 功能) +3. 從雲端頁面取得 Pairing Token,貼進 local agent +4. 刷新雲端頁面,裝置自動出現,開始推論 + +### FAQ + +- **Q:這跟 local-tool 有什麼不同?** + A:local-tool 是離線桌面 app,適合網路鎖死的 demo 場景;visionA Cloud 是雲端 SaaS,適合需要遠端操作、跨裝置管理的場景。**兩者共用同一套 UI 與核心功能**,使用者可以自由選擇或同時使用。 + +- **Q:和 Edge Impulse、SenseCraft 比?** + A:他們是「訓練模型 + 部署到裝置」的通用平台,我們是「Kneron 專用 + 雲端遠端存取」的操作平台。我們不做模型訓練,只做已訓練好的 `.nef` 模型的部署與推論操作(但有提供對接 kneron_model_converter 的介面)。 + +- **Q:定價如何?** + A:Phase 0 雛形階段免費內部使用。商業模式(訂閱 / 按推論次數 / freemium)在 Phase 2 規劃,Phase 0 只定介面不接金流。 + +- **Q:什麼時候可以用?** + A:Phase 0 雛形目標 2026 Q2 完成(架構跑得動、基本頁面可用、in-memory auth)。Phase 1 MVP 目標 2026 Q3(接真 Auth/DB/Storage)。 + +--- + +## 1.2 目標用戶 + +### 主要用戶群(Phase 0 / Phase 1) + +1. **Kneron 內部 FAE 與 Innovedus 業務**(~50 人) + - 最熟悉 Kneron 裝置的人,常跑客戶現場 + - 容忍雛形不穩,會給詳細回饋 + +2. **Kneron 生態系 ISV / SI 開發者**(~數百人) + - 已經在用 local-tool 或 edge-ai-platform POC 的現有用戶 + - 痛點明確:多客戶多裝置要集中管理 + +### 次要用戶群(Phase 2) + +3. **試用 Kneron 晶片的新開發者**(長尾) + - 從 visionA Cloud 這個「門面」第一次接觸 Kneron + - 對他們而言,雲端門檻比離線桌面 app 低 + +### 非目標用戶(明確排除) + +- ❌ 想做**模型訓練**的人 — 他們應該用 Edge Impulse 或 kneron_model_converter +- ❌ 想部署**非 Kneron 硬體**的人 — 不在產品範圍 +- ❌ 企業級 MLOps / 大規模 inference production — 我們不是 NVIDIA Triton 競品 + +--- + +## 1.3 核心問題與價值主張 + +### 問題(用戶視角) + +| 問題 | 現況 | +|------|------| +| P1:Kneron 裝置必須連實體筆電才能操作 | local-tool 是桌面 app,使用者人不在時沒辦法用 | +| P2:多裝置沒有集中式管理 | 一個 FAE 手上可能有 3-5 台機台,要一台一台開 local-tool | +| P3:POC 的叢集推論沒產品化 | 加權 round-robin 很好用,但 POC 的 auth / 多租戶 / token 都沒做 | +| P4:非 Kneron 格式的模型要手動轉檔 | kneron_model_converter 目前是獨立網站,使用者體驗斷裂 | + +### 價值主張 + +**對開發者**:用瀏覽器就能操作 Kneron 裝置,不用被綁在實體機器旁。 + +**對 SI / FAE**:一個帳號管所有客戶現場的裝置,一個畫面看到全貌。 + +**對 Kneron 生態系**:降低 Kneron 的使用門檻,讓「試用 Kneron」不用裝一堆東西。 + +### 為什麼是現在做 + +1. **local-tool 已穩定**:UI 與業務邏輯成熟,前端可以直接搬 +2. **edge-ai-platform POC 驗證了核心技術**:WebSocket + yamux tunnel、叢集推論都跑得動 +3. **Kneron 生態系正在成長**:需要一個「雲端門面」承接新用戶 +4. **kneron_model_converter 團隊要整合**:visionA Cloud 是整合入口 + +--- + +## 1.4 OKR(Phase 0 雛形階段) + +### Objective(O):建立 visionA Cloud 的技術基座與產品框架 + +- **KR1**:Phase 0 雛形 2026 Q2 完成,包含以下可驗收產出 + - visionA-frontend 所有 P0 頁面可打開、可操作(接 mock 或真 API) + - visionA-backend 兩個 binary(api-server + remote-proxy)可起得來 + - 至少一個 local-tool 可透過 Pairing Token 連上 visionA Cloud,並跑通一次端到端推論 +- **KR2**:Phase 0 內部測試,至少 5 位 Kneron FAE 完成 Pairing + 推論測試 +- **KR3**:介面契約文件(interface-contracts.md)完成,與 converter 團隊對齊一次 API spec + +### Objective(O):為 Phase 1 MVP 打好基礎 + +- **KR1**:所有 TODO 項目(Auth / DB / Storage / Converter)有明確的「替換點」與介面定義 +- **KR2**:文件完整度 — PRD / Design Spec / TDD 三方交叉審閱通過 +- **KR3**:避免 local-tool 被破壞 — local-tool 的所有既有測試持續通過(0 regression) + +--- + +## 1.5 北極星指標與指標體系 + +### 北極星指標(長期) + +**每週活躍裝置數(Weekly Active Devices, WAD)** + +定義:過去 7 天內,至少成功完成一次推論的已 pairing 裝置數。 + +為什麼選這個: +- 對 B2B / 開發者工具,WAU(User)容易膨脹(人來註冊就算),但 WAD(Device)代表**真正在用** +- Kneron 裝置本身是硬體投資,裝置被使用 = 用戶正在從中獲得價值 +- 一個用戶多個裝置 / 多個用戶共用一個裝置都反映在這個指標 + +### 指標體系 + +``` +WAD(每週活躍裝置數) + ├── 驅動指標:新 Pairing 數、每週人均推論次數、裝置留存率 + │ ├── 輸入指標:註冊到 Pairing 的轉換率 + │ ├── 輸入指標:Pairing 後 24 小時內首次推論率 + │ ├── 輸入指標:每週上傳模型數 + │ └── 輸入指標:叢集建立數 + └── 護欄指標(不能惡化的): + ├── 推論端到端延遲 P95 < 500ms(比 local-tool 多 ~300ms tunnel overhead) + ├── Tunnel session uptime > 99% + ├── API 錯誤率 < 1% + └── local-tool regression bug 數 = 0 +``` + +### Phase 0 可追蹤的指標(簡化版) + +Phase 0 雛形只追蹤最小集: + +| 指標 | 目標值 | 追蹤方式 | +|------|--------|---------| +| Pairing 成功率 | > 90% | Server log | +| 端到端推論可跑 | Yes | 手動測試 | +| API Server 崩潰 | 0 次 / 週 | Log / Monitoring | +| local-tool 測試全過 | 100% | CI | + +其他指標等 Phase 1 接 DB 後才開始埋點追蹤。 + +--- + +## 連結 + +- 下一章:[產品定位](product-positioning.md) +- 或跳回:[PRD 索引](PRD.md) diff --git a/docs/autoflow/02-prd/success-metrics.md b/docs/autoflow/02-prd/success-metrics.md new file mode 100644 index 0000000..a86e8d2 --- /dev/null +++ b/docs/autoflow/02-prd/success-metrics.md @@ -0,0 +1,178 @@ +# 9. 成功指標 — visionA Cloud + +> 父文件:[PRD.md](PRD.md) + +--- + +## 9.1 北極星指標 + +### 長期(Phase 2+):每週活躍裝置數(WAD) + +**定義**:過去 7 天內至少完成一次成功推論的已配對裝置數量。 + +**為什麼選這個**: + +- B2B 開發者工具,WAU 容易膨脹(人來註冊就算),WAD(Device)代表真正有在用 Kneron 硬體 +- Kneron 晶片本身是硬體 BOM 投資,裝置被使用 = 客戶從中獲得價值 = 會繼續買更多 Kneron +- 一個 SI 管 10 台裝置,這指標真實反映他產生的價值 + +**計算公式**: + +``` +WAD = COUNT(DISTINCT device_id) + WHERE last_inference_at >= NOW() - 7 days + AND paired = true +``` + +--- + +## 9.2 Phase 0(雛形)可追蹤指標 + +Phase 0 不接 DB,所以只能追最小集,都從 log 抓: + +| 指標 | 目標 | 追蹤方式 | +|------|------|---------| +| Pairing 成功率 | > 90% | Log:成功 Pairing 數 / 嘗試 Pairing 數 | +| 內部 FAE 完成端到端推論的人數 | >= 5 | 手動記錄 | +| API Server 崩潰次數 | 0 / 週 | Log / Monitoring | +| Remote Proxy 崩潰次數 | 0 / 週 | Log / Monitoring | +| local-tool regression bug | 0 | local-tool CI | +| Phase 0 驗收條件達成率 | 100% | 參考各 feature 的驗收條件清單 | + +--- + +## 9.3 Phase 1 MVP 指標(接 DB 後開始埋點) + +### 獲客(Acquisition) + +| 指標 | MVP 3 個月目標 | 6 個月目標 | +|------|--------------|----------| +| 註冊 user 數(累計)| 50 | 200 | +| Pairing 裝置數(累計)| 30 | 150 | + +### 啟用(Activation) + +| 指標 | 定義 | MVP 目標 | +|------|------|---------| +| Pairing 轉換率 | 註冊後 24 小時內成功 Pairing / 註冊人數 | > 60% | +| 首次推論轉換率 | Pairing 後 24 小時內完成首次推論 / Pairing | > 70% | +| Activation Rate | 註冊後 7 天內完成首次推論 / 註冊人數 | > 40% | + +### 留存(Retention) + +| 指標 | MVP 目標 | +|------|---------| +| D7 留存率(user)| > 30% | +| D30 留存率(user)| > 20% | +| W1 留存率(device:一週內再使用)| > 60% | + +### 使用量(Engagement) + +| 指標 | MVP 目標 | +|------|---------| +| 每週人均推論次數 | > 50 次 | +| 每週活躍使用者(WAU) | > 50 | +| **每週活躍裝置(WAD,北極星)** | > 30 | + +--- + +## 9.4 護欄指標(不能惡化的) + +### Phase 0 + +| 指標 | 門檻(不可超過)| +|------|--------------| +| 推論端到端延遲 P95 | < 500ms | +| Tunnel 建立失敗率 | < 10% | +| API 5xx 錯誤率 | < 5% | +| local-tool regression | 0 | + +### Phase 1 + +| 指標 | 門檻 | +|------|------| +| 推論端到端延遲 P95 | < 400ms | +| Tunnel 建立失敗率 | < 3% | +| Tunnel session uptime | > 99% | +| API 5xx 錯誤率 | < 1% | +| 頁面 FCP | < 2 秒 | +| Pairing Token 洩漏事件 | 0 | +| 資料隔離 bug(A 看到 B 的資料)| 0 | + +--- + +## 9.5 Phase 2+ 長期指標 + +### 商業指標(如啟用 Billing) + +| 指標 | 12 個月目標 | +|------|-----------| +| MRR | $5K | +| 付費用戶數 | 50 | +| 付費轉換率(註冊 → 付費)| > 5% | +| 月流失率 | < 10% | +| NPS | > 40 | + +### 生態系指標 + +| 指標 | 目標 | +|------|------| +| 跨 Phase 1 的用戶 60%+ 每月至少 pairing 2 個裝置 | ✅ | +| 叢集功能被活躍使用(有 10+ 個 user 在用叢集)| ✅ | +| 轉檔整合完成(Phase 2)| ✅ | + +--- + +## 9.6 指標體系視覺化 + +``` +WAD(每週活躍裝置)← 北極星 + │ + ├── 驅動指標: + │ ├── 新 Pairing 數(每週) + │ ├── 人均裝置數(用戶規模化) + │ └── 人均推論次數(使用深度) + │ │ + │ ├── 輸入:Pairing 轉換率 + │ ├── 輸入:首次推論轉換率 + │ ├── 輸入:推論延遲(體驗品質) + │ └── 輸入:Tunnel 穩定性 + │ + └── 護欄: + ├── 技術護欄(延遲、錯誤率、uptime) + ├── 安全護欄(token 洩漏、資料隔離) + └── 產品護欄(local-tool 0 regression) +``` + +--- + +## 9.7 Phase 0 判定 Go / No-Go 的標準 + +Phase 0 完成後,判定是否進 Phase 1 的標準: + +### 必達(硬性條件) + +- [ ] 所有 P0 user stories 驗收條件達成 +- [ ] 至少 5 位內部 FAE 完成端到端推論 +- [ ] local-tool regression = 0 +- [ ] 三方交叉審閱(PM / Design / Architect)通過 + +### 軟性條件(沒達到也可推進,但要改善) + +- [ ] Pairing 成功率 > 90% +- [ ] 端到端延遲 P95 < 500ms +- [ ] 內部 FAE 滿意度 >= 7/10(簡短訪談) +- [ ] 無重大 crash / 安全性問題 + +### 退場標準(達成就不再推進) + +- [ ] Tunnel 技術在真實企業網路(NAT / Proxy / Firewall)穿透率 < 30% +- [ ] 內部測試發現核心體驗明顯不如 local-tool(延遲過大)且無法優化 + +--- + +## 連結 + +- 上一章:[介面契約](interface-contracts.md) +- 下一章:[開發範圍與階段](roadmap.md) +- 跳回:[PRD 索引](PRD.md) diff --git a/docs/autoflow/02-prd/user-research.md b/docs/autoflow/02-prd/user-research.md new file mode 100644 index 0000000..c00e925 --- /dev/null +++ b/docs/autoflow/02-prd/user-research.md @@ -0,0 +1,139 @@ +# 4. 用戶研究 — visionA Cloud + +> 父文件:[PRD.md](PRD.md) + +--- + +## 4.1 用戶 Persona + +### Persona 1:阿哲 — Kneron FAE(主要用戶) + +| 項目 | 內容 | +|------|------| +| 角色 | Kneron FAE(Field Application Engineer)| +| 年齡 / 背景 | 32 歲,資工本科,5 年嵌入式系統經驗 | +| 工作內容 | 跑客戶 demo、POC 支援、技術諮詢 | +| 目標 | 把 Kneron 晶片賣出去,讓客戶相信 Kneron 能做到 | +| 痛點 | 1. 出差要帶整台筆電 + 裝置,體力活
2. demo 環境每次不同,配置繁瑣
3. 客戶問「能不能遠端來看進度?」目前說不行 | +| 行為模式 | 每週 2-3 次出差,每次 demo 1-3 小時,demo 後客戶還會持續發問 | +| 技術素養 | 高(Linux、Python、C++)| +| 願意付費 | 公司付,不在意 | +| 一句話描述 | 「我希望能**遠端讓客戶看到 Kneron 跑推論**,這樣我就可以**不用飛來飛去**。」 | + +### Persona 2:Sarah — SI 系統整合商(主要用戶) + +| 項目 | 內容 | +|------|------| +| 角色 | SI 技術主管(系統整合商)| +| 年齡 / 背景 | 38 歲,電機本科,資深工程師轉管理 | +| 工作內容 | 把 Kneron 導入客戶專案(例如零售店頭人流分析、工廠瑕疵檢測)| +| 目標 | 讓客戶的 Kneron 部署順利運轉,減少現場支援 | +| 痛點 | 1. 一個專案 3-10 台 Kneron 佈在不同地點,沒統一畫面
2. 客戶回報「裝置怪怪的」只能派人去現場
3. 模型改版要逐台更新 | +| 行為模式 | 每週管 2-5 個專案,每個專案生命週期 3-12 個月 | +| 技術素養 | 高,自己帶一個 3-5 人的工程團隊 | +| 願意付費 | 願意(公司成本),但要有明顯 ROI(省出差費 / 人力)| +| 一句話描述 | 「我希望能**一個儀表板看到所有客戶現場的 Kneron 狀態**,這樣我就可以**少派工程師出差**。」 | + +### Persona 3:Mike — AI 應用開發者(次要用戶) + +| 項目 | 內容 | +|------|------| +| 角色 | 獨立開發者 / 新創 ML engineer | +| 年齡 / 背景 | 28 歲,資工碩士,2 年 ML 經驗 | +| 工作內容 | 開發 AI 應用原型,評估不同邊緣硬體(在 Jetson、Coral、Kneron 間比較)| +| 目標 | 找到性價比最好的硬體 + 模型組合 | +| 痛點 | 1. 想同時跑多個模型比效能,但 local-tool 一次只能一個
2. 轉檔要去 converter 網站,使用者體驗斷裂
3. 沒辦法和隊友共享推論結果 | +| 行為模式 | 每天開發 4-6 小時,2-4 週評估一次硬體 | +| 技術素養 | 高(PyTorch、ONNX、熟悉 ML pipeline)| +| 願意付費 | 個人用戶,willing-to-pay 低(< $50/mo),但公司預算可以 | +| 一句話描述 | 「我希望能**在一個介面跑 A/B test 不同模型**,這樣我就可以**快速選出最佳方案**。」 | + +--- + +## 4.2 用戶旅程地圖 + +### 主要旅程:阿哲第一次使用 visionA Cloud + +| 階段 | 用戶行為 | 想法 / 感受 | 痛點 | 機會點 | +|------|---------|-----------|------|--------| +| **認知** | 聽到內部公告「visionA Cloud 雛形可試用」| 「終於有雲端版了,以前一直說要做」| 不知道和 local-tool 有什麼差別 | 用清楚的對照表說明定位 | +| **註冊** | 打開 visionA Cloud 首頁 → 輸入公司 email | 「希望不要填太多欄位」| 表單太長會跳出 | Phase 0 雛形:只要 email + 密碼,其他 TODO | +| **Pairing** | 登入後看到空的裝置列表 → 點「配對新裝置」→ 複製 Pairing Token → 打開筆電上的 local-tool → 貼 token | 「步驟不能太多,5 步內要搞定」| 不確定 local-tool 該從哪裡貼 token | Phase 1:local-tool 要內建「Pairing」UI;Phase 0:TODO,手動編輯 config | +| **首次推論** | 配對成功 → 裝置列表出現 → 選裝置 → 選模型 → 選 Camera 來源 → 開始推論 | 「畫面跟 local-tool 一樣,直覺」| 遠端有延遲,會不會卡 | 在 UI 明確顯示連線狀態和延遲 | +| **持續使用** | 每週 demo 前開 cloud 確認裝置在線 | 「和本機一樣順」| Tunnel 斷線重連體驗 | 自動重連 + 狀態透明 | +| **推薦** | 和其他 FAE 分享 | 「你也試試,不用帶筆電了」| — | 內建「邀請隊友」功能(Phase 2)| + +### 次要旅程:Sarah 導入 visionA Cloud 管理多客戶現場 + +| 階段 | 用戶行為 | 痛點 | 機會點 | +|------|---------|------|--------| +| 認知 | 從 Innovedus 業務聽到產品 | 擔心可靠性(企業專案不能當機)| 強調雙模式 — cloud 斷線時還有 local-tool 可用 | +| 評估 | 在內部測試環境試 Pairing | 企業網路 proxy 可能擋 WebSocket | TDD 要規劃 proxy / TLS 穿透測試 | +| 導入 | 逐個客戶現場佈署 local agent | 客戶 IT 可能要審 | 提供安全白皮書(Phase 1)| +| 日常 | 每天早上開儀表板巡房 | 裝置離線沒通知 | Phase 1:加 alerting / email 通知 | +| 升級 | 介紹給下一個客戶 | — | 打造 SI-friendly pricing(Phase 2)| + +--- + +## 4.3 關鍵洞察 + +### 洞察 1:使用者不想學新 UI + +兩個 Persona 都是已在用 local-tool 的人。visionA Cloud 的 UI **必須和 local-tool 幾乎一樣**,只加上必要的雲端元素(裝置列表含「遠端 / 本機」狀態、登入頁、Pairing 頁)。 + +**Design Agent 請留意**:設計規格 90% 參考 local-tool 現有頁面,新增的只有 `/login`、`/register`、`/account`、`/pair`、`/clusters`(從 POC 搬),其他頁面**保持一致**。 + +### 洞察 2:連線狀態必須極度透明 + +local-tool 是 localhost,連線成功率 99.99%。visionA Cloud 過 tunnel,連線會斷會重連。使用者對「遠端不可靠」有心理預期,但**不透明的斷線最讓人抓狂**。 + +**設計要求**: + +- 裝置狀態要明確分「在線 / 離線 / tunnel 斷線重連中」 +- 推論中如果 tunnel 斷,要立即提示並自動重連 +- 延遲要顯示(例如 header 上顯示「tunnel RTT: 120ms」) + +### 洞察 3:Pairing 是最大摩擦點 + +從「註冊」到「首次推論」的轉換漏斗中,**Pairing 那一步最容易掉用戶**。使用者要跨兩個介面(瀏覽器 + 筆電 local-tool),要複製貼上 token。 + +**Phase 0 的妥協**:允許 token 手動編輯 local-tool config 檔(給技術高素養用戶)。 + +**Phase 1 的理想**:local-tool 內建「配對到 visionA Cloud」按鈕,瀏覽器 callback 自動帶 token 過去。 + +### 洞察 4:SI 最在意的是「少派人出差」 + +對阿哲(FAE),核心價值是「自己少累一點」。對 Sarah(SI),核心價值是「團隊少派工程師出差」,這是**可量化的成本節省**。 + +**Phase 1 行銷素材**可以用這個角度:「每月省下 X 次出差 = 省 Y 元 + Z 天工程師時間」。 + +### 洞察 5:Mike 是次要但不能忽視的用戶 + +Mike(獨立開發者)不是主力,但他們是**未來 SI 和 FAE 的潛在來源**(獨立開發者可能被 Kneron 挖角或進 SI)。而且 Mike 的使用量高(每天開發),對 UX 細節敏感。 + +Phase 0 不會針對 Mike 做客製,但 Phase 2 要考慮: +- 個人訂閱方案(比 SI 方案便宜) +- 模型 A/B 比較功能(workspace 升級) +- 開放 API(讓 Mike 寫自動化腳本) + +--- + +## 4.4 未回答的問題(需要用戶訪談) + +Phase 0 後期 / Phase 1 前,建議做 5-10 次用戶訪談,回答以下問題: + +- [ ] SI 客戶的 IT 政策有多嚴?(NAT / Proxy / TLS 要求) +- [ ] FAE 做 demo 時 tunnel 斷一次能忍受嗎?還是直接失敗? +- [ ] 使用者期待的 pairing token 生命週期是多久?(1 小時?7 天?永久?) +- [ ] 叢集推論是否真的對 SI 有價值?或只是 nice-to-have? +- [ ] 使用者是否願意把模型(可能是商業機密)上傳到 visionA Cloud 的 S3? + +這些問題的答案會影響 Phase 1 的功能優先級與 Auth / Security 設計。 + +--- + +## 連結 + +- 上一章:[市場分析](market-analysis.md) +- 下一章:[User Stories](user-stories.md) +- 跳回:[PRD 索引](PRD.md) diff --git a/docs/autoflow/02-prd/user-stories.md b/docs/autoflow/02-prd/user-stories.md new file mode 100644 index 0000000..f09dcf1 --- /dev/null +++ b/docs/autoflow/02-prd/user-stories.md @@ -0,0 +1,120 @@ +# 5. User Stories(RICE 排序)— visionA Cloud + +> 父文件:[PRD.md](PRD.md) + +--- + +## 5.1 RICE 評分說明 + +| 維度 | 定義 | 範圍 | +|------|------|------| +| Reach | 每季觸及的用戶數(粗估,Phase 0 用戶基數小,數字為相對值)| 1-100 | +| Impact | 對北極星指標(WAD)的影響程度 | 0.25 / 0.5 / 1 / 2 / 3 | +| Confidence | 估算信心度 | 50% / 80% / 100% | +| Effort | 預估開發人月(Architect 會再核對)| 0.1 - 10 | +| **RICE** | Reach × Impact × Confidence / Effort | — | + +> 註:Phase 0 雛形的 Reach / Effort 是**相對估值**,不是絕對數字。主要目的是排優先級,不是財務預估。 + +--- + +## 5.2 所有 User Stories(RICE 排序) + +### 🔴 P0 — Phase 0 雛形必做 + +| # | Story | Persona | Reach | Impact | Conf. | Effort | RICE | 備註 | +|---|-------|---------|-------|--------|-------|--------|------|------| +| US-01 | 作為開發者,我要**在瀏覽器打開 visionA Cloud 並看到登入頁** | 全部 | 100 | 3 | 100% | 0.5 | 600 | Phase 0 雛形:UI only,auth stub | +| US-02 | 作為開發者,我要**註冊帳號(雛形只要 email + 密碼)** | 全部 | 100 | 3 | 100% | 0.3 | 1000 | Phase 0:in-memory,不存 DB | +| US-03 | 作為開發者,我要**登入後看到我名下的裝置列表(空的也要)** | 全部 | 100 | 3 | 100% | 1 | 300 | 核心頁面 | +| US-04 | 作為開發者,我要**從雲端頁面取得一組 Pairing Token** | 全部 | 100 | 3 | 100% | 1 | 300 | Pairing 核心 | +| US-05 | 作為開發者,我要**讓我筆電上的 local agent 用這個 token 連上 visionA Cloud** | 全部 | 100 | 3 | 100% | 3 | 100 | 後端重點:remote-proxy + tunnel | +| US-06 | 作為開發者,我要**Pairing 成功後,裝置自動出現在雲端頁面** | 全部 | 100 | 3 | 100% | 0.5 | 600 | WebSocket 即時更新 | +| US-07 | 作為開發者,我要**在雲端頁面看到裝置詳細(型號、韌體、健康度)** | 全部 | 100 | 2 | 100% | 0.5 | 400 | 搬自 local-tool | +| US-08 | 作為開發者,我要**瀏覽 7 個預設模型** | 全部 | 100 | 2 | 100% | 0.5 | 400 | 搬自 local-tool | +| US-09 | 作為開發者,我要**上傳我自己的 .nef 模型(雛形:走 local filesystem 實作的 S3 介面)** | 全部 | 80 | 2 | 80% | 1 | 128 | ObjectStorage 介面重點 | +| US-10 | 作為 FAE,我要**進入工作區,選遠端裝置 + 模型 + Camera 來源,開始推論** | 阿哲 | 80 | 3 | 80% | 3 | 64 | 核心體驗,tunnel 要穩 | +| US-11 | 作為 FAE,我要**看到遠端 Camera 的即時 MJPEG 串流 + 推論結果 overlay** | 阿哲 | 80 | 3 | 80% | 2 | 96 | tunnel 要能 stream binary | +| US-12 | 作為開發者,我要**在 Settings 切換語言(繁中/English)和主題(Dark Mode)** | 全部 | 100 | 0.5 | 100% | 0.2 | 250 | 沿用 local-tool i18n | +| US-13 | 作為開發者,我要**登出(雛形:清 token)** | 全部 | 100 | 1 | 100% | 0.1 | 1000 | Phase 0 簡易版 | + +### 🟡 P1 — Phase 1 MVP + +| # | Story | Persona | Reach | Impact | Conf. | Effort | RICE | 備註 | +|---|-------|---------|-------|--------|-------|--------|------|------| +| US-14 | 作為開發者,我要**看到一個儀表板,顯示所有裝置狀態 + 最近活動** | 全部 | 80 | 2 | 80% | 2 | 64 | 搬自 local-tool | +| US-15 | 作為 FAE,我要**用上傳圖片/影片做推論,不只 Camera** | 阿哲 | 60 | 2 | 80% | 2 | 48 | 搬自 local-tool | +| US-16 | 作為 SI,我要**把多台裝置組成叢集,按加權 RR 做推論** | Sarah | 40 | 3 | 70% | 3 | 28 | 從 POC 搬 + 產品化 | +| US-17 | 作為 SI,我要**看到叢集中每台裝置的負載分佈** | Sarah | 30 | 2 | 70% | 2 | 21 | POC 已有雛形 | +| US-18 | 作為開發者,我要**Pairing Token 有 expiry,過期自動失效** | 全部 | 80 | 2 | 80% | 1.5 | 85 | 安全性 | +| US-19 | 作為開發者,我要**能撤銷某個裝置的 Pairing** | 全部 | 60 | 1 | 80% | 0.5 | 96 | 安全性 | +| US-20 | 作為開發者,我要**看到每次推論的 log / history** | 全部 | 50 | 1 | 70% | 1.5 | 23 | 需要 DB | +| US-21 | 作為 SI,我要**邀請隊友加入我的 workspace,共享裝置** | Sarah | 40 | 2 | 60% | 3 | 16 | 多租戶 | +| US-22 | 作為開發者,我要**接真的 Auth 系統(JWT / OAuth)** | 全部 | 80 | 2 | 80% | 4 | 32 | 替換 Phase 0 stub | +| US-23 | 作為開發者,我要**上傳到真的 S3 / MinIO,不是 local fs** | 全部 | 80 | 1 | 80% | 2 | 32 | 替換 Phase 0 stub | + +### 🟢 P2 — Phase 2+(未來) + +| # | Story | Persona | Reach | Impact | Conf. | Effort | RICE | 備註 | +|---|-------|---------|-------|--------|-------|--------|------|------| +| US-24 | 作為開發者,我要**直接在 visionA Cloud 轉檔(非 Kneron 格式 → .nef)** | Mike | 70 | 2 | 60% | 5 | 17 | 等 converter 團隊完成 API | +| US-25 | 作為 Mike,我要**同時跑多個模型比較效能** | Mike | 40 | 2 | 60% | 3 | 16 | Workspace 升級 | +| US-26 | 作為開發者,我要**用 API key 寫自動化腳本(公開 API)** | Mike | 30 | 1 | 50% | 3 | 5 | 開放平台 | +| US-27 | 作為 SI,我要**看使用量計費(Billing)** | Sarah | 40 | 2 | 50% | 5 | 8 | 商業化 | +| US-28 | 作為開發者,我要**接收 email 通知(裝置離線、推論完成等)** | 全部 | 60 | 1 | 70% | 2 | 21 | Notification 系統 | +| US-29 | 作為 SI,我要**每個客戶現場一個獨立 tenant** | Sarah | 30 | 2 | 60% | 5 | 7 | 多租戶加強 | +| US-30 | 作為開發者,我要**用手機 app 看裝置狀態(read-only)** | 全部 | 40 | 1 | 40% | 5 | 3 | Mobile | + +### 🔵 雛形介面 TODO(Phase 0 只做介面不接實作) + +| # | Story | 雛形做什麼 | 未來要做什麼 | +|---|-------|-----------|------------| +| US-TODO-01 | 會員註冊 | 前端頁面 + 後端 stub handler | 接真 Auth(JWT / OAuth)| +| US-TODO-02 | 忘記密碼 | 前端頁面只 | 接 email 服務 | +| US-TODO-03 | 個人設定頁 | 前端骨架只 | 密碼變更、頭像上傳等 | +| US-TODO-04 | Billing 頁 | 不做 | Stripe 整合 | +| US-TODO-05 | 轉檔整合 | **定義 API 契約**給 converter 團隊 | 真的接 converter API | + +--- + +## 5.3 MVP 功能範圍(Phase 0 雛形) + +**納入 Phase 0**:所有 P0 stories(US-01 ~ US-13) + +**明確排除**: + +- 真實 Auth(用 stub) +- 真實 DB(用 in-memory) +- 真實 S3(用 local fs 實作 ObjectStorage 介面) +- 叢集功能(Phase 1) +- 儀表板(Phase 1) +- 圖片/影片推論(Phase 1,Phase 0 只做 Camera) +- 忘記密碼 / 個人設定(只有介面) +- Billing(不做) +- 轉檔整合(只定契約) + +--- + +## 5.4 驗收條件總覽(Phase 0) + +Phase 0 完成的定義:以下全部為 Yes。 + +- [ ] visionA-frontend 能部署成靜態網站並在瀏覽器打開 +- [ ] visionA-backend 的 `cmd/api-server` 可單獨跑,health check OK +- [ ] visionA-backend 的 `cmd/remote-proxy` 可單獨跑,health check OK +- [ ] 使用者能在瀏覽器完成註冊 + 登入(stub) +- [ ] 使用者能從雲端頁面取得一組 Pairing Token +- [ ] 至少一台裝有 local-tool 的筆電,能用 Pairing Token 連上 remote-proxy(可能需臨時修改 local-tool config) +- [ ] Pairing 成功後,裝置即時出現在雲端頁面 +- [ ] 使用者能從雲端選裝置 + 模型 + Camera,跑一次端到端推論 +- [ ] 模型上傳功能可用(雖然實際存 local fs) +- [ ] 所有 P0 頁面切換順暢,無明顯 bug +- [ ] local-tool 所有既有測試持續通過(0 regression) + +--- + +## 連結 + +- 上一章:[用戶研究](user-research.md) +- 下一章:[功能規格](features/) +- 跳回:[PRD 索引](PRD.md) diff --git a/docs/autoflow/03-design/components.md b/docs/autoflow/03-design/components.md new file mode 100644 index 0000000..715a61c --- /dev/null +++ b/docs/autoflow/03-design/components.md @@ -0,0 +1,452 @@ +# 元件庫清單與規格 — visionA Cloud + +> **本元件庫 100% 沿用 `local-tool/frontend/src/components/`**,Frontend Agent 實作時直接搬(或整併到 monorepo shared package)。本文件整理既有元件清單與用途,並定義雲端版**新增的 4 個元件**。 + +--- + +## 1. 元件分類總覽 + +| 類別 | 數量 | 來源 | 狀態 | +|------|------|------|------| +| 基礎 UI(Shadcn 風)| 22 | `src/components/ui/` | 沿用 | +| Layout | 4 | `src/components/layout/` | 沿用(Sidebar 需擴充) | +| Dashboard | 3 | `src/components/dashboard/` | 沿用 | +| Devices | 8 | `src/components/devices/` | 沿用(需擴充遠端狀態) | +| Models | 6 | `src/components/models/` | 沿用 | +| Inference | 5 | `src/components/inference/` | 沿用 | +| Camera | 多個 | `src/components/camera/` | 沿用 | +| 特殊 | 7 | 根目錄 | 沿用(部分可能棄用) | +| **雲端版新增** | 4 | `src/components/cloud/`(建議)| 本次新增 | + +--- + +## 2. 基礎 UI 元件(沿用 Shadcn 風) + +來源:`local-tool/frontend/src/components/ui/` + +| 元件 | 檔案 | 用途 | +|------|------|------| +| `Button` | `button.tsx` | 6 variants(default / destructive / outline / secondary / ghost / link),8 sizes(xs / sm / default / lg / icon*3)| +| `Card` | `card.tsx` | 資訊容器(含 Header / Title / Description / Content / Footer / Action 子元件)| +| `Dialog` | `dialog.tsx` | Modal 對話框(Radix Dialog 封裝)| +| `AlertDialog` | `alert-dialog.tsx` | 確認危險操作的專用 Dialog(無法 ESC 關閉)| +| `Tabs` | `tabs.tsx` | 分頁切換(Radix Tabs 封裝)| +| `Input` | `input.tsx` | 單行文字輸入框,h-9 | +| `Select` | `select.tsx` | 下拉選單(Radix Select 封裝)| +| `Badge` | `badge.tsx` | 標籤徽章,6 variants | +| `Slider` | `slider.tsx` | 滑桿(Radix Slider 封裝,用於 confidence threshold)| +| `Progress` | `progress.tsx` | 進度條(Radix Progress) | +| `Checkbox` | `checkbox.tsx` | 多選框(Radix Checkbox) | +| `Label` | `label.tsx` | 表單標籤(Radix Label) | +| `Separator` | `separator.tsx` | 分隔線 | +| `ScrollArea` | `scroll-area.tsx` | 自訂滾動條(Radix ScrollArea) | +| `Sonner` | `sonner.tsx` | Toast 通知(Sonner 封裝) | +| `EmptyState` | `empty-state.tsx` | 空狀態頁面(Icon + Title + Description + Action) | + +**狀態覆蓋(所有互動元件都需具備):** + +- Default / Hover / Active / Focus-visible / Disabled / Loading +- Focus-visible 使用 shadcn 預設 `ring-[3px]`(符合 WCAG AA) +- Disabled 統一 `disabled:pointer-events-none disabled:opacity-50` + +--- + +## 3. Layout 元件(沿用 + 擴充) + +來源:`local-tool/frontend/src/components/layout/` + +| 元件 | 檔案 | 用途 | 雲端版變更 | +|------|------|------|-----------| +| `Header` | `header.tsx` | 頂部列:平台標題 + HelpButton + ConnectionStatus | 平台標題改「visionA Cloud」;ConnectionStatus 語義變更(見下) | +| `Sidebar` | `sidebar.tsx` | 左側導航:Dashboard / Models / Devices / Workspace / Settings | **新增**:底部 UserMenu;**新增**:`/clusters` 導航項 | +| `ConnectionStatus` | `connection-status.tsx` | 伺服器連線狀態徽章(綠 / 紅點)| 語義變更:local-tool 指「本機 server」,Cloud 指「雲端 API Server」| +| `HelpButton` | `help-button.tsx` | 開啟引導(driver.js tour)| Phase 0 保留,Phase 1 重寫內容為雲端版 | + +### 3.1 Sidebar 變更規格(雲端版) + +``` +┌────────────────────────┐ +│ [Logo] visionA Cloud │ ← h-14,border-b +├────────────────────────┤ +│ ▸ Dashboard │ +│ ▸ Models │ +│ ▸ Devices │ +│ ▸ Clusters (new) │ ← 新增 +│ ▸ Workspace │ +│ ▸ Settings │ +│ │ +│ (flex-1) │ +│ │ +├────────────────────────┤ +│ [avatar] username ▾ │ ← 新增 UserMenu(點開展示 Account / Sign out) +├────────────────────────┤ +│ v0.1.0 │ +└────────────────────────┘ +``` + +**導航項目(繁中 / English):** + +| i18n key | 繁中 | English | icon | +|---------|------|---------|------| +| `nav.dashboard` | 儀表板 | Dashboard | `LayoutDashboard` | +| `nav.modelLibrary` | 模型庫 | Models | `Boxes` | +| `nav.devices` | 裝置 | Devices | `Cable` | +| `nav.clusters` | 叢集 | Clusters | `Network` | +| `nav.workspace` | 工作區 | Workspace | `Play` | +| `nav.settings` | 設定 | Settings | `Settings` | + +> **改動說明**:local-tool 目前用 `{ icon: 'H' }` 這種單字母佔位(見 `sidebar.tsx:13-18`),設計上不理想。Design Review 列為 **Minor**。**雲端版建議改用 Lucide Icon**,與產品視覺語言一致。 + +### 3.2 ConnectionStatus 語義變更 + +原 local-tool 的 `ConnectionStatus` 檢查 `fetch('/system/health')`,顯示「伺服器已連線」。 + +**雲端版需要區分兩層連線:** + +| 層級 | 顯示位置 | 含義 | 綠 / 紅 / 黃 | +|------|---------|------|-------------| +| 雲端 API | Header 右側(現有位置)| 前端能不能打雲端 API Server | 綠:OK / 紅:API 不可達 | +| 遠端 Agent | 個別裝置卡片 / 詳情頁 | 該使用者的 local agent 有沒有連上雲端 | 綠:在線 / 灰:離線 / 黃:連線中 | + +**Cloud 版 `ConnectionStatus` 行為:** + +- 綠點 + 「已連線」→ 可以正常使用 +- 紅點 + 「連線失敗,重試中」→ 整個服務不可用,顯示全域 `NetworkErrorBanner` +- 不再顯示 local-tool 的「伺服器未連線」— 雲端 API 應該永遠可達,失敗應觸發明顯警告 + +--- + +## 4. Dashboard 元件(沿用) + +來源:`local-tool/frontend/src/components/dashboard/` + +| 元件 | 用途 | 雲端版變更 | +|------|------|-----------| +| `StatCard` | 統計卡片(標題 + 數字 + icon) | 無 | +| `ActivityTimeline` | 近期活動時間軸 | 無(但資料來源從 local store 改為雲端事件)| +| `ConnectedDevicesList` | 已連線裝置列表(Dashboard 上的)| **小改動**:裝置項目要顯示「最後心跳 X 秒前」| + +--- + +## 5. Devices 元件(沿用 + 擴充) + +來源:`local-tool/frontend/src/components/devices/` + +| 元件 | 用途 | 雲端版變更 | +|------|------|-----------| +| `DeviceList` | 裝置卡片網格 | 無 | +| `DeviceCard` | 單一裝置卡片(名稱、狀態、類型、韌體、已燒錄模型、操作按鈕)| **擴充**:右上角新增 `RemoteDeviceBadge`(見第 10 節)| +| `DeviceStatus` / `DeviceStatusBadge` | 狀態點 + 文字徽章 | **新增狀態**:`offline`(遠端掉線)、`reconnecting`(重連中) | +| `DeviceHealthCard` | 健康狀態(運行時間、韌體版本、最後活動)| **擴充**:新增「Pairing Token」、「最後心跳」欄位 | +| `DeviceConnectionLog` | 連線歷史紀錄 | 無(但雲端版 log 應包含 tunnel 事件) | +| `DeviceSettingsCard` | 裝置別名、備註 | 無 | +| `FlashDialog` | 燒錄模型對話框 | 無(行為透過 tunnel 傳到 local agent) | +| `FlashProgress` | 燒錄進度 | 無(WebSocket 透過雲端中繼) | + +### 5.1 DeviceStatusBadge 擴充 + +**新增狀態(雲端版):** + +| 狀態 | 顏色 | i18n key | 文字(繁中)| +|------|------|---------|-----------| +| `offline` | `bg-gray-400` | `devices.status.offline` | 離線 | +| `reconnecting` | `bg-yellow-400 animate-pulse` | `devices.status.reconnecting` | 重新連線中 | + +> 既有的 `detected` / `disconnected` 狀態在雲端版可能用不到(使用者不會直接「scan」遠端裝置)。雛形不刪除,但 UI 上會以 Pairing 流程取代 scan。 + +--- + +## 6. Models 元件(沿用,無變更) + +來源:`local-tool/frontend/src/components/models/` + +- `ModelCard` / `ModelGrid` / `ModelFilters` / `ModelDetail` / `ModelUploadDialog` / `ModelComparisonDialog` + +雲端版行為差異:上傳 `.nef` 檔案走雲端儲存(S3-compat),但 UI 不變。 + +--- + +## 7. Inference 元件(沿用,無變更) + +來源:`local-tool/frontend/src/components/inference/` + +- `InferencePanel` / `PerformanceMetrics` / `ClassificationResult` / `VideoProgress` / `ConfidenceSlider` + +雲端版行為差異:推論結果透過 WebSocket 從 local agent → 雲端 → 瀏覽器。UI 不變。 + +--- + +## 8. Camera 元件(沿用,無變更) + +來源:`local-tool/frontend/src/components/camera/` + +- `CameraInferenceView` / `CameraControls` / `SourceSelector` / `BatchImageThumbnails` 等 + +**雲端版注意:** +- Camera stream 仍然走 local agent(硬體存取) +- MJPEG stream 透過 tunnel 從 local agent → 雲端 → 瀏覽器顯示 +- Image / Video 上傳:Phase 0 仍走 local agent 處理;Phase 1+ 考慮直上雲端 + +--- + +## 9. 特殊元件(部分調整) + +來源:`local-tool/frontend/src/components/` + +| 元件 | 用途 | 雲端版變更 | +|------|------|-----------| +| `OnboardingDialog` | 首次訪問引導 Dialog | **Phase 0 暫時保留**;Phase 1 重寫(現版本假設「插 USB Dongle」,雲端不適用)| +| `GuidedTour` | driver.js 步驟引導 | **Phase 0 暫時保留**;Phase 1 重寫為 Cloud Tour | +| `ServerStatusDashboard` | 後端 Go runtime 狀態 | **Phase 0 可移除 or 隱藏**(使用者看不到雲端 runtime);留供 Phase 1 重新定位 | +| `ServerLogViewer` | 伺服器即時日誌 | 同上 | +| `LangSync` | 語言狀態同步 | 無變更 | +| `ThemeSync` | 主題跟隨系統 | 無變更 | +| `StoreHydration` | Zustand store hydration | 無變更 | + +--- + +## 10. 雲端版新增元件 + +**建議路徑**:`visionA-frontend/src/components/cloud/` + +### 10.1 `UserMenu` + +**用途**:Sidebar 底部的使用者選單,雲端版必備。 + +**視覺**: +``` +┌──────────────────────────┐ +│ [avatar] jim@example.com ▾ │ ← trigger +└──────────────────────────┘ + ↓ click +┌──────────────────────────┐ +│ 帳號設定 (Account) │ +│ 語言切換 (Language) │ +│ ─────────────── │ +│ 登出 (Sign out) │ +└──────────────────────────┘ +``` + +**規格:** +- 基底元件:Radix DropdownMenu(shadcn 封裝) +- Trigger:Button variant=ghost,size=default +- Avatar:40px 圓形(`rounded-full`),顯示使用者 Email 首字母 or GravatarURL +- 下拉位置:`side="top" align="start"`(展開往上) +- 項目圖示:Lucide `UserCog` / `Globe` / `LogOut` + +**i18n key:** +- `account.menu.accountSettings` +- `account.menu.language` +- `account.menu.signOut` + +**雛形簡化**:Phase 0 `signOut` 直接跳回 `/login`(不接 API);`account.menu.accountSettings` 跳 `/account` stub 頁。 + +**無障礙:** +- Trigger 可 Tab 聚焦、Enter / Space 展開 +- 下拉內容鍵盤可 Arrow 導航 +- `aria-label` = 使用者 Email + +--- + +### 10.2 `PairingTokenCard` + +**用途**:`/devices/pair` 頁面的主要卡片,展示 Pairing Token 並提供複製功能。 + +**視覺**: +``` +┌────────────────────────────────────────────────┐ +│ 🔗 Pairing Token │ +│ │ +│ 使用這組 token 讓你的 local agent 連上雲端 │ +│ ─────────────────────────────────────── │ +│ │ +│ ┌────────────────────────────────────────┐ │ +│ │ vAc_a1b2c3d4e5f6a7b8 │ │ +│ │ c9d0e1f2a3b4c5d6e7f8 │ │ +│ │ (vAc_ + 32 hex,視覺切兩行; │ │ +│ │ 複製為完整 36 字元無空格) │ │ +│ └────────────────────────────────────────┘ │ +│ │ +│ [📋 複製] [🔄 重新產生] │ +│ │ +│ ⏱ 剩餘 14:52 ──────────────── │ +│ (進度條 bg-primary → amber(≤10:00) │ +│ → red(≤3:00);過期轉灰 disabled) │ +│ 📅 產生時間:14:30 │ +└────────────────────────────────────────────────┘ +``` + +**規格:** +- 基底:`Card` + CardHeader + CardContent + CardFooter +- Token 格式:`vAc_` + 32 字元 hex(總長 36),TTL **15 分鐘**,一次性使用 +- Token 顯示區:`bg-muted font-mono text-xl tracking-wider p-4 rounded-md select-all` + - 視覺切兩行(第 1 行 `vAc_` + 16 hex,第 2 行 16 hex);Mobile 降為 `text-lg` + - 複製到剪貼簿永遠是**完整 36 字元無空格無換行** + - 過期狀態:`text-muted-foreground line-through` +- 複製按鈕:`variant=default` + Lucide `Copy` icon;按下後暫態改為「已複製 ✓」2 秒,伴隨 toast;過期時 disabled +- 重新產生:`variant=outline` + Lucide `RefreshCw`;點擊後 AlertDialog 確認「重新產生會讓舊 token 失效,新 token 有效期 15 分鐘」 +- 倒數計時器(15 分鐘 TTL 必備): + - 格式 `mm:ss`,1 秒更新一次 + - 進度條 `h-1.5 w-full`,初始 `bg-primary`,≤ 10:00 → `bg-amber-500`,≤ 3:00 → `bg-red-500` 且卡片加 `ring-1 ring-red-300` + - ≤ 0:30 發一次 Toast「Token 即將過期」 + - 0:00 自動切過期狀態;主 CTA 變「重新產生 token」 + +**互動:** +- 按複製:`navigator.clipboard.writeText(token)` + `showSuccess(t('pairing.copied'))` +- 按重新產生:開 AlertDialog,確認後呼叫 API → 更新 token + 重置計時 + +**i18n key:** +- `pairing.tokenTitle` +- `pairing.tokenDescription` +- `pairing.copy` / `pairing.copied` / `pairing.regenerate` +- `pairing.expiresIn` / `pairing.generatedAt` +- `pairing.regenerateConfirm` / `pairing.regenerateWarning` + +**無障礙:** +- Token 區塊:`aria-label="Pairing token"`,`role="text"` +- 複製按鈕:Enter / Space 可觸發 +- 重新產生:AlertDialog 的焦點陷阱(shadcn 內建) + +--- + +### 10.3 `RemoteDeviceBadge` + +**用途**:在雲端場景下顯示單一遠端裝置的連線狀態,取代 / 擴充既有 `DeviceStatusBadge`。 + +**視覺變體:** + +``` +🟢 在線 ← bg-green-500 + 文字 +🟡 重新連線中 (pulse) ← bg-yellow-400 animate-pulse +⚪ 離線・最後心跳 2 分鐘前 ← bg-gray-400 + 文字 + 次要文字(timestamp) +🔴 錯誤・無法建立 tunnel ← bg-red-500 + 文字 + tooltip 顯示錯誤詳情 +``` + +**規格:** +- 基底:`div.flex.items-center.gap-2` +- 狀態點:`h-2.5 w-2.5 rounded-full`(沿用 `DeviceStatusBadge` 樣式) +- 狀態文字:`text-sm` +- 次要資訊(最後心跳):`text-xs text-muted-foreground` +- `pulse` 狀態:`animate-pulse` class + +**Props:** + +```tsx +interface RemoteDeviceBadgeProps { + status: 'online' | 'offline' | 'reconnecting' | 'error'; + lastSeenAt?: string; // ISO 8601 timestamp + errorMessage?: string; // tooltip 顯示 + size?: 'sm' | 'md'; // 預設 md +} +``` + +**使用情境:** +- `/devices` 裝置卡片右上角(取代 `DeviceStatusBadge`) +- `/devices/[id]` 裝置詳情頁的大型狀態顯示(size=md) +- Dashboard `ConnectedDevicesList` 的每個項目右側 +- `/workspace/[deviceId]` 頂部(裝置掉線時要能即時看到) + +**i18n key(新增):** +- `remote.status.online` → 在線 +- `remote.status.offline` → 離線 +- `remote.status.reconnecting` → 重新連線中 +- `remote.status.error` → 連線錯誤 +- `remote.lastSeen` → 最後心跳 {time} +- `remote.lastSeenNever` → 從未連線 + +**無障礙:** +- 角色:`role="status"` + `aria-live="polite"`(狀態改變時通知 SR) +- 不只靠顏色:圓點 + 文字雙保險 +- 錯誤狀態提供 `aria-describedby` 指向 tooltip 的完整訊息 + +**時間格式化(建議用 `date-fns` 或輕量 util):** +- < 60 秒 → 「剛剛」 +- < 60 分 → 「X 分鐘前」 +- < 24 時 → 「X 小時前」 +- ≥ 24 時 → 絕對時間「04/20 14:30」 + +--- + +### 10.4 `NetworkErrorBanner` + +**用途**:全域警告橫幅,當雲端 API 不可達時顯示於 Header 下方。 + +**視覺**: +``` +┌────────────────────────────────────────────────────────┐ +│ ⚠ 連線中斷 — 無法連上雲端服務。正在重試... [立即重試] │ +└────────────────────────────────────────────────────────┘ +``` + +**規格:** +- 背景:`bg-amber-50 dark:bg-amber-950/30` +- 邊框:`border-b border-amber-300 dark:border-amber-700` +- 文字:`text-amber-800 dark:text-amber-200 text-sm` +- 圖示:Lucide `AlertTriangle`,`text-amber-600` +- 位置:Header 下方 sticky(`sticky top-14 z-40`) +- 右側按鈕:`Button variant=outline size=sm` + +**狀態變化:** +- API 正常 → 不顯示 +- API 失敗 > 3 次連續 → 顯示 banner +- API 恢復 → banner 切換為「已恢復連線」短暫顯示 3 秒後消失 + +**i18n key:** +- `network.disconnected.title` → 連線中斷 +- `network.disconnected.description` → 無法連上雲端服務。正在重試... +- `network.disconnected.retryButton` → 立即重試 +- `network.restored` → 已恢復連線 ✓ + +**無障礙:** +- `role="alert"` + `aria-live="assertive"`(重要警告) +- 重試按鈕:`aria-label="重試連線"` + +--- + +## 11. 元件狀態覆蓋檢查表(給 Frontend QA 與 Design QA) + +每個互動元件都應有以下狀態: + +| 狀態 | 所有按鈕 / Input | 卡片列表 | 頁面 | 資料載入 | +|------|-----------------|---------|------|---------| +| Default | ✅ | ✅ | ✅ | — | +| Hover | ✅ | ✅(整個卡片)| — | — | +| Active / Pressed | ✅ | — | — | — | +| Focus-visible | ✅ | ✅ | — | — | +| Disabled | ✅ | — | — | — | +| Loading | ✅(Loader2 icon)| ✅(skeleton)| ✅(skeleton)| ✅(skeleton)| +| Empty | — | ✅(EmptyState)| ✅(EmptyState)| — | +| Error | — | ✅(error banner)| ✅(error page)| ✅(retry)| +| Success | ✅(toast)| — | — | — | + +--- + +## 12. UX Writing 規範(簡版,完整版見 Flow 子檔) + +**產品語調:** +- 對象是開發者,可用技術術語(如「Pairing Token」、「tunnel」、「session」)但不濫用 +- 錯誤訊息要**說清楚發生什麼事 + 使用者能做什麼** +- 避免過度客氣(不需要「請」「您」滿天飛) +- 繁中台灣用語(不用「鏈接」用「連結」;不用「您」頻繁出現) + +**常用文案(整合到 i18n):** + +| 場景 | 繁中 | English | +|------|------|---------| +| 載入中 | 載入中... | Loading... | +| 儲存成功 | 已儲存 | Saved | +| 儲存失敗 | 儲存失敗,請重試 | Save failed, please retry | +| 刪除確認 | 確定要刪除「{name}」嗎?此操作無法復原。 | Delete "{name}"? This cannot be undone. | +| 空列表 | 還沒有任何紀錄 | Nothing here yet | +| API 錯誤 | 連線失敗,請檢查網路或稍後再試 | Connection failed, check network or try later | +| 權限不足 | 沒有權限進行此操作 | No permission for this action | +| Session 過期 | 登入已過期,請重新登入 | Session expired, please sign in | + +--- + +## 13. 實作建議(給 Frontend Agent) + +1. **Monorepo 結構**:若 `visionA-frontend` 與 `local-tool` 之後都在同 repo,可抽出 `packages/ui/`、`packages/i18n/` 共用,避免重複。Phase 0 不需要,直接複製即可。 +2. **新元件檔案放 `src/components/cloud/`**,命名清晰,不要與既有元件混放。 +3. **測試**:新增的元件(`UserMenu`、`PairingTokenCard`、`RemoteDeviceBadge`、`NetworkErrorBanner`)都要有對應 Vitest 測試。 +4. **Storybook / Playground**:Phase 0 不要求;Phase 1 考慮引入 Storybook。 diff --git a/docs/autoflow/03-design/design-review.md b/docs/autoflow/03-design/design-review.md new file mode 100644 index 0000000..de9822d --- /dev/null +++ b/docs/autoflow/03-design/design-review.md @@ -0,0 +1,225 @@ +# Design Review 報告 — local-tool 現況 + +> 本文件對 **local-tool 既有版型** 做一次 Design Review,用於決定雲端版要沿用、微調、或未來迭代哪些部分。 +> +> **雛形階段(Phase 0)不改** — 這份報告的結論寫入 Phase 1+ 的改善清單。 +> +> 範圍:`local-tool/frontend/src/` 的 UI 層(不包含業務邏輯) + +--- + +## 1. 整體評估 + +**⚠️ 有改善空間** — 核心視覺風格(Shadcn / Radix / Tailwind 4)專業成熟,元件組織清晰,Dark Mode 與 i18n 都內建到基礎層。但在**無障礙細節、互動規格完整度、錯誤處理一致性**幾個面向有提升空間。整體品質適合做 B2B / 開發者工具,不需要大改。 + +--- + +## 2. 評分卡 + +| 維度 | 評分(1-5)| 說明 | +|------|-----------|------| +| 視覺一致性 | 4 | Shadcn 為基礎,大多數頁面視覺語言一致。少數地方(Sidebar icon 用字母、狀態色散在各元件)不一致 | +| 無障礙(WCAG 2.1 AA)| 3 | Shadcn 內建不錯,但自訂元件缺乏 ARIA 規範、部分狀態靠顏色傳達 | +| 響應式(RWD)| 3 | Desktop 設計良好;Tablet 可用;Mobile 基本不可操作 | +| 互動回饋 | 4 | Loading / Error 大部分有處理;細節如 toast 時機、disabled tooltip 不完整 | +| 資訊架構 | 4 | Sidebar 導航直觀,頁面層級清晰 | +| UX Writing | 3 | 部分文案直接寫在 tsx(沒走 i18n)、錯誤訊息參差 | +| Dark Mode 支援 | 5 | 完整,所有頁面驗證過(由 oklch + CSS 變數) | +| 圖示語言 | 3 | Lucide Icon 使用得當,但 Sidebar 用「H / M / D / W / S」字母佔位不理想 | + +**平均:3.6 / 5** — 基礎扎實,細節待打磨。 + +--- + +## 3. Critical(必須修,影響可用性 / 安全 / 法規) + +> 雛形階段**不改**,但 Phase 1 啟動時優先處理。 + +| # | 問題 | 影響頁面 | 建議 | +|---|------|---------|------| +| C1 | Sidebar 的字母佔位 icon 不符合圖示語言 | Sidebar 全域 | 改用 Lucide icon(`LayoutDashboard` / `Boxes` / `Cable` / `Play` / `Settings`) | +| C2 | Sidebar 選單項目色彩對比不足(hover 後 `text-muted-foreground` 在某些背景上對比低於 4.5:1) | Sidebar | 確認 hover 狀態文字使用 `text-accent-foreground`,驗證對比 | +| C3 | `/devices` 右上的 WinUSB driver / udev hint 等文字**直接寫在 tsx**(`devices/page.tsx:76, 92-101`),未走 i18n | `/devices` | 全部文案改走 `t()` | +| C4 | `/workspace/[deviceId]` 的「← 返回」「工作區:」「停止推論」「開始推論」等文字**直接寫在 tsx**(`workspace-client.tsx:62-79`) | Workspace | 改走 i18n | +| C5 | `/workspace/page.tsx` 整頁文字直接寫死(`23-39 行`),無 i18n | Workspace 選擇頁 | 改走 i18n | + +--- + +## 4. Major(建議修,影響體驗或一致性) + +| # | 問題 | 影響範圍 | 建議 | +|---|------|---------|------| +| M1 | 裝置狀態色(`statusColors`)**沒納入 Design Tokens**,散落在 `device-status.tsx` | 狀態系統 | 抽出為 Design Token(或至少統一 constants),方便 Dark Mode 對照與未來擴充 | +| M2 | 狀態僅靠「顏色點」傳達(綠 / 黃 / 紅 / 灰),對色盲不友善 | 裝置狀態徽章 | 除了顏色 + 文字,考慮加小 icon(✓ / ✗ / ⚠)作為冗餘 | +| M3 | `ConnectedDevicesList` / `DeviceCard` 等卡片沒有明確的 `Hover` 視覺變化(部分有 `hover:bg-accent`,部分沒有)| 裝置 / 模型卡片 | 統一 hover 規格(可點擊卡片一律加 `cursor-pointer hover:bg-accent` 或邊框變色)| +| M4 | `FlashDialog` 有成功 / 失敗狀態但**沒有明確的取消 / 中止**按鈕 | FlashDialog | 加「取消燒錄」按鈕(若 backend 支援)| +| M5 | Disabled 狀態的按鈕 **沒有 tooltip 解釋原因**(例如「為什麼 disabled?」)| 所有 disabled 按鈕 | 加 Tooltip(shadcn 有 Tooltip 元件),解釋條件 | +| M6 | `/settings` 的 `ServerStatusDashboard` / `ServerLogViewer` 資訊密度過高(開發者喜歡,但對一般使用者過度)| Settings | Phase 1 收起為可展開區塊,或移到 Developer Mode | +| M7 | `OnboardingDialog` 假設使用者剛插 USB Dongle,在雲端版不適用 | Dashboard | 雲端版重寫 Onboarding 流程(引導 Pairing)| +| M8 | Toast 持續時間未統一(Sonner 預設 vs 特定場景自訂)| 全域 | 統一 toast 持續時間政策(成功 3s、錯誤 5s)| +| M9 | `confirm()` 原生 dialog(`cluster-card.tsx:66`)視覺不一致 | Cluster 刪除 | 改用 `AlertDialog`(shadcn 既有元件)| +| M10 | Mobile 版 Sidebar 未折疊為 Sheet / Drawer,會擠壓內容 | Mobile RWD | 加 responsive Sheet,Mobile 時漢堡選單開啟 | +| M11 | `/models` 比較模式的浮動工具列(`fixed bottom-6 z-50`)在 Mobile 上可能被 safe-area 遮住 | `/models` | 加 `pb-safe` 或 `bottom-[calc(env(safe-area-inset-bottom)+1.5rem)]` | +| M12 | Form 驗證錯誤(如 ModelUploadDialog)**沒用 aria-invalid + aria-describedby**,Screen Reader 無法讀出錯誤 | 所有表單 | 加 ARIA 屬性 | + +--- + +## 5. Minor(未來修,微小改善) + +| # | 問題 | 建議 | +|---|------|------| +| m1 | Dashboard 的 StatCard icon 色(藍 / 紫 / 綠 / 黃)硬編碼,Dark Mode 下對比略弱 | 改用 `text-chart-*` token 或調整 Dark Mode 亮度 | +| m2 | Skeleton(`device-detail-client.tsx:34-35`)過於簡陋(只是灰色塊) | 改為結構相似的多區塊 skeleton,更符合載入後實際佈局 | +| m3 | `ActivityTimeline` 空狀態只顯示「尚無活動紀錄」,缺乏引導 | 加入 CTA「掃描第一台裝置」(local 版)/「配對第一台裝置」(雲端版)| +| m4 | Version 顯示寫在 sidebar 底部 `v0.1.0`,但與 Settings 頁的版本可能不同步 | 統一來源(讀 package.json 或 env variable)| +| m5 | Typography scale 未定義(只用 Tailwind 預設) | Phase 2 可考慮建立語義 typography(heading / body / caption)| +| m6 | 沒有 Focus Trap 機制在自訂 Modal(shadcn Dialog 有內建,但 custom popup 沒有)| 檢查所有 custom popup | +| m7 | 沒有 Skip to main content 連結 | 對鍵盤使用者友善,加入 | +| m8 | 部分 icon 沒 `aria-hidden="true"`(純裝飾 icon 不應被 SR 念出)| Review 全專案 icon 使用 | +| m9 | 沒有 `prefers-reduced-motion` 專屬規則(雖然 Tailwind 4 內建 utility 但未全面應用)| Review `animate-*` class 使用 | +| m10 | `sidebar.tsx` 的 `text-xs font-bold` 字母佔位 icon 在 Dark Mode 下視覺稍弱 | 改用 Lucide 後問題自然消除 | +| m11 | 沒有「Remember previous language / theme selection」UX 提示 | 加註設定會儲存到 localStorage | +| m12 | 錯誤訊息中英文夾雜(如 `'driver 安裝失敗'`)| 統一走 i18n | + +--- + +## 6. 缺失的項目 + +| 項目 | 重要性 | 建議 | +|------|--------|------| +| Loading State 統一規範 | 高 | 統一 Skeleton 策略:資料載入 < 200ms 不顯示、≥ 200ms 顯示 skeleton | +| Error State 統一規範 | 高 | 統一「網路錯誤 / 伺服器錯誤 / 資料不存在」的 UI | +| Empty State 多樣化 | 中 | 目前只有通用 `EmptyState` 元件;可加「有資料但被篩選掉」「初次使用」「永久空」三種語氣 | +| Tooltip 系統 | 中 | shadcn 有 Tooltip,但全專案使用不一致 | +| Breadcrumb | 中 | 深層頁面(`/models/[id]` / `/devices/[id]/...`)沒麵包屑 | +| Search | 中 | 全域搜尋(Cmd+K)對開發者工具很有幫助 | +| Keyboard shortcuts | 低 | Phase 1+ 可考慮(如 `/` 聚焦搜尋、`g d` 跳 Dashboard 等)| +| 無障礙審查(axe-core 自動化)| 高 | 加入 CI,防止回退 | +| Design Token 文件(Storybook / Ladle)| 中 | 方便未來團隊成員查閱 | +| User Documentation | 中 | 產品內嵌的說明文件 / Help Center | + +--- + +## 7. 優點(做得好的地方) + +1. **✅ Shadcn / Radix / Tailwind 4 技術選型成熟** — 無障礙、Dark Mode、可組合性都有好基礎 +2. **✅ `oklch()` 色彩空間** — 現代、可感知線性,Light / Dark 對應優雅 +3. **✅ i18n 系統自製但完整** — 繁中 + English 雙語支援,涵蓋率高(少數 miss 的 string 已列入 Critical) +4. **✅ Zustand stores 組織清晰** — 每個領域一個 store,易維護 +5. **✅ 元件分類良好** — `ui/` 基礎 / 業務元件(models/devices/inference/camera/dashboard)/ layout 三層結構 +6. **✅ Dark Mode 原生支援** — `ThemeSync` 跟隨系統,無需使用者手動切換 +7. **✅ GuidedTour + OnboardingDialog** — 有考慮新手引導(雖然雲端版要重寫) +8. **✅ Empty State 有專用元件** — `empty-state.tsx` 是一個好抽象 +9. **✅ 狀態色有動畫提示** — `connecting` 用 `animate-pulse` 傳達「進行中」的感覺 +10. **✅ Test coverage** — 462 個 test 檔案(前端)顯示有測試文化 + +--- + +## 8. 響應式 Review + +| 斷點 | 現況 | 評價 | +|------|------|------| +| Mobile (< 640px) | Sidebar 擠壓主內容;部分頁面(Workspace)無法正常使用 | ⚠️ 未優化,建議雛形階段不投入,使用者注意力放 desktop | +| Tablet (640-1024) | 可用,但 Sidebar 占 `w-60` 仍顯擁擠 | 🟡 可接受,未來可改 drawer | +| Desktop (≥ 1024) | 設計良好 | ✅ 主要目標 | +| Wide (≥ 1440) | 無最大寬度限制,內容在超寬螢幕會過散 | 🟡 建議加 `max-w-7xl` 或類似 | + +**雲端版 Phase 0 決策**:**Desktop First**,Mobile 不保證完整體驗,只確保能登入 + 看 Dashboard 概要。 + +--- + +## 9. 互動設計 Review + +| 項目 | 現況 | 建議 | +|------|------|------| +| Hover 狀態 | 大多 ✅ | 卡片類 hover 規格需統一 | +| Active / Pressed | 大多 ✅(shadcn 內建)| — | +| Focus ring | ✅(`ring-[3px]` 符合 WCAG AA)| — | +| Disabled | ✅(`disabled:opacity-50`)| 加 tooltip 解釋原因(Major)| +| Loading | 大多 ✅(Loader2 icon / skeleton)| skeleton 結構化(Minor)| +| Empty | ✅(EmptyState 元件)| 多樣化語氣(缺失)| +| Error | 部分 ⚠️ | 統一規範(缺失)| +| Success | ✅(toast)| 持續時間統一(Major)| +| 動畫 | 有基本 `animate-pulse` / `animate-spin` | `prefers-reduced-motion` 支援(Minor)| +| 轉場 | 無明確轉場(Next.js App Router 預設)| Phase 1+ 可加頁面轉場 | + +--- + +## 10. UX Writing Review + +**做得好:** +- 大多數文字走 i18n,集中在 `zh-TW.ts` / `en.ts` +- 語氣友善(「請插入 Kneron USB Dongle 並點擊掃描」) +- 明確的錯誤後果說明(「確定要刪除「{name}」嗎?此操作無法復原。」) + +**待改進:** +- 部分中英夾雜(M12) +- 部分直接寫在 tsx 繞過 i18n(C3 / C4 / C5) +- Toast 訊息長度不一,有些過長 +- 空狀態語氣不夠引導(m3) + +--- + +## 11. Dark Mode Review + +**全頁面抽查**(Light ↔ Dark 切換): + +| 頁面 | Dark Mode 品質 | +|------|---------------| +| `/` Dashboard | ✅ 良好 | +| `/devices` | ✅ 良好,udev hint banner 的 amber 色系在 Dark 下仍清晰 | +| `/devices/[id]` | ✅ 良好 | +| `/models` | ✅ 良好 | +| `/models/[id]` | ✅ 良好 | +| `/workspace/[deviceId]` | ✅ 良好(Camera frame 視覺略弱但可接受)| +| `/settings` | ✅ 良好 | + +**結論**:Dark Mode 執行完整,**不需要在雲端版重做**,直接沿用即可。 + +--- + +## 12. 雲端版的優先處理 + +雲端版 Phase 0 雛形**最需要處理的**(從以上清單挑出): + +1. **C3 / C4 / C5**(i18n 遺漏)— 雲端版寫新頁面時就要求走 i18n,避免累積 +2. **M1**(狀態色納入 Token)— 新增 `RemoteDeviceBadge` 時就要統一 +3. **M2**(狀態不只靠顏色)— 新增 `RemoteDeviceBadge` 時就要實作 +4. **M7**(OnboardingDialog 重寫)— 雲端版情境完全不同,直接不做(Phase 0)或重寫(Phase 1) +5. **M10**(Mobile Sidebar)— 雲端版使用者可能會在 iPad / 手機臨時查看,建議 Phase 1 做 Sheet +6. **缺失項**(Loading / Error 統一規範)— 雲端版場景掉線頻率更高,需要規範 + +其他 Critical / Major 因為在 local-tool 身上,**雛形階段暫緩**,由 local-tool 團隊自行決定何時處理。 + +--- + +## 13. 給使用者的建議 + +**如果你要在 local-tool 上修這些問題,建議順序:** + +1. **先修 Critical**(C1 – C5):影響一致性與法規 +2. **再做無障礙補強**(M2 / M12 / m6 / m7 / m8 / m9):可用一次性任務批次處理 +3. **Design Token 整理**(M1):趁重構一起做 +4. **Mobile RWD**(M10 / M11):獨立任務,優先級低 +5. **Empty / Error / Loading 規範**:跨多檔案,獨立任務 + +**不建議的做法**:在 local-tool 和 visionA Cloud 同時改,會增加複雜度。建議 local-tool 當前版本穩定(已有 462 個測試),**穩住現狀**,等 visionA Cloud Phase 0 雛形完成後,再統一迭代兩邊共用的元件。 + +--- + +## 14. 方法論 + +本審查方法: + +- **靜態檢視**:讀程式碼(`src/app/`、`src/components/`、`src/lib/i18n/`) +- **視覺推論**:根據 className 推估視覺效果,未實際運行 +- **WCAG 2.1 AA 為基準**:色彩對比、鍵盤操作、ARIA、可還原動畫 +- **與最新 Shadcn 慣例對照**:核對是否偏離標準做法 + +**未涵蓋(需要實際執行產品才能做)**: +- 實際的可用性測試(需要找真人) +- 效能 profiling +- 實機 Screen Reader 驗證(NVDA / VoiceOver) +- 實機觸控測試(iPad / 手機) + +這些建議在 Phase 1+ 的 QA 階段補。 diff --git a/docs/autoflow/03-design/design-spec.md b/docs/autoflow/03-design/design-spec.md new file mode 100644 index 0000000..a81a8ef --- /dev/null +++ b/docs/autoflow/03-design/design-spec.md @@ -0,0 +1,309 @@ +# visionA Cloud — 設計規格(Design Spec)索引 + +| 項目 | 內容 | +|------|------| +| 專案代號 | visionA Cloud(`visionA-frontend`) | +| 文件版本 | v0.1(Phase 0 雛形規劃) | +| 最後更新 | 2026-04-21 | +| 狀態 | 三方聯合討論中(Design Agent 產出) | +| 主要負責 | Design Agent | +| 基礎設計稿 | `local-tool/frontend/` 既有實作(Shadcn 風)+ `edge-ai-platform` POC 的 clusters 頁面 | +| 模式 | 既有版型反向整理 + 補齊雲端版缺失 + 局部 Design Review | + +> **本規格從既有程式碼反向整理**。版型與 Design Tokens 直接沿用 local-tool,以確保「離線版 / 雲端版同一套前端」的決策可執行。雲端版新增的頁面(登入、註冊、帳號、Pairing、Clusters)與狀態(在線 / 離線 / 遠端掉線)為本次新增設計。 + +--- + +## 文件結構 + +本規格採用模組化結構,避免單檔過大。本檔為索引,詳細內容拆成以下子檔案: + +``` +.autoflow/03-design/ +├── design-spec.md ← 本檔(索引 + 設計系統概述 + 整體策略) +├── design-tokens.md ← Design Tokens 三層架構 +├── components.md ← 元件庫清單與規格 +├── pages.md ← 頁面結構(既有 + 雲端新增) +├── flows/ +│ ├── flow-pairing.md ← Pairing 流程(最重要的新增流程) +│ ├── flow-offline-handling.md ← 離線 / 掉線 UI 行為 +│ ├── flow-auth.md ← 登入 / 註冊流程(雛形 stub) +│ └── flow-conversion.md ← 轉檔流程(Phase 0.8 新增) +├── design-review.md ← 對既有 local-tool 版型的 Design Review +└── wireframes/ ← 文字版 wireframe(僅針對雲端新增頁面) + ├── wf-login.md + ├── wf-register.md + ├── wf-account.md + ├── wf-pairing.md + ├── wf-clusters.md + └── wireframe-conversion.md ← 轉檔頁面 wireframe(Phase 0.8 新增) +``` + +> `03-design/prototype/` 在 Phase 0 雛形**暫不產出** high-fidelity prototype。版型直接沿用 local-tool 實作,前端工程師可直接參考 `local-tool/frontend/` 複用元件。 + +--- + +## 1. 設計系統概述 + +### 1.1 設計哲學 + +**「Editor-grade Console — 開發者每天要用 8 小時也不累的工作台」。** + +visionA Cloud 的使用者是**開發者 / FAE / SI**,不是一般消費者。他們: + +- 一天可能開著這個頁面數小時 +- 熟悉技術細節,不需要過度的引導 +- 重視資訊密度與快速操作,勝過視覺華麗 +- 多半在桌面環境工作(Mac / Windows) +- 對載入狀態、連線狀態、錯誤訊息的「誠實性」非常敏感 + +**設計原則(由此延伸):** + +1. **資訊優先於裝飾** — 任何視覺元素都要有功能理由。不用漸層、不用花俏動畫。 +2. **誠實呈現狀態** — 在線 / 離線 / 連線中 / 錯誤,一定要讓使用者看得出差別(不是只用顏色,必須搭配文字 / icon)。 +3. **一致性勝過驚喜** — 與 local-tool 完全共用同一套元件與版型,使用者在兩種模式間切換無感。 +4. **可預期勝過可愛** — 按鈕按下去會發生什麼事,應該從文字、位置、圖示就能預測。 +5. **鍵盤友善** — 所有操作都應該有鍵盤路徑(符合 WCAG 2.1 AA)。 +6. **Dark Mode 第一** — 跟隨系統主題,不做強制光亮。 + +### 1.2 設計目標 + +| 目標 | 衡量方式(供 Testing 參考) | +|------|--------------------------| +| 與 local-tool 視覺一致 | 同一使用者在兩種模式間能一眼認出是同個產品 | +| 雲端特性清晰 | 使用者隨時知道「我現在連的是哪台遠端裝置」「它在線嗎」 | +| 學習成本 < 10 分鐘 | 看得懂 Dashboard、能找到 `/devices/pair`、能開 Workspace | +| 無障礙符合 WCAG 2.1 AA | axe-core 自動掃描 0 個 Critical;鍵盤可操作所有核心流程 | +| Dark Mode 完整 | 100% 頁面在 Dark Mode 下色彩對比達標 | + +### 1.3 設計範圍(Phase 0 雛形) + +| 類別 | 頁面 / 元件 | 範圍 | +|------|-----------|------| +| **沿用 local-tool**(版型完全不動) | `/`、`/devices`、`/devices/[id]`、`/models`、`/models/[id]`、`/workspace`、`/workspace/[deviceId]`、`/settings` | 複用元件、複用 Design Tokens;只改 API base URL 的連法 | +| **從 POC 搬**(適配雲端) | `/clusters`、`/workspace/cluster/[clusterId]` | 視覺風格與既有頁面對齊,搬完就等同「原生雲端功能」 | +| **雲端新增 — 雛形 stub** | `/login`、`/register`、`/account` | 低保真,能 compile 可導覽即可,UI 細節可 Phase 1 細化 | +| **雲端新增 — 完整設計** | `/devices/pair`、Sidebar 的帳號區塊、裝置掉線徽章與降級體驗 | 本次規格 focus 的核心 | +| **全域行為改動** | 頂部 `ConnectionStatus`、`api.ts` 錯誤處理、WebSocket 重連 UI | 雲端場景連線邏輯不同,需要調整 | + +### 1.4 設計範圍外(不做的事) + +- 高保真 prototype(Figma 或 HTML)— 版型沿用 local-tool,沒有價值 +- 行銷 Landing Page — 不在雛形範圍 +- Onboarding Wizard — 雲端版的 onboarding 等 Phase 1 再細化 +- Mobile 響應式完整版 — 雛形只保證 Desktop,Tablet 可讀,Mobile 降級(詳見第 6 節) +- 重新發明 Design Tokens — 完全沿用 local-tool 的 CSS 變數 + +--- + +## 2. 子文件總覽 + +詳細內容在各子文件。以下是一句話摘要: + +### [2.1 Design Tokens](design-tokens.md) + +**完整沿用 local-tool 的 shadcn CSS 變數系統**,包含 Reference / Semantic / Component 三層。色彩使用 `oklch()` 色彩空間、radius base `0.625rem`、Tailwind 4 `@theme inline`。Dark Mode 透過 `.dark` class 切換。**本專案不新增、不覆寫任何 Token。** + +### [2.2 元件庫清單](components.md) + +**基礎 UI(Shadcn 風)22 個**:Button、Card、Dialog、AlertDialog、Tabs、Input、Select、Badge、Slider、Progress、Checkbox、Label、Separator、ScrollArea、Sonner(toast)、EmptyState… +**業務元件(沿用)**:models/、devices/、inference/、camera/、dashboard/ +**Layout**:Header、Sidebar、ConnectionStatus、HelpButton +**新增(雲端版)**:UserMenu、PairingTokenCard、RemoteDeviceBadge、NetworkErrorBanner + +### [2.3 頁面結構](pages.md) + +逐頁規格:沿用頁面列出「版型沿用 + 雲端適配點」,新增頁面列出「版型、主要區塊、互動重點」。雲端新增 5 個頁面(`/login`、`/register`、`/account`、`/devices/pair`、`/clusters`)。 + +### [2.4 Pairing 流程](flows/flow-pairing.md) + +**雲端版最關鍵的新增流程**。使用者在雲端 Web 登入 → 進 `/devices/pair` → 產生 Pairing Token(`vAc_` + 32 字元 hex,TTL **15 分鐘**,一次性使用;local agent 成功換取後獲得 90 天 Session Token 維持 tunnel,使用者感受不到後者)→ 複製 token → 到自己電腦的 local agent 貼上 → 顯示 local agent 已連上雲端 → 回 `/devices` 看到遠端裝置。 + +### [2.5 離線 / 掉線處理](flows/flow-offline-handling.md) + +雲端版特有:local agent 可能掉線、tunnel 可能斷。裝置列表、裝置詳情頁、Workspace 都要能「誠實顯示狀態」。建議新增一個全域的 `RemoteDeviceBadge`,在裝置列表 / 卡片 / Header 顯示「最後心跳 X 秒前」。 + +### [2.6 登入 / 註冊流程](flows/flow-auth.md) + +Phase 0 雛形只需要 **stub 頁面**:Email + Password 輸入框、一個登入按鈕、一個「還沒有帳號?註冊」連結。後端尚未接,送出後直接跳 Dashboard 即可。實際 OAuth / 密碼驗證 Phase 1 再做。 + +### [2.7 Design Review](design-review.md) + +對既有 local-tool 版型做一次檢查。目前已知的改善點整理成 Critical / Major / Minor 三級,**雛形階段不改**,但寫進文件給 Phase 1+ 參考。 + +### [2.8 轉檔流程](flows/flow-conversion.md) + [Wireframe](wireframes/wireframe-conversion.md) + +**Phase 0.8 新增的核心動線**。Sidebar 加「轉檔」tab(Wand2 icon),單頁 `/conversion` 用 state 機切 `idle / uploading / processing / completed.success / completed.failed` 五個畫面。上傳走 visionA backend streaming proxy(XHR + 進度條),轉檔以 polling(5–10 秒)追狀態,完成後**半自動**讓使用者選「加到模型庫」或「下載」(兩按鈕互不互斥)。失敗時顯示翻譯後的錯誤訊息(PRD §F5 對照表)+ suggestions。**前端不持久化 jobId**,由 backend `GET /jobs/active` 提供 source of truth,跨瀏覽器 / 多分頁 / 重新整理都能正確恢復。 + +--- + +## 3. 技術對齊 + +| 項目 | 決策 | 備註 | +|------|------|------| +| UI 框架 | Next.js App Router 16 + React 19 | 沿用 local-tool | +| 樣式 | Tailwind CSS 4 + shadcn/ui(Radix UI 封裝)| 沿用 | +| 圖示 | Lucide React | 沿用 | +| 狀態管理 | Zustand 5 | 沿用;新增 `auth-store`、`session-store` | +| Toast | Sonner | 沿用 | +| i18n | 自訂實作(`src/lib/i18n`),繁中 + English | 沿用 | +| 圖表 | Recharts 3 | 沿用(Workspace 效能圖表) | +| 引導 | driver.js | 沿用(Dashboard onboarding) | +| 無障礙 | 以 shadcn/ui 內建 ARIA 為基礎,關鍵頁面補強 | 目標 WCAG 2.1 AA | +| Dark Mode | 跟隨系統主題(`theme-sync.tsx`)| 沿用 | + +--- + +## 4. 設計與開發的職責切分 + +為避免設計與實作脫節,明確職責: + +| 誰做 | 什麼 | +|------|------| +| **Design Agent(本文件)** | Design Tokens 規格 / 元件清單 / 頁面結構 / 互動規格 / UX Writing 文案規範 / Design Review | +| **Architect Agent** | API 契約 / Session 設計 / 狀態同步策略 / Token 生命週期 | +| **Frontend Agent** | 依本文件實作 React 元件 / 接 API / 實作 WebSocket 重連邏輯 | +| **PM Agent** | User Story / 成功指標 / Persona | + +**設計規格有衝突時的排序:** 使用者口述需求 > PRD > 本設計規格 > local-tool 現況 > 個人偏好。 + +--- + +## 5. 與 PM / Architect 的協作提醒 + +### 5.1 給 PM 的提醒 + +- Phase 0 雛形**不做完整 Onboarding**。Dashboard 的 `OnboardingDialog` 在雲端版情境不合適(local-tool 的版本假設使用者「插 USB Dongle 掃描」,雲端版第一步是 Pairing),建議 Phase 1 重寫。 +- `/settings` 頁的「Advanced」分頁目前有 ServerStatusDashboard 與 ServerLogViewer,**雲端版應該移除或重新定位**(使用者看不到「他電腦上的 local agent log」,除非有專門的 remote log 設計)— 建議 Phase 1 再處理。 +- 未來如果有 Billing,`/account` 會擴展成 `/account/*`(profile / billing / api-keys / sessions)。Phase 0 只做單頁 stub。 + +### 5.2 給 Architect 的提醒 + +- **裝置狀態同步**:雲端版裝置狀態依賴 local agent 的 heartbeat / WebSocket,前端必須能顯示「上次心跳 X 秒前」。建議 API 直接回傳 `lastSeenAt` 欄位(ISO 8601)。 +- **Pairing Token 顯示格式**:設計上用「8+8 分隔顯示」(`a1b2c3d4 e5f6g7h8`)方便肉眼辨識複製;API 回傳請保持原始無空白字串,由前端格式化顯示。 +- **WebSocket 重連策略**:前端會用指數退避,但需要 Architect 確認後端能否容忍短暫重連抖動。 +- **錯誤分類**:前端需要區分「雲端 API 不可達」(整個服務掛)vs「local agent 掉線」(單裝置不可用)。API 錯誤回應的 `error.code` 請明確分類,讓 UI 可以給不同提示。 + +--- + +## 6. 響應式策略(整體總綱,詳見 pages.md) + +| 斷點 | 寬度 | 雛形策略 | +|------|------|---------| +| Mobile | < 640px | **降級顯示**:能讀但不建議操作,顯示提示「建議使用桌面版」於關鍵頁面(如 Pairing、Workspace) | +| Tablet | 640 – 1024px | **可用**:Sidebar 折疊為 drawer,主內容區單欄 | +| Desktop | 1024 – 1440px | **主要目標**:Sidebar 展開(w-60),主內容區自適應 | +| Wide | > 1440px | 最大寬度限制 max-w-7xl(1280px)內容置中 | + +**核心決策**:**Desktop First**。使用者是開發者,主要用桌機。Mobile 僅確保能登入、看 Dashboard 資訊、讀通知;不要求能操作推論。 + +--- + +## 7. 國際化策略(沿用 local-tool) + +- **支援語言**:繁體中文(`zh-TW`)、English(`en`) +- **預設語言**:跟隨系統 locale,無法辨識則 `en` +- **切換位置**:`/settings` → 一般 → 語言;使用者選擇會存 `localStorage` +- **文案位置**:`src/lib/i18n/zh-TW.ts`、`src/lib/i18n/en.ts` +- **新增雲端版的文案 key**: + +``` +auth.login.* 登入頁 +auth.register.* 註冊頁 +auth.error.* 認證錯誤訊息 +account.* 帳號設定頁 +pairing.* Pairing 頁與流程 +cluster.* 叢集(已在 POC 存在,整併) +remote.* 遠端裝置相關(掉線、心跳、最後連線) +nav.cloudAppName visionA Cloud(取代 visionA Local) +``` + +- **UX Writing 原則**:參考 `components.md` 第 8 節。 + +--- + +## 8. 無障礙基準(WCAG 2.1 AA) + +### 8.1 色彩對比 + +- 一般文字對背景 ≥ 4.5:1 +- 大文字(18pt+/14pt Bold+)≥ 3:1 +- Icon / UI 元件 ≥ 3:1 +- 不只靠顏色傳達資訊(裝置狀態徽章同時有「圓點 + 文字」) + +### 8.2 鍵盤操作 + +- 所有互動元素可 Tab 聚焦 +- Focus ring 可見(shadcn 預設 `ring-[3px]` 已達標) +- Modal 聚焦陷阱(Dialog 自動處理) +- Escape 關閉 Modal(Dialog 自動處理) +- Skip link(全新頁面需要加) + +### 8.3 ARIA 與語意 + +- 使用語意化 HTML(`