709 lines
23 KiB
Markdown
709 lines
23 KiB
Markdown
# 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` 请求体,新增可选字段:
|
||
|
||
```json
|
||
{
|
||
"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
|
||
HTTP/1.1 200 OK
|
||
Content-Type: application/json
|
||
X-Heicode-Event-Status: accepted | duplicate
|
||
|
||
{ "success": true }
|
||
```
|
||
|
||
**Heicode 端临时性错误(要 Agnet 重试)**:
|
||
|
||
```http
|
||
HTTP/1.1 503 Service Unavailable
|
||
|
||
{
|
||
"success": false,
|
||
"error": { "code": "DOWNSTREAM_DB_UNAVAILABLE", "retryable": true }
|
||
}
|
||
```
|
||
|
||
**协议永久错误(不重试)**:
|
||
|
||
```http
|
||
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**:
|
||
|
||
```json
|
||
{
|
||
"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**:
|
||
|
||
```json
|
||
{
|
||
"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**:
|
||
|
||
```json
|
||
{
|
||
"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**:
|
||
|
||
```json
|
||
{
|
||
"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**:
|
||
|
||
```json
|
||
{
|
||
"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**:
|
||
|
||
```json
|
||
{
|
||
"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**:
|
||
|
||
```json
|
||
{
|
||
"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**:
|
||
|
||
```json
|
||
{
|
||
"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**:
|
||
|
||
```json
|
||
{
|
||
"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 端等价代码(参考实现)
|
||
|
||
```go
|
||
// 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 端落库参考
|
||
|
||
```sql
|
||
-- 已存在的 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 前给反馈
|