local-tool/: visionA-local desktop app
- M1: Wails shell + Go server + Next.js UI + Mock mode (macOS dmg ready)
- M2: i18n (zh-TW/en) + Settings 4-tab refactor
- M3: Embedded Python 3.12 runtime (python-build-standalone) + KneronPLUS wheels
- M4: Windows Inno Setup script (build on Windows runner)
- M5: Linux AppImage script + udev rule (build on Linux runner)
- M6: ffmpeg (GPL, pending legal review) + yt-dlp bundled
- Lifecycle: watchServer health check, fatal native dialog,
Wails IPC raise endpoint, stale process cleanup
.autoflow/: full PRD / Design Spec / Architecture / Testing docs
(4 rounds tri-party discussion + cross review)
.github/workflows/: macOS / Windows / Linux build CI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
190 lines
13 KiB
Markdown
190 lines
13 KiB
Markdown
# Architect Round 1 — 技術可行性分析與重大決策
|
||
|
||
> 目的:visionA-local 第一輪技術評估。不寫完整 TDD,只列架構方向、關鍵風險、待決問題。
|
||
> 作者:Architect Agent | 日期:2026-04-10
|
||
|
||
## 1. 整體架構方案
|
||
|
||
**採用「Wails 殼 + Go Server 子程序 + Python sidecar」三層結構**,而非 single binary。理由:
|
||
|
||
原 `edge-ai-platform` 已經跑通 `installer/`(Wails)把 `edge-ai-server`(Go binary,內含 go:embed 的 Next.js)作為「payload」解壓到使用者目錄後啟動。這個拆分是**對的**,不該改成單一 binary:
|
||
|
||
- Go server 本身要長時間持續跑(serve HTTP、WebSocket、管理 USB 裝置),Wails 前端則是 UI 殼;把兩者綁在同一個行程會讓 UI 當掉時連服務也一起掛。
|
||
- Python KneronPLUS SDK 必須由獨立 `python3` interpreter 執行,無法塞進 Go binary,註定要用 sidecar(`os/exec` 叫 `scripts/kneron_bridge.py`),Go server 仍是中介。
|
||
- Wails 本來就適合做「啟動器 + 控制台」,現有 `installer/app.go` 已有 894 行成熟程式碼、含系統資訊偵測、進度回報、裝置偵測、瀏覽器開啟等——**幾乎可以直接改名沿用**。
|
||
|
||
**建議架構:**
|
||
|
||
```
|
||
visionA-local.app (Wails 二進位)
|
||
├─ 內嵌 payload(go:embed installer/payload/...)
|
||
│ ├─ edge-ai-server ← Go binary(含 embedded Next.js)
|
||
│ ├─ data/ ← models.json + 預置 .nef (~73MB)
|
||
│ └─ scripts/
|
||
│ ├─ kneron_bridge.py
|
||
│ ├─ requirements.txt
|
||
│ ├─ KneronPLUS-*.whl ← 每平台一份 wheel
|
||
│ └─ (ffmpeg binary — 新增)
|
||
├─ 首次執行 → 解壓到 ~/.visiona-local/
|
||
├─ 建立 venv + pip install requirements + KneronPLUS wheel
|
||
└─ 啟動 edge-ai-server 子行程 → 開啟內嵌 WebView 指向 http://127.0.0.1:3721
|
||
```
|
||
|
||
Wails 殼同時扮演:首次安裝精靈、Tray/常駐、啟動/停止 server、查看 log。第二次執行直接 detect 到已安裝、跳過解壓。
|
||
|
||
## 2. 依賴內嵌策略(關鍵)
|
||
|
||
### 2.1 Python Runtime
|
||
|
||
**結論:不要內嵌 Python runtime,改為「偵測 + 自動安裝系統 Python」。**
|
||
|
||
| 平台 | 策略 |
|
||
|------|------|
|
||
| macOS | 首選:使用者系統已有的 `python3`(macOS 12+ 幾乎都有)。無則呼叫 Homebrew 安裝(現有程式碼已有 `brew install python@3.12` 流程) |
|
||
| Windows | 首選:`winget install Python.Python.3.12`。備援:從 python.org 下載 embedded zip |
|
||
| Ubuntu | `apt-get install python3 python3-venv python3-pip`(需 sudo polkit 提權) |
|
||
|
||
**不採用 python-build-standalone / PyInstaller 的原因:**
|
||
- KneronPLUS wheel 綁定 `pyusb` + 原生 libusb,用 embedded Python 時 site-packages 的 C extension 常踩到 ABI 問題
|
||
- 每平台都內嵌完整 Python 會讓安裝檔大約 +40MB,且升級 Python 需要重發整包
|
||
- 現有 `installer/app.go` 的 `setupPythonVenv()` 邏輯已在 macOS/Windows/Linux 跑通,包含 linux 缺 `ensurepip` 的 fallback
|
||
|
||
**代價:** 使用者需要能連網做一次 `pip install`。如需完全離線,第二階段可改為 bundle wheels + `pip install --no-index --find-links`(requirements.txt 只有 numpy / opencv-python-headless / pyusb 三個,加上 KneronPLUS 共 ~80MB wheels)。**建議採「離線 wheel」路線**——完全符合「一鍵安裝不需連網」的要求。
|
||
|
||
### 2.2 KneronPLUS SDK
|
||
|
||
**結論:每平台打包對應的 wheel,透過 `pip install` 解壓到 venv。原 `installer/wheels/` 已有三平台的 wheel(共 3.9MB)。**
|
||
|
||
| 平台 | wheel 狀態 | 原生函式庫 | 注意事項 |
|
||
|------|-----------|-----------|---------|
|
||
| macOS | `KneronPLUS-2.0.0-*.whl`(含 `.dylib`) | kp/lib/*.dylib | 需要 **ad-hoc codesign**(已有程式碼)否則 Gatekeeper 擋 |
|
||
| Linux | `KneronPLUS-2.0.0 / 3.1.2-*.whl`(含 `.so`) | kp/lib/*.so | 需要 libusb-1.0(`apt-get install libusb-1.0-0`)+ udev rule 讓非 root 使用者能存取 USB |
|
||
| Windows | `KneronPLUS-3.1.2-*.whl`(含 `.dll`) | kp/lib/*.dll + libusb-1.0.dll | 需要安裝 WinUSB driver(已有 `installer/drivers/kneron_winusb.inf`)— **這一步可能需要使用者手動同意 UAC** |
|
||
|
||
**USB driver 是風險點:** Windows 必須裝 WinUSB driver 才能讓 libusb 存取裝置,現有程式碼透過 `pnputil` 或類似方式自動安裝,會彈 UAC。Linux 需要寫入 `/etc/udev/rules.d/99-kneron.rules` 也要 sudo。macOS 完全不需要 driver(IOUSB 直接用)。**這點要在 PRD 裡寫成「首次啟動需管理員權限」。**
|
||
|
||
### 2.3 ffmpeg
|
||
|
||
**結論:內嵌靜態 binary,不要走 brew/winget。**
|
||
|
||
| 平台 | 來源 | 大小 | 授權 |
|
||
|------|------|------|------|
|
||
| macOS (Intel + ARM) | evermeet.cx 的 static build,或 BtbN GitHub Release | ~40MB | LGPL build(**不要用 GPL 版**,避免產品須開源) |
|
||
| Windows | gyan.dev 或 BtbN 的 essentials_build | ~50MB | LGPL |
|
||
| Linux | johnvansickle.com 或 BtbN static build | ~50MB | LGPL |
|
||
|
||
**做法:** 在 `installer/payload/ffmpeg/{platform}/ffmpeg` 放靜態檔,解壓後 Go server 用絕對路徑呼叫,不依賴 PATH。原 `stepInstallFfmpeg()` 是 fallback 去系統 PATH 找,新版應改為**一律用內嵌版**確保可重現。
|
||
|
||
**必須使用 LGPL build 而非 GPL build**——否則整個產品就被 GPL 感染。授權聲明要寫在 About 頁。
|
||
|
||
### 2.4 預置模型 .nef
|
||
|
||
**結論:放在 payload 中一起解壓到 `~/.visiona-local/data/nef/`,不塞進 Go binary。**
|
||
|
||
現狀 nef 總量 **73MB**。理由:
|
||
- go:embed 會把 73MB 直接吃進 Go binary,link 時記憶體吃緊、IDE debug 變慢
|
||
- 解壓後使用者可手動管理、刪除、增加自訂模型(符合既有 `custom-models` 流程)
|
||
- 沿用現有 `stepExtractData` 的做法
|
||
|
||
## 3. 打包輸出格式
|
||
|
||
| 平台 | 格式 | 簽章 |
|
||
|------|------|------|
|
||
| **macOS** | `.app` 內包 Universal Binary(arm64 + amd64 用 `lipo` 合併)→ 再包成 `.dmg` | **強烈建議做 Developer ID + notarization**;否則使用者第一次開啟要右鍵 → 開啟。Wails 支援 `wails build -platform darwin/universal`。Notarization 需要 Apple Developer 帳號($99/年) |
|
||
| **Windows** | `.exe`(NSIS / Inno Setup 都可,推薦 **Inno Setup**,UI 較現代) | **Authenticode 簽章**否則 SmartScreen 會狂擋新下載。EV 憑證 $300-500/年 |
|
||
| **Ubuntu** | **`.AppImage`(首選)+ `.deb`(次選)** | 無強制簽章 |
|
||
|
||
**為何選 AppImage:** 單檔、不需安裝、不需 sudo、攜帶性最高,最符合「像一般 app」的體驗。`.deb` 需要 `apt install`、需要 sudo、只能跑 Debian 系;AppImage 跨所有 glibc ≥2.28 發行版。`.snap` / Flatpak 需要發到 store,發行流程重。
|
||
|
||
**Universal Binary 是否做?** 建議做。現在 M1/M2 使用者已經是主流,若只發 Intel 版,M 系 Mac 要走 Rosetta,效能不穩(KneronPLUS dylib 如果是 x86_64-only 更糟)。需先確認 KneronPLUS macOS wheel 是否支援 arm64——**這是風險點 R3**。
|
||
|
||
## 4. 預估安裝檔大小(壓縮後)
|
||
|
||
| 平台 | Wails shell | Go server | Next.js embed | Python wheels | ffmpeg | NEF 模型 | WinUSB driver | 合計 |
|
||
|------|------------|-----------|--------------|---------------|--------|---------|--------------|------|
|
||
| macOS (Universal) | ~12MB | ~25MB (x2 架構 ~50MB) | ~5MB | ~40MB | ~35MB | ~73MB | - | **~215MB** |
|
||
| Windows | ~10MB | ~20MB | ~5MB | ~40MB | ~45MB | ~73MB | ~1MB | **~195MB** |
|
||
| Ubuntu (AppImage) | ~12MB | ~20MB | ~5MB | ~40MB | ~45MB | ~73MB | - | **~195MB** |
|
||
|
||
以上為**壓縮後**大小。主要體積來自 ffmpeg + Python wheels + 預置模型。若要瘦身:可將 .nef 改為首次啟動時線上下載(-73MB),但違反「完全離線一鍵安裝」原則。建議接受 ~200MB。
|
||
|
||
## 5. 既有程式碼取用策略
|
||
|
||
| 目錄/檔案 | 策略 | 說明 |
|
||
|-----------|------|------|
|
||
| `installer/` | **直接複製改名** | 改為 `visiona-local/`,`app.go` 刪除 relay/dashboard 欄位,其餘流程沿用 |
|
||
| `installer/wheels/` | **直接複製** | 三平台 KneronPLUS wheel |
|
||
| `installer/drivers/` | **直接複製** | WinUSB driver 檔案 |
|
||
| `server/main.go` | **改寫** | 刪除 cluster / tunnel / relay-token / gitea-url 相關參數與初始化 |
|
||
| `server/internal/api/` | **改寫** | 刪除 cluster、tunnel handler 與 route |
|
||
| `server/internal/camera` `device` `model` `inference` `flash` `config` `deps` | **直接複製** | 核心業務邏輯全保留 |
|
||
| `server/internal/cluster` | ❌ **刪除** | |
|
||
| `server/internal/tunnel` | ❌ **刪除** | |
|
||
| `server/cmd/relay-server` | ❌ **刪除** | |
|
||
| `server/tray/` | **直接複製** | 保留 macOS tray + Windows `--gui` web controller |
|
||
| `server/scripts/` | **直接複製** | kneron_bridge.py + firmware update |
|
||
| `server/data/` | **直接複製** | models.json + 預置 nef |
|
||
| `server/web/` + `frontend/` | **改寫** | 移除 relay 模式切換、cluster 管理 UI |
|
||
| `docker/` `scripts/deploy-*` | ❌ **刪除** | 不需要部署腳本 |
|
||
| `tools/` | 視情況 | 多數是開發腳本 |
|
||
|
||
## 6. 要砍掉的程式碼清單(具體路徑)
|
||
|
||
```
|
||
server/internal/cluster/ ← 整包刪
|
||
server/internal/tunnel/ ← 整包刪
|
||
server/cmd/relay-server/ ← 整包刪
|
||
server/pkg/hwid/ ← relay token 用的,可刪
|
||
docker/ ← 整包刪
|
||
scripts/deploy-aws.sh
|
||
scripts/deploy-ec2.sh
|
||
frontend/app/.../cluster/ ← 前端 cluster 頁面(待 Design Agent 確認)
|
||
frontend/app/.../relay/ ← 前端 relay 模式切換 UI
|
||
```
|
||
|
||
`server/main.go` 需移除的 import 與初始化:
|
||
- `internal/cluster`, `internal/tunnel`, `pkg/hwid`
|
||
- `cfg.RelayURL / RelayToken / GiteaURL` 相關 flag
|
||
- `tunnelClient.Start()`、`clusterMgr`、`relayWebURL()` 等
|
||
- `config.go` 中的 `RelayURL / RelayToken / TrayMode`(tray 保留)`GUIMode`、`GiteaURL` 砍掉
|
||
|
||
## 7. 重大技術風險(Top 5)
|
||
|
||
| # | 風險 | 可能性 | 影響 | 緩解 |
|
||
|---|------|-------|------|------|
|
||
| **R1** | **KneronPLUS Linux wheel 只有特定 glibc/ABI,無法在所有 Ubuntu 跑** | 高 | 高 | 先實測 Ubuntu 22.04/24.04 x64,若不過則限定支援版本;訴求 AppImage 內帶 libusb |
|
||
| **R2** | **Windows WinUSB driver 安裝需要 UAC,使用者拒絕就裝不起來** | 中 | 高 | 首次啟動明確說明;提供「手動安裝 driver」的後援流程 |
|
||
| **R3** | **KneronPLUS macOS wheel 可能只有 x86_64**(M 系 Mac 要走 Rosetta,dylib 會抗議) | 中 | 高 | 第二輪前必須驗證:`lipo -info` 檢查 .dylib 架構;若只有 x86_64,Universal Binary 就沒意義 |
|
||
| **R4** | **離線 wheel 安裝在沒有 pip 的 Python 環境會炸**(macOS 系統 Python 有 pip,但 Ubuntu 沒有 `python3-venv` 是常態) | 中 | 中 | 已有 `installPython3Venv()` fallback,但需要 sudo;考慮改為引導使用者手動裝 |
|
||
| **R5** | **預置 .nef 模型授權可能限制重新發布** | 低 | 中 | 請 PM 確認 Kneron 預置模型的 re-distribution license |
|
||
|
||
## 8. 需要 PM / Design 回答的問題
|
||
|
||
**給 PM:**
|
||
1. 目標使用者是「開發者 / ML 工程師」還是「一般使用者」?影響 UI 的技術密度(要不要顯示 log、要不要 advanced mode)
|
||
2. 是否必須支援**完全離線**一鍵安裝?還是可以在首次啟動時連網下載 wheels?影響 R4 緩解方案
|
||
3. 預置模型清單要保留哪些?現狀 KL520/KL720 各有 5-6 個,73MB;可否精簡到 30MB?
|
||
4. 是否支援**多裝置連接**(一台電腦插兩顆 Kneron dongle)?影響裝置列表 UI
|
||
5. 要不要保留**韌體更新**功能(`update_kl720_firmware.py`)?
|
||
|
||
**給 Design:**
|
||
1. Wails 殼的 UI 要走「安裝精靈風格」還是「Dashboard 風格」?原 installer 是精靈式,但 local 版應該常駐後是 Dashboard
|
||
2. 需要 Dark Mode 嗎?
|
||
3. 首次安裝流程的進度條樣式(現有是 0-100% 橫條,可否保留?)
|
||
4. 系統列(Tray)菜單要放哪些項目?至少:開啟儀表板 / 啟動/停止 / 結束
|
||
|
||
## 9. 需要使用者確認的問題
|
||
|
||
1. **Apple 簽章與 notarization**:是否有 Apple Developer 帳號?沒有就只能 ad-hoc sign(使用者第一次要右鍵打開)
|
||
2. **Windows Authenticode**:是否願意採購 EV 簽章憑證(~$300/年)?沒有就會被 SmartScreen 警告,但仍可使用
|
||
3. **最低 OS 版本**:建議 macOS 12+、Windows 10 (1809)+、Ubuntu 22.04+,可接受?
|
||
4. **ARM Linux(Raspberry Pi 之類)**:暫不支援?(KneronPLUS 的 arm64 Linux 支援度不明)
|
||
5. **Bundle ID**:`com.innovedus.visiona-local` 可以用嗎?(progress.md 已記錄但標記待確認)
|
||
6. **離線 vs 線上安裝**:追求完全離線(~200MB 安裝檔)還是接受首次連網(~120MB 安裝檔)?
|
||
7. **預置模型**:是否需要精簡清單?
|
||
8. **應用程式名稱顯示**:visionA-local 還是 VisionA Local?品牌字風格?
|
||
|
||
---
|
||
|
||
**下一步:** 等待 PM / Design / 使用者對上述問題的回覆。回覆後進入第二輪——產出完整 Design Doc 與 TDD,拆分為模組化檔案(`04-architecture/design-doc.md` 索引 + `api/`、`database.md`、`infra.md` 等子檔)。
|