23 KiB
Agnet → Heicode Manager 反向 Callback 契约 v1(提案)
状态:DRAFT — 由 Heicode Manager 团队起草,发回给 Agent Manager 团队评审 配套阅读:
HEICODE_API_INTEGRATION.md(v2.0.0,正向:Heicode → Agent Manager) 目标版本:v1.0 草案日期:2026-05-26 联系人:Heicode Manager 团队
0. 摘要(给评审同事 30 秒看懂)
HEICODE_API_INTEGRATION.md 描述了 Heicode → Agent Manager 的正向调用(创建部署 / 拉日志 / 拉事件)。但 Agent Manager → Heicode 的反向通知协议没有定义,导致 Heicode 这边只能轮询,无法实时知道:
- 部署进到哪一个标准阶段(需求 / 设计 / 后端 / 前端 / 检查 / 测试 / 部署)
- Agent 调用了哪个 SK 工具、产出什么、是否失败
- 高危操作发起审批请求 + 客户端审批结果回流
- 预算告警触发
- Agent / Pod 异常退出
本提案定义一个 HTTP Webhook + HMAC 签名 的反向通知协议,覆盖上述 5 类共 10 个标准事件。
最小可用版本(v1.0)实施工作量评估:Agent Manager 侧约 1-1.5 周,Heicode Manager 侧约 1 周(接收端可与对方并行)。
1. 当前对接现状(梳理给评审)
1.1 正向(已实现,文档 v2.0.0)
Heicode Manager ──POST /api/agnet/deployments──▶ Agent Manager
──GET /api/agnet/.../logs────▶
──GET /api/agnet/.../events──▶
──GET /api/agnet/.../metrics─▶
1.2 反向(未定义 — 本文要解决的)
Heicode Manager ◀──??? Agent Manager 怎么告诉我们:
- 进入了"前端"阶段?
- Agent 刚调了 search_web 工具?
- 这次部署要花 $80 了,超过 80% 阈值?
- 高危操作等用户审批?
- Agent Pod 被 K8s OOMKilled?
当前只能靠 Heicode 这边轮询 /events,1 分钟轮询一次 = 用户最坏要等 1 分钟才看到状态变化,且浪费请求。
2. 协议总览
┌──────────────────┐ ┌────────────────────┐
│ Heicode Manager │ │ Agent Manager │
│ (生产环境位于 │ │ (deploys 子环境) │
│ code.xinghanlab │ │ │
│ .com) │ │ │
│ │ │ │
│ ① 创建部署时注册│ ──── POST /deployments──▶│ │
│ callback_url │ body 带 callback_url│ │
│ │ │ │
│ │ │ ② 子环节切换 / │
│ │ │ SK 调用 / │
│ │ │ 审批等事件触发 │
│ │ │ │
│ ③ 接收回调 │ ◀──POST {callback_url}───│ │
│ /api/agnet │ 含 HMAC 签名 + │ │
│ /callback │ X-Agnet-Event-Id 幂等 │ │
│ │ │ │
│ ④ 200 OK 回执 │ ────────────────────────▶│ │
│ │ │ │
│ 非 200 → 退避重试│ ◀──────────────────────│ 按重试策略最多 5 次 │
└──────────────────┘ └────────────────────┘
3. Heicode 侧注册(callback_url 怎么告诉 Agnet)
3.1 创建部署时携带
扩展 POST /api/agnet/deployments 请求体,新增可选字段:
{
"orchestration_plan": "...",
"agents": [ ... ],
"callback": {
"url": "https://code.xinghanlab.com/api/agnet/callbacks/swarm-events",
"signing_secret_ref": "azkv://heicode-kv.vault.azure.net/secrets/agnet-callback-signing-key",
"subscribed_events": [
"phase.changed",
"sk_tool.called",
"sk_tool.completed",
"sk_tool.failed",
"approval.requested",
"approval.granted",
"approval.rejected",
"budget.alert",
"deployment.status_changed",
"agent.crashed"
]
}
}
url:Heicode 接收端点;必须 HTTPSsigning_secret_ref:HMAC 签名密钥的 Vault 引用(不传明文)subscribed_events:可选,省略则推送全部;后续允许只订阅子集
3.2 后绑定 / 修改(可选 P2 阶段)
PATCH /api/agnet/deployments/{deployment_id}/callback
允许在部署运行期间更换 callback URL(例如 Heicode 灰度发布切换接收端)。
4. Callback 接收接口(核心)
4.1 Endpoint
| 方法 | URL | 说明 |
|---|---|---|
POST |
{callback_url} |
Agnet 推送事件 |
Heicode 生产端点(建议):
POST https://code.xinghanlab.com/api/agnet/callbacks/swarm-events
4.2 必需 Headers
| Header | 必需 | 说明 |
|---|---|---|
Content-Type |
✅ | 固定 application/json; charset=utf-8 |
X-Agnet-Event-Id |
✅ | UUID v4。幂等去重 key,Heicode 端基于此判重 |
X-Agnet-Event-Type |
✅ | 事件类型短码(见 §5),冗余字段方便日志快速过滤 |
X-Agnet-Deployment-Id |
✅ | 关联的部署 ID |
X-Agnet-Timestamp |
✅ | 事件发生时刻(unix-ms) |
X-Agnet-Signature |
✅ | HMAC-SHA256 签名(见 §4.3) |
X-Agnet-Signature-Version |
✅ | 固定 v1,方便未来切换 |
X-Agnet-Delivery-Id |
✅ | 本次投递的 UUID(同事件重试时 event_id 不变、delivery_id 变) |
X-Agnet-Delivery-Attempt |
✅ | 投递尝试次数,1-base。首次=1,重试=2/3/4/5 |
4.3 HMAC 签名规范
为什么需要签名:Heicode 的 callback endpoint 必须能被公网访问(Agnet 跨网络推过来),如果不签名,任何人都能伪造事件骗 Heicode 改状态。
签名算法:
canonical_string = X-Agnet-Event-Id + "\n"
+ X-Agnet-Event-Type + "\n"
+ X-Agnet-Deployment-Id + "\n"
+ X-Agnet-Timestamp + "\n"
+ X-Agnet-Delivery-Id + "\n"
+ X-Agnet-Delivery-Attempt + "\n"
+ sha256_hex(request_body)
signature = base64( HMAC-SHA256(signing_secret, canonical_string) )
Heicode 侧校验顺序(务必按此顺序,先廉价后昂贵):
- 检查
X-Agnet-Timestamp在当前时间 ±5 分钟内 → 防回放 - 检查
X-Agnet-Event-Id不在最近 24h 已处理列表 → 幂等去重 - 计算 canonical_string → 比对
X-Agnet-Signature→ 验真 - 解析 body → 校验事件结构 → 分发处理
密钥管理:
- 签名密钥由 Heicode 端生成并写入 Vault
- 创建部署时 Heicode 把 vault 引用(
signing_secret_ref)传给 Agent Manager - Agent Manager 从 Vault 取出 → 签名 → 立刻丢弃,不长期持有
4.4 幂等性约定
Heicode 端必须:
- 用
X-Agnet-Event-Id作为去重 key(Redis SETNX,TTL 24h) - 同一
event_id第 2 次到达 → 返回 200 OK + 不重复处理(不是 409,避免 Agnet 误判失败再重试) - 响应头返回
X-Heicode-Event-Status: duplicate让 Agnet 知道已收过
4.5 重试策略
Agent Manager 端必须实现:
| 触发条件 | 策略 |
|---|---|
| 收到 2xx | 投递成功,结束 |
| 收到 4xx(除 408 / 429) | 不重试 — 签名错 / 体格式错等是协议错,重试也没用,记 dead letter |
| 收到 408 / 429 / 5xx 或网络超时 | 重试 |
| 重试间隔 | 指数退避:5s, 30s, 2min, 10min, 30min |
| 最大尝试次数 | 5 次(首发 + 4 次重试) |
| 全部失败后 | 写 dead letter queue + 发告警,可由人工触发 replay |
4.6 响应格式
成功:
HTTP/1.1 200 OK
Content-Type: application/json
X-Heicode-Event-Status: accepted | duplicate
{ "success": true }
Heicode 端临时性错误(要 Agnet 重试):
HTTP/1.1 503 Service Unavailable
{
"success": false,
"error": { "code": "DOWNSTREAM_DB_UNAVAILABLE", "retryable": true }
}
协议永久错误(不重试):
HTTP/1.1 400 Bad Request
{
"success": false,
"error": { "code": "SIGNATURE_INVALID", "retryable": false }
}
5. 事件类型清单(v1 必须实现 10 个)
5.1 phase.changed — 部署阶段切换
业务价值:解锁 Heicode Manager 任务详情页的 7 阶段实时进度条(产品文档 M10)。
触发时机:Agent Manager 检测到部署整体进入新阶段。
payload:
{
"event_type": "phase.changed",
"event_id": "evt_a1b2c3d4...",
"deployment_id": "dep_a1b2c3d4",
"occurred_at": "2026-05-25T10:30:00Z",
"data": {
"from_phase": "requirements",
"to_phase": "design",
"agent_instance_id": null,
"summary": "Requirements gathering complete, moving to design"
}
}
phase 必须是以下 7 个标准值之一(v1 闭集):
| phase | 中文 | 说明 |
|---|---|---|
requirements |
需求 | 产品 Agent 在澄清目标 |
design |
设计 | 架构 Agent 在出原型 / 设计方案 |
backend |
后端 | 后端 Agent 在写服务端代码 |
frontend |
前端 | 前端 Agent 在写 UI |
review |
检查 | Reviewer Agent 在审代码 |
test |
测试 | 跑测试套件 |
deploy |
部署 | Ops Agent 在部署 / 高危审批中 |
特殊值:
from_phase可为null(首次进入,即从无到 requirements)to_phase不可为null
5.2 sk_tool.called — SK 工具被调用
业务价值:解锁 Heicode 任务详情页的"Agent 调用了 search_web 工具"实时展示(产品文档 M11)。
触发时机:某个 Agent 实例开始调用一个 SK 工具,调用前推。
payload:
{
"event_type": "sk_tool.called",
"event_id": "evt_xxx",
"deployment_id": "dep_xxx",
"occurred_at": "2026-05-25T10:30:15Z",
"data": {
"agent_instance_id": "agi_123abc",
"agent_role": "researcher",
"tool_name": "search_web",
"tool_invocation_id": "inv_xyz789",
"input_summary": "query: 'kubernetes operator best practices' (full args redacted)",
"input_size_bytes": 142,
"sk_source_ref": "git:heicode-tools@v1.2.0/search_web.py"
}
}
安全:input_summary 是 Agent Manager 自己截断的人类可读摘要,不能包含密钥 / token / 数据库连接串等敏感原文。Heicode 端只展示 input_summary,永不展示完整 args。
5.3 sk_tool.completed — SK 工具调用成功
触发时机:工具返回成功结果时。
payload:
{
"event_type": "sk_tool.completed",
"event_id": "evt_xxx",
"deployment_id": "dep_xxx",
"occurred_at": "2026-05-25T10:30:18Z",
"data": {
"agent_instance_id": "agi_123abc",
"tool_invocation_id": "inv_xyz789",
"duration_ms": 2814,
"output_summary": "10 results returned, top-3 about CRDs and reconcile loops",
"output_size_bytes": 8421,
"cost_usd": 0.0034
}
}
tool_invocation_id 与 5.2 配对,Heicode 端 join 起来形成"开始-完成-耗时"链。
5.4 sk_tool.failed — SK 工具调用失败
触发时机:工具抛异常 / 超时 / 返回错误码。
payload:
{
"event_type": "sk_tool.failed",
"event_id": "evt_xxx",
"deployment_id": "dep_xxx",
"occurred_at": "2026-05-25T10:30:18Z",
"data": {
"agent_instance_id": "agi_123abc",
"tool_invocation_id": "inv_xyz789",
"duration_ms": 30000,
"failure_code": "TOOL_TIMEOUT",
"failure_message": "search_web exceeded 30s timeout",
"is_recoverable": true,
"retry_count": 2
}
}
failure_code 建议枚举(不强制):
TOOL_TIMEOUTTOOL_AUTH_FAILEDTOOL_QUOTA_EXCEEDEDTOOL_INPUT_INVALIDTOOL_INTERNAL_ERRORTOOL_NETWORKTOOL_UNKNOWN
5.5 approval.requested — 高危操作请求审批
业务价值:解锁产品文档要求的"高危操作必须客户端审批"闭环。
触发时机:Agent Manager 遇到 risk_level=high 的具体动作(生产部署、DB 写、删云资源、访问生产密钥等),暂停执行,向 Heicode 请求审批。
payload:
{
"event_type": "approval.requested",
"event_id": "evt_xxx",
"deployment_id": "dep_xxx",
"occurred_at": "2026-05-25T10:35:00Z",
"data": {
"approval_id": "apv_abc123",
"agent_instance_id": "agi_456def",
"operation": "production_deploy",
"target_resource": "azkv://heicode-kv.vault.azure.net/secrets/aks-cluster-prod",
"risk_level": "high",
"human_readable_description": "Deploy commit 7a3f9c to production AKS cluster (replaces 2 running pods)",
"auto_deny_at": "2026-05-25T11:35:00Z",
"blocking": true
}
}
approval_id:Heicode 后续审批回调要带这个 ID 让 Agnet 知道针对哪次auto_deny_at:超过这个时间还没审批 → Agent Manager 自动拒绝并失败blocking=true:Agent Manager 已暂停部署,等审批
Heicode 拿到这个事件后做什么:
- 推到桌面客户端的审批 UI(已有契约)
- 用户点同意 / 拒绝
- Heicode 调正向接口:
POST /api/agnet/deployments/{id}/approvals/{approval_id}body{ "decision": "granted" | "rejected", "reason": "..." }
5.6 approval.granted / 5.7 approval.rejected
触发时机:Agent Manager 收到 Heicode 的审批决定之后,回推一个确认事件(让 Heicode 知道 Agent Manager 已恢复 / 已中止)。
payload:
{
"event_type": "approval.granted",
"event_id": "evt_xxx",
"deployment_id": "dep_xxx",
"occurred_at": "2026-05-25T10:36:42Z",
"data": {
"approval_id": "apv_abc123",
"decision_by_user_id": "user_42",
"resumed_at": "2026-05-25T10:36:42Z"
}
}
approval.rejected payload 类似,加 reason 字段。
5.8 budget.alert — 预算告警
触发时机:累计花费跨过 alert_threshold_pct(默认 80%)或硬上限 max_usd。
payload:
{
"event_type": "budget.alert",
"event_id": "evt_xxx",
"deployment_id": "dep_xxx",
"occurred_at": "2026-05-25T10:40:00Z",
"data": {
"alert_level": "warning",
"consumed_usd": 80.5,
"max_usd": 100.0,
"consumed_pct": 80.5,
"threshold_pct": 80,
"projected_overrun": false
}
}
alert_level 枚举:warning(80% 阈值)/ critical(≥95%)/ exceeded(已超 max_usd,Agent Manager 自动停掉)
5.9 deployment.status_changed — 部署整体状态变更
业务价值:替代当前的/deployments/:id 轮询,实时通知顶层状态。
触发时机:deployment.status 字段值变化(pending → running → stopped / failed)。
payload:
{
"event_type": "deployment.status_changed",
"event_id": "evt_xxx",
"deployment_id": "dep_xxx",
"occurred_at": "2026-05-25T10:30:05Z",
"data": {
"from_status": "pending",
"to_status": "running",
"failure_code": null,
"failure_message": null
}
}
stopped / failed 时 failure_code / failure_message 必填。
5.10 agent.crashed — Agent 异常退出
触发时机:单个 Agent Pod 被 K8s 终止(OOM / SIGKILL / 退出码非 0 / liveness probe 失败)。不包括 Agent 自然完成。
payload:
{
"event_type": "agent.crashed",
"event_id": "evt_xxx",
"deployment_id": "dep_xxx",
"occurred_at": "2026-05-25T10:42:00Z",
"data": {
"agent_instance_id": "agi_123abc",
"role": "researcher",
"exit_code": 137,
"exit_reason": "OOMKilled",
"restart_count": 2,
"will_restart": true,
"last_log_tail": "MemoryError: out of memory while parsing 4GB JSON",
"uptime_before_crash_sec": 287
}
}
will_restart=true 时 Agent Manager 自动重启,Heicode 端只是记录;will_restart=false 表示重启次数已达上限,部署会进 failed。
6. 联调 / 沙箱支持(必需)
6.1 Mock 事件触发接口(Agent Manager 端实现)
为了让 Heicode 这边在没有真实部署的情况下也能联调 callback 接收逻辑:
POST /api/agnet/_mock/emit_event
Authorization: Bearer <SERVICE_TOKEN>
{
"callback_url": "https://code.xinghanlab.com/api/agnet/callbacks/swarm-events",
"event_type": "phase.changed",
"deployment_id": "dep_mock_001",
"data": { "from_phase": null, "to_phase": "requirements" }
}
调用后 Agent Manager 立刻按真实流程签名 + POST 一次给 callback_url。
必须只在 staging / dev 环境暴露,生产环境 403。
6.2 Mock 工具
提供一个 CLI:
agnet-cli mock-emit \
--target https://staging.heicode.local/api/agnet/callbacks \
--event sk_tool.called \
--deployment dep_mock_001 \
--signing-secret "$(cat /tmp/test-secret)"
让两边联调时不依赖真实 Agent 跑起来。
7. 错误码(Heicode 接收端返回的)
| HTTP 状态码 | error.code | retryable | 说明 |
|---|---|---|---|
| 200 | — | — | 正常受理 |
| 200 | — | — | 重复(duplicate event_id),返回 200 不让 Agnet 重试 |
| 400 | SIGNATURE_INVALID |
false | HMAC 验证失败 |
| 400 | SIGNATURE_VERSION_UNSUPPORTED |
false | 用了我们不支持的签名版本 |
| 400 | EVENT_BODY_MALFORMED |
false | JSON 解析失败 / 必填字段缺失 |
| 400 | EVENT_TYPE_UNKNOWN |
false | 不在 §5 枚举里的事件类型 |
| 408 | RECEIVE_TIMEOUT |
true | Heicode 端 DB 写慢,建议重试 |
| 409 | — | — | 不使用 — 重复 event_id 走 200 路径 |
| 413 | BODY_TOO_LARGE |
false | 超过 64KB(建议每条事件 < 16KB) |
| 422 | DEPLOYMENT_NOT_KNOWN |
false | Heicode 这边查不到这个 deployment_id(Agnet 推得太早 / Heicode 还没记录) |
| 429 | RATE_LIMITED |
true | Heicode 端短期被刷爆 |
| 503 | DOWNSTREAM_UNAVAILABLE |
true | Heicode 后端 DB / Redis 暂时不可用 |
| 5xx | — | true | 任何 5xx 都按 retryable 处理 |
8. 时间戳偏移宽容度
由于两侧服务器时钟可能漂移:
- Heicode 端校验
X-Agnet-Timestamp落在now ± 5 分钟内 - 超出 → 返回
400 SIGNATURE_INVALID(防止回放) - Agnet 端必须用 NTP 同步时钟,最大允许偏移 ±60 秒
9. 事件投递顺序保证(重要)
9.1 不保证全局有序
跨不同 deployment_id 的事件不保证投递顺序(不同 deployment 在不同 worker 处理)。
9.2 同一 deployment 内的事件
Agent Manager 应尽力按 occurred_at 顺序投递,但 Heicode 端不依赖顺序正确做处理:
- 每个事件自己带
occurred_at - Heicode 按 occurred_at 排序后再展示,不按到达顺序
- 这避免了"重试一个旧 phase.changed 时已经收到新的"导致 phase 倒退
9.3 推荐保证级别
| 保证 | v1 提案 |
|---|---|
| 至少一次(at-least-once)投递 | ✅ 必需 |
| 同 deployment 内顺序 | ⚠️ 尽力 |
| 精确一次(exactly-once)处理 | ✅ 由 Heicode 端用 event_id 幂等保证 |
10. 实施时间线建议
Phase 1(1-1.5 周,可并行)
Agent Manager 侧:
- 实现 §5 中的 10 个事件触发点
- 实现 §4.2-4.5 HMAC 签名 + 重试
- 实现 §6 mock-emit 接口
Heicode Manager 侧(不依赖 Agent Manager 完成):
- 实现
/api/agnet/callbacks/swarm-events接收端 - 实现 §4.3 HMAC 校验、§4.4 幂等去重(复用 Redis SETNX,参考 V2 device-signature nonce 实现)
- 事件入审计表(复用
agnet_audit_events) - 给桌面客户端 push 接口(已有 SSE 通道复用)
Phase 2(1 周联调)
- 两侧用 mock-emit 联调各 event_type
- 故意制造签名错 / 体格式错 / 网络断 / 慢响应等 edge case,验证重试 + dead letter
- 真实部署端到端验证:phase 变化 / SK 工具调用 / 预算告警
Phase 3(生产上线 + 观察 2 周)
- 灰度 1 个真实部署
- 监控:投递成功率(> 99%)、重试率(< 5%)、p95 接收延迟(< 300ms)、dead letter 数(每天 < 5)
- 全量上线
11. 安全与合规
11.1 数据最小化
- payload 里严禁包含原始 prompt、原始代码、原始密钥
- 所有"内容"字段都是
*_summary,由 Agent Manager 主动截断 + 脱敏 - 文件 / 工具输出超过 1KB 时只传摘要 + size + 引用 ID
11.2 IP 白名单(可选)
Heicode 可在 callback endpoint 加 IP CIDR 白名单(Agent Manager 出网 IP 段),HMAC 之上再加一层。但 Cloudflare 代理后这个白名单意义有限,HMAC 是真正的安全边界。
11.3 审计
每个收到的事件都落 agnet_audit_events 表(v1.4.2 已经实装),含:
- event_id / event_type / deployment_id
- delivery_attempt(看重试情况)
- signature_verified(true/false)
- processed_at / processing_duration_ms
12. 版本演进
- 本契约为
v1.0 - 未来添加新
event_type是 minor(v1.1),Heicode 端忽略未知 event 应返回 200 + warning(不让 Agent 重试) - 修改现有 event 字段或签名规范是 major(v2.0),双方协商升级
- Heicode 收到
X-Agnet-Signature-Version: v2但本机只支持 v1 → 返回 400SIGNATURE_VERSION_UNSUPPORTED
13. 待 Agent Manager 团队确认的开放问题
- ❓
signing_secret_ref走 Vault 引用,需要 Agent Manager 这边有 Vault 客户端能解 — 现状如何?是否需要换成 Heicode 直接给明文密钥? - ❓ §6 mock-emit 接口你们能在 staging 提供吗?没这个我们这边没法联调
- ❓ §5.5 高危审批的
auto_deny_atTTL 默认多久合适?我们这边建议 1 小时 - ❓ §5.10
agent.crashed的last_log_tail截断到多少字节?建议 1KB - ❓ §10 phase 1 的 1-1.5 周评估,跟你们实际工作量是否一致?
14. 附录
14.1 Heicode 端等价代码(参考实现)
// heicode/middleware/agnet_callback_signature.go (待实现)
//
// 校验 X-Agnet-Signature 的中间件。复用 V2 device-signature 那套
// canonical-string + HMAC 模式,只是密钥源改成 Vault 引用 + signing
// scheme 改成 HMAC-SHA256 而非 Ed25519。
//
// 失败响应统一走 400 + retryable:false,不让 Agent Manager 在协议
// 错的情况下白重试。
14.2 Heicode 端落库参考
-- 已存在的 agnet_audit_events 表
-- (Sprint 1, 2026-05-22 上线)
-- 加一个 event_source 列区分 'control_plane' (Heicode 自己触发的)
-- 和 'callback' (从 Agent Manager 反推的)
ALTER TABLE agnet_audit_events ADD COLUMN event_source VARCHAR(32) DEFAULT 'control_plane';
Heicode Manager 团队联系人:陈晨 (zsbgnw@gmail.com) 草案版本:v1.0-draft-1 期望评审周期:2026-05-30 前给反馈