visionA/local-tool/docs/autoflow/04-architecture/build-troubleshooting.md
jim800121chen 9397a4d31f docs: 新增三平台 build troubleshooting 參考
把 M10 這輪三平台 build 踩的雷整理成 troubleshooting 文件,供未來
build 安裝包時參考。

涵蓋 9 條逐項 troubleshooting(統一格式:症狀 → 根因 → 為什麼發生
→ 解法 → 如何根治)、快速症狀索引表、5 條橫向教訓、KneronPLUS
2.0.0 vs 3.1.2 版本相容附錄、build 驗收 checklist。

核心教訓:症狀常不指向根因(Error 12 是 input size、pip 秒退是 wheels
多版本、中文亂碼是 stdio code page),macOS 開發環境測不到大量
Windows-only 問題(編碼、DLL 路徑、SDK 版本差異)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 09:08:13 +08:00

372 lines
26 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Build Troubleshooting — visionA-local
> **這份文件的用途**:記錄 visionA-local 打包安裝檔macOS / Windows / Linux時實際踩過的雷
> 讓下次遇到同樣症狀能**快速定位**,而不是重新 debug 一輪。
>
> **目標讀者**:未來要在 Windows / Linux build 這個專案安裝包的開發者(可能是你自己,也可能是團隊成員)。
>
> **這不是** build 教學。build 流程本身看 `build-pipeline.md`Makefile 骨架、vendor 目錄、CI 策略)。
> 這份是它的 troubleshooting 補充:**症狀 → 根因 → 為什麼會發生 → 解法 → 如何預防/根治**。
>
> **紀錄來源**2026-07 這輪M10 classification + 特規打包 + 三平台實機 build踩的雷
> 每條都對到 progress.md 或實際 code 的 `檔案:行號`。若與現況不符,以 code 為準。
---
## 快速症狀索引表
先在這張表找你看到的症狀,跳到對應章節。**症狀常常不指向真正的根因**——這正是這份文件存在的理由。
| 你看到的症狀 | 可能的雷 | 跳到 |
|-------------|---------|------|
| 裝置 reset 後 `load_model Error code 24`、停在 `KDP2 Loader` | 開發模式 `dist/scripts/``firmware/` | §1 |
| 前端顯示「載入模型失敗」、terminal 出現 `darwin_usb.c:584` / SIGABRT | 除錯時 curl 與 UI 搶同一顆 USB | §2 |
| Windows build`go: command not found` / `wails: command not found` / `pnpm not found`(明明裝了)| MSYS2 login shell 重建 PATH | §3 |
| Build 後置檢查失敗「預期 2 個 .nef實際 8 個」 | `copy_bundled_data` 沒清空目標、殘留舊 .nef | §4 |
| Windows 推論 `KP_ERROR_INVALID_PARAM_12`Error 12 | input size 用了使用者亂填的 declared 值 | §5 |
| 每次啟動都「Python 相依未通過」但又不重裝,怎樣都好不了 | venv 半套安裝、只檢查 python.exe 存在就跳過 | §6 |
| 明明環境是好的卻被判「Python 相依驗證失敗」擋住啟動 | 健康檢查 probe 沒補 Windows DLL 搜尋路徑 | §7 |
| App 一啟動 pip 就秒退 `ResolutionImpossible`certifi ×3 之類) | vendor wheels 累積多版本 | §8 |
| Windows 中文標籤亂碼「布」→「撣<E3808C>」、log 出現 `<60>` | Python stdio 綁 cp950 而非 UTF-8 | §9 |
如果症狀不在表上,先讀 §10 共通教訓——很多雷的「表面症狀」都不指向根因。
---
## 環境前置需求(三平台)
打包前的一次性環境安裝,用 bootstrap 腳本,不要手動裝:
| 平台 | 腳本 | 裝什麼 |
|------|------|--------|
| Windows | `scripts/bootstrap-windows.ps1` | winget 裝 git / go / node / pnpm / python / **MSYS2**(提供 bash + make/ Inno Setup + wails |
| Linux | `scripts/bootstrap-linux.sh` | apt 裝 go 1.22.5 / node 20 / pnpm / wails + GTK/WebKit/libusb dev headers |
| macOS | (手動)| go / node / pnpm / wails / `brew install create-dmg`DMG 美化,選用) |
三平台都需要 `make vendor-sync` 先把第三方二進位Python runtime / wheels / ffmpeg下載到 `vendor/`
**⚠️ Windows 特別注意**make 在 Windows 上是透過 `C:\msys64\usr\bin\bash.exe` 執行的Windows 沒有原生 make
這帶來 §3 的 PATH 坑。詳見該節。
---
## 逐條 Troubleshooting
### §1 開發模式資源不同步 → 裝置 reset 後 load_model Error 24
**症狀**
- 裝置首次連線、reset 後,`load_model``Error code 24`
- Python bridge 卡在 `KDP2 Loader` 狀態、進不到 `KDP2 Comp`
- **只在手動開發模式(`dist/scripts/`)發生,打包出來的安裝包沒這問題。**
**根因**
- 開發模式下 `dist/scripts/` 需要同時含 **`kneron_bridge.py` + `firmware/` + `drivers/`**`dist/data/` 需含 `models.json``.nef`
- 曾經只手動複製了 `kneron_bridge.py`、**漏掉 `firmware/`** → 裝置 reset 後 bridge 重啟時找不到 firmware → 停在 Loader → `load_model Error 24`
**為什麼會發生**
- 手動同步「就複製那個我改到的檔案」很直覺,但 bridge 執行期會去讀 `firmware/``drivers/` 這些不會每次都動、容易被忘記的相依資源。
- 打包流程之所以沒事,是因為 Makefile 三平台都用整包搬(`cp -R server/scripts/*` / `find | cp` 整包),不是逐檔挑。
**解法**
- 開發模式同步時,`dist/scripts/` 一律帶齊 `kneron_bridge.py` + `firmware/` + `drivers/``dist/data/` 帶齊 `models.json` + `.nef`。不要只複製「這次改到的那個檔」。
**如何預防 / 根治**
- 走打包流程驗證而不是手動同步:`make payload-macos`progress.md L413 實跑驗證過 firmware/drivers 都在)。
- 已寫入 memory`~/.claude/projects/-Users-jimchen-visionA/memory/project_local_tool_dev_env.md`
- 來源progress.md L409-414。
---
### §2 除錯時 curl 戳 flash 與 UI 搶同一顆 USB → SIGABRT
**症狀**
- 使用者端顯示「載入模型失敗」。
- terminal 出現 libusb 的 assert`darwin_usb.c:584`,程式 SIGABRT 直接掛掉。
**根因**
- 除錯時用 `curl` 觸發 flashload model**同時**使用者還在用 UI 操作同一顆裝置 → 兩個行程同時對同一個 USB endpoint 下命令 → libusb 在 macOS 上直接 assert / abort。
**為什麼會發生**
- USB 裝置不是可多路複用的資源。KneronPLUS SDK / libusb 沒有替你做互斥;兩個 client 同時搶就炸。
**解法 / 預防**
- **除錯期間不要碰裝置**——要嘛用 curl 手動測、要嘛用 UI 測,不要兩個同時來。
- 這不是 code bug是除錯操作紀律。記住這個 assert 訊息(`darwin_usb.c:584`),下次看到就知道是雙頭搶 USB不用往 code 裡挖。
- 來源progress.md L412。
---
### §3 bootstrap-windows 的 MSYS2 PATH → Go / wails / node / pnpm 找不到
**症狀**
- Windows build 時,`go` / `wails` / `node` / `pnpm` 明明用 winget 裝好了,跑 make 卻報 `command not found`
**根因**
- Windows 沒有原生 make本專案透過 `C:\msys64\usr\bin\bash.exe -l`**login shell**)來跑 Makefile。
- login shell 啟動時會重跑 `/etc/profile`**把 PATH 整個重建**成 MSYS2 自己的一套Windows 上用 winget 裝的工具目錄就這樣被洗掉了。
- `MSYS2_PATH_TYPE=inherit`bootstrap 有設,`bootstrap-windows.ps1:74`**只影響 MSYS2 自己的 shell 啟動器**`msys2.exe` / `mingw64.exe`);直接呼叫 `bash.exe -l` 時它不生效。
**為什麼會發生**
- `inherit` 的作用範圍與「直接呼叫 bash.exe -l」的實際入口不重疊是一個很容易誤解的設定。這也是為什麼 Inno Setup 呼叫、Python 呼叫本來就必須各自手動 export PATH。
**解法**
- 跟 Inno Setup 一樣,**明確把工具目錄轉成 MSYS2 路徑格式(`C:\foo``/c/foo`)後再 export**,補在 `$PATH` 之前。
- 實作見 `bootstrap-windows.ps1:73-113`(註解完整說明)、`:200`(去重合併成單一 export避免多行 export 互相覆蓋)、`:284-294`
**如何預防 / 根治**
- 任何要在 MSYS2 `bash.exe -l` 下被找到的工具,都不能依賴 `inherit`,一律顯式 export 轉譯後的 MSYS2 路徑。
- 來源:`bootstrap-windows.ps1:73-113`、progress.md L1401M7 同型坑的前身)。
---
### §4 copy_bundled_data 不冪等 → 「預期 2 個,實際 8 個」
**症狀**
- Build 後置檢查失敗:`!! ERROR: 預期 2 個 .nef實際 8 個 !!`build 中止。
**根因**
- `payload-windows` / `payload-linux` **刻意不 `rm -rf` 整個 `payload/<os>/`**(因為 `build-server-*` 已先把 binary 放進 `bin/`),所以前次 build 的 `data/` 殘留會留著。
- 白名單機制M5-a、`BUNDLED_NEFS` 只留 2 個 .nef上線前舊 build 會把 8 個 .nef 全複製進去。升級到白名單版本後再 build就變成「新複製 2 個 + 殘留 6 個 = 8 個」,被後置檢查擋下。
- `payload-macos` 沒踩到,只是因為它有 `rm -rf payload/darwin`
**為什麼這個後置檢查是「對的」**
- installer`installer/windows/visiona-local.iss`)是用 `data\* + recursesubdirs` **整包收**,殘留什麼就出貨什麼。所以「數 .nef 個數不符就 fail」是防止安裝包偷偷多帶不該帶的模型——這個 fail-loud 檢查要保留。
**解法**
- `copy_bundled_data` helper 先**清空整個目標 data 目錄**再複製(步驟 (a)),確保重複執行結果一致、不受既有內容影響。
- 為什麼清整包而不是只清 `nef/`:目標目錄內容 100% 由這個 helper 產生Makefile 中只有這裡寫入 `payload/<os>/data/`),沒有其他來源需要保留;只清 `nef/` 的話,未來從 `server/data/` 移除的其他檔案殘留仍會被 installer 整包打包出貨。清整包才是真正冪等。
- 實作 + 完整註解見 `Makefile:39-118``define copy_bundled_data`)。
**如何預防 / 根治**
- helper 內建 fail-loud 後置檢查:白名單缺檔直接 fail`Makefile:104-108`)、複製後驗 `models.json` 存在(`:101-103`+ .nef 數量 == 白名單長度(`:113-116`)。
- **不用 rsync**Windows CI 跑在 Git Bash`windows-2022` + `shell: bash`),該環境**沒有 rsync** → 一律用 POSIX `find` / `cp``Makefile:71-72`)。
- 來源:`Makefile:39-118`、progress.md L112-124。
---
### §5 input size 優先序錯 → Windows 推論 Error 12
**症狀**
- Windows 上推論回 `KP_ERROR_INVALID_PARAM_12`Error 12
- 同一個模型在 macOS 上正常。
**根因**
- input size 的來源可信度排序,**declared使用者在上傳表單手填的值不能排在檔名解析之前**。
- 上傳表單的 `inputSize` 欄位長期沒有實際作用,使用者是「隨手填」的(實際案例:填 640×640模型其實是 224×224
- KneronPLUS **3.1.2Windows不再提供 2.0.0macOS的 `shape_onnx` 屬性**SDK 這層在 Windows 直接落空 → 於是**垃圾 declared 值成為實際採用值**,送進 NPU 得到 Error 12。
- 檔名的 `wNNNhNNN` 是模型編譯工具鏈產生的,沒有人為亂填空間,**明確解析出來時比 declared 可信**。
**為什麼是 Windows 才炸**
- 見 §附錄「KneronPLUS 版本相容性」macOS 用 2.0.0、Windows 用 3.1.2`shape` 欄位在 3.x 搬進巢狀 union`TensorDescriptor.tensor_shape_info.data`),舊寫法 `getattr(node, "shape_onnx")` 在 3.1.2 拋 AttributeError 被靜默吃掉 → SDK 來源落空 → 掉到 declared。
**解法**
- input size 可信度排序改為:**SDK(0) → filename(1) → declared(2) → known-model-id(3) → default(4)**。declared 降到第 3。
- 常數與完整根因註解見 `kneron_bridge.py:351-371``INPUT_SIZE_SOURCE_RANK`)。
- 逐軸解析(不再沿用單一純量 `_model_input_size`,因為模型輸入不保證正方形):`kneron_bridge.py:159-193`
- 相容兩版 SDK 的 shape 讀取(先試 3.x 巢狀 `tensor_shape_info`、再退平鋪欄位):`kneron_bridge.py:479-523`
**如何預防 / 根治**
- 實機驗收時 grep log 印出的 input size source`kneron_bridge.py:347-349` 說明這是唯一能一眼看出尺寸怎麼來的線索)——尺寸錯掉時 NPU **不一定報錯**,可能只是安靜給錯結果。
- ⚠️ 這是 §10「湊巧正確」教訓的實例舊版檔名猜 224 湊巧對,改成「更可信」的 declared 反而錯。
- 來源:`kneron_bridge.py:345-376, 479-523`
---
### §6 venv 半套安裝 → 永久卡住、怎樣都好不了
**症狀**
- 每次啟動都跑「Python 相依驗證未通過」,但又不重裝、也沒有提示,怎樣都好不了。
- 只能手動刪掉 `runtime/venv` 才能恢復。
**根因**
- 舊邏輯只檢查 `python.exe``venv/Scripts/python.exe``venv/bin/python3`**存在**就跳過安裝。
- 但「python 執行檔存在」**不等於**「相依裝好了」pip install 中途失敗(斷網 / 磁碟滿 / 防毒攔截)會留下 venv 與 python.exe 都在、但 `import kp` 失敗的**半套環境**。
- 於是每次啟動都「看到 python.exe → 跳過安裝 → 但 import 失敗」,永久卡死且無提示。
**解法**
- venv 已存在的日常啟動路徑改為「驗健康、必要時嘗試修復」:`reuseExistingVenv``app.go:1078-1103`)。
- venv 在但相依看起來壞了 → **只補裝 wheels、不整個重建 venv**(重建要重解壓 ~100MB tarball而失敗幾乎都出在 pip 階段):`app.go:1091-1093`
- 修復失敗時記錄 warning 但**不阻斷啟動**(見 §7改以現有 venv 繼續:`app.go:1096-1101`
**如何預防 / 根治**
- 狀態檢查要驗「真的能用」而非「檔案在」——這是 §10 的橫向教訓。
- 快路徑用標記檔 `venv-ready.txt` 記錄「wheels 已成功裝完 + 當前 wheels 指紋」,指紋不符才真的跑一次 import 驗證(`app.go:1105-1152`)。
- 來源:`app.go:995-1103`、progress.mdM4 之後的 fix
---
### §7 健康檢查阻斷啟動(自造 regression→ 健康的 venv 被判壞、擋住啟動
**症狀**
- 環境明明是好的bridge 實際能跑、能掃到裝置app 卻因「Python 相依驗證失敗」擋住啟動。
**根因(這是修 §6 時自己造成的 regression**
- §6 的健康檢查 probe 裸跑 `python -c "import kp"`**沒有補 Windows 的 DLL 搜尋路徑**。
- `kp` 在 import 時就會 `ctypes.CDLL` 載入 `kp/lib` 下的 native DLLlibkplus / libusb-1.0 / libwdi + MinGW runtime共 6 個。Windows 上這些 DLL 不在預設搜尋路徑、必須先 `add_dll_directory`
- 真正跑 bridge 時 `kl720_driver.go``startPython()``platform_windows.go` 的 driver 安裝腳本**有**做這件事,但 probe 沒做 → 健康的環境也 import 失敗 → 被判壞掉 → 擋住啟動。
**解法**
- probe 的環境要盡量貼近「真正跑 bridge 的環境」:`pythonProbeScript` 加一段 preamble先把 `site-packages/*/lib` 掛進 `PATH` + `os.add_dll_directory` 再 import`app.go:1182-1205`)。
- **更重要的原則:健康檢查絕不可以成為啟動的阻斷點。** 完整理由見 `app.go:999-1016`
1. 這個 probe 會誤判(環境貼合度永遠不可能 100%)。
2. 就算真的壞了,讓 bridge 自己回報「掃不到裝置」這類具體錯誤,比在啟動前用一個間接的 probe 攔死更好——kneron_bridge.py 本來就把 import kp 失敗當可降級的情況處理。
- 型別設計落地這個原則:`reuseExistingVenv` **沒有 error 回傳值**`app.go:1078-1081`)——型別本身就保證了「健康檢查不會阻斷啟動」。
**如何預防 / 根治**
- 加任何「啟動前健康檢查」時,先問:檢查失敗會不會擋住啟動?如果會,它就有能力誤殺健康環境。健康檢查應該是「發警告 + 嘗試修復」,不是 gate。
- 來源:`app.go:999-1016, 1078-1103, 1154-1205`
---
### §8 wheels 多版本 → pip ResolutionImpossible、app 一啟動就秒退
**症狀**
- App 首次啟動安裝依賴時pip 在 ~2 秒內 `ResolutionImpossible` 失敗,使用者完全無法啟動。
- vendor 目錄裡同一套件有多顆(如 `certifi ×3``numpy ×2``idna ×2`)。
**根因**
- `vendor-wheels*``pip download --dest`,而 `pip download` **只「補下載缺的」、不移除舊版**
- upstream 每發一次新版,`vendor/wheels/<os>` 就多留一顆 whl → 實機累積成 16 顆(正常應為 9 顆)。
- 這些多版本原封不動被 `payload-*` 複製進安裝包 → app 啟動 `pip install a.whl b.whl ...` 同時收到 `certifi 2026.2.25``2026.6.17` → 直接 ResolutionImpossible。
- **debug 成本放大**:這個 pip 錯誤還被 Makefile 的 Auto 分支吞掉(`|| echo WARN`),導致三輪來回都拿不到線索。
**解法(兩層都要)**
- **源頭**`clean_wheels_dir` helper 在 `vendor-wheels*` 前先清空並重建 `vendor/wheels/<os>/`,確保冪等、不累積多版本(`Makefile:143-166`,完整事故迴歸註解在 `:121-142`)。
- **救援**app 端 `selectLatestWheelPerPackage` 對每個套件只保留版本最高的一顆再交給 pip`app.go:1314-1325+`),能救「已出貨」的舊安裝包。
- 兩層都要有:源頭杜絕讓新安裝包乾淨、救援讓舊安裝包也能自癒。
**如何預防 / 根治**
- 版本比較用逐段整數比較的簡化 PEP 440`compareWheelVersions``app.go:1282-1312`);套件名依 PEP 503 正規化,讓 `opencv_python_headless``opencv-python-headless` 視為同套件(`normalizeDistName``app.go:1255-1271`)。
- **錯誤不要被吞掉**:這條的三輪 debug 成本就是 pip 錯誤被 `|| echo WARN` 吞掉造成的。詳見 §10。
- 來源:`Makefile:121-166``app.go:1207-1325`、progress.md L135-136。
---
### §9 Windows 中文亂碼 → 標籤「布」變「撣<E3808C>
**症狀**
- Windows繁中上 classification 標籤亂碼「布」變「撣<E3808C>」。
- stderr log 的中文與 em dashU+2014出現 `<60>`(如 `<60>X SDK did not report`)。
**根因**
- Windows 的 Python 把 stdio 綁到「系統 ANSI code page」而非 UTF-8。繁中 Windows 的 ANSI code page 是 **cp950**
- bridge 的 JSON-RPC 兩個方向**不對稱**
- **Go → Python (stdin)**Go 的 `encoding/json` **不** escape 非 ASCII`{"labels":["布"]}` 在 wire 上是 raw UTF-8 `e5 b8 83`。以 cp950 解碼得到「撣」+ U+FFFD截圖的亂碼嚴格模式下直接 UnicodeDecodeError 讓整個 bridge 掛掉。
- **Python → Go (stdout)**`json.dumps` 預設 `ensure_ascii=True`,中文被 escape 成 `\uXXXX` 純 ASCII所以這條「目前剛好沒壞」——但那是隱性依賴任何人加上 `ensure_ascii=False` 就會壞。
- **stderr**`_log()` 的中文與 em dash 現在就會壞。
**解法**
- `_force_utf8_stdio()` 把 stdin / stdout / stderr **一律** `reconfigure(encoding="utf-8", errors="replace")``kneron_bridge.py:22-71`),不理會系統預設編碼。
- **在 module import 時就呼叫**`kneron_bridge.py:71`),不是在 `main()` 裡——必須早於任何 I/O`main()``os.dup()` stdout、import 期間的例外也走 stderr
-`errors="replace"` 而非 `strict`:這是診斷 log 通道與協定通道,遇到極端無效位元組寧可看到一個 U+FFFD 也不要讓整個 bridge 因一行 log 而崩潰bridge 掛掉 = 裝置失聯,比壞字元嚴重)。
**如何預防 / 根治**
- 把「stdout 是 UTF-8」變成**顯式契約而非巧合**(連目前沒壞的 stdout 方向也一併綁定)。
- ⚠️ `PYTHONUTF8``PYTHONIOENCODING` 管的範圍不同:前者是整個 Python 的 UTF-8 mode含檔案系統編碼等、後者只管 stdio 的編碼。這裡用程式內 `reconfigure` 是最直接、不依賴環境變數是否被正確傳入的做法。
- 測試釘死:`server/scripts/test_kneron_bridge_encoding.py`
- 來源:`kneron_bridge.py:22-71`
---
## §10 共通教訓(橫向 pattern
**這節比逐條 bug 更有價值**——上面 9 個雷裡反覆出現的幾個 pattern下次寫 code / debug 時記住這些,能少踩一半。
### 10.1 macOS 開發 ≠ Windows 實機
好幾個雷都是「macOS 有、Windows 沒有」或「兩邊行為不同」造成的。macOS 上測不到、只有 Windows 實機才會爆的類別:
| 類別 | macOS | Windows | 踩到的雷 |
|------|-------|---------|---------|
| stdio 編碼 | 預設 UTF-8 | 預設 cp950繁中 ANSI code page | §9 中文亂碼 |
| native DLL 搜尋路徑 | dyld可用 ctypes 絕對路徑預載) | 需 `add_dll_directory`、不在預設路徑 | §7 健康檢查誤殺 |
| KneronPLUS SDK 版本 | 2.0.0(有 `shape_onnx` | 3.1.2shape 搬進巢狀 union | §5 input size Error 12 |
| shell / make | 原生 bash + make | MSYS2 login shell 重建 PATH | §3 工具找不到 |
| 既有環境 | 開發機常有現成工具 | 乾淨機、什麼都要 bootstrap | §3 / §4 |
**原則**:任何「編碼 / DLL 路徑 / SDK 版本 / code page / shell」相關的東西**不能只在 macOS 驗**,一定要 Windows 實機或至少想清楚兩邊差異。
### 10.2 「檔案存在」≠「可用」
§6venv 半套安裝)和 §7健康檢查都栽在這。
- venv 只檢查 `python.exe` 存在就跳過安裝 → 半套環境永久卡死。
- 狀態檢查要驗**「真的能用」**(實際 import / 實際跑一次),而非「檔案在」。
- 但驗「能用」時要注意 §7 的教訓:驗證環境要貼近真實執行環境,否則會誤殺健康的環境;而且**健康檢查不能是啟動阻斷點**。
### 10.3 錯誤被吞掉會讓 debug 成本爆炸
§8wheels 多版本)的三輪來回,根因是 pip 錯誤被 Makefile 的 `|| echo WARN` 分支吞掉,拿不到真正的錯誤訊息。
- 吞錯誤 = 把「一次就能定位」變成「三輪還在猜」。
- fail-loud 優於 fail-silent。§4 的後置檢查(數 .nef 個數不符就直接 fail就是正面教材——它讓「安裝包偷偷多帶模型」在 build 期就爆,而不是等出貨後使用者才發現。
- 對照 §5input size 錯掉時 NPU **不報錯只給錯結果**——這種「靜默失敗」最貴,所以要靠 log 印出 source 當唯一線索。
### 10.4 「湊巧正確」的行為改動要格外小心
§5input size 優先序)是經典案例:
- 舊版用檔名猜測,某些模型湊巧猜到 224、剛好對。
- 改成「看起來更可信」的 declared使用者手填值反而錯——因為使用者是隨手填的。
- **教訓**當你把一個「能動但你覺得不夠嚴謹」的邏輯改成「更正確」的版本時先確認新來源真的更可信。「更正式的欄位」不代表「更可信的值」——declared 是正式欄位、但值是垃圾。
### 10.5 兩層防護(源頭 + 救援)
§8 的解法是兩層都做:`clean_wheels_dir` 從源頭杜絕(新安裝包乾淨)+ `selectLatestWheelPerPackage` 救援(舊安裝包自癒)。
- 只做源頭 → 已出貨的舊安裝包救不了。
- 只做救援 → 每個新安裝包都帶著髒 vendor 出貨、依賴 app 端補救。
- 涉及「已出貨產物」的問題,通常源頭與救援都要有。
---
## §附錄 AKneronPLUS 版本相容性
三平台的 KneronPLUS wheel 版本**不一致**,這是好幾個雷的隱形根因。寫任何碰 SDK 的 code 時必讀。
| 平台 | KneronPLUS wheel 版本 | 影響 |
|------|----------------------|------|
| macOS | 2.0.0 | `TensorDescriptor` 有平鋪的 `shape_onnx` / `shape_npu` 屬性 |
| Linux | 2.0.0 | 同上 |
| Windows | 3.1.2 | **無** `shape_onnx` 屬性shape 搬進巢狀 `tensor_shape_info.data`V1/V2 unionDLL 需求更嚴 |
**具體差異與注意事項**
1. **shape 欄位位置不同**(→ §5 Error 12
- 2.0.0`node.shape_onnx` / `node.shape_npu` 直接可讀。
- 3.1.2`node.tensor_shape_info.version``ModelTensorShapeInformationVersion`+ `.data``TensorShapeInfoV1``shape_onnx`/`shape_npu``TensorShapeInfoV2` 只有 `.shape`docstring 明寫是 ONNX shape
- 舊寫法 `getattr(node, "shape_onnx")` 在 3.1.2 拋 AttributeError → 被 except 靜默吃掉 → SDK 這層以為「模型沒帶 shape」其實是讀錯欄位。
- 相容兩版的讀法見 `kneron_bridge.py:479-523`(先試巢狀、再退平鋪)。
2. **native DLL 需求**(→ §7 健康檢查誤殺)
- `import kp` 在 Windows 會載入 `kp/lib` 下的 native DLLlibkplus / libusb-1.0 / libwdi + MinGW runtime共 6 個),不在預設搜尋路徑 → 需 `add_dll_directory`
- macOS 走 dyld可用 ctypes 絕對路徑預載(`_preload_kneron_dylibs_macos``kneron_bridge.py:74+`)。
3. **wheel 三平台版本不一致本身是風險**progress.md M9-6 findings L587
- 若未來要加 KL630/KL730 等新晶片支援macOS/Linux 的 2.0.0 wheel **沒有**對應 enum必須先升 wheel。
- `update_kdp_firmware_from_files` 在 3.1.2 Python wrapper 中不存在warrenchen 是 ctypes 直打 .so C symbol這類 API 差異在跨版本時要逐一確認。
**原則**:任何讀 SDK 結構shape / enum / API 簽章)的 code都要同時對 2.0.0 與 3.1.2 驗,或明確寫成「先試新版結構、失敗再退舊版」的相容寫法。
---
## §附錄 BBuild 完驗收 Checklist
Build 完一個安裝包後,**至少**驗這些(每項標了對應的雷,避免重蹈覆轍):
- [ ] **.nef 數量正確**:打包版(不是開發模式)確認只帶白名單的 2 個 `.nef``FCOS Detection` / `Tiny YOLOv3`),不是 8 個。→ §4
(開發模式驗不出來:`server/data/nef/` 8 個都在、過濾器只濾「檔案不存在」,開發模式會顯示 7 個模型。M5 必須用打包版驗、progress.md L206。
- [ ] **models.json 有進安裝包**:啟動後 `/api/models` 回非空、log 印 `Loaded N built-in models`(不是 0
- [ ] **中文顯示正常**Windows 繁中機classification 標籤、log 的中文都不亂碼。→ §9
- [ ] **input size source 正確**:實機推論後 grep log 的 input size source確認不是掉到 declared / default 撿到垃圾值Windows 上尤其要看。→ §5
- [ ] **推論不報 Error 12 / Error 24**Windows 推論不 Error 12§5、裝置 reset 後 load_model 不 Error 24§1
- [ ] **Python venv 健康**:全新機器首次啟動能自動裝好 wheels不 ResolutionImpossible §8且啟動不被健康檢查誤擋§7
- [ ] **driver 綁定**Windows 上 KneronPLUS native DLL 能被載入(`import kp` 成功、掃得到裝置)。→ §7 / 附錄 A
- [ ] **Wails 視窗確認****開 app window** 確認主 UI 是 Next.js 而非 splash / installer wizard / 白畫面progress.md L1602 的歷史教訓M1 只用瀏覽器連 localhost 驗、沒開 window讓 wizard 殘留混過 M1-M6
**驗收原則**不要只驗「server 有回應」——很多雷§1 / §4 / §5 / §9`/api/health` 200 的情況下照樣存在。要驗到「真的能推論 + 顯示正確」。
---
## 相關文件
- `build-pipeline.md` — Makefile 骨架、vendor 目錄結構、CI 策略、版本號管理build 流程本體)。
- progress.md`.autoflow/progress.md`)— M10 各段開發紀錄與踩坑細節的一手來源。
- memory`~/.claude/projects/-Users-jimchen-visionA/memory/project_local_tool_dev_env.md` — 開發模式資源同步坑。