20 KiB
Manager ↔ Swarm Runtime Contract(Swarm 侧拥有)
状态:FROZEN v1(契约冻结) —— 回应
agent_swarm#14(来自 HM #28/#45 客户端「任务驾驶舱」查询面:stop 端点 + ID 映射 + 状态机)。对齐对象:Heicode Manager Runtime Team。冻结结论(供 HM #45 接 stop 真实调用):
- stop 端点:
POST /api/agent/swarm/deployments/{id}/stop(§3),已实现,幂等键X-Idempotency-Key。- ID 映射:
deployment_id ↔ swarm_id ↔ manager_deployment_id(§3.2),get_run_by_identifier支持任一 id 查询。- 状态机:运行时真实状态见 §4;与客户端 #28 期望(含
preparing/degraded/verifying)的映射表见 §4.1(运行时不新增臆造状态,规则 #9)。依据:
- HM 侧裁定
heicode-mananger/docs/integration/heicode-swarm-deferred.md:HM 当前不实现 swarm runtime;多 Agent 蜂群归agent_swarm/HeiCode-Swarm(本仓)。- 既有运行时集成范式
heicode-mananger/docs/integration/heicode-am-contract.md(单 Agent 模板 Agent,经 AM 启动)。本契约沿用其鉴权、回调、env、路径覆盖等约定。- 事件 envelope/类型见
event-schema.md(同批冻结,agent_swarm#15)。
1. 角色与边界
- HM(Heicode Manager):控制面。负责用户输入、资源/权限/计费/审计、模型网关(
/v1/*)。HM 不承载蜂群编排。 - Swarm Runtime(本仓):执行面。接收 HM 下发的编排请求,分解→派发→执行→评审→汇总,并通过带签名回调回报生命周期事件。
- HM 不在对话/执行回路;Swarm 通过回调把状态推回 HM,HM 也可主动拉取(见 §4)。
2. 鉴权(沿用 AM 约定)
- HM → Swarm:
Authorization: Bearer <service_token>。Swarm 侧校验AGENT_RUNTIME_SERVICE_TOKEN(兼容AGNET_RUNTIME_SERVICE_TOKEN)。未配置时为非安全开发模式(仅本地)。 - 统一响应信封:成功
{ "success": true, "data": {...} };失败{ "success": false, "error": { "code", "message", "request_id" } }。 - 常用请求头:
X-Correlation-ID、X-Idempotency-Key、Authorization。
3. 生命周期接口(已实现 / 待对齐)
Swarm 暴露以下接口(三组别名等价,便于 HM 路径覆盖):
/api/swarms、/api/agent/swarm/deployments、/api/agnet/deployments。
| 动作 | 方法 + 路径 | 状态 | 说明 |
|---|---|---|---|
| create | POST /api/agent/swarm/deployments |
✅ 已实现 | 创建蜂群部署;幂等键 X-Idempotency-Key |
| status | GET …/{deployment_id} |
✅ 已实现 | 返回部署概要与状态 |
| task graph | GET …/{deployment_id}/tasks |
✅ 已实现 | 任务 DAG |
| logs / events | GET …/{deployment_id}/logs、/events |
✅ 已实现 | 事件流(分页 cursor/limit) |
| metrics / workflow / diagnostics | GET …/{deployment_id}/metrics、/workflow、/diagnostics |
✅ 已实现 | 指标与编排视图 |
| input(追加输入) | POST …/{deployment_id}/input |
✅ 已实现 | 接收用户后续 prompt,注入为 source=user_append 任务(终态 run 自动 reopen 为 running;stopped 拒绝)。指令文本仅作任务描述下发给 agent,不回显进事件流(task.created 仅类别 message)。见 #40 |
| result(结果) | GET …/{deployment_id}/result |
✅ 已实现 | 返回用户面结果:{summary, deliverable, artifacts[], termination_reason, status};产物内容在各 artifact 的 uri(git/runtime),非内联 |
| cancel | POST …/{deployment_id}/stop |
✅ 已实现 | 停止并取消非终态任务,向 Agent 下发 cancel_task |
| approve | POST …/{deployment_id}/approvals/{approval_id} |
✅ 已实现 | 接收 Manager 审批决定(approved/rejected) |
| resume | — | 🟡 待对齐 | 当前仅「审批通过」隐式恢复(approvals);无独立 resume 端点 |
| retry | — | 🟡 待对齐 | 任务级重试 / 评审重做为内部机制;无外部 retry 端点 |
3.1 create 请求(必填校验)
必填:orchestration_plan.objective、callback.url、metadata.manager_deployment_id。
约束:mode 必须为 swarm;billing_context.secret_ref(若有)必须 azkv:// 前缀;resource_grants/metadata/callback 不得含明文密钥。
create 响应 data:deployment_id、runtime_deployment_id、manager_deployment_id、swarm_id、mode、status、runtime_execution_status、created。
3.2 ID 语义(回应 HM 标记的 deployment_id ↔ swarm_id 缺口)
deployment_id/runtime_deployment_id:Swarm 侧运行时部署 ID(runtime-dep-…)。swarm_id:工作流 ID(swarm-…),作为workflow_id;事件按swarm_id持久化与查询。manager_deployment_id:HM 侧部署 ID(请求metadata.manager_deployment_id透传)。correlation_id:贯穿一次交付的追踪 ID(X-Correlation-ID),作为trace_id。- 三者映射在 Swarm 持久化中维护:
deployment_id ↔ swarm_id ↔ manager_deployment_id,get_run_by_identifier支持三者任一查询。
HM 侧
heicode-swarm-deferred.md记录的「Swarm 仅暴露/tasks、缺deployment_id↔swarm_id」为旧状态;本仓 v5/v6 已实现上述映射与/api/agent/swarm/*接口,需 HM 复核更新该锚点。
3.3 蜂群专家 agent 拉起环境契约(agent launch env,回应 agent_swarm#16)
状态(团队决议):Swarm 运行时负责拉起专家 agent 并执行每用户限额(不再由 AM 拉起)。下表 env 字段 + 拉起方/限额口径已定。实现:
orchestrator/agent_launcher.py+main.launch_swarm_agents,测试scripts/test-agent-launcher.py。架构师裁定(2026-06-15,取代此前逃生门口径):所有蜂群一律走去中心化主路——Swarm 永远播种单一目标任务(
build_seed_task_specs),并按用户拉起 agent 池。orchestration_plan.agents(HM 现会发[{role:general}])不控制任务创建,也不控制 agent 拉起:既不会让运行时跳过播种、改按 breakdown 建任务,也不会让运行时认为「caller 自行拉起 agent」而不拉池。此前「Manager 显式 agent breakdown 由 caller 拉起 / honored as-is」的逃生门(_manager_provided_agents)已彻底退役删除,本节其余口径(拉起后端、限额、模型 key 注入 §3.3.1)不变。这与 CLAUDE.md「去中心化蜂群是唯一行为(cutover 已完成)」一致。
拓扑:专家 agent 仍是独立进程,主动出站连编排器 WS(/ws/{agent_id},见 §5/security-boundary §6)。拉起方 = Swarm 运行时:编排器在 create 无条件播种单一目标任务后,由 agent_launcher 按 provisioning 策略拉起一个能力多样的 agent 池。去中心化下无「按 run 分解算出 agent 数/角色」这一步,且无「按 caller 提供的 agent breakdown 拉起」这一步(种子任务无 required caps、任意 agent 可认领,子任务按能力自路由),故 agent 池按用户拉起、为固定能力集。§1 的「派发」= 把任务指派给已连入的 agent;拉起 agent 进程是本节定义的 Swarm 新职责。
拉起后端(AGENT_LAUNCH_BACKEND,fail-soft——拉起失败不影响 create):
none(默认):不自动拉起,agent 由外部供给(保留 CI/e2e 与「外部拉起」部署);subprocess:本地起python -m agent.main(仅 dev——进程在编排器 pod 内,无隔离/限额);command:执行部署注入的模板AGENT_LAUNCH_CMD(env 经进程环境传入,密钥不上 argv);kubernetes(生产):每 agent 一个 Pod,带资源 requests/limits + 标签(heicode-swarm-id/heicode-user-id,供 GC/teardown)。模型 key 经每-swarm k8s Secret(build_secret_manifest,via stdin 应用)由 PodsecretKeyRef引用——绝不内联进 PodSpec env(否则进 etcd/kubectl get -o yaml)。停止时按标签kubectl delete pod,secret。前置:编排器镜像含kubectl+ 一个对AGENT_POD_NAMESPACE有 pod/secret RBAC 的 ServiceAccount;ORCHESTRATOR_URL= 集群内 Service DNS(如ws://swarm-orchestrator.<ns>.svc.cluster.local:8000);NetworkPolicy 放行出网到 HM/v1+ git。硬化替代:用 azkv CSI SecretProviderClass 让 Pod 直接挂载密钥(编排器全程不碰明文),见 security-boundary §6。config:AGENT_POD_IMAGE/AGENT_POD_NAMESPACE/AGENT_POD_SERVICE_ACCOUNT/AGENT_POD_CPU|MEM_REQUEST|LIMIT。
限额(Swarm 执行):池大小 AGENT_LAUNCH_POOL_SIZE(默认 3);实际拉起数 = min(池大小, MAX_AGENTS_PER_USER − 该用户已连接数)——Swarm 在拉起时限额,并在 agent 注册时兜底硬拒(security-boundary §6 / PR#32)。
Swarm 拉起每个专家 agent 时注入以下进程环境变量(agent/main.py 实读):
| env | 含义 | 来源 |
|---|---|---|
OPENAI_API_KEY |
模型调用凭据 = 该用户 per-user sk-(NewAPI token),扣发起用户 user.Quota |
Swarm 从 billing_context.secret_ref 解析(HM 现签写入 KV) |
OPENAI_API_BASE |
HM 模型网关 /v1(OpenAI 兼容) |
部署(AGENT_OPENAI_API_BASE/OPENAI_API_BASE) |
OPENAI_MODEL |
所选模型 id | billing_context.default_model_id/计划 |
ORCHESTRATOR_URL |
编排器 WS 基址;agent 据此回连、自选任务 | 部署(ORCHESTRATOR_PUBLIC_URL/AGENT_RUNTIME_WS_URL) |
AGENT_ID |
agent 实例 id(唯一) | Swarm launcher |
AGENT_CAPABILITIES |
能力集合(逗号分隔) | Swarm launcher(AGENT_LAUNCH_CAPABILITIES 池策略) |
HEICODE_USER_ID |
发起用户;用于每用户并发上限 MAX_AGENTS_PER_USER(注册时强制) |
Swarm 从 create x-user-id 透传 |
WORKSPACE_DIR |
可选:工作区根 | 部署(WORKSPACE_DIR) |
GIT_REPO_URL |
代码仓克隆地址;agent 有此值才 clone | Swarm 从 git resource_grant.metadata.repo_url 解析 |
GIT_USERNAME / GIT_PASSWORD |
git HTTPS 凭据(注入 clone URL) | Swarm 从 git resource_grant.secret_ref(azkv://)解析 |
GIT_BASE_BRANCH |
可选:基线分支(默认 main) |
git grant metadata.base_branch |
约束:
- 模型 key 由 Swarm 服务端从
billing_context.secret_ref(azkv://)解析(resolve_model_key:override → azkv(部署 SecretResolver / devHEICODE_SECRET_<name>)→ 编排器OPENAI_API_KEY兜底)后注入被拉起 agent 的 env。key 不入 create 请求体 / 回调 / 日志 / argv(满足 §3.1);解析不到则 agent keyless 启动并明确报错(不伪造)。billing_context的newapi_user_ref/quota_ref仍仅为归因元数据。见 usage-billing-schema.md §2。 - git 仓库绑定(agent_swarm#63 / HM #92)由 Swarm 服务端从
resource_grants里resource_type=git的 grant 解析(resolve_git_grant):仓库地址 =grant.metadata.repo_url(非密文,inline 注入GIT_REPO_URL);git 凭据 =grant.secret_ref(azkv://,同模型 key 路径解析 → 注入GIT_USERNAME/GIT_PASSWORD)。git 凭据不入 create 请求体 / 回调 / 日志 / argv;k8s 后端GIT_PASSWORD经 per-swarm Secret 的secretKeyRef注入(绝不内联 PodSpec)。grant 有 repo 无凭据时仍注入GIT_REPO_URL(公有仓可 clone;私有仓 clone 报错,不伪造)。git KV secret 值约定为 JSON{"git_username","git_password"}(接受git_token/token形式 + 裸 token),待 HM #92 对齐。同一用户 agent 池注入同一 git grant。 - 同一用户的专家 agent 注入同一把
sk-+OPENAI_API_BASE=HM/v1,计费归一到发起用户;task_executor随模型请求带X-Agent-*归因头供 HM/NewAPI 关联。token 吊销走 §3.3.1 A.5 的swarm.pool_terminated握手(per-user,全部 run stop 后吊销)。 - 运行时事件前置:只有 (i) HM 真把 create 派发到编排器(
SWARM_RUNTIME_ENABLED=true,非 manager-local 适配器)且 (ii) Swarm 已按上表拉起 agent 并连入,编排器才回推task.*/swarm.*运行时事件(否则 events feed 仅有 HM 控制面deployment.status_changed,见 #39)。
3.3.1 模型 key 注入对接参数(HM #60,Swarm 侧已定死)
HM 实现 #60(计费 key 注入)前需 Swarm 定死的参数,逐条口径如下(实现:agent_launcher.resolve_model_key / _resolve_secret_ref,swarm_runtime 吊销握手;测试 scripts/test-key-injection-contract.py):
| # | 参数 | Swarm 口径(已定) |
|---|---|---|
| A.1 | sk- 粒度 + mint 时机 |
每用户一把(首次蜂群开通 mint、跨该用户所有 run 复用);同一用户的专家 agent 注入同一把 sk-、计费归一到发起用户。 |
| A.1 | KV secret 命名约定 | swarm-model-key-<user_id>(Swarm 只取 secret_ref 末段做名,不强约束路径;命名供 HM 定位)。 |
| A.2 | KV secret value 格式 | JSON {"openai_api_key":"sk-..."}(对齐 callback 签名密钥的 {"callback_signing_secret":"..."} 约定,可扩展)。Swarm 解析字段名 = openai_api_key;裸 sk- 字符串亦兼容(_extract_model_key);解析不到/字段缺失 → 不伪造、agent keyless 明确报错。 |
| A.3 | Swarm 读 KV 身份 / RBAC | ⚠ 联调阻塞前置(分两步):① Swarm 侧先 provision Pod 的 Workload Identity(建 UAMI → federate 到 orchestrator-sa SA → 用 client-id 注解 SA)——当前尚未建(仓内无 azure.workload.identity/client-id 注解、无 IaC);② HM/运维 在其库 heicode-vault(https://heicode-vault.vault.azure.net)给该身份授 Key Vault Secrets User(只读、限 swarm-model-key-*)。heicode-vault 是 HM 侧库,故授权由 HM/运维做;Swarm 只提供 Pod 身份 id(clientId + objectId)+ 收窄作用域。不通则 secret_ref 解不出。生产 KV 适配器(SECRET_RESOLVER)在仓外经部署接线,须指向 heicode-vault;dev/CI 用 HEICODE_SECRET_<name> 环境映射。(库名更正自 agent_swarm#56:实库为 heicode-vault,非早期文档的 heicode-kv。) |
| A.4 | OPENAI_API_BASE |
Swarm 部署常量(AGENT_OPENAI_API_BASE/OPENAI_API_BASE,设为 HM 网关 https://code.heicode.cc/v1),不经 create 的 billing_context 下发。(域名更正自 agent_swarm#56:code.heicode.cc 为正式地址,code.xinghanlab.com 为前期过渡域名。) |
| A.5 | 吊销信号 | 事件驱动(方案 A):sk- per-user 长存;stop 是唯一终态(completed/failed 可经 POST …/input 重开,故仍保留 key)。当某用户所有 run 均被 stop(retained 集清空)时,运行时发恰好一次 swarm.pool_terminated{user_id, secret_ref},HM 收到即吊销 sk- + 清 KV。单 run 的 swarm.stopped 不触发吊销(key per-user 复用);账户停用 / HM 主动 delete 走同一 stop 路径。 |
swarm.pool_terminated是 HM 控制面生命周期事件(非客户端 cockpit 事件,不入FROZEN_CLIENT_EVENT_TYPES);HM 需在subscribed_events+agent_callback登记以收吊销信号。payloadsecret_ref为azkv://引用(非明文 key)。
4. 状态机
部署状态:waiting_approval → running →(blocked ⇄ running)→ 终态 completed / failed / stopped。
| 状态 | 含义 | 进入方式 |
|---|---|---|
waiting_approval |
高危/需审批,等待 Manager 审批 | create 时命中审批条件 |
running |
任务派发与执行中 | 审批通过 / 有在途任务 |
blocked |
任务因移交/依赖阻塞 | 子任务 blocked_on_handoff |
completed |
所有任务终态且评审通过 | 全部完成(评审循环可选) |
failed |
存在失败且不可恢复 | 任务终态含 failed |
stopped |
Manager 主动停止 | …/stop |
每次状态变更通过回调 deployment.status_changed 推送(见 §5);终态另发 swarm.completed/swarm.failed/swarm.stopped(客户端驾驶舱据此切终态横幅,见 event-schema §4)。
4.1 与客户端期望状态的映射(#14.3 决议:映射,不新增运行时状态)
客户端 #28 期望 created/preparing/running/waiting_approval/degraded/verifying/completed/failed/stopped。本运行时不臆造 preparing/degraded/verifying 等中间态(规则 #9:无真实信号不造态),由 HM/客户端按下表映射现有真实状态:
| 客户端期望态 | 运行时真实来源 | 映射口径 |
|---|---|---|
created |
create 成功、尚无在途任务 | 初始 running 前的瞬态;HM 可在落 runtime_swarm_id 后、首个 task.* 前显示 |
preparing |
同上(播种中) | 映射到 running(种子已注入、Agent 尚未自选);无独立状态 |
running |
running |
直通 |
waiting_approval |
waiting_approval |
直通 |
degraded |
blocked |
blocked(移交/依赖/审批驳回阻塞)映射为 degraded;P-guard run.metadata["health"] 不健康亦可佐证 |
verifying |
running + 交叉评审进行中 |
评审期仍是 running;如需细分,由 timeline.updated(Review cycle N) / run.metadata["cross_review"] 提示 |
completed/failed/stopped |
同名终态 | 直通;并有 swarm.{completed,failed,stopped} 终态事件 |
即:运行时真实状态集 =
waiting_approval/running/blocked/completed/failed/stopped(§4 表);created/preparing/degraded/verifying是展示层别名,不改变运行时语义,也不进入回调status字段。如客户端坚持要真实细分态,需另立工单评估状态机扩展(非本次冻结范围)。
5. 回调(已与 HM 处理器对齐)
Swarm → HM:POST {callback.url}(create 时下发,默认 HM 的 /api/agent/callbacks/runtime-events)。
鉴权与签名(与 HM agent_callback.go 一致,已实现):
- 头:
X-Agent-Service-Token(或Authorization: Bearer)、X-Agent-Timestamp(Unix 毫秒)、X-Agent-Signature、X-Agent-Event-Id、X-Correlation-ID(均含X-Agnet-兼容别名)。 - 签名:
signature = "sha256=" + hex(HMAC_SHA256(secret, f"{timestamp}.{event_id}.{raw_body}"))。 - secret:
AGENT_CALLBACK_SIGNING_SECRET(兼容AGNET_…);HM 侧容差默认 300s。 - 幂等:HM 按
X-Agent-Event-Id→ bodyevent_id→idempotency_key去重。
事件 envelope 字段与 event_type 取值见 event-schema.md(与 HM 注册表一致)。
6. 路径与超时覆盖(沿用 AM 约定)
HM 侧可用 env 覆盖:AGENT_RUNTIME_BASE_URL、AGENT_RUNTIME_SERVICE_TOKEN、AGENT_RUNTIME_AGENT_START_PATH、AGENT_RUNTIME_AGENT_PATH、AGENT_RUNTIME_AGENT_STOP_PATH、AGENT_RUNTIME_START_TIMEOUT_SECONDS、AGENT_RUNTIME_CALLBACK_URL。
7. 安全
env/请求体不得含明文密钥;凭据经secret_ref(azkv://)注入。详见security-boundary.md。- Swarm 启动接口必须 HTTPS / 私网;回调走 HTTPS。
8. 冻结状态与剩余对齐项
本次冻结(agent_swarm#14)已定稿:stop 端点(§3)、ID 映射(§3.2)、状态机 + 客户端映射表(§4/§4.1)、终态 swarm.* 事件。HM #45 可据此接 stop 真实调用。
剩余非阻塞项(不影响 #45 Phase1 接入):
- resume / retry 是否需要独立外部端点,还是沿用 approvals + 内部重做(当前:无独立端点)。
- HM 是否以 AM 同款
/agents生命周期(而非/api/agent/swarm/*)调用 Swarm;若是,需路径映射。 - HM 注册表(
agent_callback.go)登记本次新增的swarm.completed/failed/stopped、approval.approved/rejected、handoff.created(见 event-schema §4 ⭐)。 - 更新 HM 侧
heicode-swarm-deferred.md锚点,登记本契约。 - #46 Phase2(SSE 长连 + 追加输入写语义):待本契约 + event-schema 冻结后由 HM 把轮询升级 SSE(见 frontend-event-api.md §4)。