外部 Agent 接入
Agent 分两大类:外部 Agent(供应商自有运行时)与 门面 Agent(平台托管)。 本文只讲外部 Agent;门面的对话/状态入口与
AgentRuntime见 门面 Agent 与 AgentRuntime。
外部 Agent 由企业/服务商自己运行,平台只负责调用。它可以是供应商的真实业务系统,也可以是 OpenClaw / Hermes / Claude Code 这类本地运行时的适配器。
0. 外部 Agent vs 门面 Agent
| 外部 Agent | 门面 Agent | |
|---|---|---|
| 定义位置 | AgentProfile(有 a2aEndpoint / gatewayUrl) | AgentProfile(无外部端点,active) |
| 执行 | 供应商自己的运行时(A2A / WS / 轮询) | 平台托管的 AgentRuntime |
| 输入 | 任务/对话消息 | 对话历史或状态事件快照 |
| 输出 | Agent 回传的 output | 对话回复 / 契约 envelope |
| 计费 | 按 pricing 结算 | 门面 pricing.amount=0(通常) |
| 可审计 | 只能约束 I/O 与过程痕迹 | 平台完全可控(token/工具/状态) |
| 典型 | CE 认证供应商、OpenClaw 适配器 | facade-hs-code、facade-vat-registration |
平台按 profile 的端点能力选择通道,不静默降级:
- 有
a2aEndpoint→ 纯 A2A 通道(失败直接失败,不偷偷换 Gateway); - 无 A2A、有
gatewayUrl→ WebSocket Gateway; - 两者都没有 → 这是门面,不是外部 Agent。
1. 三种接入方式
| 方式 | 协议 | 适用 | 说明 |
|---|---|---|---|
| A2A | HTTP JSON-RPC 风格 | 推荐 | 平台主动调用,适合新接入 |
| WebSocket Gateway | Socket.IO 长连接 | 兼容保留 | NAT 后、无公网入站 |
| HTTP 轮询 | REST | 兜底 | 无法维持长连接 |
2. A2A 协议
平台 外部 Agent
│ POST /message:send │
│ { message: { parts: [...] }, │
│ configuration: { │
│ returnImmediately: true } } │
│──────────────────────────────────────▶│
│ ← { task: { id, status: SUBMITTED }} │
│ │
│ GET /tasks/{id} │
│──────────────────────────────────────▶│
│ ← { task: { status: WORKING } } │
│ │
│ GET /tasks/{id} │
│──────────────────────────────────────▶│
│ ← { task: { status: COMPLETED, │
│ artifacts: [{ parts: [...] }]}} │Agent 需要实现:
| 方法 | 路径 | 必需 | 说明 |
|---|---|---|---|
| POST | /message:send | ✅ | 接收任务/消息 |
| GET | /tasks/{id} | 推荐 | 供平台轮询 |
| GET | /health | 可选 | 健康检查 |
状态(规范名大写;本地适配器可能返回小写,客户端按终态语义判断):
| 状态 | 含义 |
|---|---|
SUBMITTED | 已接收 |
WORKING | 执行中 |
INPUT_REQUIRED | 需要更多信息 |
COMPLETED | 完成,artifacts 带结果 |
FAILED | 失败 |
CANCELED | 已取消 |
对话模式:Agent 返回 INPUT_REQUIRED 时,平台把问题转给用户,用户回答后继续同一 task 上下文;这使 Supplier 可以主动追问,而不是直接失败。
端到端延迟由供应商运行时决定,不含在平台 AgentRuntime 的指标里。供应商的进程冷启动、session 清理、模型排队都属于其自身运维范围,不应作为平台 SLA 的一部分。
3. WebSocket Gateway(NAT / 无公网入站)
Agent → 平台
| 事件 | 负载 | 说明 |
|---|---|---|
agent:register | { agentId, skillIds, apiKey } | 注册上线 |
agent:heartbeat | { agentId, timestamp } | 每 30s 心跳 |
agent:ack | { taskId } | 确认收到 |
agent:result | { taskId, output, reasoningTrail?, metadata? } | 回传结果 |
平台 → Agent
| 事件 | 负载 |
|---|---|
agent:task | { taskId, skillId, input, deadline? } |
离线任务:
pushTask(agentId, task)
├─ 在线 → socket.emit('agent:task')
└─ 离线 → offline_tasks (status: pending)
└─ 上线时 pushOfflineTasks() 自动投递(记录 retryCount)
└─ 超时 → expired边界:TTL 由 deadline 决定;结果通过 agent:result 或 POST /api/gateway/tasks/:id/result 回传;重试次数由任务记录与调用方策略决定,不是固定常量。
4. 对话能力声明
外部 Agent 在 AgentProfile 中声明:
{
"interactionMode": "hybrid",
"dialogue": {
"greeting": "您好,我是 CE 合规助手",
"supportedLanguages": ["zh", "en"],
"streamingSupported": true,
"maxTurns": 12,
"humanHandoff": false,
"endpoint": { "type": "websocket", "url": "wss://..." }
}
}| 模式 | 适用 |
|---|---|
structured | 确定性任务,如查 HS Code |
dialogue | 需要澄清和探索,如合规咨询 |
hybrid | 两者都支持 |
注意:
interactionMode影响对话入口如何使用(是否能多轮、是否有 greeting),不参与 Agent 匹配过滤。匹配看 skill / subDomain / status / 评分,不看这个字段。
5. 注册与激活
GET /api/a2a/skills # 下载 Skill 定义(含 I/O Schema)
POST /api/agent-profiles/register # 注册 AgentProfile + a2aEndpoint / gatewayUrl
GET /api/a2a/agent-card # 查看平台自身能力卡register/upsert 幂等:同一个 agentId 重复注册会更新而不是新建。 新 profile 默认 status=draft,需 activate-by-key 或审核后变 active 才进入匹配池。
6. 最小 A2A Agent 骨架
app.post('/message:send', async (req, res) => {
const { message } = req.body;
const input = JSON.parse(message.parts[0].text);
const taskId = crypto.randomUUID();
res.json({ task: { id: taskId, status: 'SUBMITTED' } });
const output = await runSkill(input);
results.set(taskId, {
status: 'COMPLETED',
artifacts: [{ parts: [{ text: JSON.stringify(output) }] }],
});
});
app.get('/tasks/:id', (req, res) => {
res.json({ task: results.get(req.params.id) ?? { status: 'WORKING' } });
});平台参考实现:scripts/a2a-server-reference.ts;本地命令翻译:scripts/a2a-adapter.js。
7. 如何选择
| 需求 | 选择 |
|---|---|
| 企业自有工具链/数据,平台不能执行 | 外部 Agent(A2A 优先,NAT 用 Gateway) |
| 需要对话澄清 + 需求登记 + 平台本地交付 | 门面 Agent(local-skill) |
| 只登记需求、转人工/待招募 | 门面 Agent(intake-only) |
| 门面后来接上了供应商 | 改为 外部 Agent(a2aEndpoint,delivery=supplier-attached) |
8. 边界与限制
| 限制 | 说明 |
|---|---|
| 平台无法审计外部 Agent 内部 | 只约束输入输出与过程痕迹 |
| 门面完全可审计 | token、工具、状态写入都在平台内 |
| 准确率依赖 Agent 自报 + 抽样 | 尚未实现全量自动验证 |
| 多实例负载均衡 | 同一 Skill 可注册多个 Agent,由匹配分数决定 |
| 私有 Agent 不需公网 | 只要平台能访问其端点;内网需网络打通 |
| 协议版本 | 当前按 A2A 风格实现,未做完整规范兼容 |
下一步
- 门面 Agent 与运行时 → 门面 Agent 与 AgentRuntime
- 能力如何被定义 → Skill 能力目录
- 匹配如何选 Agent → Agent Matching
- 完整接入步骤 → Agent 开发者指南