- 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
9.6 KiB
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)
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
- 部署 Forgejo:自建实例,配置 webhook
- 部署 Orchestrator:Node.js 进程,连接 Forgejo + Matrix
- 实现 Claude Code adapter:第一个 adapter,验证端到端流程
- 实现 Codex adapter:第二个 adapter
- 实现 OpenClaw adapter:第三个 adapter(可选 Phase 2)
- 逐步迁移现有任务到 Forgejo Issue
Rollback
- Orchestrator 可随时停止,不影响 Forgejo 和 Matrix
- Agent 可独立运行(只是没有编排)
- 所有状态在 Forgejo Issue + SQLite,可手动接管
Open Questions
Forgejo 部署位置:✅ 已部署在 arm0.0x08.org,域名 git.0x08.org- Orchestrator 部署位置:TBD(arm0 / WSL2 / 独立机器)
- Agent 工作目录策略:每个任务 clone 新 worktree 还是共享 workspace?
- 权限模型:Issue comment 中的
/assign命令是否需要权限控制? - cc-connect 集成:✅ 放 Phase 2