Files
agent_management/docs/HEICODE_AGNET_CALLBACK_CONTRACT_v1.md

23 KiB
Raw Permalink Blame History

Agent Runtime → 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 这边只能轮询,无法实时知道:

  1. 部署进到哪一个标准阶段(需求 / 设计 / 后端 / 前端 / 检查 / 测试 / 部署)
  2. Agent 调用了哪个 SK 工具、产出什么、是否失败
  3. 高危操作发起审批请求 + 客户端审批结果回流
  4. 预算告警触发
  5. 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/agent/sub-agile/deployments──▶  Agent Manager
                ──GET  /api/agent/.../logs────▶
                ──GET  /api/agent/.../events──▶
                ──GET  /api/agent/.../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/agent     │   含 HMAC 签名 +         │                    │
│   /callback      │   X-Agnet-Event-Id 幂等  │                    │
│                  │                          │                    │
│  ④ 200 OK 回执   │ ────────────────────────▶│                    │
│                  │                          │                    │
│  非 200 → 退避重试│ ◀──────────────────────│  按重试策略最多 5 次 │
└──────────────────┘                          └────────────────────┘

3. Heicode 侧注册(callback_url 怎么告诉 Agent Runtime)

3.1 创建部署时携带

扩展 POST /api/agent/sub-agile/deployments 请求体,新增可选字段:

{
  "orchestration_plan": "...",
  "agents": [ ... ],
  "callback": {
    "url": "https://code.xinghanlab.com/api/agent/callbacks/runtime-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 接收端点;必须 HTTPS
  • signing_secret_ref:HMAC 签名密钥的 Vault 引用(不传明文)
  • subscribed_events:可选,省略则推送全部;后续允许只订阅子集

3.2 后绑定 / 修改(可选 P2 阶段)

PATCH /api/agent/sub-agile/deployments/{deployment_id}/callback

允许在部署运行期间更换 callback URL(例如 Heicode 灰度发布切换接收端)。


4. Callback 接收接口(核心)

4.1 Endpoint

方法 URL 说明
POST {callback_url} Agent Runtime 推送事件

Heicode 生产端点(建议):

POST https://code.xinghanlab.com/api/agent/callbacks/runtime-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 侧校验顺序(务必按此顺序,先廉价后昂贵):

  1. 检查 X-Agnet-Timestamp 在当前时间 ±5 分钟内 → 防回放
  2. 检查 X-Agnet-Event-Id 不在最近 24h 已处理列表 → 幂等去重
  3. 计算 canonical_string → 比对 X-Agnet-Signature → 验真
  4. 解析 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_TIMEOUT
  • TOOL_AUTH_FAILED
  • TOOL_QUOTA_EXCEEDED
  • TOOL_INPUT_INVALID
  • TOOL_INTERNAL_ERROR
  • TOOL_NETWORK
  • TOOL_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 拿到这个事件后做什么:

  1. 推到桌面客户端的审批 UI(已有契约)
  2. 用户点同意 / 拒绝
  3. Heicode 调正向接口:POST /api/agent/sub-agile/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/agent/_mock/emit_event
Authorization: Bearer <SERVICE_TOKEN>

{
  "callback_url": "https://code.xinghanlab.com/api/agent/callbacks/runtime-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/agent/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(防止回放)
  • Agent Runtime 端必须用 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/agent/callbacks/runtime-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 → 返回 400 SIGNATURE_VERSION_UNSUPPORTED

13. 待 Agent Manager 团队确认的开放问题

  1. ❓ signing_secret_ref 走 Vault 引用,需要 Agent Manager 这边有 Vault 客户端能解 — 现状如何?是否需要换成 Heicode 直接给明文密钥?
  2. ❓ §6 mock-emit 接口你们能在 staging 提供吗?没这个我们这边没法联调
  3. ❓ §5.5 高危审批的 auto_deny_at TTL 默认多久合适?我们这边建议 1 小时
  4. ❓ §5.10 agent.crashed 的 last_log_tail 截断到多少字节?建议 1KB
  5. ❓ §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 前给反馈