237 lines
11 KiB
Markdown
237 lines
11 KiB
Markdown
# AgentRun 架构参考
|
||
|
||
AgentRun 是面向 UniDesk 与 HWLAB 的共享 Agent 执行面。它不是 UniDesk Code Queue 的改名,也不能默认替换现有 Code Queue 行为。Code Queue 仍是当前 UniDesk 任务队列;AgentRun 是新的基础设施线,重点是 Agent run 生命周期、runner 隔离和可插拔执行 backend。
|
||
|
||
## 产品边界
|
||
|
||
AgentRun 负责通用执行基础设施:
|
||
|
||
- 创建和跟踪 run;
|
||
- 接收 `turn`、`steer`、`interrupt`、`resume` 等 durable command;
|
||
- 将 run 分配给短生命周期 runner;
|
||
- 归一化 backend event、stdout/stderr、assistant message、tool call 和 terminal status;
|
||
- 管理 lease、heartbeat、基础设施恢复导致的 retry 语义和 run 可观测性;
|
||
- 注册 backend capability,并定义 credential 注入边界。
|
||
|
||
UniDesk 与 HWLAB 是 tenant/client。UniDesk 负责平台入口、provider 清单、CLI/frontend 集成和现有 Code Queue 兼容。HWLAB 负责实验室任务 policy、设备/硬件语义、operation/audit/evidence 模型和 HWLAB workspace 规则。AgentRun 不判断某个 HWLAB live device mutation 是否被授权,也不判断某个 UniDesk production deployment 是否允许执行;它只执行 tenant policy 已授权的 run。
|
||
|
||
每个 run 都必须显式携带隔离字段:
|
||
|
||
- `tenantId`,例如 `unidesk` 或 `hwlab`;
|
||
- `projectId`,例如 `pikasTech/unidesk` 或 `pikasTech/HWLAB`;
|
||
- `workspaceRef`,用于定位 source/worktree/workspace;
|
||
- `providerId`,例如 `G14` 或 `D601`;
|
||
- `backendProfile`,例如 `codex`、`opencode`、`claudecode`、`host-native` 或 `windows-native`;
|
||
- `executionPolicy`,包含 sandbox、approval、timeout、network 和 secret scope;
|
||
- `traceSink`,说明标准化 event 镜像到哪里。
|
||
|
||
## 服务形态
|
||
|
||
AgentRun 应构建为小型服务族:
|
||
|
||
```text
|
||
agentrun-mgr
|
||
公共 RESTful API、durable facts、tenant/policy/idempotency 检查
|
||
|
||
agentrun-runner
|
||
短生命周期 per-run 或 per-attempt executor;claim 一个 run,连接一个 backend,写回 events/status
|
||
|
||
agentrun-backend-*
|
||
Codex、Claude Code、OpenCode、host-native 或 Windows-native 执行适配器
|
||
|
||
agentrun-scheduler
|
||
后续自动 dispatcher;扫描 pending runs,选择 backend/profile/capacity,创建 runner Jobs
|
||
```
|
||
|
||
Manager 是稳定 API 和审计点。Runner 是执行者,不应成为业务客户端直接调用的公共 API。MVP 阶段 operator 可以人工启动 runner 进程或 Kubernetes Job,但 runner 仍必须从 `agentrun-mgr` claim run,并把所有事实写回 manager。
|
||
|
||
Backend adapter 隐藏具体工具协议。Codex stdio JSON-RPC、OpenCode JSON events、Claude Code、host-native process 和 Windows-native execution 可以使用不同内部协议,但 AgentRun 公共 API 必须保持稳定且与 backend 无关。
|
||
|
||
## v0.1 实现技术栈
|
||
|
||
AgentRun `v0.1` 自研 runtime 优先使用 Bun + TypeScript:manager、runner、backend adapter、Codex backend、CLI 和后续 scheduler 都按这一技术栈实现。`scripts/agentrun-cli.ts` 是官方 CLI 入口;复杂 CLI 逻辑进入 `scripts/src/`,服务和 runner 逻辑进入 `src/`。YAML manifest、Tekton/Argo CD 配置、Postgres 和 Kubernetes 仍按各自原生生态管理。
|
||
|
||
Codex backend 固定采用 Codex CLI app-server JSON-RPC over stdio。实现必须启动受控 `codex app-server --listen stdio://`,执行 `initialize`、`thread/start` 或 `thread/resume`、`turn/start`,并把 stdout/stderr、notification、tool lifecycle、assistant output 和 terminal/error 状态归一化为 AgentRun events。直接 Responses HTTP、OpenAI SDK wrapper、`codex exec` 一次性输出或文本 fallback 不能作为 `v0.1` Codex backend 的正式执行路径。
|
||
|
||
实现参考优先级:UniDesk Code Queue 的 `src/components/microservices/code-queue/src/code-agent/codex.ts`、`common.ts`,以及 HWLAB v0.2 的 `internal/cloud/codex-stdio-session.mjs`、`scripts/code-agent-chat-smoke.mjs`。AgentRun 复用其协议、trace、redaction、Secret projection 和 failure 分类经验,但不复制 tenant 业务规则、环境专用路径或密钥材料。
|
||
|
||
## MVP 顺序
|
||
|
||
AgentRun 必须按纵向切片推进,不要一开始大规模并行开发。
|
||
|
||
### M0: 契约骨架
|
||
|
||
只定义最小资源模型和状态机:
|
||
|
||
- `Run`
|
||
- `Command`
|
||
- `Event`
|
||
- `Runner`
|
||
- `Backend`
|
||
|
||
第一切片只要求 `turn`、`interrupt`、`status` 和分页 `events`。不要一开始就做 `steer`、`resume`、judge/retry、UI、多 backend 路由或自动调度。
|
||
|
||
### M1: 最小 Runner 加一个 Backend
|
||
|
||
第一份可执行证明不依赖 manager 或 scheduler。Runner 读取本地 run spec,调用一个 backend,并输出标准化 events。
|
||
|
||
验收标准:
|
||
|
||
- 一个 `turn` 能通过 backend 执行;
|
||
- assistant/output/error events 被归一化;
|
||
- terminal status 被写出;
|
||
- interrupt 至少有 durable cancellation 路径,backend 支持时再传播到真实进程中断。
|
||
|
||
第一个 backend 固定选择 Codex app-server stdio,用最窄实现证明真实 Agent 原语。如果 Codex 上游或凭据短暂不可用,可以用 controlled process 或 fake app-server 做自测试,但不能替代综合联调,也不能据此宣称 `v0.1` backend 通过。
|
||
|
||
### M2: Manager 加 Runner Claim
|
||
|
||
加入 `agentrun-mgr` 作为 durable fact store 和公共 API。Client 创建 run;operator 或 CLI 用 run id 人工启动 runner;runner claim、poll commands、append events、heartbeat 并退出。
|
||
|
||
验收标准:
|
||
|
||
- run create/query 是 durable 的;
|
||
- runner claim 幂等,并拒绝双 owner;
|
||
- events 是 append-only,并按 seq 分页;
|
||
- command ack state 可见;
|
||
- heartbeat expiration 可观察。
|
||
|
||
### M3: 手动 Dispatch CLI
|
||
|
||
增加 CLI,为指定 run 启动本地 runner process 或 Kubernetes Job。这是 manual dispatch,不是 manager 侧同步编排。Manager 仍拥有事实,runner 仍拥有执行。
|
||
|
||
验收标准:
|
||
|
||
- CLI 快速返回 JSON;
|
||
- job/process identity 和 log path 可见;
|
||
- run status 可从 manager 轮询;
|
||
- runner 启动失败被报告为基础设施失败,不能静默写成任务成功。
|
||
|
||
### M4: 自动 Scheduler
|
||
|
||
只有 M1-M3 稳定后才加入 `agentrun-scheduler`。Scheduler 扫描 pending runs,应用 policy/capacity/backend selection,创建 runner Jobs,并处理 stale lease recovery。Scheduler 不直接执行 backend。
|
||
|
||
验收标准:
|
||
|
||
- pending run 自动变为 running;
|
||
- scheduler restart 不影响已经运行的 runner;
|
||
- stale lease recovery 留下显式 audit event;
|
||
- scheduler rollout 不等同于 active run failure。
|
||
|
||
### M5: Tenant Canary 集成
|
||
|
||
核心生命周期证明后,再接入 UniDesk 和 HWLAB canary:
|
||
|
||
- UniDesk 可以新增 `agentrun` CLI/API route,同时保持现有 Code Queue 不变。
|
||
- HWLAB 可以把一个窄范围 Code Agent canary 路由到 AgentRun。
|
||
- 每个 run 都必须显式带 tenant policy、workspace、secret scope 和 trace sink。
|
||
|
||
## RESTful MVP 契约
|
||
|
||
MVP 只使用短 RESTful HTTP/JSON 请求。长时间 Agent 工作用 durable command resource、run status 和分页 event polling 表示。不要让一个 HTTP 请求等待完整模型 turn。
|
||
|
||
Manager 公共 API:
|
||
|
||
```http
|
||
POST /api/v1/runs
|
||
GET /api/v1/runs/:runId
|
||
GET /api/v1/runs/:runId/events?afterSeq=0&limit=100
|
||
POST /api/v1/runs/:runId/commands
|
||
GET /api/v1/runs/:runId/commands/:commandId
|
||
GET /api/v1/backends
|
||
```
|
||
|
||
Runner 到 manager 的私有 API:
|
||
|
||
```http
|
||
POST /api/v1/runners/register
|
||
POST /api/v1/runs/:runId/claim
|
||
PATCH /api/v1/runs/:runId/lease
|
||
GET /api/v1/runs/:runId/commands?afterSeq=0&limit=20
|
||
POST /api/v1/runs/:runId/events
|
||
PATCH /api/v1/runs/:runId/status
|
||
POST /api/v1/commands/:commandId/ack
|
||
```
|
||
|
||
Runner inbound API 应保持本地/私有且最小:
|
||
|
||
```http
|
||
GET /health
|
||
GET /debug/status
|
||
```
|
||
|
||
不要依赖客户端调用短生命周期 runner Pod 地址。该方式在 Job、namespace、host-native backend 和重启场景下都会变脆。
|
||
|
||
## Command 状态
|
||
|
||
Command 是 durable resource。`turn`、`steer`、`interrupt` 和 `resume` 不能实现为 client 到 runner 的同步进程调用。
|
||
|
||
初始 command 状态机:
|
||
|
||
```text
|
||
accepted -> delivered -> confirmed
|
||
accepted -> delivered -> failed
|
||
accepted -> expired
|
||
```
|
||
|
||
所有 command 写入都应支持 idempotency key。相同 idempotency key 且 payload hash 相同的重复请求返回既有 command;相同 key 但 payload hash 不同必须显式失败。
|
||
|
||
## Event 模型
|
||
|
||
Event 是 append-only,并按 seq 分页:
|
||
|
||
- `seq` 在单个 run 内单调递增。
|
||
- `eventId` 或 `(runId, seq)` 支持幂等去重。
|
||
- `GET /events?afterSeq=N&limit=M` 是第一阶段观察 API。
|
||
- 后续 SSE 可以流式传输相同 event resource,但不能替代 REST polling contract。
|
||
|
||
最小 event 类别:
|
||
|
||
- `system`
|
||
- `assistant_message`
|
||
- `tool_call`
|
||
- `command_output`
|
||
- `diff`
|
||
- `error`
|
||
- `backend_status`
|
||
- `terminal_status`
|
||
|
||
## 数据模型方向
|
||
|
||
`v0.1` 使用 Postgres 作为唯一 durable store;file、sqlite、JSONL 或 Pod 本地目录只能用于临时测试或日志,不作为运行面事实来源。第一版实现可以使用紧凑 schema,但不应把所有事实都隐藏在一个 JSON blob 中。稳定方向是:
|
||
|
||
- `agentrun_schema_migrations`:migration id、checksum 和 applied timestamp;
|
||
- `agentrun_runs`:run identity、tenant/project/workspace/backend policy、status 和 timestamps;
|
||
- `agentrun_commands`:command type、idempotency key、payload hash、state 和 ack timestamps;
|
||
- `agentrun_events`:按 run 和 seq 索引的 append-only event records;
|
||
- `agentrun_runners`:registered runner identity、placement 和 heartbeat;
|
||
- `agentrun_backends`:backend profile、capabilities、capacity 和 health;
|
||
- `agentrun_leases`:当前 ownership 和 expiry。
|
||
|
||
Postgres DSN、provider credential 和未来 tenant credential 的分发边界见 [spec-v01-secret-distribution.md](spec-v01-secret-distribution.md);Codex 测试凭据通过 Kubernetes Secret projection 注入 `~/.codex/auth.json` 与 `~/.codex/config.toml`,source、GitOps、event、trace、日志和 CLI 输出都不得保存 Secret 明文。
|
||
|
||
## 部署方向
|
||
|
||
AgentRun 从 `v0.1` 开始按版本 lane 滚动,废弃 `dev/prod` 管理口径。`v0.1` 的固定 source workspace 是 `G14:/root/agentrun-v01`,固定 source branch 是 `v0.1`,固定运行目标是 G14 原生 k3s namespace `agentrun-v01`。后续 `v0.2`、`v0.3` 必须拥有自己的 branch、workspace、namespace、GitOps branch、runtime path 和发布验收。
|
||
|
||
Control-plane service 应是长驻服务;runner 应是短生命周期 Job 或受控 host-native process。Backend adapter 可以作为 pod 或 host-native service 运行,但必须通过 AgentRun 注册 capability 和 health,不能通过临时地址被 ad hoc 调用。
|
||
|
||
广泛 tenant 使用前,需要先设计 namespace isolation、RBAC、Secret scope、NetworkPolicy 和 ResourceQuota。独立 cluster 是后续成熟选项;第一版应优先在 `agentrun-v01` namespace 内证明服务,除非出现明确隔离 blocker。`agentrun_dev` 和 `agentrun_prod` 不再作为当前架构规格或验收目标。
|
||
|
||
## MVP 非目标
|
||
|
||
第一版 MVP 不包含:
|
||
|
||
- 迁移 UniDesk Code Queue;
|
||
- 全局替换 HWLAB Code Agent;
|
||
- 多 backend 路由;
|
||
- 最小诊断之外的 UI;
|
||
- judge/retry 自动化;
|
||
- 自动扩缩容;
|
||
- 跨集群调度;
|
||
- SSE/WebSocket 流式输出;
|
||
- 完整权限系统;
|
||
- production rollout 自动化。
|
||
|
||
第一目标是稳定跑通一条纵向 run 生命周期:create run、人工启动 runner、执行一个 backend turn、append events、观察 final status,并能发出可见的 interrupt/cancel command。
|