504 lines
12 KiB
Markdown
504 lines
12 KiB
Markdown
# P2A 协议(Person/Program-to-Agent Protocol)
|
||
|
||
> 版本:1.0.0
|
||
> 状态:草案
|
||
> 类型:实现规范
|
||
> 基于:[灵隙原始协议(Synapse Raw Protocol) v1.0.0](./01-a2a-raw-protocol.md)
|
||
|
||
---
|
||
|
||
## 1. 概述
|
||
|
||
P2A(Person/Program-to-Agent)调用协议定义了**非 Agent 调用方**(如普通后端服务、前端应用、第三方系统、人类用户)如何调用 Agent 能力的规范。
|
||
|
||
本协议将 A2A 协议的复杂性封装起来,提供简洁的 RESTful API,使调用方无需了解 A2A 协议细节即可使用 Agent 能力。
|
||
|
||
### 1.1 适用场景
|
||
- 前端应用直接调用 Agent 能力
|
||
- 后端业务系统集成 Agent 服务
|
||
- 第三方开发者通过 API 接入
|
||
- 无需实现完整 A2A 协议的调用方
|
||
|
||
### 1.2 设计原则
|
||
- **简单易用**:标准 RESTful API,JSON 格式
|
||
- **两种模式**:回调模式 + 轮询模式,调用方按需选择
|
||
- **认证简单**:API Key 认证,无需 HMAC 签名
|
||
- **文件支持**:支持 multipart/form-data 上传文件
|
||
- **错误友好**:清晰的错误码和错误信息
|
||
|
||
### 1.3 与 A2A 协议的关系
|
||
|
||
```
|
||
外部调用方 ──P2A 协议──▶ External API Layer ──A2A 协议──▶ Agent
|
||
```
|
||
|
||
P2A 协议是 A2A 协议的"简化入口",所有请求最终会转换为 灵隙消息发送给 Agent。
|
||
|
||
---
|
||
|
||
## 2. 认证方式
|
||
|
||
### 2.1 API Key 认证
|
||
- 在 HTTP Header 中传递:`X-API-Key: {api_key}`
|
||
- 或在 URL Query 中传递:`?api_key={api_key}`
|
||
- 如果服务端未配置 API Key,则跳过认证
|
||
|
||
### 2.2 回调验证(可选)
|
||
如果调用方提供了 `callback_token`,则服务端在回调时会在 Header 中带上 `X-Callback-Token`,用于调用方验证回调来源。
|
||
|
||
---
|
||
|
||
## 3. API 端点
|
||
|
||
所有端点默认前缀:`/api/v1/synapse`
|
||
|
||
### 3.1 端点清单
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| POST | `/task` | 提交任务(JSON) |
|
||
| POST | `/task/multipart` | 提交任务(带文件上传) |
|
||
| GET | `/task/{task_id}` | 获取任务详情 |
|
||
| GET | `/task/{task_id}/status` | 获取任务状态(简化版) |
|
||
| GET | `/tasks` | 获取任务列表 |
|
||
| POST | `/task/{task_id}/cancel` | 取消任务 |
|
||
| GET | `/capabilities` | 获取能力清单 |
|
||
| POST | `/callback` | 接收回调(调用方实现) |
|
||
|
||
---
|
||
|
||
## 4. 任务提交
|
||
|
||
### 4.1 提交任务(JSON)
|
||
|
||
**请求:**
|
||
```http
|
||
POST /api/v1/synapse/task
|
||
Content-Type: application/json
|
||
X-API-Key: your-api-key
|
||
|
||
{
|
||
"task_type": "text_generation",
|
||
"payload": {
|
||
"prompt": "写一首诗",
|
||
"style": "古典"
|
||
},
|
||
"external_task_id": "my-task-001",
|
||
"priority": "normal",
|
||
"callback_url": "https://your-domain.com/callback",
|
||
"callback_token": "your-callback-token"
|
||
}
|
||
```
|
||
|
||
**请求字段说明:**
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| `task_type` | string | 是 | 任务类型,如 `video_generation`, `image_generation` |
|
||
| `payload` | object | 是 | 任务参数,具体结构取决于 task_type |
|
||
| `external_task_id` | string | 否 | 调用方的任务 ID,用于关联 |
|
||
| `priority` | string | 否 | 优先级:`low`, `normal`, `high`, `urgent`,默认 `normal` |
|
||
| `callback_url` | string | 否 | 回调 URL,不提供则需轮询 |
|
||
| `callback_token` | string | 否 | 回调验证 token |
|
||
|
||
**响应:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"task_id": "task-abc123",
|
||
"status": "received",
|
||
"message": "任务已接收"
|
||
}
|
||
```
|
||
|
||
**响应字段说明:**
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `success` | bool | 是否成功 |
|
||
| `task_id` | string | 服务端生成的任务 ID |
|
||
| `status` | string | 初始状态,通常为 `received` |
|
||
| `message` | string | 提示信息 |
|
||
| `error` | string | 错误信息(失败时) |
|
||
|
||
### 4.2 提交任务(Multipart 带文件)
|
||
|
||
**请求:**
|
||
```http
|
||
POST /api/v1/synapse/task/multipart
|
||
Content-Type: multipart/form-data
|
||
X-API-Key: your-api-key
|
||
|
||
------WebKitFormBoundary
|
||
Content-Disposition: form-data; name="task_type"
|
||
|
||
image_generation
|
||
|
||
------WebKitFormBoundary
|
||
Content-Disposition: form-data; name="payload"
|
||
|
||
{"prompt":"一只猫","style":"写实"}
|
||
|
||
------WebKitFormBoundary
|
||
Content-Disposition: form-data; name="priority"
|
||
|
||
normal
|
||
|
||
------WebKitFormBoundary
|
||
Content-Disposition: form-data; name="file"; filename="reference.jpg"
|
||
Content-Type: image/jpeg
|
||
|
||
[二进制数据]
|
||
|
||
------WebKitFormBoundary--
|
||
```
|
||
|
||
**表单字段:**
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| `task_type` | string | 是 | 任务类型 |
|
||
| `payload` | string(JSON) | 是 | 任务参数(JSON 字符串) |
|
||
| `external_task_id` | string | 否 | 外部任务 ID |
|
||
| `priority` | string | 否 | 优先级 |
|
||
| `callback_url` | string | 否 | 回调 URL |
|
||
| `callback_token` | string | 否 | 回调 token |
|
||
| `file` | file | 否 | 上传的文件(可多个) |
|
||
|
||
**响应:** 同 JSON 提交方式
|
||
|
||
---
|
||
|
||
## 5. 任务查询
|
||
|
||
### 5.1 获取任务详情
|
||
|
||
**请求:**
|
||
```http
|
||
GET /api/v1/synapse/task/{task_id}
|
||
X-API-Key: your-api-key
|
||
```
|
||
|
||
**响应:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"task": {
|
||
"id": "task-abc123",
|
||
"type": "text_generation",
|
||
"status": "completed",
|
||
"progress": 100,
|
||
"message": "任务已完成",
|
||
"created_at": "2026-01-01T00:00:00Z",
|
||
"updated_at": "2026-01-01T00:01:00Z",
|
||
"completed_at": "2026-01-01T00:01:00Z",
|
||
"eta": "2026-01-01T00:01:00Z",
|
||
"result": {
|
||
"text": "生成的文本...",
|
||
"metadata": { "tokens": 1024 }
|
||
},
|
||
"error": null,
|
||
"external_task_id": "my-task-001"
|
||
}
|
||
}
|
||
```
|
||
|
||
**任务状态:**
|
||
|
||
| 状态 | 说明 |
|
||
|------|------|
|
||
| `received` | 已接收 |
|
||
| `processing` | 处理中 |
|
||
| `completed` | 已完成 |
|
||
| `failed` | 已失败 |
|
||
| `cancelled` | 已取消 |
|
||
|
||
### 5.2 获取任务状态(简化版)
|
||
|
||
**请求:**
|
||
```http
|
||
GET /api/v1/synapse/task/{task_id}/status
|
||
X-API-Key: your-api-key
|
||
```
|
||
|
||
**响应:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"task_id": "task-abc123",
|
||
"status": "processing",
|
||
"progress": 50
|
||
}
|
||
```
|
||
|
||
### 5.3 获取任务列表
|
||
|
||
**请求:**
|
||
```http
|
||
GET /api/v1/synapse/tasks?status=processing&limit=20&offset=0
|
||
X-API-Key: your-api-key
|
||
```
|
||
|
||
**查询参数:**
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| `status` | string | 否 | 按状态过滤 |
|
||
| `limit` | int | 否 | 每页数量,默认 20 |
|
||
| `offset` | int | 否 | 偏移量,默认 0 |
|
||
|
||
**响应:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"tasks": [
|
||
{
|
||
"id": "task-abc123",
|
||
"type": "text_generation",
|
||
"status": "completed",
|
||
"progress": 100,
|
||
"created_at": "2026-01-01T00:00:00Z",
|
||
"updated_at": "2026-01-01T00:01:00Z"
|
||
}
|
||
],
|
||
"total": 1,
|
||
"limit": 20,
|
||
"offset": 0,
|
||
"has_more": false
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 任务取消
|
||
|
||
**请求:**
|
||
```http
|
||
POST /api/v1/synapse/task/{task_id}/cancel
|
||
X-API-Key: your-api-key
|
||
```
|
||
|
||
**响应:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"task_id": "task-abc123",
|
||
"status": "cancelled",
|
||
"message": "任务已取消"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 能力查询
|
||
|
||
**请求:**
|
||
```http
|
||
GET /api/v1/synapse/capabilities
|
||
X-API-Key: your-api-key
|
||
```
|
||
|
||
**响应:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"capabilities": {
|
||
"agent_id": "my-agent",
|
||
"agent_name": "我的Agent",
|
||
"system_id": "my-system",
|
||
"protocol_version": "1.0.0",
|
||
"supported_task_types": [
|
||
{
|
||
"type": "text_generation",
|
||
"name": "文本生成",
|
||
"description": "生成各种类型的文本内容",
|
||
"estimated_time": "30s",
|
||
"input_schema": "...",
|
||
"output_schema": "..."
|
||
}
|
||
],
|
||
"supported_transport_modes": ["callback", "polling"],
|
||
"max_file_size": 104857600,
|
||
"max_concurrent_tasks": 10,
|
||
"support_progress_updates": true,
|
||
"support_streaming": false,
|
||
"support_hil": true,
|
||
"support_groups": true
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 回调机制
|
||
|
||
如果调用方提供了 `callback_url`,服务端会在任务状态变化时主动回调。
|
||
|
||
### 8.1 回调请求
|
||
|
||
```http
|
||
POST {callback_url}
|
||
Content-Type: application/json
|
||
X-Callback-Token: {callback_token}
|
||
|
||
{
|
||
"task_id": "task-abc123",
|
||
"status": "completed",
|
||
"progress": 100,
|
||
"message": "任务已完成",
|
||
"result": {
|
||
"text": "生成的文本..."
|
||
},
|
||
"error": null,
|
||
"timestamp": "2026-01-01T00:01:00Z"
|
||
}
|
||
```
|
||
|
||
### 8.2 回调类型
|
||
|
||
| 触发时机 | 说明 |
|
||
|---------|------|
|
||
| 任务开始处理 | `status=processing`, `progress=0` |
|
||
| 进度更新 | `status=processing`, `progress=N` |
|
||
| 任务完成 | `status=completed`, `progress=100`, 包含 result |
|
||
| 任务失败 | `status=failed`, 包含 error |
|
||
| 任务取消 | `status=cancelled` |
|
||
|
||
### 8.3 回调响应
|
||
|
||
调用方应返回 2xx 状态码表示成功接收。如果返回非 2xx,服务端会重试(指数退避,最多 5 次)。
|
||
|
||
---
|
||
|
||
## 9. 错误处理
|
||
|
||
### 9.1 错误响应格式
|
||
|
||
```json
|
||
{
|
||
"success": false,
|
||
"error": "invalid_task_type",
|
||
"message": "不支持的任务类型"
|
||
}
|
||
```
|
||
|
||
### 9.2 错误码列表
|
||
|
||
| 错误码 | HTTP 状态码 | 说明 |
|
||
|--------|------------|------|
|
||
| `unauthorized` | 401 | API Key 无效或缺失 |
|
||
| `invalid_request` | 400 | 请求格式无效 |
|
||
| `invalid_task_type` | 400 | 任务类型不支持 |
|
||
| `invalid_params` | 400 | 参数无效 |
|
||
| `task_not_found` | 404 | 任务不存在 |
|
||
| `task_already_completed` | 409 | 任务已完成,无法取消 |
|
||
| `file_too_large` | 413 | 文件过大 |
|
||
| `rate_limited` | 429 | 请求过于频繁 |
|
||
| `internal_error` | 500 | 服务内部错误 |
|
||
| `service_unavailable` | 503 | 服务不可用 |
|
||
|
||
---
|
||
|
||
## 10. SDK 客户端接口
|
||
|
||
为方便调用方集成,提供 SDK 客户端。
|
||
|
||
### 10.1 SDK 初始化
|
||
|
||
```go
|
||
client := sdk.NewClient(sdk.ClientConfig{
|
||
BaseURL: "https://agent-api.example.com",
|
||
APIKey: "your-api-key",
|
||
Timeout: 30 * time.Second,
|
||
})
|
||
```
|
||
|
||
### 10.2 SDK 方法清单
|
||
|
||
| 方法 | 说明 |
|
||
|------|------|
|
||
| `SubmitTask(ctx, req)` | 提交任务(JSON) |
|
||
| `SubmitTaskWithFiles(ctx, req, files)` | 提交任务(带文件) |
|
||
| `GetTask(ctx, taskID)` | 获取任务详情 |
|
||
| `GetTaskStatus(ctx, taskID)` | 获取任务状态 |
|
||
| `ListTasks(ctx, status, limit, offset)` | 获取任务列表 |
|
||
| `CancelTask(ctx, taskID)` | 取消任务 |
|
||
| `GetCapabilities(ctx)` | 获取能力清单 |
|
||
| `WaitForTask(ctx, taskID, interval)` | 等待任务完成(轮询) |
|
||
|
||
### 10.3 数据结构
|
||
|
||
```go
|
||
type TaskRequest struct {
|
||
TaskType string
|
||
Payload map[string]any
|
||
ExternalTaskID string
|
||
Priority string
|
||
CallbackURL string
|
||
CallbackToken string
|
||
}
|
||
|
||
type TaskInfo struct {
|
||
ID string
|
||
Type string
|
||
Status string
|
||
Progress int
|
||
Message string
|
||
CreatedAt string
|
||
UpdatedAt string
|
||
CompletedAt string
|
||
ETA string
|
||
Result map[string]any
|
||
Error *TaskError
|
||
ExternalTaskID string
|
||
}
|
||
```
|
||
|
||
**完整 SDK 实现:** [sdk/client.go](../sdk/client.go)
|
||
|
||
---
|
||
|
||
## 11. 参考实现
|
||
|
||
本协议的参考实现:
|
||
|
||
| 模块 | 文件路径 | 说明 |
|
||
|------|---------|------|
|
||
| 服务端 | [externalapi/handler.go](../externalapi/handler.go) | External API Handler |
|
||
| 客户端 SDK | [sdk/client.go](../sdk/client.go) | Go 语言 SDK |
|
||
|
||
---
|
||
|
||
## 附录 A:调用模式选择
|
||
|
||
### 回调模式 vs 轮询模式
|
||
|
||
| 对比项 | 回调模式 | 轮询模式 |
|
||
|--------|---------|---------|
|
||
| 实时性 | 高,状态变化即时通知 | 低,取决于轮询间隔 |
|
||
| 实现复杂度 | 调用方需提供公网可访问的回调接口 | 简单,只需调用 GET 接口 |
|
||
| 网络要求 | 调用方需要公网可达 | 只需调用方能访问服务端 |
|
||
| 适用场景 | 生产环境、需要实时通知 | 开发测试、内网环境 |
|
||
|
||
### 选择建议
|
||
|
||
- **有公网服务器**:使用回调模式
|
||
- **本地开发 / 内网环境**:使用轮询模式
|
||
- **长时间任务**:建议回调模式,避免频繁轮询
|
||
- **短任务(<10s)**:可直接同步等待或高频轮询
|
||
|
||
---
|
||
|
||
## 附录 B:从 A2A 协议到 P2A 协议的映射
|
||
|
||
| 灵隙概念 | P2A 对应 | 说明 |
|
||
|---------|---------|------|
|
||
| `p2p:task_request` | `POST /task` | 任务提交 |
|
||
| `p2p:task_status_update` | 回调 / GET /task | 状态更新 |
|
||
| `p2p:task_complete` | 回调(status=completed) | 任务完成 |
|
||
| `p2p:task_failed` | 回调(status=failed) | 任务失败 |
|
||
| `p2p:task_cancel` | `POST /task/{id}/cancel` | 任务取消 |
|
||
| `p2p:capability_query` | `GET /capabilities` | 能力查询 |
|
||
| Agent 身份 | API Key | 简化认证 |
|
||
| HMAC 签名 | Callback Token | 简化验证 |
|