## 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; } interface Task { task_id: string; source: string; // "forgejo:#" 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; } ``` ## 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