- 7 capabilities: agent-registry, task-router, receipt-protocol, forgejo-integration, matrix-chatops, agent-adapter, orchestrator-core - Tech stack: Rust + axum + zigbuild (single binary) - Forgejo as task source of truth - Matrix as real-time ChatOps layer - Adapter pattern for multi-agent type support
250 lines
9.6 KiB
Markdown
250 lines
9.6 KiB
Markdown
## Context
|
||
|
||
当前有多台机器(WSL2 workstation、MacBook、可能的云服务器)运行不同类型的 AI Agent:
|
||
- **OpenClaw**:Jeeves orchestrator + 多个 sub-agent
|
||
- **Claude Code**:本地 CLI 编码
|
||
- **Codex CLI**:OpenAI 编码
|
||
- **Hermes Agent**:其他类型
|
||
- **ACP-compatible agents**:Devin 等
|
||
|
||
现状:Agent 之间无统一协同机制,任务靠人工传递,进度靠人工追踪,没有结构化的完成验证。
|
||
|
||
已有基础设施:
|
||
- **Matrix**:0x08.org homeserver 已运行
|
||
- **Forgejo**:需要部署(建议自建,或用 Gitea 替代)
|
||
- 各机器可以互相网络通信
|
||
|
||
## Goals / Non-Goals
|
||
|
||
**Goals:**
|
||
- 建立统一的 Agent 编排平台,支持任意类型 Agent 加入
|
||
- Forgejo Issue/PR 作为任务事实来源,人类和 Agent 共用同一界面
|
||
- Matrix 作为实时协同层,提供 ChatOps 和通知
|
||
- 结构化 receipt 协议,不信任模型口头声明
|
||
- 最小化对现有 Agent 的侵入性(adapter 模式)
|
||
- 可扩展:新 Agent 类型只需实现 adapter 即可接入
|
||
|
||
**Non-Goals:**
|
||
- 不做 Agent 自身的改进(Agent 内部行为不在 scope 内)
|
||
- 不做 IDE 集成(VS Code / JetBrains 插件)
|
||
- 不做 LLM provider 管理(使用各 Agent 自己的 provider 配置)
|
||
- 不做前端 Web UI(Phase 1 用 Forgejo + Matrix 作为 UI)
|
||
- 不替换 cc-connect / cc-telegram-bridge(它们可以作为 adapter 接入)
|
||
|
||
## Decisions
|
||
|
||
### Decision 1: Forgejo(而非 Gitea)作为 Git 平台
|
||
|
||
**选择**: Forgejo
|
||
|
||
**理由**:
|
||
- Forgejo 是 Gitea 的社区 fork,治理更开放
|
||
- 内置 CI/CD(Forgejo Actions),未来可扩展
|
||
- API 兼容 Gitea,迁移成本低
|
||
- 支持 F3 (Friendly Forge Format),数据可移植
|
||
|
||
**替代方案**:
|
||
- Gitea:更成熟但治理有争议(公司化)
|
||
- GitHub:不方便自建,网络可达性差
|
||
- GitLab:太重,资源消耗大
|
||
|
||
### Decision 2: Orchestrator 用 Rust + zigbuild
|
||
|
||
**选择**: Rust + cargo-zigbuild(交叉编译)
|
||
|
||
**理由**:
|
||
- 内存安全:无 GC、无 data race、无 null pointer
|
||
- 单二进制部署:zigbuild 交叉编译,无 libc 兼容性问题
|
||
- 性能:原生性能,适合长运行服务
|
||
- 类型系统:强类型 + algebraic data types,适合状态机建模
|
||
- 生态:reqwest(HTTP)、matrix-sdk(Matrix)、rusqlite(SQLite)、serde(JSON/TOML)
|
||
- 交叉编译:cargo-zigbuild 一行命令产出 Linux/macOS/Windows 二进制
|
||
|
||
**替代方案**:
|
||
- TypeScript (Node.js):开发快但需要运行时,非单二进制
|
||
- Go:单二进制但内存安全不如 Rust,交叉编译有 libc 问题
|
||
|
||
### Decision 3: Agent-Orchestrator 通信用 HTTP API(axum)
|
||
|
||
**选择**: Agent 通过 HTTP API 与 Orchestrator 通信,Orchestrator 端用 axum 框架
|
||
|
||
**理由**:
|
||
- 跨语言、跨平台:任何 Agent 都能发 HTTP 请求
|
||
- 防火墙友好
|
||
- 与 Forgejo webhook 统一协议栈
|
||
- axum 是 Rust 生态最成熟的 async HTTP 框架
|
||
|
||
**替代方案**:
|
||
- WebSocket:实时性好但复杂度高,重连逻辑麻烦
|
||
- gRPC:性能好但 Agent 端实现门槛高
|
||
- Message Queue(NATS/Redis):增加基础设施依赖
|
||
|
||
### Decision 4: Event sourcing 作为审计基础(rusqlite)
|
||
|
||
**选择**: 所有状态变更作为不可变事件 append 到 SQLite,通过 rusqlite 访问
|
||
|
||
**理由**:
|
||
- 完整审计追踪
|
||
- 可重放
|
||
- 轻量,不需要外部数据库,适合单二进制打包
|
||
- rusqlite 是 Rust 生态最成熟的 SQLite 绑定
|
||
- 适合中小规模 Agent 舰队(< 100 agents)
|
||
|
||
**替代方案**:
|
||
- PostgreSQL:强大但引入运维复杂度,破坏单二进制部署
|
||
- JSON 文件:简单但无查询能力
|
||
- 只记录当前状态:无法审计
|
||
|
||
### Decision 5: Adapter 模式接入多类型 Agent
|
||
|
||
**选择**: 定义统一 adapter interface,各 Agent 实现自己的 adapter
|
||
|
||
**理由**:
|
||
- 最小侵入:Agent 不需要改内部逻辑
|
||
- 可扩展:新 Agent 类型只需写 adapter
|
||
- 隔离:adapter 故障不影响 Orchestrator
|
||
|
||
**adapter 执行方式**:
|
||
- Claude Code adapter:spawn `claude -p` 子进程
|
||
- Codex adapter:spawn `codex exec` 子进程
|
||
- OpenClaw adapter:调用 gateway HTTP API
|
||
- ACP adapter:通过 ACP 协议 WebSocket
|
||
- Shell adapter:执行任意 shell 命令(最通用)
|
||
|
||
### Decision 6: 配置用 TOML
|
||
|
||
**选择**: TOML(与 cc-connect 一致)
|
||
|
||
**理由**:
|
||
- 人类可读性好
|
||
- 类型比 YAML 更明确
|
||
- cc-connect 已验证
|
||
|
||
## Risks / Trade-offs
|
||
|
||
### Risk: Agent 执行环境差异
|
||
不同机器上的 Agent 运行环境(OS、依赖、权限)不同,可能导致同一任务在不同 Agent 上行为不一致。
|
||
→ **Mitigation**: adapter 负责环境检查;任务描述尽量环境无关;receipt 验证在 Forgejo 侧(与执行环境解耦)。
|
||
|
||
### Risk: Orchestrator 单点故障
|
||
Orchestrator 挂了,整个舰队停摆。
|
||
→ **Mitigation**: Phase 1 接受单点,Orchestrator 做到无状态(状态在 SQLite + Forgejo),快速重启恢复。
|
||
|
||
### Risk: Forgejo webhook 延迟或丢失
|
||
网络抖动可能导致 webhook 丢失。
|
||
→ **Mitigation**: Orchestrator 定期轮询 Forgejo 做对账(reconciliation),弥补 webhook 丢失。
|
||
|
||
### Risk: Agent 心跳风暴
|
||
大量 Agent 同时发心跳可能导致 Orchestrator 负载过高。
|
||
→ **Mitigation**: 心跳间隔可配置(默认 60s);Orchestrator 做好限流。
|
||
|
||
### Trade-off: 简单性 vs 实时性
|
||
选择 HTTP 而非 WebSocket/MQ 意味着实时性稍差。
|
||
→ 对于任务编排场景,秒级延迟完全可接受。Matrix 通知提供实时感。
|
||
|
||
## Architecture Overview
|
||
|
||
```
|
||
┌─────────────┐ webhook ┌──────────────────┐
|
||
│ Forgejo │ ───────────────→ │ Orchestrator │
|
||
│ (Git+Issue) │ ←─── API ────── │ (Node.js) │
|
||
└─────────────┘ │ │
|
||
│ - Agent Registry │
|
||
┌─────────────┐ HTTP API │ - Task Router │
|
||
│ Matrix │ ←──────────────→│ - Event Log │
|
||
│ (ChatOps) │ │ - Receipt Validator│
|
||
└─────────────┘ └────────┬──────────┘
|
||
│ HTTP API
|
||
┌───────────────────┼───────────────────┐
|
||
│ │ │
|
||
┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
|
||
│ Adapter A │ │ Adapter B │ │ Adapter C │
|
||
│ Claude Code │ │ Codex CLI │ │ OpenClaw │
|
||
│ (host-01) │ │ (host-02) │ │ (host-03) │
|
||
└─────────────┘ └─────────────┘ └─────────────┘
|
||
```
|
||
|
||
## Data Model (Core)
|
||
|
||
```typescript
|
||
interface Agent {
|
||
agent_id: string;
|
||
agent_type: "openclaw" | "claude-code" | "codex-cli" | "hermes" | "acp" | "shell";
|
||
hostname: string;
|
||
capabilities: string[];
|
||
max_concurrency: number;
|
||
current_tasks: number;
|
||
status: "online" | "offline" | "draining";
|
||
last_heartbeat_at: string; // ISO 8601
|
||
registered_at: string;
|
||
metadata: Record<string, string>;
|
||
}
|
||
|
||
interface Task {
|
||
task_id: string;
|
||
source: string; // "forgejo:<repo>#<issue>"
|
||
type: string; // from label: "code", "review", "test", "deploy", "research"
|
||
priority: "low" | "normal" | "high" | "urgent";
|
||
status: TaskStatus;
|
||
assigned_agent_id?: string;
|
||
requirements: string; // Issue body
|
||
labels: string[];
|
||
created_at: string;
|
||
assigned_at?: string;
|
||
started_at?: string;
|
||
completed_at?: string;
|
||
retry_count: number;
|
||
max_retries: number;
|
||
timeout_seconds: number;
|
||
}
|
||
|
||
type TaskStatus = "created" | "assigned" | "running" | "completed" | "failed" | "agent_lost" | "cancelled";
|
||
|
||
interface Receipt {
|
||
task_id: string;
|
||
agent_id: string;
|
||
status: "completed" | "failed" | "partial";
|
||
duration_seconds: number;
|
||
summary: string;
|
||
artifacts: Artifact[];
|
||
error?: string;
|
||
}
|
||
|
||
interface Artifact {
|
||
type: "pr" | "commit" | "file" | "comment" | "url";
|
||
url?: string;
|
||
path?: string;
|
||
description?: string;
|
||
}
|
||
|
||
interface TaskEvent {
|
||
event_id: string;
|
||
task_id: string;
|
||
event_type: string; // "created", "assigned", "started", "receipt_submitted", "completed", "failed", ...
|
||
agent_id?: string;
|
||
timestamp: string;
|
||
payload: Record<string, unknown>;
|
||
}
|
||
```
|
||
|
||
## Migration Plan
|
||
|
||
1. **部署 Forgejo**:自建实例,配置 webhook
|
||
2. **部署 Orchestrator**:Node.js 进程,连接 Forgejo + Matrix
|
||
3. **实现 Claude Code adapter**:第一个 adapter,验证端到端流程
|
||
4. **实现 Codex adapter**:第二个 adapter
|
||
5. **实现 OpenClaw adapter**:第三个 adapter(可选 Phase 2)
|
||
6. **逐步迁移现有任务到 Forgejo Issue**
|
||
|
||
### Rollback
|
||
- Orchestrator 可随时停止,不影响 Forgejo 和 Matrix
|
||
- Agent 可独立运行(只是没有编排)
|
||
- 所有状态在 Forgejo Issue + SQLite,可手动接管
|
||
|
||
## Open Questions
|
||
|
||
1. ~~**Forgejo 部署位置**~~:✅ 已部署在 arm0.0x08.org,域名 git.0x08.org
|
||
2. **Orchestrator 部署位置**:TBD(arm0 / WSL2 / 独立机器)
|
||
3. **Agent 工作目录策略**:每个任务 clone 新 worktree 还是共享 workspace?
|
||
4. **权限模型**:Issue comment 中的 `/assign` 命令是否需要权限控制?
|
||
5. **cc-connect 集成**:✅ 放 Phase 2
|