127 lines
8.9 KiB
Markdown
127 lines
8.9 KiB
Markdown
|
|
# 技術架構
|
|||
|
|
|
|||
|
|
AnyBot 是一個 Rust 程式(`anybot`),用 egui/eframe 畫介面。同一個執行檔有三種跑法:
|
|||
|
|
|
|||
|
|
| 跑法 | 指令 | 用途 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| 視窗 | `anybot` | 桌面 App |
|
|||
|
|
| 無視窗 | `anybot --serve` | 伺服器、一直開著的電腦、手腳:介面邏輯照跑,只是不畫畫面 |
|
|||
|
|
| 設定 | `anybot setup …` | 終端機裡把這台接到帳號(`src/setup.rs`) |
|
|||
|
|
|
|||
|
|
另一個小程式 `anybot-relay`(`relay/`)是帳號伺服器。
|
|||
|
|
|
|||
|
|
## 程式分層
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────── 介面(src/app.rs、src/app/*)───────────────┐
|
|||
|
|
│ 聊天、側邊欄、設定、電腦、檔案預覽、大腦同步(mirror_ui) │
|
|||
|
|
│ 每個畫面一個 App 方法;狀態全部在 App.state(model::State) │
|
|||
|
|
└───────┬───────────────────────────────┬────────────────────┘
|
|||
|
|
│ 送出訊息 │ 手機/其他 AnyBot 的要求
|
|||
|
|
┌───────▼──────────┐ ┌────────▼───────────────────┐
|
|||
|
|
│ 回合(agent.rs) │ │ 遠端(remote.rs) │
|
|||
|
|
│ 呼叫模型、跑工具、 │ │ 手機 API、裝置連結、大腦同步 │
|
|||
|
|
│ 等你核准、整理摘要 │ │ 長連線(tunnel.rs、wire.rs) │
|
|||
|
|
└───┬─────────┬────┘ └──────────────────────────────┘
|
|||
|
|
│ │
|
|||
|
|
┌───▼───┐ ┌───▼──────────────────────────────────────────────┐
|
|||
|
|
│ 模型 │ │ 工具(tools.rs 為主) │
|
|||
|
|
│ provi- │ │ 檔案與指令、Bot 的電腦(cloudbox.rs)、瀏覽器 │
|
|||
|
|
│ ders.rs│ │ (botbrowser.rs、browser.rs、web.rs)、螢幕操作 │
|
|||
|
|
└────────┘ │ (computer.rs、desktop.rs、axread.rs;Linux:x11.rs、 │
|
|||
|
|
│ wl.rs、portal.rs、desk.rs)、Mac App(macapps.rs)、 │
|
|||
|
|
│ 保險庫(vault*.rs)、MCP(mcp.rs)、記憶(memory.rs) │
|
|||
|
|
└──────────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| 檔案 | 做什麼 |
|
|||
|
|
|---|---|
|
|||
|
|
| `model.rs` | 資料:State、Bot、Message、設定;讀寫 `state.json` |
|
|||
|
|
| `agent.rs` | 一個回合:組系統指示、送模型、收串流、跑工具、核准關卡、長對話摘要 |
|
|||
|
|
| `providers.rs` | 各家模型的 API(OpenAI 相容、Anthropic、OpenCode Go…)、串流、重試、看不到圖時的看圖小幫手 |
|
|||
|
|
| `tools.rs` | 檔案、指令、背景指令;指令在 macOS 沙盒裡跑(見「安全」) |
|
|||
|
|
| `policy.rs` | 哪些動作直接做、先問、拒絕 |
|
|||
|
|
| `cloudbox.rs`、`box/` | Bot 自己的電腦:一個 Docker 容器(有桌面、瀏覽器) |
|
|||
|
|
| `computer.rs`、`desktop.rs`、`axread.rs` | 看畫面、滑鼠鍵盤;讀 App 的按鈕清單(agent-desktop) |
|
|||
|
|
| `botbrowser.rs`、`browser.rs`、`web.rs` | AnyBot 自己的瀏覽器(Chrome DevTools 協定)、讀你的瀏覽器、抓網頁 |
|
|||
|
|
| `routines.rs`、`triggers.rs` | 例行任務和觸發 |
|
|||
|
|
| `channels.rs` | 訊息管道(Slack、Discord、Telegram) |
|
|||
|
|
| `remote.rs` | 手機 API、帳號登入、裝置連結、大腦同步的請求端 |
|
|||
|
|
| `tunnel.rs`、`wire.rs` | 和帳號伺服器之間的長連線(WebSocket) |
|
|||
|
|
| `app/mirror_ui.rs` | 大腦和螢幕之間的同步 |
|
|||
|
|
| `setup.rs` | `anybot setup` |
|
|||
|
|
| `httpd.rs` | 很小的 HTTP 伺服器(AnyBot 和帳號伺服器共用) |
|
|||
|
|
| `mobile/index.html` | 手機網頁(單一檔案,編進程式裡) |
|
|||
|
|
|
|||
|
|
## 資料存放
|
|||
|
|
|
|||
|
|
都在資料資料夾裡(macOS `~/Library/Application Support/AnyBot/`、Linux `~/.local/share/AnyBot/`、Windows `%APPDATA%\AnyBot\`;`ANYBOT_DATA_DIR` 可以改):
|
|||
|
|
|
|||
|
|
| 檔案 | 內容 |
|
|||
|
|
|---|---|
|
|||
|
|
| `state.json` | 全部的 Bot、聊天、設定和金鑰 |
|
|||
|
|
| `attachments/bot-<id>/` | 聊天附件 |
|
|||
|
|
| `browser/` | AnyBot 瀏覽器的設定檔(登入狀態) |
|
|||
|
|
| `brain-copy.json` | 螢幕電腦保留的大腦副本(離線時還能看) |
|
|||
|
|
| `brain-files/` | 螢幕電腦從大腦下載的附件 |
|
|||
|
|
| `image-notes.json` | 看圖小幫手對每張圖的描述(不用重看) |
|
|||
|
|
|
|||
|
|
寫入時先寫暫存檔再改名,不會寫一半。
|
|||
|
|
|
|||
|
|
## 安全
|
|||
|
|
|
|||
|
|
- **核准關卡**:工具動作照規則直接做、先問或拒絕(`policy.rs`,FEATURES.md 第 16、19 節)。「直接操作模式」可以跳過詢問。
|
|||
|
|
- **指令的沙盒(macOS)**:Bot 執行的指令在 `sandbox-exec` 裡跑,讀不到也寫不到 AnyBot 的資料資料夾和 `.ssh`、`.gnupg`、鑰匙圈、`.aws` 等;只有 `attachments/` 可以讀(不能寫)。環境變數裡的模型金鑰會先拿掉。
|
|||
|
|
- **Bot 的電腦**:放在容器裡,和你的電腦分開。
|
|||
|
|
- **保險庫**:密碼、卡號、CVC 不會交給模型,由 AnyBot 直接填進網頁。
|
|||
|
|
- **多台電腦**:金鑰只留在大腦(下面說明);手腳只接受帳號裡的 AnyBot,用配對碼加入的只接受那一個大腦。
|
|||
|
|
|
|||
|
|
## 多台電腦怎麼運作
|
|||
|
|
|
|||
|
|
### 連線
|
|||
|
|
|
|||
|
|
每台 AnyBot(大腦、螢幕、手腳)都保持一條 WebSocket 連到帳號伺服器(`/relay/host/ws`),雙方每 15–20 秒互相確認,45–60 秒沒聲音就重連。帳號伺服器沒有 WebSocket 時(舊版),改用 HTTP 長輪詢(`/relay/host/next`)。
|
|||
|
|
|
|||
|
|
- 手機的要求:手機 → 帳號伺服器(HTTPS)→ 這條連線 → AnyBot → 回覆原路回去。
|
|||
|
|
- AnyBot 找另一台 AnyBot(同步、手腳):從自己的連線送 `call` 上去,伺服器轉給對方的連線,答案以 `ret` 回來。
|
|||
|
|
- 連線斷掉時,等待中的請求立刻收到「斷線了」,不用等逾時;伺服器也立刻知道這台離線。
|
|||
|
|
- 超過 1 KB、而且壓得小的內容用 deflate 壓縮(圖片、PDF 這類已壓縮的照原樣送)。
|
|||
|
|
|
|||
|
|
格式見 [protocol.md](protocol.md)。
|
|||
|
|
|
|||
|
|
### 大腦和螢幕的同步(`app/mirror_ui.rs`)
|
|||
|
|
|
|||
|
|
大腦回答「從版本 N 之後改了什麼」:
|
|||
|
|
|
|||
|
|
- **設定**:State 去掉聊天、拿掉金鑰後算指紋,變了才送整份。
|
|||
|
|
- **聊天**:每則訊息有指紋,只送第一個不同的那則之後的部分。
|
|||
|
|
- **討論串**、**誰在工作**、**等你核准的事**。
|
|||
|
|
|
|||
|
|
螢幕的請求會等最多 20 秒,有變化就馬上回(長輪詢,跑在長連線上)。大腦上的改動通常 1 秒內出現在螢幕上。
|
|||
|
|
|
|||
|
|
螢幕上的改動變成小操作送回大腦:
|
|||
|
|
|
|||
|
|
- 單一值(`set`、`bot_set`);
|
|||
|
|
- 清單和對照表逐項修改(`patch`、`bot_patch`:新增或修改某幾項、刪掉某幾項、改順序),所以兩台同時改同一份清單的不同項目,兩邊都會保留;
|
|||
|
|
- 新增、刪除、排序 Bot(`bot_add`、`bot_del`、`bot_order`)。
|
|||
|
|
|
|||
|
|
每個改動有編號,大腦回報收到第幾號;還沒確認的改動,會疊在大腦傳回來的新設定上,打字不會被蓋回去。所有改動和聊天動作由一個工作排隊依序送出。送失敗時,螢幕重新要一份完整資料。
|
|||
|
|
|
|||
|
|
聊天動作(傳訊息、停止、核准、重新產生、開新對話…)送 `act`,由大腦執行。附件先上傳到大腦。
|
|||
|
|
|
|||
|
|
### 金鑰只在大腦
|
|||
|
|
|
|||
|
|
送給螢幕的設定裡,模型金鑰、訊息管道的 Token、連接器的環境變數和標頭都換成「••••(已設定,在大腦上)」。螢幕改設定送回來時,大腦看到這個替代字,就保留原本的值(清單裡依 id 或名字對上)。螢幕要取得模型清單時,由大腦用自己的金鑰去抓。
|
|||
|
|
|
|||
|
|
### 手腳
|
|||
|
|
|
|||
|
|
大腦的 Bot 呼叫工具時可以指定 `computer`。如果是帳號裡的另一台,就透過長連線送 `/api/device/run` 給它,由它在自己身上執行(螢幕、檔案、指令、它的 AnyBot 瀏覽器),再把文字和截圖傳回來。那台要開「裝置連結」才接受;若設了只給某個大腦用(`hands_for`),帳號伺服器會附上是哪一台在問,不是那台就拒絕。
|
|||
|
|
|
|||
|
|
### 配對碼
|
|||
|
|
|
|||
|
|
已登入的手機或 AnyBot 向帳號伺服器要一組 6 位數字(10 分鐘、一次)。新電腦用這組數字換到自己的身分(主機 id 和金鑰),不經過密碼。配對碼記得它是給哪個大腦的,新電腦就只讓那個大腦使用。
|
|||
|
|
|
|||
|
|
### 看圖小幫手
|
|||
|
|
|
|||
|
|
模型拒收圖片時,AnyBot 記下這個模型看不到圖。之後送給它的請求裡,你傳的每張圖會先交給你帳號裡看得到圖的模型(依序試 xAI、Anthropic、OpenAI、Gemini、OpenRouter…,先用你設的預設模型),把描述(包括圖裡的文字)放進訊息。描述依圖片記住,同一張圖不會再看一次;失敗的模型 10 分鐘內跳過。
|