iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0
AI Engineering

從單一agent 到多agent 集群的開發流水帳以及應用系列 第 21

Day 17|中斷五天之後:把 9/10 到 9/14 的紀錄補回來,手機真的成了節點

  • 分享至 

  • xImage
  •  

系列:「一人艦隊:用一群 AI CLI 打造跨平台代理 mesh」— 第 17 篇,不設天數
紀錄日期:2026-09-15

先道歉

這個系列從 9/9 的 Day 16 之後停了五天。原因很單純:那幾天在趕一個跨機 demo,每天做到很晚,文章排在最後一件事,最後一件事就掉了。鐵人賽的 30 天連續挑戰在這裡算失敗,我不打算找理由。

但這個系列不會停。開賽時我就寫過「會一直寫到我的專案真的每天在服務我為止」,這句話還算數。之後每天照樣記錄,直到專案完成;賽制上算不算完賽,不影響我寫。今天同時另開了一個新系列(Day 01|先畫地圖再上路),那邊寫「AI 工程師的能力」,這邊繼續寫專案本身,兩邊互相引用,不重複。

這篇把中斷期間的紀錄補回來。材料全部來自 git log 和各台機器的報告,數字是真的,沒做到的也照寫。

9/10 到 9/11:零 commit

先誠實:這兩天 repo 上一個 commit 都沒有。不是在做別的功能,是真的沒碰。

9/12:先把契約寫下來,再動手

四個 commit,全部是文件,沒有程式:

  • 統一 v0.7.1 的發布契約(RC-071),把 requirement/test ID 和平台 profile 定成一份。
  • 定義「iPhone 與 M1 的 demo 契約」:手機不是遠端遙控器,而是艦隊裡的一個節點。
  • 定義手機端的 coding 執行 profile,以及 iOS 上的 sandboxed agent profile:手機上不可能跑 CLI,這點在文件裡直接寫死,不留想像空間。

這一天決定了接下來三天的方向。Day 13 我寫過「手機也是工作節點」,當時只是目標;9/12 把它變成了一份有欄位、有雜湊、有失敗碼的 API 契約(MONDAY-MESH-API):roster 怎麼簽章、fan-out 的 body 長什麼樣、participants 要排序後做 sha256、停止指令送出後畫面只能顯示「停止中」,要等到終止事件才准寫「已停止」。這份契約後來成了每一台機器、每一個 AI CLI 對齊的唯一依據。

9/13:MacBook Pro M5 上線,一天之內從零到手機端到端

這台 M5 是 9/13 才加入艦隊的節點,代號 m5。當天的紀錄:

上午:盤點。 五個 AI CLI 的版本與狀態:Codex 0.154.0(配額用完,要到 9/19 才恢復)、Claude Code 2.1.261、OpenCode 1.18.30、AGY 1.2.2、Copilot 沒裝。這份清單寫進報告,因為後面派工要知道哪台的哪個 CLI 能用。

中午:先寫 fixture,再寫畫面。 手機端的 Mesh 畫面和 App-local executor 完全對著契約寫。不先開 App,先用契約裡的 roster 和 events 範例當測試資料,把 participants 排序、confirmation 雜湊、事件游標做到和 core 的 Rust 端一模一樣。這裡抓到兩個如果直接寫畫面絕對不會發現的 bug:

  • participants 的排序我用了 JavaScript 的 localeCompare,core 用的是 byte-wise 比較,兩邊算出的 participants_sha256 不一樣,fan-out 會被 coordinator 直接拒絕。改成 byte-wise,加一個 golden 測試把正確的 sha 釘死。
  • 事件游標:core 的語意是 seq 大於 since,next_seq 是 max 加一。我原本用「收到幾筆」當游標,會漏事件。

下午:手機不能跑 CLI,那它跑什麼。 契約第 6 節給的答案是 App-local executor:手機拿自己的 API key(BYOK),推論在雲端 provider,手機只負責 pull 任務、報告結果、附上 nonce 回顯和 output 的 sha256。手機在畫面上明確標示「remote inference via groq」,不准寫成「在 iPhone 上本機執行」。

