Merge branch 'main' into feat/swarm-io

This commit is contained in:
Fasthei
2026-06-11 17:03:58 +08:00
committed by GitHub
9 changed files with 568 additions and 2 deletions
+13
View File
@@ -56,3 +56,16 @@
- 🔴 蜂群 graph/timeline/cost 的统一前端 schema 未定义。
- 🟡 review/retry/collaboration 缺独立事件类型(当前借 `task.retried`/`timeline.updated`)。
- 待对齐:Frontend / Product Team 确认蜂群三视图(graph / timeline / cost)入口与事件流形态;与 HM 的 `/api/user/tasks/{id}/events` 通道如何衔接。
## 6. Swarm 侧冻结口径(#18:run/task/event API + Agent Registry/调度)
回应 #18 两项 DoD,明确本仓**已满足**的范围与**外部/后续**项(按规则 #9 据实标注,不夸大):
- **「前端可渲染 run/task/event 稳定 API」——本仓已冻结**:前端(经 HM 或受控读接口)用 §3 的 REST 即可稳定渲染:`GET …/{id}`(run 状态)、`/tasks`(task DAG)、`/events?after=<sequence>`(事件流,**per-swarm 严格递增 `sequence` 去重/续传**)、`/workflow`、`/metrics`、`/diagnostics`、`/audit`。事件类型与 envelope 见 [`event-schema.md`](./event-schema.md)(**FROZEN v1**,客户端事件集 + `sequence`)。即 run/task/event 的**数据契约已稳定冻结**。
- **「Agent Registry / 调度入口统一」——蜂群内已统一(单实现)**:Agent 经 WS `/ws/{agent_id}` 注册能力(`agent_registry`);调度由 `swarm_dispatch` **自选**(能力 + 信息素 τ + 负载 + 预算,见 [`agent-capability-schema.md`](./agent-capability-schema.md)、`orchestrator/dispatch_score.py`、`orchestrator/decision_engine.py`)。蜂群内**单一调度入口,无双实现**。
- **外部 / 后续(非本仓范围)**:
- ① 统一 **SSE/WS 推送传输**:HM Phase2(见 #46 / runtime-contract §8);当前为 REST 轮询 + `events?after=` 续传,前端不丢步。
- ② **跨平台统一 agent registry / capability center / quota / region·GPU / provider routing**:Infra/Scheduling Team(#2 对齐对象),非本仓编排器。
- ③ cockpit 三视图(graph / timeline / cost)**渲染**:客户端仓子工单。
> 结论:#18 在**本仓范围内已满足**(run/task/event 稳定 API + 蜂群调度入口统一);SSE 传输与跨平台 registry 为外部/Phase2。建议 owner 据此关闭本仓部分,或保留 #18 跟踪上述外部项(同 #19 口径)。
+31
View File
@@ -58,6 +58,37 @@ create 响应 `data`:`deployment_id`、`runtime_deployment_id`、`manager_depl
> 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`。
**拓扑**:专家 agent 仍是**独立进程**,主动出站连编排器 WS(`/ws/{agent_id}`,见 §5/security-boundary §6)。**拉起方 = Swarm 运行时**:编排器在 create 播种后由 `agent_launcher` 按 provisioning 策略拉起一个能力多样的 agent 池。去中心化下**无「按 run 分解算出 agent 数/角色」这一步**(种子任务无 required caps、任意 agent 可认领,子任务按能力自路由),故 agent 池**按用户**拉起、为固定能力集。§1 的「派发」= 把任务指派给**已连入**的 agent;**拉起 agent 进程**是本节定义的 Swarm 新职责。
**拉起后端**(`AGENT_LAUNCH_BACKEND`,fail-soft——拉起失败不影响 create):
- `none`(默认):不自动拉起,agent 由外部供给(保留 CI/e2e 与「外部拉起」部署);
- `subprocess`:本地起 `python -m agent.main`(dev);
- `command`:执行部署注入的模板 `AGENT_LAUNCH_CMD`(env 经进程环境传入,模板包装真实拉起如 kubectl/pod-create;**密钥不上 argv**)。
**限额(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` / `GIT_REPO_URL` | 可选:工作区 / 代码仓 | 资源授权 |
**约束**:
- 模型 key 由 **Swarm 服务端从 `billing_context.secret_ref`(`azkv://`)解析**(`resolve_model_key`:override → azkv(部署 SecretResolver / dev `HEICODE_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](./usage-billing-schema.md)。
- 同一用户的专家 agent 注入**同一把** `sk-` + `OPENAI_API_BASE=HM/v1`,计费归一到发起用户;`task_executor` 随模型请求带 `X-Agent-*` 归因头供 HM/NewAPI 关联。token 由 HM 按 agent 生命周期吊销(stop/delete)。
- **运行时事件前置**:只有 (i) HM 真把 create 派发到编排器(`SWARM_RUNTIME_ENABLED=true`,非 manager-local 适配器)且 (ii) Swarm 已按上表拉起 agent 并连入,编排器才回推 `task.*`/`swarm.*` 运行时事件(否则 events feed 仅有 HM 控制面 `deployment.status_changed`,见 #39)。
## 4. 状态机
部署状态:`waiting_approval` → `running` →(`blocked` ⇄ `running`)→ 终态 `completed` / `failed` / `stopped`。
+1
View File
@@ -59,6 +59,7 @@
- **Swarm 模型**:Agent 主动出站连编排器 WebSocket(`/ws/{agent_id}`),**不**对公网暴露每 Agent 子域名。AM 单 Agent 模型里的「客户端↔agent 直连 + `AGENT_ACCESS_TOKEN` 本地校验」**不适用于** swarm(无直连回路)。
- 服务间鉴权:HM→Swarm 用 `AGENT_RUNTIME_SERVICE_TOKEN`(Bearer);回调 HMAC 签名。
- **每用户并发 Agent 配额**:一个 `user_id` 同时连接的 Agent 数上限为 `MAX_AGENTS_PER_USER`(env,默认 10)。注册(WS `register` 消息携带 `user_id`)超额即被拒绝(回 `registration_rejected` 并关闭,code 1008),断开后释放名额。归因主轴仍为 `user.id`/`channelId`。未带 `user_id` 的 Agent 为 unbound,不计入该配额。实现:`ConnectionManager.can_bind_user/bind_user/unbind` + 注册处强制;测试 `scripts/test-max-agents-per-user.py`。
- **Swarm 拉起 agent + 服务端解析 key(team 决议,runtime-contract §3.3)**:由 **Swarm 运行时**(`orchestrator/agent_launcher.py`)拉起专家 agent 池(拉起数 `min(池大小, MAX_AGENTS_PER_USER − 已连)`,与上面的注册兜底一致)。模型 key 由 **Swarm 从 `billing_context.secret_ref`(`azkv://`)服务端解析**后注入被拉起 agent 的 env——**不入** create 请求体 / 回调 / 日志 / argv(`command` 后端的密钥经进程 env 传入,不上命令行)。azkv 真实解析为部署侧 SecretResolver;dev/CI 用 `HEICODE_SECRET_<name>`。解析不到即 keyless 启动并明确报错(不伪造)。
- 🟡 待接入:多租户运行时隔离(命名空间/网络/配额)由 Agent 平台(AKS Workload Identity)承载,非本仓编排器;归因主轴为 `user.id`/`channelId`(见 `usage-billing-schema.md`),不引入 tenant 概念。
## 7. 外部 API 与传输
+5 -2
View File
@@ -62,6 +62,8 @@ Swarm 在运行中按时长/成本比例发 `budget.alert`(默认 80% 阈值
{
"model_id": "...", "model_tokens": 0, "prompt_tokens": 0, "completion_tokens": 0,
"model_cost_usd": 0.0, "runtime_seconds": 0.0, "billing_source": "...",
"cost_phase": "initial | review_retry", // #16:返工再执行的用量标 review_retry,供成本归属
"attempt": 0, // 该任务的重做次数(retry_count)
"manager_deployment_id": "...", "swarm_id": "...", "task_id": "...",
"agent_role": "...", "correlation_id": "...",
"budget": { "max_tokens": null, "max_cost_usd": null, "consumed_usd": 0.0, "remaining_usd": null }
@@ -84,7 +86,8 @@ PayPal 说明 §5 给出的运行时用量回传目标:
## 5. 多 Agent / 评审重做 成本聚合
- 一次 swarm 请求拆成多 task / 多 agent / 多评审轮;**每个 task 的 `usage` 可按 `swarm_id` 聚合**得到运行级合计(观测用途)。
- **评审重做成本**:每轮重做都会重新执行任务并累计 `usage`,因此**已隐含计入**运行级合计;但当前**未单独打标**「review_retry 成本」。
- **评审重做成本(#16,已打标)**:每轮重做重新执行任务并累计 `usage`,且其用量事件带 `cost_phase="review_retry"`(cross-review 已把该任务记入 `rework_attributions` 之后的再执行);初次执行为 `cost_phase="initial"`。计费/采集侧按 `swarm_id` 聚合时,可用 `cost_phase` 拆分 **initial vs review_retry 成本**——「review retry 成本可归属」由此满足,无需从 `retry_count` 反推。
- **详情聚合(#37)**:除逐条事件的 `cost_phase` 外,`GET …/{id}/metrics` 另返回 run 级滚动汇总 `cost_by_phase: { initial: {cost_usd, model_tokens}, review_retry: {cost_usd, model_tokens} }`,供 HM 详情用量聚合 / 客户端用量抽屉直接展示「初次 vs 返工」拆分,无需从事件流推导。
## 6. 字段覆盖与缺口
@@ -96,7 +99,7 @@ PayPal 说明 §5 给出的运行时用量回传目标:
| `reasoning_tokens` / `cache_tokens` | 🔴 未采集(取决于 provider usage 返回) |
| `tool_cost`(工具调用成本) | 🔴 未采集(无 SK 工具计量) |
| `cpu_core_seconds` / `memory_mb_seconds`(基础设施) | 🔴 未采集(K8s 指标未接入账本) |
| `review_retry` 成本单独打标 | 🟡 隐含累计,未单独标注 |
| `review_retry` 成本单独打标 | ✅ 已打标(usage 事件 `cost_phase=review_retry`,#16) |
| provider 成本拆分 | 🟡 由 NewAPI/账本侧负责,非本仓 |
| tenant 归因 | ❌ 按标准不使用(见 §1) |