lazyBoy/docs/agent-experience.md

6.8 KiB
Raw Blame History

AI 使用體驗

← 回到 README

這份文件記錄 LazyBoy 怎麼讓「AI 用電腦」這件事變得跟人一樣順,以及背後的取捨。 三個主題其實是同一件事:系統如果把 AI 使用電腦當成一系列無狀態的請求AI 就必須每輪重新 交代自己在哪裡、在做什麼,界面也只能用輪詢去猜現在發生了什麼。

主題 一句話 細節
任務長度 沒有輪數額度,只有偵測鬼打牆的保險絲 輪次政策
容器內終端機 一台活的 tmux人和 AI 看同一個 shell 終端機
聊天即時性 事件推送,輪詢只是兜底 本文件下面

輪數不是額度,是偵錯工具

以前一個 run 固定 40 輪,等於假設所有任務一樣長:洗資料這種事情做不完,但也不會因此變成 錯誤,只是被腰斬。現在的正常結局是把事情做完(模型提出驗證,或明白說它卡在哪裡),會 被停下來的只有鬼打牆:同一個會改變狀態的動作連做六次、同一個錯誤連錯八次、連續十四個動作 全部失敗。觀察、等待、輪詢不算重複動作,所以等一個長 build、等下載、輪詢佇列都不會被打斷。

設計上要記住兩件事:

  • 提示(「你已經做同一件事三次了」)永远不會停掉任務,只有重複到像故障才會暫停,暫停也是停在 現況、按「繼續」接下去,不重來。
  • 保險絲(budget_exhausted)代表迴圈失控,是故障不是成績;它照樣可以續跑,但值得去看執行記錄。

純聊天仍然是 4 輪上限:那是避免模型在閒聊裡燒額度,跟任務長度無關。

終端機是一台活的 tmux

shell 工具以前每條命令都是一次性的 bash -lccd 留不住、export 留不住、背景起的服務 跟著一起死,模型每次都要重新走回工作目錄,遇到互動式程式就整個卡死。現在每個終端機名字對應 容器內一個 tmux sessionlazyboy-mainAI 用的就是人在桌面上會用的那個 shell。

有了持久終端機,模型才做得順這些事:

  • python -m venv .venv && source .venv/bin/activate 之後,下一條命令還在同一個環境裡。
  • npm run dev & 之後可以繼續做別的,回來用 log_lines 讀同一個終端機的輸出。
  • sshgdbpsql、Python REPL 這類會問你話的程式,用 keys 回它,而不是直接超時。
  • 按錯 Ctrl-C 只會中斷那台終端機裡正在跑的東西,不會把整個工作環境帶走。

完成是怎麼判定的。 API 進到容器是一次性的 execargv 進、stdout 出,沒有串流),所以 「跑完了沒」不是看 exec 有沒有結束,而是看終端機裡印了什麼:每次呼叫會在 shell 內 source 一個 檔名隨機的腳本,腳本用 trap ... EXIT 印出 LB_END <nonce> rc=<code>;還看到 LB_READY 代表上一件事已經被中斷、終端機是可用的。標記只在行首比對——命令本身會被 shell echo 一次, 那行也帶有標記,比錯就會以為已經跑完了。

死了會自己站起來。 exitexec bash、被 kill 掉都會讓 pane 不見;這時候終端機重新 respawn,並把剛才的工作目錄與 export 過的環境變數還原回去,模型不需要重新交代。

人可以隨時看同一台終端機,或直接接手(指令見 容器內終端機。這不隻是好看AI 卡在同一個地方時,你看到的 就是它看到的那個 shell。

行為用 python3 tests/shell-session.test.py 驗證10 個案例,含 exit code 還原、自我重生、 中斷後可續用)。

聊天是推送,不是輪詢

事件早就寫進 events 表,也有 SSE 端點,但兩端都在猜:伺服器每 750 ms 掃一次資料庫,瀏覽器 每 2 秒重刷一次全部狀態(訊息、電腦狀態、螢幕、技能、心跳一起打)。所以一個回覆最快也要兩輪 輪詢才被看見。

現在的管線:

  1. 寫事件的事實不變——events 表仍然是順序與重放的依據(游標是 threads.next_event_seq 斷線重連用 Last-Event-ID 從資料庫重放,不會漏也不會重複)。
  2. 提交之後往進程內的 wake channel 敲一下(WakeBus。SSE 端點不再固定間隔輪詢,而是等敲; 敲不到時(多副本、讀者落後)以 5 秒兜底輪詢補上。
  3. 瀏覽器用 EventSource 訂閱 /api/sessions/{id}/events,事件在 120 ms 的窗口裡合流, 一回合變動只刷新一次。

實測(本機 loopback、丟棄式資料庫、同一台機器

事件 以前 現在
自己發的訊息出現在畫面 最多 2 秒 送出去後約 25 ms
「思考中」指示出現 最多 2 秒 約 32 msworker 也被敲醒,不再等 200 ms 定時器)
模型回覆出現 寫入後最多 2.75 秒 寫入後約 25 ms模型本身要幾秒是另一回事

順帶把浪費掉的工作拿掉:分頁藏在後面時不再刷新(回到前景立刻補一次),心跳從 2 秒改成 60 秒——一次心跳買的是 15 分鐘控制租約、避免 10 分鐘閒置暫停60 秒是很寬的餘裕。

壞了會怎樣。 SSE 被 proxy 擋住或斷線,瀏覽器察覺後自動回到 2 秒輪詢,等於退回以前的行為, 不會變成不更新伺服器端事件順序仍然只以資料庫為準wake 不見得可靠,只是慢。

自己量一次:

curl -N -b cookies.txt http://127.0.0.1:3101/api/sessions/<id>/events
# 另開一個終端機送訊息,看事件幾毫秒後出現在這條串流裡

已知取捨

  • 模型回覆還是「整個完成」才出現,沒有串流 token。思考中的狀態有顯示但要像 ChatGPT 那樣逐字 冒出,需要把 completion 換成串流並處理中斷/接續,這是下一件事。
  • 電腦狀態(執行中/暫停、誰在控制)沒有自己的事件,只能靠兜底輪詢更新,LIVE_POLL_MS 目前是 4 秒; 桌面畫面本身是即時串流,只有狀態列會慢幾秒。要真正事件化得把 computers 的狀態變更也寫進 events(狀態是 bot 層、事件是執行緒層,要先決定寫到哪條執行緒)。
  • 滑鼠停在頭像上看到的執行記錄泡泡,開啟時仍是輪詢(它只在滑鼠停留時開啟,量很小)。
  • wake channel 是單進程的。多副本部署時,其他副本靠 5 秒安全輪詢追上;真要横向擴充應改用 Postgres LISTEN/NOTIFY
  • 持久終端機需要桌面映像檔裡的 tmux;舊映像檔會退回一次性 shelldocker compose build computer 之後就有了。
  • 容器執行檔仍以 uid 1000、限制環境變數的方式進入容器持久化的是 shell 狀態,不是憑證。