Files
hermes-mac13 93758f3720 feat: 从 tutti-selfhost 拆出 local_rig 独立仓
- local_rig/ 完整包(7 源文件 + ui.html + CHANGELOG)
- tests/test_local_rig.py + test_rig_real_agent.py(纯 local_rig 测试)
- scripts/15-criterion4-live.sh(判据④活环境门)
- README.md(定位/快速开始/与 tutti-selfhost 的关系)
- requirements.txt(唯一外部依赖 pyyaml)
- .gitignore

来源: figmar/tutti-selfhost @ 3b23853 (2026-10-06)
原 PR #43 已合入 tutti-selfhost main
2026-10-06 13:36:14 +08:00

160 lines
6.2 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.
# local_rig 变更记录
> 分支:`feat/local-rig` → main
> PR:#43
> 基线:main `992ad7c`(rebase 后,2026-10-05)
> 最后更新:2026-10-05
## 与多智能体协作工作台的关系
local_rig 是工作台的**单机执行层**:
| 层 | 组件 | 职责 |
|---|---|---|
| **协作层**(Matrix 房间) | `22-agent-bus.sh` + bridge + tuttid | 人 + 多 Agent 在 #agent-hub-test 里 @ 派任务、收回执、翻历史 |
| **执行层**(本 PR) | `local_rig` daemon | 单台机器上跑多个 agent 实例(hermes CLI / terminal),任务入队 → on-demand spawn → 结果回报 |
对接点:
- 协作层里 @ 一个 agent 后,bridge 把任务经 tuttid 派到 `local_rig` daemon(`:27900`)
- `local_rig` 执行完把结果写回 tuttid → bridge → Matrix 房间(`com.apescale.tutti.result.v1`)
- 没有 local_rig 时,bridge 只能回执"accepted → running → succeeded"但没有真实产出(TY02 当前状态)
- local_rig 是**让 @ 之后有真 agent 执行并回报**的那一环(dsh 在 issue#2 里指出的断链)
## 回滚锚点
| 锚点 | 含义 |
|---|---|
| `main @ 8f547cc` | 合并前 main 的 HEAD(完全回滚 = `git reset --hard 8f547cc`) |
| `feat/local-rig @ 3f688bb` | 第一版(rig_schema/queue/daemon/cli/runtime + 测试) |
| `feat/local-rig @ f0ff9fe` | 第二版(on-demand dispatch + profile + dsh/workbuddy) |
| 下一个 commit(待提交) | 第三版(UI dashboard) |
**回滚操作**:
```bash
# 完全回滚(丢弃 local_rig)
git checkout main && git reset --hard 8f547cc
# 回到第一版(去掉 on-demand/profile 改动)
git checkout feat/local-rig && git reset --hard 3f688bb
# 回到第二版(去掉 UI)
git checkout feat/local-rig && git reset --hard f0ff9fe
```
## 文件清单
| 文件 | 行数 | 职责 | 新增/修改 |
|---|---|---|---|
| `local_rig/__init__.py` | 7 | 版本号 | 新增 |
| `local_rig/rig_schema.py` | 195 | YAML 解析 + 校验(pods/members/edges) | 新增 |
| `local_rig/queue.py` | 219 | SQLite WAL 持久化队列,状态机 | 新增 |
| `local_rig/daemon.py` | 326 | HTTP daemon + on-demand dispatch + `/ui` | 新增 |
| `local_rig/runtime_terminal.py` | 90 | tmux 裸终端运行时 | 新增 |
| `local_rig/runtime_hermes.py` | 157 | hermes CLI 运行时(profile + 自动检测 bin) | 新增 |
| `local_rig/cli.py` | 136 | `rig start/status/assign/tasks/stop` | 新增 |
| `local_rig/ui.html` | 183 | 单文件 HTML dashboard(vanilla JS) | 新增 |
| `demo/rig.yaml` | 50 | 示例团队(5 Agent 含 dsh/workbuddy) | 新增 |
| `tests/test_local_rig.py` | 401 | 29 个测试(schema/queue/daemon) | 新增 |
| `README.md` | +81 | 新增「单机轻量版」章节 | 修改 |
**总计**:11 个新文件 + 1 个修改,~1,830 行
## 变更时间线
### Commit 1: `3f688bb` — 单机轻量 Agent 团队
- `rig_schema.py`:YAML → `Rig` 对象(pods/members/edges,runtime 枚举校验)
- `queue.py`:SQLite WAL,`pending→running→done|failed|timeout`,`BEGIN IMMEDIATE` 原子领取
- `daemon.py`:`ThreadingHTTPServer`,API 见下
- `runtime_terminal.py`:tmux session 管理(start/stop/status)
- `runtime_hermes.py`:spawn `hermes chat`(预起常驻)
- `cli.py`:`rig start/status/assign/tasks/stop`
- `tests/test_local_rig.py`:29 例
- `demo/rig.yaml`:2 pod(dev + ops),3 member
### Commit 2: `f0ff9fe` — on-demand dispatch + profile
- **daemon.py**:
- `start_member()` hermes-cli 改 on-demand(不预起,任务到达时 spawn)
- `enqueue()` 触发 `_dispatch_hermes()` → 独立线程跑 `hermes -z "prompt"`
- `terminal` 失败不阻塞(tmux 未装 graceful degrade)
- `stop()` 加 `server_close()`(修端口释放)
- **runtime_hermes.py**:
- 自动检测 hermes bin(PATH → `~/Library/Application Support/.../desktop-bin/hermes`)
- `--profile` 支持(`HermesProcess.profile` 字段)
- `available()` 绝对路径走 `Path.exists()` 不走 `which`
- **demo/rig.yaml**:加 `dev.impl`(profile=dsh)+ `dev.qa`(profile=workbuddy)
### Commit 3(待提交):UI dashboard
- **ui.html**:单文件 HTML(暗色主题,vanilla JS,3s 轮询)
- Members 面板:状态灯 + runtime + profile
- Tasks 面板:状态 badge + result/error
- Assign 表单:选 member + 输入 prompt + 派发
- **daemon.py**:`GET /ui` 返回 `ui.html`
## API 一览
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/health` | `{"status":"ok","rig":"name"}` |
| GET | `/rig` | rig 定义(pods/members/edges) |
| GET | `/status` | 所有 member 状态 |
| GET | `/tasks` | 任务列表(`?status=&assignee=&limit=`) |
| GET | `/tasks/{id}` | 单个任务 |
| GET | `/ui` | HTML dashboard |
| POST | `/tasks` | 入队 `{"assignee","prompt","deadline_s"}` |
| POST | `/tasks/{id}/claim` | 领取 |
| POST | `/tasks/{id}/done` | 完成 `{"result"}` |
| POST | `/tasks/{id}/fail` | 失败 `{"error"}` |
| POST | `/start` | 启动所有 member |
| POST | `/start/{member}` | 启动单个 |
| POST | `/stop` | 停止所有 |
| POST | `/stop/{member}` | 停止单个 |
## CLI 用法
```bash
# 启动(前台,Ctrl+C 停)
python3 -m local_rig.cli -f demo/rig.yaml start --port 27900
# 状态
python3 -m local_rig.cli -f demo/rig.yaml status --port 27900
# 派任务
python3 -m local_rig.cli -f demo/rig.yaml assign --to dev.lead "修复登录 bug"
# 查任务
python3 -m local_rig.cli -f demo/rig.yaml tasks --status done
# 停止
python3 -m local_rig.cli -f demo/rig.yaml stop --port 27900
```
## 本机部署
```bash
# 1. 启动 daemon(后台)
cd <repo>
python3 -c "
import sys; sys.path.insert(0,'.')
from local_rig.daemon import serve
d = serve('demo/rig.yaml', port=27900)
import time
while True: time.sleep(60)
" &
# 2. 打开 UI
open http://127.0.0.1:27900/ui
# 3. CLI
python3 -m local_rig.cli -f demo/rig.yaml status
python3 -m local_rig.cli -f demo/rig.yaml assign --to dev.lead "reply OK"
```
## 已知限制
| 限制 | 影响 | 后续 |
|---|---|---|
| tmux 未装 | `terminal` 运行时不可用(graceful degrade) | `brew install tmux` |
| dsh/workbuddy 无独立 CLI | 通过 `hermes --profile` 代理 | 等 DSH CLI / WorkBuddy API |
| hermes-acp 未实现 | 需 tuttid 运行时 | 第二版接入 `:27800` WS |
| 单 daemon 单进程 | 不支持多 rig 并发 | 多端口多实例 |