Initial design for plane-agentd: Plane-driven generic agent dispatcher
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
state/
|
||||||
|
*.log
|
||||||
|
config/agentd.local.yaml
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# plane-agentd
|
||||||
|
|
||||||
|
Plane 看板驱动的通用 agent 调度服务:在 Plane 建工作项并指派给某个终端,agentd 在该终端起对应的 CLI agent(Codex / Claude Code / 脚本)执行,把结果贴回工作项并流转状态。调度是确定性代码;模型只出现在执行器和总指挥里。
|
||||||
|
|
||||||
|
- 设计:`docs/design.md`(架构、契约、失败策略、配置、阶段)
|
||||||
|
- 首个租户:GWENT 项目(看板 https://plane.kappiee.com/kappiee/projects/79cb5a60-aa4f-4138-9864-1dd70426e665/issues/ )
|
||||||
|
- 凭据全部经 Agent Vault 注入内存,仓库和配置里只有键名。
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# plane-agentd 设计
|
||||||
|
|
||||||
|
Plane 看板驱动的通用 agent 调度服务:任何人/agent 在 Plane 建工作项并指派给一个"终端",agentd 在该终端起对应的 CLI agent(Codex、Claude Code、脚本…)执行,把结果贴回工作项并流转状态。调度是确定性代码,模型只出现在执行器和总指挥里。
|
||||||
|
|
||||||
|
## 1. 目标与非目标
|
||||||
|
- 目标:多项目隔离(一份配置一段)、多终端(本机/经 Warpgate 的远程机)、多 CLI(执行器适配器)、多轮(线程持久、进程不常驻)、失败可解释可重试、凭据只经 Vault 注入内存、任何会话可接手。
|
||||||
|
- 非目标:不是工作流引擎(无 DAG/分支,顺序交给 Plane "blocked by");不替代 Plane/Temporal 的界面;不做账号系统;不存密钥。
|
||||||
|
|
||||||
|
## 2. 架构
|
||||||
|
```
|
||||||
|
Plane ──webhook v2(HMAC)/ 轮询兜底──► agentd(接收端 + 调度核心 + 执行器适配器)
|
||||||
|
│ 按 assignee→终端、label→执行器/模型,串行组,重试,超时,心跳
|
||||||
|
┌───────────────┼───────────────┐
|
||||||
|
codex(远程/本机) claude-code(本机/远程) shell
|
||||||
|
└───────────────┴───────────────┘
|
||||||
|
执行器只产出"最后消息 + 产物路径";agentd 统一回帖、改状态、通知(ntfy)
|
||||||
|
```
|
||||||
|
阶段 3 之后调度核心搬到 Temporal(workflow = 一个工作项的生命周期,activity = 起执行器/等结果/回帖),用 Temporal UI 看运行与重试。
|
||||||
|
|
||||||
|
## 3. 概念与契约
|
||||||
|
- **终端(terminal)**:怎么到达一台机器 — `transport`(local | warpgate-ssh)、`os`、`launch`(subprocess | schtasks | nohup)、`repo`、`plane_user`(回帖身份,对应 Vault 键)、`concurrency`、`capabilities`、可用 `executors`。
|
||||||
|
- **执行器(executor)**:某个 CLI 的适配器 — 起一轮的命令模板、续线程命令模板、`resume: true|false`、可用模型、默认推理强度、沙箱参数、最后消息取法、线程 ID 取法。
|
||||||
|
- **路由(routing)**:`by_assignee`(Plane 用户 → 终端)、`by_label`(`exec:*`、`model:*`、`effort:*`、`needs-*` 串行组、`cap:*` 能力、`budget:*` 时限)、每终端默认执行器。
|
||||||
|
- **工作项契约**:正文 = 任务书;评论 = 消息;执行器最后消息首行 ∈ `[review] | [question] | [blocked] | [progress]`;agentd 回帖格式固定(轮次、run id、退出码、日志路径)。
|
||||||
|
- **状态机**:Backlog → Todo(指派即派单)→ In Progress → Waiting(question/blocked,指派给应答者)→ Review(指派给验收者)→ Done / Cancelled。`[progress]` 保持 Todo 并续线程。
|
||||||
|
- **线程**:每工作项一个执行器线程 ID(Codex `exec resume`,Claude `-p --resume`);Plane 评论是长期记忆,线程只是缓存;换终端/换执行器/线程损坏/轮次超阈值 → 重建线程,首轮输入附自动交接摘要并在评论注明。
|
||||||
|
|
||||||
|
## 4. 失败策略
|
||||||
|
| 情况 | 动作 | 状态 |
|
||||||
|
|---|---|---|
|
||||||
|
| 执行器回 `[blocked]`/`[question]` | 原样回帖 | Waiting → 应答者 |
|
||||||
|
| 退出码≠0 / 无最后消息 | 结构化失败说明(类型 crash/timeout/env/logic、run、exit、重试 n/N、日志、最后一帧) | 临时性(网络/中继/429/5xx/超时)自动重试 ≤3,退避 2/5/10 min,续原线程;否则 Waiting |
|
||||||
|
| 超时(默认 4 h,`budget:*` 覆盖) | 结束任务进程,失败说明 | Waiting |
|
||||||
|
| 环境类(焦点丢失/弹窗/断网) | 当 blocked 处理,暂停同串行组的派发 | Waiting |
|
||||||
|
| agentd 自身 | cron 拉起;心跳 10 min 无更新 → ntfy 报警 | — |
|
||||||
|
回到 Todo 的工作项无条件重新拾起;逻辑性失败永不自动重试。
|
||||||
|
|
||||||
|
## 5. 配置(示例,值用 Vault 键名)
|
||||||
|
```yaml
|
||||||
|
plane: {base_url: https://plane.kappiee.com, workspace: kappiee}
|
||||||
|
vault: {client: ~/.local/share/server-access-client/agent-access.py, config: https://nas.tail9531b.ts.net:8443/identity/agent.json}
|
||||||
|
terminals:
|
||||||
|
win10: {transport: warpgate-ssh, target: win10, os: windows, launch: schtasks, repo: 'C:\Users\Administrator\Documents\GwentAssistantProject\gwent', plane_user: window10, token_key: PLANE_WINDOW10_API_TOKEN, concurrency: 1, capabilities: [android, game], executors: [codex, powershell]}
|
||||||
|
oregon: {transport: local, os: linux, launch: subprocess, repo: /home/ubuntu/Documents/wjj/project/gwent, plane_user: oregon, token_key: PLANE_OREGON_API_TOKEN, concurrency: 2, executors: [claude-code, codex, shell]}
|
||||||
|
executors:
|
||||||
|
codex: {run: 'codex exec --json -C {repo} -s workspace-write -m {model} -c model_reasoning_effort="{effort}" {tier} -o {out} -', resume: 'codex exec resume {thread} --json -m {model} -c model_reasoning_effort="{effort}" {tier} -o {out} -', thread_from: events.jsonl:thread.started.thread_id, last_message: '{out}', models: [gpt-6-astra, gpt-6.1-sol], default_effort: medium}
|
||||||
|
claude-code: {run: 'claude -p --model {model} --output-format json --permission-mode {perm} -', resume: 'claude -p --resume {thread} --model {model} --output-format json -', thread_from: json:session_id, models: [opus, sonnet, haiku]}
|
||||||
|
shell: {run: '{script}', resume: false}
|
||||||
|
projects:
|
||||||
|
GWENT:
|
||||||
|
id: 79cb5a60-aa4f-4138-9864-1dd70426e665
|
||||||
|
webhook_secret_key: PLANE_WEBHOOK_GWENT
|
||||||
|
states: {todo: Todo, running: In Progress, waiting: Waiting, review: Review, done: Done}
|
||||||
|
reviewer: oregon
|
||||||
|
routing:
|
||||||
|
by_assignee: {window10: win10, oregon: oregon}
|
||||||
|
by_label: {'model:astra': {model: gpt-6-astra, effort: high}, 'model:sol': {model: gpt-6.1-sol, tier: fast}, 'model:opus': {model: opus}, 'exec:claude': {executor: claude-code}, 'needs-game': {serialize_group: game}}
|
||||||
|
default_executor: {win10: codex, oregon: claude-code}
|
||||||
|
retry: {transient_max: 3, backoff_minutes: [2, 5, 10]}
|
||||||
|
timeout_hours: 4
|
||||||
|
notify: {ntfy_topic: gwent, on: [waiting, review, failed]}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 仓库布局
|
||||||
|
```
|
||||||
|
agentd/ 调度核心、plane 客户端、vault 注入、状态存储(json/sqlite)、心跳
|
||||||
|
agentd/executors/ codex.py claude_code.py shell.py(适配器契约见 docs/executor-contract.md)
|
||||||
|
agentd/transports/ local.py warpgate_ssh.py(含 Windows schtasks 启动)
|
||||||
|
agentd/web/ 配置页面(阶段 2):读写 YAML + schema 校验 + 连通/冒烟测试 + 最近运行
|
||||||
|
cli/ agentd new|show|comment|state|assign|smoke
|
||||||
|
config/ agentd.yaml(示例 + 实际)、schema.json
|
||||||
|
docs/ design.md executor-contract.md runbook.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. 阶段
|
||||||
|
1. **核心**:配置驱动调度(轮询)、codex-remote(win10) + claude-local(oregon) 两个执行器、线程续接与重建、失败说明与重试、心跳、CLI;gwent 项目作为首个租户切换过来(替换现有 board-worker-plane)。
|
||||||
|
2. **配置页面**:单页,三个标签(终端/执行器/路由)+ 状态条;保存即校验、提交 Git、热加载;不显示密钥。
|
||||||
|
3. **Webhook + ntfy**:Plane webhook v2 签名校验、即时触发;ntfy 通知 Waiting/Review/失败;轮询保留兜底。前提:Plane VM 进 tailnet 或接收端放在可达位置。
|
||||||
|
4. **Temporal**:调度核心迁为 workflow/activity,获得重试/超时/历史/UI;agentd 变为 worker + webhook 入口。
|
||||||
|
5. **Gitea 联动**:执行器产物以分支/PR 提交并回帖链接;可选 Gitea webhook 回写 Plane。
|
||||||
|
|
||||||
|
## 8. 安全
|
||||||
|
配置里只有 Vault 键名;token 由 `agent-access.py exec --secret-env` 注入 agentd 进程;执行器不持有 Plane 凭据、不能直接改工作项;webhook 按项目签名校验;页面仅 tailnet 可达,登录走既有网关。
|
||||||
Reference in New Issue
Block a user