agent-fleet/openspec/changes/agent-fleet-platform/design.md
Zer4tul aabd52ed52 init: OpenSpec project scaffolding with proposal, design, specs, tasks
- 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
2026-05-11 14:37:43 +08:00

250 lines
9.6 KiB
Markdown
Raw 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.

## 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 UIPhase 1 用 Forgejo + Matrix 作为 UI
- 不替换 cc-connect / cc-telegram-bridge它们可以作为 adapter 接入)
## Decisions
### Decision 1: Forgejo而非 Gitea作为 Git 平台
**选择**: Forgejo
**理由**:
- Forgejo 是 Gitea 的社区 fork治理更开放
- 内置 CI/CDForgejo 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适合状态机建模
- 生态reqwestHTTP、matrix-sdkMatrix、rusqliteSQLite、serdeJSON/TOML
- 交叉编译cargo-zigbuild 一行命令产出 Linux/macOS/Windows 二进制
**替代方案**:
- TypeScript (Node.js):开发快但需要运行时,非单二进制
- Go单二进制但内存安全不如 Rust交叉编译有 libc 问题
### Decision 3: Agent-Orchestrator 通信用 HTTP APIaxum
**选择**: Agent 通过 HTTP API 与 Orchestrator 通信Orchestrator 端用 axum 框架
**理由**:
- 跨语言、跨平台:任何 Agent 都能发 HTTP 请求
- 防火墙友好
- 与 Forgejo webhook 统一协议栈
- axum 是 Rust 生态最成熟的 async HTTP 框架
**替代方案**:
- WebSocket实时性好但复杂度高重连逻辑麻烦
- gRPC性能好但 Agent 端实现门槛高
- Message QueueNATS/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 adapterspawn `claude -p` 子进程
- Codex adapterspawn `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**: 心跳间隔可配置默认 60sOrchestrator 做好限流
### 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 部署位置**TBDarm0 / WSL2 / 独立机器
3. **Agent 工作目录策略**每个任务 clone worktree 还是共享 workspace
4. **权限模型**Issue comment 中的 `/assign` 命令是否需要权限控制
5. **cc-connect 集成**:✅ Phase 2