晚上:真機。 iOS 和 iPad 各裝一版,過程中踩到的坑,每一個都花了時間:

  • iPadOS 的 WebView 說自己是「Macintosh」。App 因此選了桌面殼、走了錯的網路路徑、device_id 也猜錯。三處都改用 maxTouchPoints 判斷。
  • App 啟動時根本沒讀 env 檔,key 一重開就消失。
  • Groq 免費層的 llama-3.3-70b 一夜之間 404 退役,換成 qwen。
  • 有一台的設定把 base URL 寫成 …/openai/v1,程式再接一次 /openai,變成 /openai/openai/v1,404。core 端修成不會重複接。
  • 真機走 USB 的 link-local 路徑、iOS 的本地網路隱私權限,各卡一次。

當天最後一版 IPA 裝上 iPhone 和 iPad,兩台都能 pull、能報告,receipt 驗證通過。另外把 App icon 改成和桌面版同一張圖,介面砍到只剩 Mesh 和設定兩個分頁:demo 只需要這兩個,其他都是干擾。

同一天還補了契約 §3.1 的「規劃式 fan-out」:coordinator 先把 prompt 拆成每台一份子任務,再派下去。真機上跑出六份不同的子任務,receipt 全部驗證過。

9/14:接上 Z13 的艦隊,從手機畫面跑完整個 fleet

接入。 這台 Mac 加進由 Z13 當 coordinator 的 mesh:驗證 cluster secret 的 SHA-256 前綴、備份設定、只改 peers 和 secret、重啟 daemon、用簽章請求打 Z13 的 /rpc/mesh/self 確認身分。過程中出了一次錯:我用 sed 做遮罩時,工具輸出把新舊密鑰都印了出來。這個違反了「全程不印密鑰」的規則,當場承認,建議輪換。之後遮罩改用 awk,不再出事。

Fleet fan-out。 從 Z13 發起,8 個參與者全部完成,含真機 iPhone 和 iPad,跑了兩次都是 8/8。手機畫面的預設勾選改成「coordinator 凍結的參與者集合」,因為契約要求 fan-out 的 participants 必須和凍結的 manifest 逐 byte 相符,勾錯一個就會被拒。

從手機畫面發起。 12 個參與者跑完 13 個 attempt,其中 ipad-pro 拿到一個 Groq 429:四支手機共用一把 key,免費層每分鐘 1000 output token 的上限一下就撞到。畫面上紅字誠實顯示 429,沒有藏。應對是 maxTokens 從 512 降到 256,之後每支手機要用自己的 key。

完整回答。 到這裡,手機能「一個 prompt → 每台各自輸出」,但沒有「合成一份回答」。當天補上:task 結束後,發起端手機用自己的 BYOK 模型把所有參與者的輸出合併成一份,顯示在畫面最上方,標示「本機彙整,非 receipt」。實測 task 2bb27031:12 個參與者全部完成,合併出一份三段式的回答。


(iPad 畫面:合併後的完整回答)


(iPad 畫面:每台機器各自的子任務、pid 與輸出)

兩件沒做到的事,照寫。

  • iPhone 那時上鎖,App 不在前景就不會 pull,所以這輪 12 個參與者不含它。契約規定手機只在前景執行,這是設計,不是 bug,但 demo 時要有人拿著手機。
  • Z13 的 daemon 中途重啟了一次,凍結的 manifest 直接消失,fan-out 被拒,得從 Mac 重新凍結才能送。manifest 不落地,是契約裡已知的限制,這次又撞到一次。

五天的數字

項目 數字
分支上的 commit(所有節點,9/12 起) 128
其中這台 M5 的 commit 35
艦隊節點 9 台(6 桌機、3 手機)
從手機畫面發起、全部完成的 fleet task 12/12
真機 iPhone + iPad 參與且 receipt 驗證通過的 fleet run 2 次,各 8/8
誠實記錄的失敗 Groq 429 一次、密鑰誤印一次、manifest 隨重啟消失一次

接下來

  • 手機在背景 30 秒內要變 offline 的測試(DEMO-m5-04)還是 NOT_RUN,下一篇補。
  • 每支手機用自己的 API key,避免共用一把撞 429。
  • 合併回答目前是發起端手機自己做的,不進 receipt。已經向 coordinator 那邊提了契約備註:加一個 coordinator 端的 synthesis 步驟並附 receipt,讓桌面 App 和手機看到同一份最終回答。
  • Codex 9/19 才有配額,這幾天派工繞開它。

明天照常。


上一篇
Day 16|從「已停止」到真的停下來:補齊 Mac 與 iOS 的生命週期
系列文
從單一agent 到多agent 集群的開發流水帳以及應用21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言