lazyBoy/docs/agent-experience.md

92 lines
5.8 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.

# AI 使用體驗
[← 回到 README](../README.zh-TW.md)
這份文件記錄 LazyBoy 怎麼讓「AI 用電腦」這件事變得跟人一樣順,以及背後的取捨。
三個主題其實是同一件事:系統如果把 AI 使用電腦當成一系列無狀態的請求AI 就必須每輪重新
交代自己在哪裡、在做什麼,界面也只能用輪詢去猜現在發生了什麼。
| 主題 | 一句話 | 細節 |
| --- | --- | --- |
| 任務長度 | 沒有輪數額度,只有偵測鬼打牆的保險絲 | [輪次政策](./operations.md#任務跑多久輪次政策) |
| 容器內終端機 | 一台活的 tmux人和 AI 看同一個 shell | [終端機](./operations.md#容器內終端機) |
| 聊天即時性 | 事件推送,輪詢只是兜底 | 本文件下面 |
## 輪數不是額度,是偵錯工具
以前一個 run 固定 40 輪,等於假設所有任務一樣長:洗資料這種事情做不完,但也不會因此變成
錯誤,只是被腰斬。現在的正常結局是**把事情做完**(模型提出驗證,或明白說它卡在哪裡),會
被停下來的只有鬼打牆:同一個會改變狀態的動作連做六次、同一個錯誤連錯八次、連續十四個動作
全部失敗。觀察、等待、輪詢不算重複動作,所以等一個長 build、等下載、輪詢佇列都不會被打斷。
設計上要記住兩件事:
- 提示(「你已經做同一件事三次了」)永远不會停掉任務,只有重複到像故障才會暫停,暫停也是停在
現況、按「繼續」接下去,不重來。
- 保險絲(`budget_exhausted`)代表迴圈失控,是故障不是成績;它照樣可以續跑,但值得去看執行記錄。
純聊天仍然是 4 輪上限:那是避免模型在閒聊裡燒額度,跟任務長度無關。
## 終端機是一台活的 tmux
`shell` 透過 Cua 在共用 VNC 桌面上開啟有名稱的終端機。相同 `session` 保留工作目錄、
環境變數與背景工作;`keys: "C-c"` 會在該終端機送出中斷,`reset` 會換成乾淨的登入 shell。
輸出以桌面截圖回傳。模型必須確認提示字元已回來才能輸入下一條命令;省略 `command`
即可再次查看,長輸出可以透過 Cua 捲動終端機。`wait_ms` 預設 1000、上限 10000
等待結束不會終止命令。終端機若被關閉,下次呼叫會重新開啟,已關閉 shell 的環境不會還原。
檔案列出、分頁讀取與寫入也使用這個可見終端機。所有這些工具都需要視覺模型與桌面控制鎖。
## 聊天是推送,不是輪詢
事件早就寫進 `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 不見得可靠,只是慢。
自己量一次:
```bash
curl -N -b cookies.txt http://127.0.0.1:3101/api/sessions/<id>/events
# 另開一個終端機送訊息,看事件幾毫秒後出現在這條串流裡
```
長任務還有一層上下文保險絲:舊的網頁快照會收成摘要,最新畫面保持完整。這不是另開一個模型做壓縮,而是避免 70 輪 DOM 把 128k 窗口塞爆。細節見 [模型上下文](./operations.md#模型上下文)。
## 已知取捨
- 模型回覆還是「整個完成」才出現,沒有串流 token。思考中的狀態有顯示但要像 ChatGPT 那樣逐字
冒出,需要把 completion 換成串流並處理中斷/接續,這是下一件事。
- 電腦狀態(執行中/暫停、誰在控制)沒有自己的事件,只能靠兜底輪詢更新,`LIVE_POLL_MS` 目前是 4 秒;
桌面畫面本身是即時串流,只有狀態列會慢幾秒。要真正事件化得把 `computers` 的狀態變更也寫進
`events`(狀態是 bot 層、事件是執行緒層,要先決定寫到哪條執行緒)。
- 滑鼠停在頭像上看到的執行記錄泡泡,開啟時仍是輪詢(它只在滑鼠停留時開啟,量很小)。
- wake channel 是單進程的。多副本部署時,其他副本靠 5 秒安全輪詢追上;真要横向擴充應改用
Postgres `LISTEN/NOTIFY`
- 持久終端機需要桌面映像檔裡的 `tmux`;舊映像檔會退回一次性 shell`docker compose build computer`
之後就有了。
- 容器執行檔仍以 uid 1000、限制環境變數的方式進入容器持久化的是 shell 狀態,不是憑證。