对外 API 接入指南
让第三方系统(任务管理系统、工作流引擎、内部平台)调用本项目的 AI agent: 批量投递任务、跟踪执行、查询节点与项目。
- 机器可读规范:
openapi/zqagent.v1.yaml - 适用于:服务端到服务端调用(浏览器直连不支持,见「安全边界」)
1. 五分钟跑通
1.1 创建 Key
在控制台 设置 → 集成(/settings/integrations)点「新建 Key」:
- 名称:
生产-工单系统 - 权限范围:默认
jobs:write+jobs:read+resources:read - 限定节点 / 项目:留空表示不限
- 回调地址:
https://team.cd-zq.com/api/zqagent/webhook - 每分钟上限:留空用服务端默认(600)
- IP 白名单:如
203.0.113.10, 198.51.100.0/24;留空不限
创建后页面只显示一次完整 Key 与 webhook 密钥,请立即保存。
生产环境建议同时配 IP 白名单与每分钟上限:Key 泄露时损失可控。
1.2 查可用项目
curl https://hub.example.com/v1/projects \
-H "Authorization: Bearer $ZQA_API_KEY"{
"data": {
"items": [
{
"projectId": "proj_order",
"name": "订单服务",
"nodeId": "node_be",
"online": true,
"workspaceRoot": "/srv/repos/backend-order",
"sessionCount": 12,
"running": 1,
"queued": 3
}
]
},
"requestId": "req_8Hq2..."
}1.3 提交两个任务
curl -X POST https://hub.example.com/v1/jobs \
-H "Authorization: Bearer $ZQA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-20261109-001" \
-d '{
"batchId": "sprint-42",
"items": [
{
"kind": "ai",
"projectId": "proj_order",
"prompt": "检查 src 下所有 TODO 并生成 reports/todo.md",
"externalId": "TASK-1024"
},
{
"kind": "script",
"projectId": "proj_order",
"command": "pnpm test",
"timeoutSeconds": 300,
"externalId": "TASK-1025"
}
]
}'{
"data": {
"batchId": "sprint-42",
"accepted": 2,
"items": [
{ "id": "job_9f2c", "externalId": "TASK-1024", "status": "queued", "nodeId": "node_be", "projectId": "proj_order" },
{ "id": "job_9f2d", "externalId": "TASK-1025", "status": "queued", "nodeId": "node_be", "projectId": "proj_order" }
]
},
"requestId": "req_..."
}1.4 等结果
两个任务的 projectId 相同,因此串行执行(同一工作区不并发改文件)。 推荐用 webhook 收结果(见 webhooks.md),或轮询:
curl https://hub.example.com/v1/jobs/job_9f2c \
-H "Authorization: Bearer $ZQA_API_KEY"2. 认证
Authorization: Bearer zqa_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| 项 | 说明 |
|---|---|
| 格式 | zqa_live_ + 32 位随机字符 |
| 存储 | 服务端只存 sha256,明文仅在创建时返回一次 |
| 吊销 | 立即生效(控制台点「吊销」,或 DELETE /api/hub/agent-api/keys/{id}) |
| 轮换 | 新 Key 立即生效;可设宽限期让旧 Key 多活一段时间(见下);id 不变,日志仍能对上 |
| 过期 | 创建时可设天数;过期后返回 401 |
| 限流 | 默认 600 次/分钟(每 Key 独立),超限返回 429 + Retry-After |
| IP 白名单 | 可限定只允许某些 IPv4 / CIDR 调用;不命中返回 403 ip_not_allowed |
轮换与宽限期
换 Key 时如果旧的立刻失效,正在跑的批量任务会中途断掉。轮换接口支持宽限期:
curl -X POST https://hub.example.com/api/hub/agent-api/keys/$KEY_ID/rotate \
-H "Authorization: Bearer $CONSOLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"graceHours": 24}'宽限期内新旧 Key 都可用;到期后旧 Key 自动返回 401。响应里的 previousExpiresAt 就是这个时刻。 graceHours 取值 0–720,缺省 0(立即失效)。
限流响应头
每个成功响应都带:
| 头 | 含义 |
|---|---|
X-RateLimit-Limit | 该 Key 的每分钟上限 |
X-RateLimit-Remaining | 当前窗口剩余次数 |
X-RateLimit-Reset | 窗口重置的 Unix 秒 |
超限时额外带 Retry-After(秒)。建议按它退避,别固定 1 秒重试。
权限范围(scope)
| scope | 能做什么 |
|---|---|
jobs:write | 提交 / 取消 / 重试任务 |
jobs:read | 查询任务与批次 |
resources:read | 看节点、项目、总览 |
usage:read | 查用量汇总 /v1/usage |
缺权限返回 403 insufficient_scope。
3. 端点一览
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /v1/overview | 全网负载/排队/耗时(?format=compact 供 LLM 用) |
| GET | /v1/projects | 项目清单(投递路由表) |
| GET | /v1/nodes | 节点清单 |
| GET | /v1/nodes/{nodeId} | 节点详情(项目与负载) |
| GET | /v1/nodes/{nodeId}/health | 节点健康快照 |
| GET | /v1/usage | 用量汇总(按天/项目/Key) |
| POST | /v1/chat/completions | OpenAI 兼容(给现成工具用) |
| POST | /v1/jobs | 批量提交(≤100) |
| GET | /v1/jobs | 列表(按状态/节点/项目/批次/外部 id 过滤) |
| GET | /v1/jobs/{id} | 单任务详情(含 progress) |
| GET | /v1/jobs/{id}/events | 事件流(SSE,实时跟进度) |
| POST | /v1/jobs/{id}/cancel | 取消 |
| POST | /v1/jobs/{id}/retry | 重试 |
| GET | /v1/batches/{batchId} | 批次汇总 |
| POST | /v1/batches/{batchId}/cancel | 整批取消 |
详见 jobs.md 与 resources.md。
3.1 OpenAI 兼容(给现成工具用)
如果调用方是 LangChain / Dify / n8n / 自研的 OpenAI SDK 封装,可以不改代码直接接:
curl -X POST https://hub.example.com/v1/chat/completions \
-H "Authorization: Bearer $ZQA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "zqagent",
"project_id": "proj_order",
"messages": [{"role": "user", "content": "6 乘 7 是多少"}]
}'{
"id": "chatcmpl-job_9f2c",
"object": "chat.completion",
"model": "zqagent",
"choices": [{ "index": 0, "message": { "role": "assistant", "content": "42" }, "finish_reason": "stop" }],
"usage": { "prompt_tokens": 12, "completion_tokens": 3, "total_tokens": 15 },
"zq_job_id": "job_9f2c",
"zq_session_id": "sess_xxx"
}要点:
- 必须提供
project_id(顶层或metadata里):本实现要靠它定位工作区。 - 这是同步接口,默认等 120 秒(
x-zq-timeout头可设 5–600 秒)。 - 超时返回
504 job_timeout并带jobId,任务没有被取消,可改用/v1/jobs/{id}继续追。 - 返回是 OpenAI 标准形状,不套
{data}信封。
3.2 实时跟进(SSE)
长任务不必轮询,直接订阅事件流:
curl -N https://hub.example.com/v1/jobs/$JOB_ID/events \
-H "Authorization: Bearer $ZQA_API_KEY"id: 1
event: job.state
data: {"id":"job_9f2c","status":"running","progress":{"toolCalls":3,...}}
id: 2
event: job.progress
data: {...}
id: 3
event: job.completed
data: {"id":"job_9f2c","status":"done","result":{"summary":"..."}}事件名:job.state(首帧当前状态)、job.progress、job.completed、job.failed、job.cancelled、job.deleted。 到达终态后服务端主动关闭连接;每 15 秒有一次 : ping 保活。
说明:这是服务端轮询作业表推断出来的变化,不是逐 token 的实时流。 Hub 到节点没有入站通道,逐 token 流是控制台的需求;集成方通常只需要"开始/进度/结束"。
4. 并发模型(决定你的预期)
一个节点(设备)
├── 工作区 A(项目) ← 同时只跑 1 个任务
├── 工作区 B(项目) ← 同时只跑 1 个任务
└── 工作区 C(项目) ← 同时只跑 1 个任务
A、B、C 之间并行- 同一
projectId严格串行:agent 会真实改文件,同目录并发会让两份改动互相覆盖。 - 不同
projectId并行:上限由节点配置HUB_JOB_CONCURRENCY决定(默认 4)。 - 不同节点天然并行。
对你的影响:100 个任务投给 1 个项目 = 按顺序跑完;投给 4 个项目 = 约 1/4 时间。 如果一个项目就要高吞吐,需要把它拆成多个工作区(每区一个 projectId)。
5. 安全边界
- 不支持浏览器直连:服务端不发 CORS 头。Key 属于机密,不应下发到浏览器。
- 回调地址必须 HTTPS,且拒绝内网/回环地址(防 SSRF);可配置站点白名单。
- Key 只能访问自己的资源:别人的
jobId一律返回 404,不暴露存在性。 - 建议给不同系统/环境分别建 Key,便于单独吊销。
- 生产 Key 建议同时配 IP 白名单 + 每分钟上限:即使泄露,也只能从固定出口调用有限次数。
6. 版本与兼容
- 路径前缀
/v1,字段只增不改;破坏性变更走/v2。 - 弃用流程:先在响应头加
Deprecation,至少保留 12 个月。 - 每个响应都带
X-Request-Id,排障时提供它即可定位。
7. 文档索引
| 文档 | 内容 |
|---|---|
| jobs.md | 提交、查询、批次、幂等、重试、失败类型 |
| webhooks.md | 签名验签、载荷结构、重试与幂等 |
| resources.md | overview / nodes / projects 字段说明 |
| errors.md | 错误码、限流、常见问题 |
| en/README.md | English guide(same scope, condensed) |
| ../../examples/api/ | 可直接运行的 curl / Python / Node 客户端 |
