灵隙(synapse) v1.0.0 版本
This commit is contained in:
@@ -1,328 +0,0 @@
|
||||
# 灵隙原始协议(Synapse Raw Protocol)
|
||||
|
||||
> 版本:1.0.0
|
||||
> 状态:草案
|
||||
> 类型:纲领性文档
|
||||
|
||||
---
|
||||
|
||||
## 1. 协议愿景
|
||||
|
||||
灵隙(Synapse,原 Agent-to-Agent)协议旨在定义一套通用的、可扩展的智能体间通信标准,使不同系统、不同厂商、不同能力的智能体能够在统一的协议框架下进行协作、交易与能力组合。
|
||||
|
||||
本协议是灵隙协议族(Synapse Protocol Suite)的原始纲领,定义核心设计理念、架构原则与术语体系,为所有衍生协议提供顶层指导。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心设计理念
|
||||
|
||||
### 2.1 去中心化对等(Peer-to-Peer)
|
||||
- 所有 Agent 在协议层面地位平等,无中心化控制节点
|
||||
- 每个 Agent 既是服务提供者,也是服务消费者
|
||||
- Agent 间直接通信,不依赖中间 broker(传输层可选择不同实现)
|
||||
|
||||
### 2.2 能力驱动(Capability-Driven)
|
||||
- 通信以能力为核心,而非以身份为核心
|
||||
- Agent 声明自身能力,按需发现与调用
|
||||
- 能力可组合、可分解、可升级
|
||||
|
||||
### 2.3 任务导向(Task-Oriented)
|
||||
- 所有交互围绕"任务"展开
|
||||
- 任务有明确的生命周期、状态机与超时机制
|
||||
- 任务支持拆分、合并、委派与协作
|
||||
|
||||
### 2.4 人类可介入(Human-in-the-Loop)
|
||||
- 协议原生支持人类介入决策
|
||||
- Agent 可主动请求人类判断
|
||||
- 人类决策优先于 Agent 自动决策
|
||||
- 支持多级升级(escalation)机制
|
||||
|
||||
### 2.5 群组协作(Group Collaboration)
|
||||
- 支持多 Agent 组成临时或永久群组
|
||||
- 群组内有角色分工与权限控制
|
||||
- 支持任务拆分-执行-合并的 MapReduce 模式
|
||||
- 支持投票、共识等群体决策机制
|
||||
|
||||
### 2.6 传输无关(Transport Agnostic)
|
||||
- 协议语义与底层传输分离
|
||||
- 支持 HTTP/2、gRPC、WebSocket、消息队列等多种传输
|
||||
- 支持回调(Callback)、轮询(Polling)、同步(Sync)、混合(Hybrid)等多种交互模式
|
||||
|
||||
### 2.7 安全可信(Secure & Trustworthy)
|
||||
- 消息级签名与验证
|
||||
- Agent 身份认证与信任等级
|
||||
- 支持加密传输
|
||||
- 可审计、可追溯
|
||||
|
||||
---
|
||||
|
||||
## 3. 协议分层架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 应用层 (Application) │
|
||||
│ 业务逻辑、工作流、领域特定逻辑 │
|
||||
├─────────────────────────────────────────┤
|
||||
│ 协议层 (Protocol) │
|
||||
├─────────────┬───────────────────────────┤
|
||||
│ A2A 协议 │ A2N 组群协议 │ ← 本协议族
|
||||
│ (P2P) │ (Group) │
|
||||
├─────────────┴───────────────────────────┤
|
||||
│ 传输层 (Transport) │
|
||||
│ HTTP / gRPC / WebSocket / MQ ... │
|
||||
├─────────────────────────────────────────┤
|
||||
│ 网络层 (Network) │
|
||||
│ TCP / UDP / QUIC ... │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.1 协议族构成
|
||||
|
||||
| 协议名称 | 缩写 | 描述 | 适用场景 |
|
||||
|---------|------|------|---------|
|
||||
| 灵隙原始协议(Synapse Raw Protocol) | A2A-Raw | 顶层纲领,定义理念、术语、原则 | 指导所有协议设计 |
|
||||
| A2A 协议 | A2A-Base | 点对点任务协作协议 | 两个 Agent 间的任务交互 |
|
||||
| A2N 组群协议 | A2N-Group | 多 Agent 群组协作协议 | 多个 Agent 协同完成任务 |
|
||||
| P2A 调用协议 | P2A-Caller | 非 Agent 调用者接入协议 | 普通服务/前端调用 Agent 能力 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 核心概念与术语
|
||||
|
||||
### 4.1 Agent(智能体)
|
||||
能够自主感知环境、做出决策并执行动作的计算实体。
|
||||
|
||||
**属性:**
|
||||
- `agent_id`:Agent 唯一标识
|
||||
- `system_id`:所属系统标识
|
||||
- `endpoint`:通信端点地址
|
||||
- `capabilities`:能力清单
|
||||
- `trust_level`:信任等级
|
||||
|
||||
### 4.2 Task(任务)
|
||||
Agent 间协作的基本单位,具有明确的输入、输出与生命周期。
|
||||
|
||||
**属性:**
|
||||
- `task_id`:任务唯一标识
|
||||
- `task_type`:任务类型
|
||||
- `payload`:任务载荷
|
||||
- `status`:任务状态
|
||||
- `priority`:优先级
|
||||
- `deadline`:截止时间
|
||||
|
||||
### 4.3 Message(消息)
|
||||
Agent 间通信的基本单位。
|
||||
|
||||
**属性:**
|
||||
- `message_id`:消息唯一标识
|
||||
- `type`:消息类型
|
||||
- `sender`:发送方
|
||||
- `receiver`:接收方(P2P)
|
||||
- `group_id`:群组标识(群组)
|
||||
- `payload`:消息载荷
|
||||
- `correlation_id`:关联标识(用于请求-响应关联)
|
||||
- `timestamp`:时间戳
|
||||
- `signature`:签名
|
||||
|
||||
### 4.4 Capability(能力)
|
||||
Agent 能够执行的任务类型的声明。
|
||||
|
||||
**属性:**
|
||||
- `type`:能力对应的任务类型
|
||||
- `name`:能力名称
|
||||
- `description`:能力描述
|
||||
- `input_schema`:输入参数 schema
|
||||
- `output_schema`:输出结果 schema
|
||||
- `estimated_time`:预估耗时
|
||||
|
||||
### 4.5 Group(群组)
|
||||
由多个 Agent 组成的协作单元。
|
||||
|
||||
**属性:**
|
||||
- `group_id`:群组唯一标识
|
||||
- `name`:群组名称
|
||||
- `topology`:拓扑结构
|
||||
- `members`:成员列表
|
||||
- `roles`:角色定义
|
||||
- `status`:群组状态
|
||||
|
||||
### 4.6 Human-in-the-Loop(人在回路)
|
||||
在 Agent 协作过程中,人类可介入进行决策的机制。
|
||||
|
||||
**关键概念:**
|
||||
- `escalation`:升级(将问题提交给人类)
|
||||
- `decision`:决策(人类做出的判断)
|
||||
- `resume`:恢复(人类决策后继续执行)
|
||||
|
||||
---
|
||||
|
||||
## 5. 设计原则
|
||||
|
||||
### 5.1 简约性(Simplicity)
|
||||
- 核心机制保持简单
|
||||
- 复杂功能通过组合基础机制实现
|
||||
- 可选功能通过能力声明来协商
|
||||
|
||||
### 5.2 可扩展性(Extensibility)
|
||||
- 消息类型可扩展
|
||||
- 能力类型可扩展
|
||||
- 传输方式可扩展
|
||||
- 通过版本号与能力协商保证向后兼容
|
||||
|
||||
### 5.3 容错性(Fault Tolerance)
|
||||
- 网络不可靠是常态
|
||||
- 消息可能丢失、重复、延迟
|
||||
- Agent 可能故障、离线
|
||||
- 协议必须有重试、超时、降级机制
|
||||
|
||||
### 5.4 可观测性(Observability)
|
||||
- 所有交互可追踪
|
||||
- 任务状态可查询
|
||||
- 支持日志、指标、链路追踪
|
||||
|
||||
### 5.5 互操作性(Interoperability)
|
||||
- 不同语言、不同框架的 Agent 可互操作
|
||||
- 协议规范优先于具体实现
|
||||
- 提供标准测试用例验证兼容性
|
||||
|
||||
---
|
||||
|
||||
## 6. 消息模型总则
|
||||
|
||||
### 6.1 统一消息信封
|
||||
所有协议共享统一的消息信封结构,具体载荷由消息类型决定。
|
||||
|
||||
```
|
||||
UnifiedSynapseMessage
|
||||
├── id // 消息ID
|
||||
├── version // 协议版本
|
||||
├── type // 消息类型
|
||||
├── sender // 发送方 AgentEndpoint
|
||||
├── receiver? // 接收方(P2P 时)
|
||||
├── group_id? // 群组ID(群组时)
|
||||
├── correlation_id // 关联ID
|
||||
├── reply_to? // 回复的消息ID
|
||||
├── payload // 消息载荷(JSON)
|
||||
├── priority? // 优先级
|
||||
├── signature? // 签名
|
||||
├── timestamp // 发送时间
|
||||
└── expires_at? // 过期时间
|
||||
```
|
||||
|
||||
### 6.2 消息命名规范
|
||||
- 使用 `领域:动作` 格式
|
||||
- 全小写,用下划线分隔
|
||||
- 示例:`p2p:task_request`, `group:create`, `hil:decision`
|
||||
|
||||
### 6.3 请求-响应模式
|
||||
- 请求消息必须携带唯一 `id`
|
||||
- 响应消息通过 `reply_to` 关联请求
|
||||
- 支持 ACK/NACK 机制
|
||||
- 超时未收到响应应重试或报错
|
||||
|
||||
---
|
||||
|
||||
## 7. 任务生命周期总则
|
||||
|
||||
### 7.1 状态机
|
||||
|
||||
```
|
||||
┌──────────┐
|
||||
│ pending │ 待确认
|
||||
└────┬─────┘
|
||||
│ 确认/拒绝
|
||||
┌─────┴──────┐
|
||||
▼ ▼
|
||||
┌─────────┐ ┌──────────┐
|
||||
│confirmed│ │ rejected │ 已拒绝(终态)
|
||||
└────┬────┘ └──────────┘
|
||||
│ 开始执行
|
||||
▼
|
||||
┌──────────┐
|
||||
│executing │ 执行中
|
||||
└────┬─────┘
|
||||
│ 信息缺失
|
||||
┌────▼─────┐
|
||||
│info_needed│ 需要信息
|
||||
└────┬─────┘
|
||||
│ 信息补充
|
||||
▼
|
||||
回到 executing
|
||||
│
|
||||
├──────────────┐
|
||||
│ 需人类决策 │
|
||||
▼ ▼
|
||||
┌──────────┐ ┌───────────┐
|
||||
│need_human│ │ 各种终态 │
|
||||
└────┬─────┘ └───────────┘
|
||||
│ 人类决策
|
||||
▼
|
||||
回到 executing
|
||||
│
|
||||
┌────┴────┬─────────┐
|
||||
▼ ▼ ▼
|
||||
completed failed cancelled
|
||||
(完成) (失败) (取消)
|
||||
```
|
||||
|
||||
### 7.2 终态
|
||||
- `completed`:成功完成
|
||||
- `failed`:执行失败
|
||||
- `cancelled`:被取消
|
||||
- `rejected`:被拒绝
|
||||
|
||||
---
|
||||
|
||||
## 8. 安全模型总则
|
||||
|
||||
### 8.1 身份认证
|
||||
- 每个 Agent 有唯一身份
|
||||
- 支持 HMAC、JWT、OAuth2 等多种认证方式
|
||||
- 信任等级机制
|
||||
|
||||
### 8.2 消息完整性
|
||||
- 消息签名机制
|
||||
- 防止篡改与伪造
|
||||
|
||||
### 8.3 机密性
|
||||
- 支持端到端加密
|
||||
- 敏感载荷加密传输
|
||||
|
||||
### 8.4 访问控制
|
||||
- 基于信任等级的权限控制
|
||||
- 群组内基于角色的权限控制
|
||||
|
||||
---
|
||||
|
||||
## 9. 非目标
|
||||
|
||||
以下内容**不在**本原始协议的讨论范围内:
|
||||
|
||||
1. **具体传输协议的实现细节** - 由各传输层实现定义
|
||||
2. **特定领域的任务类型** - 由应用层或领域扩展定义
|
||||
3. **Agent 内部实现架构** - 仅定义对外接口与行为
|
||||
4. **具体编程语言绑定** - 语言无关,可多语言实现
|
||||
5. **UI/UX 设计** - 人机界面不在协议范围内
|
||||
|
||||
---
|
||||
|
||||
## 10. 参考实现
|
||||
|
||||
本协议族的参考实现位于:`dp-headless/synapse/`
|
||||
|
||||
| 模块 | 对应协议 | 文件路径 |
|
||||
|------|---------|---------|
|
||||
| types 包 | 公共类型定义 | `types/` |
|
||||
| p2p 包 | A2A 协议 | `p2p/` |
|
||||
| group 包 | A2N 组群协议 | `group/` |
|
||||
| externalapi 包 | P2A 调用协议 | `externalapi/` |
|
||||
| sdk 包 | P2A 客户端 SDK | `sdk/` |
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:协议演进
|
||||
|
||||
本原始协议作为顶层指导,保持相对稳定。各具体协议可独立版本化演进,但需遵循本协议定义的核心原则。
|
||||
|
||||
**版本历史:**
|
||||
- v1.0.0:初始版本,定义核心概念与设计原则
|
||||
@@ -1,465 +0,0 @@
|
||||
# A2A 协议(Agent-to-Agent Protocol)
|
||||
|
||||
> 版本:1.0.0
|
||||
> 状态:草案
|
||||
> 类型:实现规范
|
||||
> 基于:[灵隙原始协议(Synapse Raw Protocol) v1.0.0](./01-synapse-raw-protocol.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 概述
|
||||
|
||||
A2A 协议定义了两个 Agent 之间进行点对点(P2P)任务协作的完整规范,包括消息类型、交互流程、状态机、接口定义与错误处理。
|
||||
|
||||
### 1.1 适用场景
|
||||
- 单个任务请求与响应
|
||||
- 任务状态实时同步
|
||||
- 任务取消
|
||||
- 信息补全交互
|
||||
- 人类介入决策
|
||||
|
||||
### 1.2 非适用场景
|
||||
- 多 Agent 群组协作(参见 A2N 组群协议)
|
||||
- 非 Agent 调用方接入(参见 P2A 调用协议)
|
||||
|
||||
---
|
||||
|
||||
## 2. 消息类型
|
||||
|
||||
### 2.1 消息类型清单
|
||||
|
||||
所有消息类型前缀:`p2p:`
|
||||
|
||||
| 消息类型 | 方向 | 说明 |
|
||||
|---------|------|------|
|
||||
| `p2p:task_request` | 请求方 → 执行方 | 提交任务请求 |
|
||||
| `p2p:task_confirm` | 执行方 → 请求方 | 确认接受任务 |
|
||||
| `p2p:task_reject` | 执行方 → 请求方 | 拒绝任务 |
|
||||
| `p2p:task_need_info` | 执行方 → 请求方 | 请求补充信息 |
|
||||
| `p2p:task_info_provided` | 请求方 → 执行方 | 提供补充信息 |
|
||||
| `p2p:task_status_update` | 执行方 → 请求方 | 任务进度更新 |
|
||||
| `p2p:task_complete` | 执行方 → 请求方 | 任务完成 |
|
||||
| `p2p:task_failed` | 执行方 → 请求方 | 任务失败 |
|
||||
| `p2p:task_cancel` | 请求方 → 执行方 | 取消任务 |
|
||||
| `p2p:task_ack` | 双向 | 任务级确认 |
|
||||
| `p2p:ack` | 双向 | 通用确认 |
|
||||
| `p2p:nack` | 双向 | 通用否定确认(错误) |
|
||||
| `p2p:heartbeat` | 双向 | 心跳保活 |
|
||||
| `p2p:capability_query` | 双向 | 查询对方能力 |
|
||||
| `p2p:capability_response` | 双向 | 返回能力清单 |
|
||||
| `p2p:task_need_human` | 执行方 → 请求方 | 请求人类介入 |
|
||||
| `p2p:human_decision` | 请求方 → 执行方 | 人类决策结果 |
|
||||
|
||||
### 2.2 消息类型完整定义
|
||||
|
||||
参见代码:[types/types.go](../types/types.go)
|
||||
|
||||
---
|
||||
|
||||
## 3. 任务状态机
|
||||
|
||||
### 3.1 状态定义
|
||||
|
||||
| 状态 | 说明 |
|
||||
|------|------|
|
||||
| `idle` | 空闲(初始状态) |
|
||||
| `pending` | 待确认(已发送请求,等待确认) |
|
||||
| `confirmed` | 已确认(对方接受任务) |
|
||||
| `executing` | 执行中 |
|
||||
| `info_needed` | 需要信息 |
|
||||
| `info_provided` | 信息已提供 |
|
||||
| `need_human` | 需要人类介入 |
|
||||
| `human_deciding` | 人类决策中 |
|
||||
| `human_approved` | 人类已批准 |
|
||||
| `human_rejected` | 人类已拒绝 |
|
||||
| `completed` | 已完成(终态) |
|
||||
| `failed` | 已失败(终态) |
|
||||
| `cancelled` | 已取消(终态) |
|
||||
| `rejected` | 已拒绝(终态) |
|
||||
|
||||
### 3.2 状态流转图
|
||||
|
||||
```
|
||||
请求方视角:
|
||||
|
||||
idle → pending → confirmed → executing → completed
|
||||
↓ ↓ → failed
|
||||
rejected info_needed → cancelled
|
||||
↑↓
|
||||
info_provided
|
||||
↓
|
||||
need_human
|
||||
↑↓
|
||||
human_decision
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 核心交互流程
|
||||
|
||||
### 4.1 正常任务提交流程
|
||||
|
||||
```
|
||||
请求方 Agent 执行方 Agent
|
||||
| |
|
||||
|-------- task_request ------------->| 1. 提交任务
|
||||
| |
|
||||
|<------- task_confirm / task_reject -| 2. 确认/拒绝
|
||||
| |
|
||||
|<------- task_status_update --------| 3. 进度更新(多次)
|
||||
| |
|
||||
|<------- task_complete -------------| 4. 完成通知
|
||||
| |
|
||||
|-------- task_ack ----------------->| 5. 确认收到
|
||||
| |
|
||||
```
|
||||
|
||||
### 4.2 信息补全流程
|
||||
|
||||
```
|
||||
请求方 Agent 执行方 Agent
|
||||
| |
|
||||
|-------- task_request ------------->|
|
||||
| |
|
||||
|<------- task_need_info ------------| 发现信息缺失
|
||||
| |
|
||||
|-------- task_info_provided ------->| 补充信息
|
||||
| |
|
||||
|<------- task_status_update --------| 继续执行
|
||||
| |
|
||||
```
|
||||
|
||||
### 4.3 人类介入流程
|
||||
|
||||
```
|
||||
请求方 Agent 执行方 Agent
|
||||
| |
|
||||
|-------- task_request ------------->|
|
||||
| |
|
||||
|<------- task_need_human -----------| 需要人类决策
|
||||
| |
|
||||
| [人类做出决策] |
|
||||
| |
|
||||
|-------- human_decision ----------->| 决策结果
|
||||
| |
|
||||
|<------- task_status_update --------| 继续执行
|
||||
| |
|
||||
```
|
||||
|
||||
### 4.4 任务取消流程
|
||||
|
||||
```
|
||||
请求方 Agent 执行方 Agent
|
||||
| |
|
||||
|-------- task_request ------------->|
|
||||
| |
|
||||
|-------- task_cancel -------------->| 取消任务
|
||||
| |
|
||||
|<------- task_ack ------------------| 确认取消
|
||||
| |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 接口规范
|
||||
|
||||
### 5.1 P2P 协议处理器接口
|
||||
|
||||
**接口定义:**
|
||||
|
||||
```go
|
||||
type P2PProtocol interface {
|
||||
// 处理收到的消息
|
||||
HandleMessage(ctx context.Context, msg *UnifiedSynapseMessage) (*UnifiedSynapseMessage, error)
|
||||
|
||||
// 设置任务处理器
|
||||
SetTaskHandler(h TaskHandler)
|
||||
|
||||
// 设置回调发送器
|
||||
SetCallbackSender(s CallbackSender)
|
||||
|
||||
// 获取 Agent 端点
|
||||
AgentEndpoint() AgentEndpoint
|
||||
|
||||
// 查询任务状态
|
||||
GetTaskStatus(taskID string) (P2PTaskStatus, bool)
|
||||
|
||||
// 列出所有任务
|
||||
ListTasks() map[string]P2PTaskStatus
|
||||
}
|
||||
```
|
||||
|
||||
**参考实现:** [p2p/protocol.go](../p2p/protocol.go)
|
||||
|
||||
### 5.2 任务处理器接口
|
||||
|
||||
```go
|
||||
type TaskHandler interface {
|
||||
// 执行任务并返回结果
|
||||
ExecuteTask(ctx context.Context, taskType string, payload map[string]interface{}) (any, error)
|
||||
|
||||
// 验证任务是否可执行,返回缺失字段列表和是否可处理
|
||||
ValidateTask(taskType string, payload map[string]interface{}) ([]MissingField, bool)
|
||||
|
||||
// 获取能力列表
|
||||
GetCapabilities() []string
|
||||
}
|
||||
```
|
||||
|
||||
**说明:**
|
||||
- `ExecuteTask`:实际执行业务逻辑的入口
|
||||
- `ValidateTask`:在任务确认前调用,用于判断是否接受任务或需要补充信息
|
||||
- `GetCapabilities`:返回 Agent 支持的任务类型列表
|
||||
|
||||
### 5.3 回调发送器接口
|
||||
|
||||
```go
|
||||
type CallbackSender interface {
|
||||
// 向远程 Agent 发送消息
|
||||
SendCallback(ctx context.Context, endpoint string, msg *UnifiedSynapseMessage) error
|
||||
}
|
||||
```
|
||||
|
||||
**说明:**
|
||||
- 传输层实现此接口
|
||||
- 支持多种传输方式(HTTP、gRPC、WebSocket 等)
|
||||
|
||||
### 5.4 配置项
|
||||
|
||||
```go
|
||||
type P2PConfig struct {
|
||||
MaxRetries int // 最大重试次数(默认 5)
|
||||
RetryBaseDelay time.Duration // 重试基础延迟(默认 2s,指数退避)
|
||||
TaskTimeout time.Duration // 任务执行超时(默认 5 分钟)
|
||||
HeartbeatInterval time.Duration // 心跳间隔(默认 30 秒)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 载荷格式
|
||||
|
||||
### 6.1 TaskRequestPayload(任务请求)
|
||||
|
||||
```json
|
||||
{
|
||||
"task": {
|
||||
"id": "task-xxx",
|
||||
"type": "text_generation",
|
||||
"priority": "normal",
|
||||
"deadline": "2026-01-01T00:00:00Z",
|
||||
"payload": {
|
||||
"prompt": "写一首诗",
|
||||
"style": "古典"
|
||||
}
|
||||
},
|
||||
"context": {
|
||||
"session_id": "sess-xxx",
|
||||
"parent_task_id": "parent-xxx",
|
||||
"correlation_id": "corr-xxx",
|
||||
"metadata": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 TaskConfirmPayload(任务确认)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"confirmed": true,
|
||||
"estimated_duration": 120,
|
||||
"expected_completion": "2026-01-01T00:02:00Z",
|
||||
"capabilities_used": ["text_generation"],
|
||||
"constraints": {
|
||||
"max_retries": 3,
|
||||
"fallback_enabled": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 TaskRejectPayload(任务拒绝)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"reason": {
|
||||
"code": "capability_lacking",
|
||||
"message": "不支持该任务类型",
|
||||
"details": null
|
||||
},
|
||||
"alternatives": {
|
||||
"suggested_agent_id": "other-agent",
|
||||
"retry_after": 300,
|
||||
"partial_support": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.4 TaskStatusUpdatePayload(状态更新)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"status": "executing",
|
||||
"progress": 50,
|
||||
"message": "正在生成内容",
|
||||
"intermediate_result": null
|
||||
}
|
||||
```
|
||||
|
||||
### 6.5 TaskCompletePayload(任务完成)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"result": {
|
||||
"output_type": "text",
|
||||
"output": "生成的文本内容...",
|
||||
"metadata": {
|
||||
"tokens": 1024,
|
||||
"model": "gpt-4"
|
||||
}
|
||||
},
|
||||
"summary": "任务已完成"
|
||||
}
|
||||
```
|
||||
|
||||
### 6.6 TaskFailedPayload(任务失败)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"error": {
|
||||
"code": "execution_error",
|
||||
"message": "模型调用超时",
|
||||
"recoverable": true,
|
||||
"details": null
|
||||
},
|
||||
"partial_result": null
|
||||
}
|
||||
```
|
||||
|
||||
### 6.7 TaskNeedInfoPayload(需要信息)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"missing_fields": [
|
||||
{
|
||||
"field": "style",
|
||||
"reason": "未指定生成风格",
|
||||
"suggested_value": "古典",
|
||||
"alternatives": ["现代", "浪漫"]
|
||||
}
|
||||
],
|
||||
"question": "请选择生成风格",
|
||||
"required_by": "2026-01-01T00:01:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### 6.8 TaskNeedHumanPayload(需要人类)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"reason": {
|
||||
"code": "ethical_decision",
|
||||
"message": "涉及伦理决策需要人工审核"
|
||||
},
|
||||
"context": {
|
||||
"task_summary": "生成一张人物肖像",
|
||||
"current_progress": 30,
|
||||
"blocked_reason": "需要确认肖像使用授权"
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"field": "authorized",
|
||||
"value": true,
|
||||
"pros_cons": "继续生成,假设已授权",
|
||||
"confidence": 70
|
||||
}
|
||||
],
|
||||
"escalation_level": 1,
|
||||
"deadline": "2026-01-01T00:05:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### 6.9 HumanDecisionPayload(人类决策)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"decision_id": "dec-xxx",
|
||||
"reply_to": "msg-xxx",
|
||||
"decided_by": {
|
||||
"user_id": "user-123",
|
||||
"user_name": "张三",
|
||||
"role": "reviewer"
|
||||
},
|
||||
"decision": {
|
||||
"action": "approve",
|
||||
"approved_fields": {
|
||||
"authorized": true
|
||||
},
|
||||
"rejection_reason": ""
|
||||
},
|
||||
"notes": "已确认授权",
|
||||
"timestamp": "2026-01-01T00:03:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**完整载荷定义:** [types/types.go](../types/types.go)
|
||||
|
||||
---
|
||||
|
||||
## 7. 错误码
|
||||
|
||||
| 错误码 | 说明 | HTTP 状态码(如适用) |
|
||||
|--------|------|---------------------|
|
||||
| `message_format_invalid` | 消息格式无效 | 400 |
|
||||
| `capability_lacking` | 能力不足 | 403 |
|
||||
| `agent_not_ready` | Agent 未就绪 | 503 |
|
||||
| `task_not_found` | 任务不存在 | 404 |
|
||||
| `task_already_completed` | 任务已完成 | 409 |
|
||||
| `execution_timeout` | 执行超时 | 504 |
|
||||
| `execution_failed` | 执行失败 | 500 |
|
||||
| `permission_denied` | 权限不足 | 403 |
|
||||
| `invalid_params` | 参数无效 | 400 |
|
||||
| `rate_limited` | 限流 | 429 |
|
||||
|
||||
**完整错误定义:** [types/errors.go](../types/errors.go)
|
||||
|
||||
---
|
||||
|
||||
## 8. 传输要求
|
||||
|
||||
### 8.1 HTTP 传输
|
||||
- Content-Type: `application/json`
|
||||
- 请求方法:`POST`
|
||||
- 端点路径:由 Agent 自行定义,建议 `/synapse/callback`
|
||||
- 超时:建议 30 秒
|
||||
|
||||
### 8.2 签名验证(可选)
|
||||
- 使用 HMAC-SHA256 签名
|
||||
- 签名放在 Header:`X-Synapse-Signature`
|
||||
- 或放在消息体:`signature` 字段
|
||||
|
||||
---
|
||||
|
||||
## 9. 参考实现
|
||||
|
||||
本协议的参考实现位于 `p2p/` 目录:
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| [p2p/protocol.go](../p2p/protocol.go) | P2P 协议核心实现 |
|
||||
| [types/types.go](../types/types.go) | 数据类型定义 |
|
||||
| [types/errors.go](../types/errors.go) | 错误定义 |
|
||||
| [transport/transport.go](../transport/transport.go) | HTTP 传输实现 |
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:能力声明格式
|
||||
|
||||
参见 [灵隙原始协议(Synapse Raw Protocol) - Capability](./01-synapse-raw-protocol.md#44-capability能力)
|
||||
@@ -1,465 +0,0 @@
|
||||
# A2A 协议(Agent-to-Agent Protocol)
|
||||
|
||||
> 版本:1.0.0
|
||||
> 状态:草案
|
||||
> 类型:实现规范
|
||||
> 基于:[灵隙原始协议(Synapse Raw Protocol) v1.0.0](./01-a2a-raw-protocol.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 概述
|
||||
|
||||
A2A 协议定义了两个 Agent 之间进行点对点(P2P)任务协作的完整规范,包括消息类型、交互流程、状态机、接口定义与错误处理。
|
||||
|
||||
### 1.1 适用场景
|
||||
- 单个任务请求与响应
|
||||
- 任务状态实时同步
|
||||
- 任务取消
|
||||
- 信息补全交互
|
||||
- 人类介入决策
|
||||
|
||||
### 1.2 非适用场景
|
||||
- 多 Agent 群组协作(参见 A2N 组群协议)
|
||||
- 非 Agent 调用方接入(参见 P2A 调用协议)
|
||||
|
||||
---
|
||||
|
||||
## 2. 消息类型
|
||||
|
||||
### 2.1 消息类型清单
|
||||
|
||||
所有消息类型前缀:`p2p:`
|
||||
|
||||
| 消息类型 | 方向 | 说明 |
|
||||
|---------|------|------|
|
||||
| `p2p:task_request` | 请求方 → 执行方 | 提交任务请求 |
|
||||
| `p2p:task_confirm` | 执行方 → 请求方 | 确认接受任务 |
|
||||
| `p2p:task_reject` | 执行方 → 请求方 | 拒绝任务 |
|
||||
| `p2p:task_need_info` | 执行方 → 请求方 | 请求补充信息 |
|
||||
| `p2p:task_info_provided` | 请求方 → 执行方 | 提供补充信息 |
|
||||
| `p2p:task_status_update` | 执行方 → 请求方 | 任务进度更新 |
|
||||
| `p2p:task_complete` | 执行方 → 请求方 | 任务完成 |
|
||||
| `p2p:task_failed` | 执行方 → 请求方 | 任务失败 |
|
||||
| `p2p:task_cancel` | 请求方 → 执行方 | 取消任务 |
|
||||
| `p2p:task_ack` | 双向 | 任务级确认 |
|
||||
| `p2p:ack` | 双向 | 通用确认 |
|
||||
| `p2p:nack` | 双向 | 通用否定确认(错误) |
|
||||
| `p2p:heartbeat` | 双向 | 心跳保活 |
|
||||
| `p2p:capability_query` | 双向 | 查询对方能力 |
|
||||
| `p2p:capability_response` | 双向 | 返回能力清单 |
|
||||
| `p2p:task_need_human` | 执行方 → 请求方 | 请求人类介入 |
|
||||
| `p2p:human_decision` | 请求方 → 执行方 | 人类决策结果 |
|
||||
|
||||
### 2.2 消息类型完整定义
|
||||
|
||||
参见代码:[types/types.go](../types/types.go)
|
||||
|
||||
---
|
||||
|
||||
## 3. 任务状态机
|
||||
|
||||
### 3.1 状态定义
|
||||
|
||||
| 状态 | 说明 |
|
||||
|------|------|
|
||||
| `idle` | 空闲(初始状态) |
|
||||
| `pending` | 待确认(已发送请求,等待确认) |
|
||||
| `confirmed` | 已确认(对方接受任务) |
|
||||
| `executing` | 执行中 |
|
||||
| `info_needed` | 需要信息 |
|
||||
| `info_provided` | 信息已提供 |
|
||||
| `need_human` | 需要人类介入 |
|
||||
| `human_deciding` | 人类决策中 |
|
||||
| `human_approved` | 人类已批准 |
|
||||
| `human_rejected` | 人类已拒绝 |
|
||||
| `completed` | 已完成(终态) |
|
||||
| `failed` | 已失败(终态) |
|
||||
| `cancelled` | 已取消(终态) |
|
||||
| `rejected` | 已拒绝(终态) |
|
||||
|
||||
### 3.2 状态流转图
|
||||
|
||||
```
|
||||
请求方视角:
|
||||
|
||||
idle → pending → confirmed → executing → completed
|
||||
↓ ↓ → failed
|
||||
rejected info_needed → cancelled
|
||||
↑↓
|
||||
info_provided
|
||||
↓
|
||||
need_human
|
||||
↑↓
|
||||
human_decision
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 核心交互流程
|
||||
|
||||
### 4.1 正常任务提交流程
|
||||
|
||||
```
|
||||
请求方 Agent 执行方 Agent
|
||||
| |
|
||||
|-------- task_request ------------->| 1. 提交任务
|
||||
| |
|
||||
|<------- task_confirm / task_reject -| 2. 确认/拒绝
|
||||
| |
|
||||
|<------- task_status_update --------| 3. 进度更新(多次)
|
||||
| |
|
||||
|<------- task_complete -------------| 4. 完成通知
|
||||
| |
|
||||
|-------- task_ack ----------------->| 5. 确认收到
|
||||
| |
|
||||
```
|
||||
|
||||
### 4.2 信息补全流程
|
||||
|
||||
```
|
||||
请求方 Agent 执行方 Agent
|
||||
| |
|
||||
|-------- task_request ------------->|
|
||||
| |
|
||||
|<------- task_need_info ------------| 发现信息缺失
|
||||
| |
|
||||
|-------- task_info_provided ------->| 补充信息
|
||||
| |
|
||||
|<------- task_status_update --------| 继续执行
|
||||
| |
|
||||
```
|
||||
|
||||
### 4.3 人类介入流程
|
||||
|
||||
```
|
||||
请求方 Agent 执行方 Agent
|
||||
| |
|
||||
|-------- task_request ------------->|
|
||||
| |
|
||||
|<------- task_need_human -----------| 需要人类决策
|
||||
| |
|
||||
| [人类做出决策] |
|
||||
| |
|
||||
|-------- human_decision ----------->| 决策结果
|
||||
| |
|
||||
|<------- task_status_update --------| 继续执行
|
||||
| |
|
||||
```
|
||||
|
||||
### 4.4 任务取消流程
|
||||
|
||||
```
|
||||
请求方 Agent 执行方 Agent
|
||||
| |
|
||||
|-------- task_request ------------->|
|
||||
| |
|
||||
|-------- task_cancel -------------->| 取消任务
|
||||
| |
|
||||
|<------- task_ack ------------------| 确认取消
|
||||
| |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 接口规范
|
||||
|
||||
### 5.1 P2P 协议处理器接口
|
||||
|
||||
**接口定义:**
|
||||
|
||||
```go
|
||||
type P2PProtocol interface {
|
||||
// 处理收到的消息
|
||||
HandleMessage(ctx context.Context, msg *UnifiedSynapseMessage) (*UnifiedSynapseMessage, error)
|
||||
|
||||
// 设置任务处理器
|
||||
SetTaskHandler(h TaskHandler)
|
||||
|
||||
// 设置回调发送器
|
||||
SetCallbackSender(s CallbackSender)
|
||||
|
||||
// 获取 Agent 端点
|
||||
AgentEndpoint() AgentEndpoint
|
||||
|
||||
// 查询任务状态
|
||||
GetTaskStatus(taskID string) (P2PTaskStatus, bool)
|
||||
|
||||
// 列出所有任务
|
||||
ListTasks() map[string]P2PTaskStatus
|
||||
}
|
||||
```
|
||||
|
||||
**参考实现:** [p2p/protocol.go](../p2p/protocol.go)
|
||||
|
||||
### 5.2 任务处理器接口
|
||||
|
||||
```go
|
||||
type TaskHandler interface {
|
||||
// 执行任务并返回结果
|
||||
ExecuteTask(ctx context.Context, taskType string, payload map[string]interface{}) (any, error)
|
||||
|
||||
// 验证任务是否可执行,返回缺失字段列表和是否可处理
|
||||
ValidateTask(taskType string, payload map[string]interface{}) ([]MissingField, bool)
|
||||
|
||||
// 获取能力列表
|
||||
GetCapabilities() []string
|
||||
}
|
||||
```
|
||||
|
||||
**说明:**
|
||||
- `ExecuteTask`:实际执行业务逻辑的入口
|
||||
- `ValidateTask`:在任务确认前调用,用于判断是否接受任务或需要补充信息
|
||||
- `GetCapabilities`:返回 Agent 支持的任务类型列表
|
||||
|
||||
### 5.3 回调发送器接口
|
||||
|
||||
```go
|
||||
type CallbackSender interface {
|
||||
// 向远程 Agent 发送消息
|
||||
SendCallback(ctx context.Context, endpoint string, msg *UnifiedSynapseMessage) error
|
||||
}
|
||||
```
|
||||
|
||||
**说明:**
|
||||
- 传输层实现此接口
|
||||
- 支持多种传输方式(HTTP、gRPC、WebSocket 等)
|
||||
|
||||
### 5.4 配置项
|
||||
|
||||
```go
|
||||
type P2PConfig struct {
|
||||
MaxRetries int // 最大重试次数(默认 5)
|
||||
RetryBaseDelay time.Duration // 重试基础延迟(默认 2s,指数退避)
|
||||
TaskTimeout time.Duration // 任务执行超时(默认 5 分钟)
|
||||
HeartbeatInterval time.Duration // 心跳间隔(默认 30 秒)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 载荷格式
|
||||
|
||||
### 6.1 TaskRequestPayload(任务请求)
|
||||
|
||||
```json
|
||||
{
|
||||
"task": {
|
||||
"id": "task-xxx",
|
||||
"type": "text_generation",
|
||||
"priority": "normal",
|
||||
"deadline": "2026-01-01T00:00:00Z",
|
||||
"payload": {
|
||||
"prompt": "写一首诗",
|
||||
"style": "古典"
|
||||
}
|
||||
},
|
||||
"context": {
|
||||
"session_id": "sess-xxx",
|
||||
"parent_task_id": "parent-xxx",
|
||||
"correlation_id": "corr-xxx",
|
||||
"metadata": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 TaskConfirmPayload(任务确认)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"confirmed": true,
|
||||
"estimated_duration": 120,
|
||||
"expected_completion": "2026-01-01T00:02:00Z",
|
||||
"capabilities_used": ["text_generation"],
|
||||
"constraints": {
|
||||
"max_retries": 3,
|
||||
"fallback_enabled": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 TaskRejectPayload(任务拒绝)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"reason": {
|
||||
"code": "capability_lacking",
|
||||
"message": "不支持该任务类型",
|
||||
"details": null
|
||||
},
|
||||
"alternatives": {
|
||||
"suggested_agent_id": "other-agent",
|
||||
"retry_after": 300,
|
||||
"partial_support": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.4 TaskStatusUpdatePayload(状态更新)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"status": "executing",
|
||||
"progress": 50,
|
||||
"message": "正在生成内容",
|
||||
"intermediate_result": null
|
||||
}
|
||||
```
|
||||
|
||||
### 6.5 TaskCompletePayload(任务完成)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"result": {
|
||||
"output_type": "text",
|
||||
"output": "生成的文本内容...",
|
||||
"metadata": {
|
||||
"tokens": 1024,
|
||||
"model": "gpt-4"
|
||||
}
|
||||
},
|
||||
"summary": "任务已完成"
|
||||
}
|
||||
```
|
||||
|
||||
### 6.6 TaskFailedPayload(任务失败)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"error": {
|
||||
"code": "execution_error",
|
||||
"message": "模型调用超时",
|
||||
"recoverable": true,
|
||||
"details": null
|
||||
},
|
||||
"partial_result": null
|
||||
}
|
||||
```
|
||||
|
||||
### 6.7 TaskNeedInfoPayload(需要信息)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"missing_fields": [
|
||||
{
|
||||
"field": "style",
|
||||
"reason": "未指定生成风格",
|
||||
"suggested_value": "古典",
|
||||
"alternatives": ["现代", "浪漫"]
|
||||
}
|
||||
],
|
||||
"question": "请选择生成风格",
|
||||
"required_by": "2026-01-01T00:01:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### 6.8 TaskNeedHumanPayload(需要人类)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"reason": {
|
||||
"code": "ethical_decision",
|
||||
"message": "涉及伦理决策需要人工审核"
|
||||
},
|
||||
"context": {
|
||||
"task_summary": "生成一张人物肖像",
|
||||
"current_progress": 30,
|
||||
"blocked_reason": "需要确认肖像使用授权"
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"field": "authorized",
|
||||
"value": true,
|
||||
"pros_cons": "继续生成,假设已授权",
|
||||
"confidence": 70
|
||||
}
|
||||
],
|
||||
"escalation_level": 1,
|
||||
"deadline": "2026-01-01T00:05:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### 6.9 HumanDecisionPayload(人类决策)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"decision_id": "dec-xxx",
|
||||
"reply_to": "msg-xxx",
|
||||
"decided_by": {
|
||||
"user_id": "user-123",
|
||||
"user_name": "张三",
|
||||
"role": "reviewer"
|
||||
},
|
||||
"decision": {
|
||||
"action": "approve",
|
||||
"approved_fields": {
|
||||
"authorized": true
|
||||
},
|
||||
"rejection_reason": ""
|
||||
},
|
||||
"notes": "已确认授权",
|
||||
"timestamp": "2026-01-01T00:03:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**完整载荷定义:** [types/types.go](../types/types.go)
|
||||
|
||||
---
|
||||
|
||||
## 7. 错误码
|
||||
|
||||
| 错误码 | 说明 | HTTP 状态码(如适用) |
|
||||
|--------|------|---------------------|
|
||||
| `message_format_invalid` | 消息格式无效 | 400 |
|
||||
| `capability_lacking` | 能力不足 | 403 |
|
||||
| `agent_not_ready` | Agent 未就绪 | 503 |
|
||||
| `task_not_found` | 任务不存在 | 404 |
|
||||
| `task_already_completed` | 任务已完成 | 409 |
|
||||
| `execution_timeout` | 执行超时 | 504 |
|
||||
| `execution_failed` | 执行失败 | 500 |
|
||||
| `permission_denied` | 权限不足 | 403 |
|
||||
| `invalid_params` | 参数无效 | 400 |
|
||||
| `rate_limited` | 限流 | 429 |
|
||||
|
||||
**完整错误定义:** [types/errors.go](../types/errors.go)
|
||||
|
||||
---
|
||||
|
||||
## 8. 传输要求
|
||||
|
||||
### 8.1 HTTP 传输
|
||||
- Content-Type: `application/json`
|
||||
- 请求方法:`POST`
|
||||
- 端点路径:由 Agent 自行定义,建议 `/synapse/callback`
|
||||
- 超时:建议 30 秒
|
||||
|
||||
### 8.2 签名验证(可选)
|
||||
- 使用 HMAC-SHA256 签名
|
||||
- 签名放在 Header:`X-Synapse-Signature`
|
||||
- 或放在消息体:`signature` 字段
|
||||
|
||||
---
|
||||
|
||||
## 9. 参考实现
|
||||
|
||||
本协议的参考实现位于 `p2p/` 目录:
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| [p2p/protocol.go](../p2p/protocol.go) | P2P 协议核心实现 |
|
||||
| [types/types.go](../types/types.go) | 数据类型定义 |
|
||||
| [types/errors.go](../types/errors.go) | 错误定义 |
|
||||
| [transport/transport.go](../transport/transport.go) | HTTP 传输实现 |
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:能力声明格式
|
||||
|
||||
参见 [灵隙原始协议(Synapse Raw Protocol) - Capability](./01-a2a-raw-protocol.md#44-capability能力)
|
||||
@@ -1,545 +0,0 @@
|
||||
# A2N 协议(Agent-to-N Protocol)
|
||||
|
||||
> 版本:1.0.0
|
||||
> 状态:草案
|
||||
> 类型:实现规范
|
||||
> 基于:[灵隙原始协议(Synapse Raw Protocol) v1.0.0](./01-synapse-raw-protocol.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 概述
|
||||
|
||||
A2N(Agent-to-N)组群协议定义了多个 Agent 组成群组进行协同工作的完整规范,包括群组管理、任务拆分与合并、群体决策、角色与权限等。
|
||||
|
||||
### 1.1 适用场景
|
||||
- 复杂任务的拆分与并行执行(MapReduce 模式)
|
||||
- 多 Agent 协同完成单一目标
|
||||
- 群体投票与共识决策
|
||||
- 临时或永久的 Agent 工作组
|
||||
- 分级升级(escalation)机制
|
||||
|
||||
### 1.2 依赖
|
||||
- 依赖 A2A 协议的点对点通信能力
|
||||
- 群组内成员间的单播通信使用 A2A 协议
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心概念
|
||||
|
||||
### 2.1 Group(群组)
|
||||
由多个 Agent 组成的协作单元,具有明确的拓扑结构、角色分工与协作规则。
|
||||
|
||||
### 2.2 Topology(拓扑结构)
|
||||
|
||||
| 拓扑类型 | 说明 | 适用场景 |
|
||||
|---------|------|---------|
|
||||
| `star` | 星型,中心 coordinator 协调 | 简单任务分发 |
|
||||
| `mesh` | 网状,成员间可直接通信 | 对等协作 |
|
||||
| `chain` | 链式,流水线模式 | 顺序处理 |
|
||||
| `hierarchical` | 层级型,多层管理 | 大型复杂任务 |
|
||||
| `fullmesh` | 全连接网状 | 高可靠协作 |
|
||||
|
||||
### 2.3 Role(角色)
|
||||
|
||||
| 角色 | 说明 | 权限 |
|
||||
|------|------|------|
|
||||
| `coordinator` | 协调者 | 创建/解散群组、任务拆分、发起投票 |
|
||||
| `worker` | 工作者 | 执行子任务、提交结果 |
|
||||
| `reviewer` | 审核者 | 审核结果、发起投票 |
|
||||
| `observer` | 观察者 | 只读,查看进度 |
|
||||
|
||||
### 2.4 GroupStatus(群组状态)
|
||||
|
||||
| 状态 | 说明 |
|
||||
|------|------|
|
||||
| `creating` | 创建中 |
|
||||
| `active` | 活跃 |
|
||||
| `frozen` | 冻结(不可执行新任务) |
|
||||
| `disbanded` | 已解散(终态) |
|
||||
|
||||
---
|
||||
|
||||
## 3. 消息类型
|
||||
|
||||
### 3.1 消息类型清单
|
||||
|
||||
所有消息类型前缀:`group:` 或 `inter_group:`
|
||||
|
||||
#### 群组管理
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `group:create` | 创建群组 |
|
||||
| `group:disband` | 解散群组 |
|
||||
| `group:join` | 加入群组 |
|
||||
| `group:leave` | 离开群组 |
|
||||
| `group:invite` | 邀请加入 |
|
||||
| `group:accept_invite` | 接受邀请 |
|
||||
| `group:reject_invite` | 拒绝邀请 |
|
||||
| `group:member_update` | 成员更新通知 |
|
||||
| `group:role_change` | 角色变更 |
|
||||
|
||||
#### 群组通信
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `group:broadcast` | 广播(所有成员) |
|
||||
| `group:multicast` | 组播(指定成员) |
|
||||
| `group:direct` | 群内单播 |
|
||||
| `group:relay` | 消息中继 |
|
||||
|
||||
#### 任务协作
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `group:task_split` | 任务拆分 |
|
||||
| `group:task_assign` | 任务分配 |
|
||||
| `group:task_merge` | 结果合并 |
|
||||
| `group:task_progress` | 子任务进度 |
|
||||
|
||||
#### 共识与投票
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `group:vote` | 发起投票 |
|
||||
| `group:vote_submit` | 提交投票 |
|
||||
| `group:consensus` | 达成共识 |
|
||||
|
||||
#### 人在回路(HiL)
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `group:consensus_escalate` | 共识僵局升级给人类 |
|
||||
| `group:subtask_escalate` | 子任务阻塞升级给人类 |
|
||||
| `group:human_decision` | 人类决策结果 |
|
||||
| `inter_group:escalate` | 跨群组升级 |
|
||||
|
||||
**完整定义:** [types/types.go](../types/types.go)
|
||||
|
||||
---
|
||||
|
||||
## 4. 群组生命周期
|
||||
|
||||
### 4.1 状态机
|
||||
|
||||
```
|
||||
creating → active → frozen → disbanded
|
||||
↑ ↓
|
||||
└─────────┘
|
||||
解冻
|
||||
```
|
||||
|
||||
### 4.2 群组创建流程
|
||||
|
||||
```
|
||||
创建者 被邀请成员
|
||||
| |
|
||||
|------ group:invite -------------->| 发送邀请
|
||||
| |
|
||||
|<----- group:accept_invite --------| 接受邀请
|
||||
| |
|
||||
|------ group:member_update ------->| 通知成员更新
|
||||
| |
|
||||
[群组创建完成,状态变为 active] |
|
||||
```
|
||||
|
||||
### 4.3 群组解散流程
|
||||
|
||||
```
|
||||
coordinator 所有成员
|
||||
| |
|
||||
|------ group:disband ------------->| 通知解散
|
||||
| |
|
||||
[群组状态变为 disbanded] |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 任务协作流程
|
||||
|
||||
### 5.1 MapReduce 模式(拆分-执行-合并)
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ coordinator │
|
||||
└──────┬───────┘
|
||||
│
|
||||
group:task_split(拆分任务)
|
||||
│
|
||||
┌────────────────┼────────────────┐
|
||||
▼ ▼ ▼
|
||||
┌─────────┐ ┌─────────┐ ┌─────────┐
|
||||
│ worker1 │ │ worker2 │ │ worker3 │
|
||||
└────┬────┘ └────┬────┘ └────┬────┘
|
||||
│ │ │
|
||||
│ 执行子任务 │ 执行子任务 │ 执行子任务
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
子任务结果 子任务结果 子任务结果
|
||||
└────────────────┼────────────────┘
|
||||
│
|
||||
group:task_merge(合并结果)
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 最终结果 │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
### 5.2 详细流程
|
||||
|
||||
1. **任务拆分阶段**
|
||||
- coordinator 接收原始任务
|
||||
- 将任务拆分为多个子任务(subtask)
|
||||
- 为每个子任务指定执行的 worker
|
||||
- 定义合并策略(merge strategy)
|
||||
|
||||
2. **并行执行阶段**
|
||||
- 各 worker 并行执行子任务
|
||||
- 定期上报进度(`group:task_progress`)
|
||||
- 子任务完成后上报结果
|
||||
|
||||
3. **结果合并阶段**
|
||||
- 所有子任务完成后,coordinator 合并结果
|
||||
- 根据合并策略决定最终结果
|
||||
- 输出最终结果
|
||||
|
||||
---
|
||||
|
||||
## 6. 合并策略(Merge Strategy)
|
||||
|
||||
| 策略 | 说明 |
|
||||
|------|------|
|
||||
| `all_success` | 全部成功才认为成功,否则失败 |
|
||||
| `majority` | 多数子任务成功即可,取多数结果 |
|
||||
| `any_complete` | 任意一个完成即返回结果 |
|
||||
| `priority` | 按优先级,高优先级子任务结果优先 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 投票与共识
|
||||
|
||||
### 7.1 投票流程
|
||||
|
||||
```
|
||||
发起者 投票者
|
||||
| |
|
||||
|-------- group:vote -------------->| 发起投票
|
||||
| |
|
||||
|<------- group:vote_submit --------| 提交投票
|
||||
| |
|
||||
[计票,判断是否达到阈值] |
|
||||
| |
|
||||
|-------- group:consensus --------->| 公布结果
|
||||
| |
|
||||
```
|
||||
|
||||
### 7.2 投票规则
|
||||
|
||||
- `threshold`:通过阈值(0-1),如 0.5 表示过半
|
||||
- `deadline`:投票截止时间
|
||||
- `voters`:有投票权的成员列表
|
||||
- 超时未投票视为弃权
|
||||
|
||||
### 7.3 共识僵局(Consensus Deadlock)
|
||||
|
||||
当投票无法达成共识(如平票、未达阈值且超时)时,可升级给人类决策。
|
||||
|
||||
---
|
||||
|
||||
## 8. 人在回路(HiL)
|
||||
|
||||
群组协作中有两种升级场景:
|
||||
|
||||
### 8.1 共识僵局升级
|
||||
投票无法达成共识时,升级给人类裁决。
|
||||
|
||||
**消息:** `group:consensus_escalate`
|
||||
|
||||
### 8.2 子任务阻塞升级
|
||||
某个子任务遇到无法自动解决的问题时,升级给人类。
|
||||
|
||||
**消息:** `group:subtask_escalate`
|
||||
|
||||
### 8.3 跨群组升级
|
||||
问题超出当前群组能力时,可升级到上级群组或人类。
|
||||
|
||||
**消息:** `inter_group:escalate`
|
||||
|
||||
---
|
||||
|
||||
## 9. 接口规范
|
||||
|
||||
### 9.1 GroupProtocol 接口
|
||||
|
||||
```go
|
||||
type GroupProtocol interface {
|
||||
// 处理群组消息
|
||||
HandleMessage(ctx context.Context, msg *UnifiedSynapseMessage) (*UnifiedSynapseMessage, error)
|
||||
|
||||
// 设置任务处理器
|
||||
SetTaskHandler(h GroupTaskHandler)
|
||||
|
||||
// 查询群组列表
|
||||
GetGroups() []*AgentGroup
|
||||
|
||||
// 查询特定群组
|
||||
GetGroup(groupID string) (*AgentGroup, bool)
|
||||
}
|
||||
```
|
||||
|
||||
**参考实现:** [group/protocol.go](../group/protocol.go)
|
||||
|
||||
### 9.2 GroupTaskHandler 接口
|
||||
|
||||
```go
|
||||
type GroupTaskHandler interface {
|
||||
// 执行子任务
|
||||
ExecuteSubtask(ctx context.Context, subtaskID string, payload map[string]interface{}) (any, error)
|
||||
|
||||
// 合并多个子任务结果
|
||||
MergeResults(results []SubtaskResult, strategy MergeStrategy) (any, error)
|
||||
|
||||
// 验证任务是否可拆分,返回拆分方案
|
||||
ValidateTaskSplit(taskType string, requirements map[string]interface{}) ([]Subtask, bool)
|
||||
}
|
||||
```
|
||||
|
||||
**说明:**
|
||||
- `ExecuteSubtask`:执行单个子任务的业务逻辑
|
||||
- `MergeResults`:根据策略合并多个子任务的结果
|
||||
- `ValidateTaskSplit`:验证任务是否可拆分,并给出拆分方案
|
||||
|
||||
### 9.3 配置项
|
||||
|
||||
```go
|
||||
type GroupConfig struct {
|
||||
MaxGroupSize int // 最大群组人数(默认 20)
|
||||
MaxSubtasks int // 最大子任务数(默认 50)
|
||||
ConsensusTimeout time.Duration // 共识超时(默认 5 分钟)
|
||||
TaskTimeout time.Duration // 任务超时(默认 10 分钟)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 载荷格式
|
||||
|
||||
### 10.1 GroupCreatePayload(创建群组)
|
||||
|
||||
```json
|
||||
{
|
||||
"group": {
|
||||
"name": "视频制作组",
|
||||
"topology": "star",
|
||||
"config": {
|
||||
"allow_self_leave": true,
|
||||
"requires_approval": false,
|
||||
"consensus_required": false,
|
||||
"consensus_threshold": 0.5,
|
||||
"broadcast_mode": "all"
|
||||
},
|
||||
"initial_members": [
|
||||
{
|
||||
"agent_id": "worker-1",
|
||||
"system_id": "sys-a",
|
||||
"endpoint": "http://worker1/synapse"
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
{
|
||||
"id": "coordinator",
|
||||
"name": "协调者",
|
||||
"permissions": ["create", "disband", "split", "vote"],
|
||||
"max_tasks": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 GroupTaskSplitPayload(任务拆分)
|
||||
|
||||
```json
|
||||
{
|
||||
"original_task": {
|
||||
"id": "task-xxx",
|
||||
"type": "video_generation",
|
||||
"requirements": {
|
||||
"script": "xxx",
|
||||
"duration": 60
|
||||
},
|
||||
"deadline": "2026-01-01T00:10:00Z"
|
||||
},
|
||||
"subtasks": [
|
||||
{
|
||||
"id": "sub-1",
|
||||
"description": "生成脚本",
|
||||
"assigned_to": "writer-agent",
|
||||
"dependencies": [],
|
||||
"estimated_duration": 120,
|
||||
"priority": 1
|
||||
},
|
||||
{
|
||||
"id": "sub-2",
|
||||
"description": "生成配音",
|
||||
"assigned_to": "voice-agent",
|
||||
"dependencies": ["sub-1"],
|
||||
"estimated_duration": 180,
|
||||
"priority": 2
|
||||
}
|
||||
],
|
||||
"merge_strategy": "all_success"
|
||||
}
|
||||
```
|
||||
|
||||
### 10.3 GroupTaskMergePayload(结果合并)
|
||||
|
||||
```json
|
||||
{
|
||||
"original_task_id": "task-xxx",
|
||||
"subtask_results": [
|
||||
{
|
||||
"subtask_id": "sub-1",
|
||||
"assigned_to": "writer-agent",
|
||||
"status": "success",
|
||||
"result": { "script": "..." },
|
||||
"completed_at": "2026-01-01T00:02:00Z"
|
||||
}
|
||||
],
|
||||
"merge_strategy_used": "all_success",
|
||||
"merged_result": {
|
||||
"video_url": "http://..."
|
||||
},
|
||||
"consensus_achieved": true
|
||||
}
|
||||
```
|
||||
|
||||
### 10.4 GroupVotePayload(投票)
|
||||
|
||||
```json
|
||||
{
|
||||
"vote_id": "vote-xxx",
|
||||
"question": "是否采用方案A?",
|
||||
"options": ["方案A", "方案B", "方案C"],
|
||||
"voters": ["agent-1", "agent-2", "agent-3"],
|
||||
"deadline": "2026-01-01T00:05:00Z",
|
||||
"threshold": 0.66,
|
||||
"status": "open",
|
||||
"votes": []
|
||||
}
|
||||
```
|
||||
|
||||
### 10.5 GroupConsensusEscalatePayload(共识升级)
|
||||
|
||||
```json
|
||||
{
|
||||
"vote_id": "vote-xxx",
|
||||
"escalation_id": "esc-xxx",
|
||||
"question": "是否采用方案A?",
|
||||
"votes": [
|
||||
{
|
||||
"voter_id": "agent-1",
|
||||
"voter_role": "reviewer",
|
||||
"option": "方案A",
|
||||
"reason": "质量更好"
|
||||
}
|
||||
],
|
||||
"consensus_status": {
|
||||
"required_threshold": 0.66,
|
||||
"actual_threshold": 0.33,
|
||||
"leading_option": "方案A",
|
||||
"options_tied": false
|
||||
},
|
||||
"escalate_reason": {
|
||||
"code": "no_consensus",
|
||||
"message": "无法达成共识"
|
||||
},
|
||||
"escalation_level": 2,
|
||||
"deadline": "2026-01-01T00:10:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**完整载荷定义:** [types/types.go](../types/types.go)
|
||||
|
||||
---
|
||||
|
||||
## 11. 数据结构
|
||||
|
||||
### 11.1 AgentGroup(群组)
|
||||
|
||||
```go
|
||||
type AgentGroup struct {
|
||||
ID string
|
||||
Name string
|
||||
Topology GroupTopology
|
||||
CreatedBy string
|
||||
CreatedAt Timestamp
|
||||
Config GroupConfig
|
||||
Members []GroupMember
|
||||
Roles []GroupRole
|
||||
Status GroupStatus
|
||||
}
|
||||
```
|
||||
|
||||
### 11.2 GroupMember(群组成员)
|
||||
|
||||
```go
|
||||
type GroupMember struct {
|
||||
AgentID string
|
||||
AgentName string
|
||||
Role string
|
||||
JoinedAt Timestamp
|
||||
Status GroupMemberStatus
|
||||
Capabilities []string
|
||||
LoadFactor float64 // 负载因子 0-1,用于任务分配
|
||||
}
|
||||
```
|
||||
|
||||
### 11.3 Subtask(子任务)
|
||||
|
||||
```go
|
||||
type Subtask struct {
|
||||
ID string
|
||||
Description string
|
||||
AssignedTo string
|
||||
Dependencies []string
|
||||
EstimatedDuration int
|
||||
Priority int
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 错误码
|
||||
|
||||
群组协议特有错误码:
|
||||
|
||||
| 错误码 | 说明 |
|
||||
|--------|------|
|
||||
| `group_not_found` | 群组不存在 |
|
||||
| `group_not_active` | 群组不活跃 |
|
||||
| `group_full` | 群组已满 |
|
||||
| `already_member` | 已是成员 |
|
||||
| `not_group_member` | 不是群组成员 |
|
||||
| `member_not_found` | 成员不存在 |
|
||||
| `permission_denied` | 权限不足 |
|
||||
| `invalid_params` | 参数无效 |
|
||||
|
||||
---
|
||||
|
||||
## 13. 参考实现
|
||||
|
||||
本协议的参考实现位于 `group/` 目录:
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| [group/protocol.go](../group/protocol.go) | 群组协议核心实现 |
|
||||
| [types/types.go](../types/types.go) | 群组相关数据类型 |
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:与 A2A 协议的关系
|
||||
|
||||
- 群组内的点对点通信使用 A2A 协议
|
||||
- 群组协议在 A2A 协议之上提供群组管理、任务拆分合并、共识投票等能力
|
||||
- 群组消息同样使用统一消息信封(`UnifiedSynapseMessage`),通过 `group_id` 字段标识群组上下文
|
||||
@@ -1,545 +0,0 @@
|
||||
# A2N 协议(Agent-to-N Protocol)
|
||||
|
||||
> 版本:1.0.0
|
||||
> 状态:草案
|
||||
> 类型:实现规范
|
||||
> 基于:[灵隙原始协议(Synapse Raw Protocol) v1.0.0](./01-a2a-raw-protocol.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 概述
|
||||
|
||||
A2N(Agent-to-N)组群协议定义了多个 Agent 组成群组进行协同工作的完整规范,包括群组管理、任务拆分与合并、群体决策、角色与权限等。
|
||||
|
||||
### 1.1 适用场景
|
||||
- 复杂任务的拆分与并行执行(MapReduce 模式)
|
||||
- 多 Agent 协同完成单一目标
|
||||
- 群体投票与共识决策
|
||||
- 临时或永久的 Agent 工作组
|
||||
- 分级升级(escalation)机制
|
||||
|
||||
### 1.2 依赖
|
||||
- 依赖 A2A 协议的点对点通信能力
|
||||
- 群组内成员间的单播通信使用 A2A 协议
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心概念
|
||||
|
||||
### 2.1 Group(群组)
|
||||
由多个 Agent 组成的协作单元,具有明确的拓扑结构、角色分工与协作规则。
|
||||
|
||||
### 2.2 Topology(拓扑结构)
|
||||
|
||||
| 拓扑类型 | 说明 | 适用场景 |
|
||||
|---------|------|---------|
|
||||
| `star` | 星型,中心 coordinator 协调 | 简单任务分发 |
|
||||
| `mesh` | 网状,成员间可直接通信 | 对等协作 |
|
||||
| `chain` | 链式,流水线模式 | 顺序处理 |
|
||||
| `hierarchical` | 层级型,多层管理 | 大型复杂任务 |
|
||||
| `fullmesh` | 全连接网状 | 高可靠协作 |
|
||||
|
||||
### 2.3 Role(角色)
|
||||
|
||||
| 角色 | 说明 | 权限 |
|
||||
|------|------|------|
|
||||
| `coordinator` | 协调者 | 创建/解散群组、任务拆分、发起投票 |
|
||||
| `worker` | 工作者 | 执行子任务、提交结果 |
|
||||
| `reviewer` | 审核者 | 审核结果、发起投票 |
|
||||
| `observer` | 观察者 | 只读,查看进度 |
|
||||
|
||||
### 2.4 GroupStatus(群组状态)
|
||||
|
||||
| 状态 | 说明 |
|
||||
|------|------|
|
||||
| `creating` | 创建中 |
|
||||
| `active` | 活跃 |
|
||||
| `frozen` | 冻结(不可执行新任务) |
|
||||
| `disbanded` | 已解散(终态) |
|
||||
|
||||
---
|
||||
|
||||
## 3. 消息类型
|
||||
|
||||
### 3.1 消息类型清单
|
||||
|
||||
所有消息类型前缀:`group:` 或 `inter_group:`
|
||||
|
||||
#### 群组管理
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `group:create` | 创建群组 |
|
||||
| `group:disband` | 解散群组 |
|
||||
| `group:join` | 加入群组 |
|
||||
| `group:leave` | 离开群组 |
|
||||
| `group:invite` | 邀请加入 |
|
||||
| `group:accept_invite` | 接受邀请 |
|
||||
| `group:reject_invite` | 拒绝邀请 |
|
||||
| `group:member_update` | 成员更新通知 |
|
||||
| `group:role_change` | 角色变更 |
|
||||
|
||||
#### 群组通信
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `group:broadcast` | 广播(所有成员) |
|
||||
| `group:multicast` | 组播(指定成员) |
|
||||
| `group:direct` | 群内单播 |
|
||||
| `group:relay` | 消息中继 |
|
||||
|
||||
#### 任务协作
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `group:task_split` | 任务拆分 |
|
||||
| `group:task_assign` | 任务分配 |
|
||||
| `group:task_merge` | 结果合并 |
|
||||
| `group:task_progress` | 子任务进度 |
|
||||
|
||||
#### 共识与投票
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `group:vote` | 发起投票 |
|
||||
| `group:vote_submit` | 提交投票 |
|
||||
| `group:consensus` | 达成共识 |
|
||||
|
||||
#### 人在回路(HiL)
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `group:consensus_escalate` | 共识僵局升级给人类 |
|
||||
| `group:subtask_escalate` | 子任务阻塞升级给人类 |
|
||||
| `group:human_decision` | 人类决策结果 |
|
||||
| `inter_group:escalate` | 跨群组升级 |
|
||||
|
||||
**完整定义:** [types/types.go](../types/types.go)
|
||||
|
||||
---
|
||||
|
||||
## 4. 群组生命周期
|
||||
|
||||
### 4.1 状态机
|
||||
|
||||
```
|
||||
creating → active → frozen → disbanded
|
||||
↑ ↓
|
||||
└─────────┘
|
||||
解冻
|
||||
```
|
||||
|
||||
### 4.2 群组创建流程
|
||||
|
||||
```
|
||||
创建者 被邀请成员
|
||||
| |
|
||||
|------ group:invite -------------->| 发送邀请
|
||||
| |
|
||||
|<----- group:accept_invite --------| 接受邀请
|
||||
| |
|
||||
|------ group:member_update ------->| 通知成员更新
|
||||
| |
|
||||
[群组创建完成,状态变为 active] |
|
||||
```
|
||||
|
||||
### 4.3 群组解散流程
|
||||
|
||||
```
|
||||
coordinator 所有成员
|
||||
| |
|
||||
|------ group:disband ------------->| 通知解散
|
||||
| |
|
||||
[群组状态变为 disbanded] |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 任务协作流程
|
||||
|
||||
### 5.1 MapReduce 模式(拆分-执行-合并)
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ coordinator │
|
||||
└──────┬───────┘
|
||||
│
|
||||
group:task_split(拆分任务)
|
||||
│
|
||||
┌────────────────┼────────────────┐
|
||||
▼ ▼ ▼
|
||||
┌─────────┐ ┌─────────┐ ┌─────────┐
|
||||
│ worker1 │ │ worker2 │ │ worker3 │
|
||||
└────┬────┘ └────┬────┘ └────┬────┘
|
||||
│ │ │
|
||||
│ 执行子任务 │ 执行子任务 │ 执行子任务
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
子任务结果 子任务结果 子任务结果
|
||||
└────────────────┼────────────────┘
|
||||
│
|
||||
group:task_merge(合并结果)
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 最终结果 │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
### 5.2 详细流程
|
||||
|
||||
1. **任务拆分阶段**
|
||||
- coordinator 接收原始任务
|
||||
- 将任务拆分为多个子任务(subtask)
|
||||
- 为每个子任务指定执行的 worker
|
||||
- 定义合并策略(merge strategy)
|
||||
|
||||
2. **并行执行阶段**
|
||||
- 各 worker 并行执行子任务
|
||||
- 定期上报进度(`group:task_progress`)
|
||||
- 子任务完成后上报结果
|
||||
|
||||
3. **结果合并阶段**
|
||||
- 所有子任务完成后,coordinator 合并结果
|
||||
- 根据合并策略决定最终结果
|
||||
- 输出最终结果
|
||||
|
||||
---
|
||||
|
||||
## 6. 合并策略(Merge Strategy)
|
||||
|
||||
| 策略 | 说明 |
|
||||
|------|------|
|
||||
| `all_success` | 全部成功才认为成功,否则失败 |
|
||||
| `majority` | 多数子任务成功即可,取多数结果 |
|
||||
| `any_complete` | 任意一个完成即返回结果 |
|
||||
| `priority` | 按优先级,高优先级子任务结果优先 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 投票与共识
|
||||
|
||||
### 7.1 投票流程
|
||||
|
||||
```
|
||||
发起者 投票者
|
||||
| |
|
||||
|-------- group:vote -------------->| 发起投票
|
||||
| |
|
||||
|<------- group:vote_submit --------| 提交投票
|
||||
| |
|
||||
[计票,判断是否达到阈值] |
|
||||
| |
|
||||
|-------- group:consensus --------->| 公布结果
|
||||
| |
|
||||
```
|
||||
|
||||
### 7.2 投票规则
|
||||
|
||||
- `threshold`:通过阈值(0-1),如 0.5 表示过半
|
||||
- `deadline`:投票截止时间
|
||||
- `voters`:有投票权的成员列表
|
||||
- 超时未投票视为弃权
|
||||
|
||||
### 7.3 共识僵局(Consensus Deadlock)
|
||||
|
||||
当投票无法达成共识(如平票、未达阈值且超时)时,可升级给人类决策。
|
||||
|
||||
---
|
||||
|
||||
## 8. 人在回路(HiL)
|
||||
|
||||
群组协作中有两种升级场景:
|
||||
|
||||
### 8.1 共识僵局升级
|
||||
投票无法达成共识时,升级给人类裁决。
|
||||
|
||||
**消息:** `group:consensus_escalate`
|
||||
|
||||
### 8.2 子任务阻塞升级
|
||||
某个子任务遇到无法自动解决的问题时,升级给人类。
|
||||
|
||||
**消息:** `group:subtask_escalate`
|
||||
|
||||
### 8.3 跨群组升级
|
||||
问题超出当前群组能力时,可升级到上级群组或人类。
|
||||
|
||||
**消息:** `inter_group:escalate`
|
||||
|
||||
---
|
||||
|
||||
## 9. 接口规范
|
||||
|
||||
### 9.1 GroupProtocol 接口
|
||||
|
||||
```go
|
||||
type GroupProtocol interface {
|
||||
// 处理群组消息
|
||||
HandleMessage(ctx context.Context, msg *UnifiedSynapseMessage) (*UnifiedSynapseMessage, error)
|
||||
|
||||
// 设置任务处理器
|
||||
SetTaskHandler(h GroupTaskHandler)
|
||||
|
||||
// 查询群组列表
|
||||
GetGroups() []*AgentGroup
|
||||
|
||||
// 查询特定群组
|
||||
GetGroup(groupID string) (*AgentGroup, bool)
|
||||
}
|
||||
```
|
||||
|
||||
**参考实现:** [group/protocol.go](../group/protocol.go)
|
||||
|
||||
### 9.2 GroupTaskHandler 接口
|
||||
|
||||
```go
|
||||
type GroupTaskHandler interface {
|
||||
// 执行子任务
|
||||
ExecuteSubtask(ctx context.Context, subtaskID string, payload map[string]interface{}) (any, error)
|
||||
|
||||
// 合并多个子任务结果
|
||||
MergeResults(results []SubtaskResult, strategy MergeStrategy) (any, error)
|
||||
|
||||
// 验证任务是否可拆分,返回拆分方案
|
||||
ValidateTaskSplit(taskType string, requirements map[string]interface{}) ([]Subtask, bool)
|
||||
}
|
||||
```
|
||||
|
||||
**说明:**
|
||||
- `ExecuteSubtask`:执行单个子任务的业务逻辑
|
||||
- `MergeResults`:根据策略合并多个子任务的结果
|
||||
- `ValidateTaskSplit`:验证任务是否可拆分,并给出拆分方案
|
||||
|
||||
### 9.3 配置项
|
||||
|
||||
```go
|
||||
type GroupConfig struct {
|
||||
MaxGroupSize int // 最大群组人数(默认 20)
|
||||
MaxSubtasks int // 最大子任务数(默认 50)
|
||||
ConsensusTimeout time.Duration // 共识超时(默认 5 分钟)
|
||||
TaskTimeout time.Duration // 任务超时(默认 10 分钟)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 载荷格式
|
||||
|
||||
### 10.1 GroupCreatePayload(创建群组)
|
||||
|
||||
```json
|
||||
{
|
||||
"group": {
|
||||
"name": "视频制作组",
|
||||
"topology": "star",
|
||||
"config": {
|
||||
"allow_self_leave": true,
|
||||
"requires_approval": false,
|
||||
"consensus_required": false,
|
||||
"consensus_threshold": 0.5,
|
||||
"broadcast_mode": "all"
|
||||
},
|
||||
"initial_members": [
|
||||
{
|
||||
"agent_id": "worker-1",
|
||||
"system_id": "sys-a",
|
||||
"endpoint": "http://worker1/synapse"
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
{
|
||||
"id": "coordinator",
|
||||
"name": "协调者",
|
||||
"permissions": ["create", "disband", "split", "vote"],
|
||||
"max_tasks": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 GroupTaskSplitPayload(任务拆分)
|
||||
|
||||
```json
|
||||
{
|
||||
"original_task": {
|
||||
"id": "task-xxx",
|
||||
"type": "video_generation",
|
||||
"requirements": {
|
||||
"script": "xxx",
|
||||
"duration": 60
|
||||
},
|
||||
"deadline": "2026-01-01T00:10:00Z"
|
||||
},
|
||||
"subtasks": [
|
||||
{
|
||||
"id": "sub-1",
|
||||
"description": "生成脚本",
|
||||
"assigned_to": "writer-agent",
|
||||
"dependencies": [],
|
||||
"estimated_duration": 120,
|
||||
"priority": 1
|
||||
},
|
||||
{
|
||||
"id": "sub-2",
|
||||
"description": "生成配音",
|
||||
"assigned_to": "voice-agent",
|
||||
"dependencies": ["sub-1"],
|
||||
"estimated_duration": 180,
|
||||
"priority": 2
|
||||
}
|
||||
],
|
||||
"merge_strategy": "all_success"
|
||||
}
|
||||
```
|
||||
|
||||
### 10.3 GroupTaskMergePayload(结果合并)
|
||||
|
||||
```json
|
||||
{
|
||||
"original_task_id": "task-xxx",
|
||||
"subtask_results": [
|
||||
{
|
||||
"subtask_id": "sub-1",
|
||||
"assigned_to": "writer-agent",
|
||||
"status": "success",
|
||||
"result": { "script": "..." },
|
||||
"completed_at": "2026-01-01T00:02:00Z"
|
||||
}
|
||||
],
|
||||
"merge_strategy_used": "all_success",
|
||||
"merged_result": {
|
||||
"video_url": "http://..."
|
||||
},
|
||||
"consensus_achieved": true
|
||||
}
|
||||
```
|
||||
|
||||
### 10.4 GroupVotePayload(投票)
|
||||
|
||||
```json
|
||||
{
|
||||
"vote_id": "vote-xxx",
|
||||
"question": "是否采用方案A?",
|
||||
"options": ["方案A", "方案B", "方案C"],
|
||||
"voters": ["agent-1", "agent-2", "agent-3"],
|
||||
"deadline": "2026-01-01T00:05:00Z",
|
||||
"threshold": 0.66,
|
||||
"status": "open",
|
||||
"votes": []
|
||||
}
|
||||
```
|
||||
|
||||
### 10.5 GroupConsensusEscalatePayload(共识升级)
|
||||
|
||||
```json
|
||||
{
|
||||
"vote_id": "vote-xxx",
|
||||
"escalation_id": "esc-xxx",
|
||||
"question": "是否采用方案A?",
|
||||
"votes": [
|
||||
{
|
||||
"voter_id": "agent-1",
|
||||
"voter_role": "reviewer",
|
||||
"option": "方案A",
|
||||
"reason": "质量更好"
|
||||
}
|
||||
],
|
||||
"consensus_status": {
|
||||
"required_threshold": 0.66,
|
||||
"actual_threshold": 0.33,
|
||||
"leading_option": "方案A",
|
||||
"options_tied": false
|
||||
},
|
||||
"escalate_reason": {
|
||||
"code": "no_consensus",
|
||||
"message": "无法达成共识"
|
||||
},
|
||||
"escalation_level": 2,
|
||||
"deadline": "2026-01-01T00:10:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**完整载荷定义:** [types/types.go](../types/types.go)
|
||||
|
||||
---
|
||||
|
||||
## 11. 数据结构
|
||||
|
||||
### 11.1 AgentGroup(群组)
|
||||
|
||||
```go
|
||||
type AgentGroup struct {
|
||||
ID string
|
||||
Name string
|
||||
Topology GroupTopology
|
||||
CreatedBy string
|
||||
CreatedAt Timestamp
|
||||
Config GroupConfig
|
||||
Members []GroupMember
|
||||
Roles []GroupRole
|
||||
Status GroupStatus
|
||||
}
|
||||
```
|
||||
|
||||
### 11.2 GroupMember(群组成员)
|
||||
|
||||
```go
|
||||
type GroupMember struct {
|
||||
AgentID string
|
||||
AgentName string
|
||||
Role string
|
||||
JoinedAt Timestamp
|
||||
Status GroupMemberStatus
|
||||
Capabilities []string
|
||||
LoadFactor float64 // 负载因子 0-1,用于任务分配
|
||||
}
|
||||
```
|
||||
|
||||
### 11.3 Subtask(子任务)
|
||||
|
||||
```go
|
||||
type Subtask struct {
|
||||
ID string
|
||||
Description string
|
||||
AssignedTo string
|
||||
Dependencies []string
|
||||
EstimatedDuration int
|
||||
Priority int
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 错误码
|
||||
|
||||
群组协议特有错误码:
|
||||
|
||||
| 错误码 | 说明 |
|
||||
|--------|------|
|
||||
| `group_not_found` | 群组不存在 |
|
||||
| `group_not_active` | 群组不活跃 |
|
||||
| `group_full` | 群组已满 |
|
||||
| `already_member` | 已是成员 |
|
||||
| `not_group_member` | 不是群组成员 |
|
||||
| `member_not_found` | 成员不存在 |
|
||||
| `permission_denied` | 权限不足 |
|
||||
| `invalid_params` | 参数无效 |
|
||||
|
||||
---
|
||||
|
||||
## 13. 参考实现
|
||||
|
||||
本协议的参考实现位于 `group/` 目录:
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| [group/protocol.go](../group/protocol.go) | 群组协议核心实现 |
|
||||
| [types/types.go](../types/types.go) | 群组相关数据类型 |
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:与 A2A 协议的关系
|
||||
|
||||
- 群组内的点对点通信使用 A2A 协议
|
||||
- 群组协议在 A2A 协议之上提供群组管理、任务拆分合并、共识投票等能力
|
||||
- 群组消息同样使用统一消息信封(`UnifiedSynapseMessage`),通过 `group_id` 字段标识群组上下文
|
||||
@@ -1,503 +0,0 @@
|
||||
# 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 | 简化验证 |
|
||||
@@ -1,503 +0,0 @@
|
||||
# P2A 协议(Person/Program-to-Agent Protocol)
|
||||
|
||||
> 版本:1.0.0
|
||||
> 状态:草案
|
||||
> 类型:实现规范
|
||||
> 基于:[灵隙原始协议(Synapse Raw Protocol) v1.0.0](./01-synapse-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 | 简化验证 |
|
||||
@@ -1,519 +0,0 @@
|
||||
# SDK 实现说明
|
||||
|
||||
> 版本:1.0.0
|
||||
> 更新日期:2026-06-27
|
||||
> 协议版本:1.0.0
|
||||
> 关联协议:
|
||||
> - [A2A 协议](./02-a2a-protocol.md)
|
||||
> - [A2N 协议](./03-a2n-protocol.md)
|
||||
> - [P2A 协议](./04-p2a-protocol.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 概述
|
||||
|
||||
SDK 按照灵隙协议族(Synapse Protocol Suite)的三个子协议进行了模块化设计,分为三个子包,命名与协议名保持一致:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ SDK (sdk/) │
|
||||
├──────────────┬───────────────────┬───────────────────────────┤
|
||||
│ sdk/p2a │ sdk/a2a │ sdk/a2n │
|
||||
│ P2A 协议 │ A2A 协议 │ A2N 协议 │
|
||||
│ (外部调用) │ (Agent间通信) │ (群组协作) │
|
||||
│ ✅ 完整实现 │ ✅ 完整实现 │ ✅ 完整实现 │
|
||||
└──────────────┴───────────────────┴───────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. SDK 目录结构
|
||||
|
||||
```
|
||||
sdk/
|
||||
├── doc.go # SDK 包文档(使用说明)
|
||||
├── p2a/
|
||||
│ └── client.go # P2A 协议 - 外部调用客户端
|
||||
├── a2a/
|
||||
│ └── client.go # A2A 协议 - Agent间通信客户端
|
||||
└── a2n/
|
||||
└── client.go # A2N 协议 - 群组协作客户端
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 第一部分:sdk/p2a - P2A 协议
|
||||
|
||||
**协议对应**:[P2A 协议](./04-p2a-protocol.md)
|
||||
**版本**:1.0.0
|
||||
**用途**:非 Agent 调用方(前端、后端服务、第三方应用)通过 REST API 调用 Agent 能力
|
||||
|
||||
### 3.1 实现对应关系
|
||||
|
||||
| P2A 协议功能 | SDK 方法 | 位置 |
|
||||
|-------------|---------|------|
|
||||
| 认证方式 | `setHeaders()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 提交任务(JSON) | `SubmitTask()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 提交任务(Multipart) | `SubmitTaskWithFiles()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 获取任务详情 | `GetTask()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 获取任务状态(简化版) | `GetTaskStatus()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 获取任务列表 | `ListTasks()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 取消任务 | `CancelTask()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 能力查询 | `GetCapabilities()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 回调验证 | `VerifyCallback()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 轮询等待 | `WaitForTask()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
|
||||
### 3.2 核心数据结构
|
||||
|
||||
```go
|
||||
// Client 客户端
|
||||
type Client struct {
|
||||
baseURL string
|
||||
apiKey string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// 配置
|
||||
type ClientConfig struct {
|
||||
BaseURL string
|
||||
APIKey string
|
||||
Timeout time.Duration
|
||||
MaxRetries int
|
||||
}
|
||||
|
||||
// 任务请求
|
||||
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
|
||||
Result map[string]any
|
||||
Error *TaskError
|
||||
}
|
||||
|
||||
// 文件上传参数
|
||||
type FileParam struct {
|
||||
FieldName string
|
||||
Filename string
|
||||
Data []byte
|
||||
MimeType string
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 使用示例
|
||||
|
||||
```go
|
||||
import "dp-headless/synapse/sdk/p2a"
|
||||
|
||||
client := p2a.NewClient(p2a.ClientConfig{
|
||||
BaseURL: "https://agent-api.example.com",
|
||||
APIKey: "your-api-key",
|
||||
})
|
||||
|
||||
// 提交任务并等待完成
|
||||
taskInfo, err := client.CreateAndWait(ctx, "text_generation", map[string]any{
|
||||
"prompt": "写一首诗",
|
||||
"style": "古典",
|
||||
}, 5*time.Second)
|
||||
|
||||
// 使用回调模式
|
||||
resp, err := client.CreateWithCallback(ctx, "image_generation", payload,
|
||||
"https://your-domain.com/callback",
|
||||
"callback-token",
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 第二部分:sdk/a2a - A2A 协议
|
||||
|
||||
**协议对应**:[A2A 协议](./02-a2a-protocol.md)
|
||||
**版本**:1.0.0
|
||||
**用途**:Agent 之间的点对点通信,包含消息发送、签名验证、回调处理
|
||||
|
||||
### 4.1 实现对应关系
|
||||
|
||||
| A2A 协议功能 | SDK 方法 | 位置 |
|
||||
|------------|---------|------|
|
||||
| 发送任务请求 | `SendTaskRequest()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
| 发送任务取消 | `SendTaskCancel()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
| 发送人类决策 | `SendHumanDecision()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
| 查询能力 | `SendCapabilityQuery()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
| 消息签名 | `signMessage()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
| 签名验证 | `verifySignature()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
| 任务状态管理 | `GetTaskState()`, `ListTasks()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
| 等待任务完成 | `WaitForTask()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
| 回调服务 | `startCallbackServer()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
|
||||
### 4.2 核心数据结构
|
||||
|
||||
```go
|
||||
// Client A2A客户端
|
||||
type Client struct {
|
||||
agentEndpoint types.AgentEndpoint
|
||||
authSecret string
|
||||
callbackHandler MessageCallbackHandler
|
||||
tasks map[string]*A2ATaskState
|
||||
}
|
||||
|
||||
// 配置
|
||||
type ClientConfig struct {
|
||||
AgentEndpoint types.AgentEndpoint
|
||||
AuthSecret string
|
||||
CallbackPort int
|
||||
Timeout time.Duration
|
||||
MaxRetries int
|
||||
}
|
||||
|
||||
// 消息回调处理器接口
|
||||
type MessageCallbackHandler interface {
|
||||
HandleTaskRequest(ctx context.Context, msg *types.UnifiedSynapseMessage) (*types.UnifiedSynapseMessage, error)
|
||||
HandleTaskStatusUpdate(ctx context.Context, msg *types.UnifiedSynapseMessage) error
|
||||
HandleTaskComplete(ctx context.Context, msg *types.UnifiedSynapseMessage) error
|
||||
HandleTaskFailed(ctx context.Context, msg *types.UnifiedSynapseMessage) error
|
||||
HandleNeedHuman(ctx context.Context, msg *types.UnifiedSynapseMessage) error
|
||||
}
|
||||
|
||||
// 任务状态
|
||||
type A2ATaskState struct {
|
||||
TaskID string
|
||||
Status types.P2PTaskStatus
|
||||
Progress int
|
||||
Message string
|
||||
Result any
|
||||
Error *types.SynapseError
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 使用示例
|
||||
|
||||
```go
|
||||
import "dp-headless/synapse/sdk/a2a"
|
||||
|
||||
a2aClient, err := a2a.NewClient(&a2a.ClientConfig{
|
||||
AgentEndpoint: types.AgentEndpoint{
|
||||
AgentID: "my-agent",
|
||||
AgentName: "我的Agent",
|
||||
SystemID: "my-system",
|
||||
Endpoint: "http://localhost:8080/synapse/callback",
|
||||
},
|
||||
AuthSecret: "my-hmac-secret",
|
||||
CallbackPort: 8080,
|
||||
})
|
||||
|
||||
// 设置消息处理器
|
||||
a2aClient.SetMessageHandler(&myHandler{})
|
||||
|
||||
// 发送任务请求给其他Agent
|
||||
taskID, err := a2aClient.SendTaskRequest(ctx, targetAgent, "video_generation", map[string]any{
|
||||
"prompt": "生成一段视频",
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 第三部分:sdk/a2n - A2N 协议
|
||||
|
||||
**协议对应**:[A2N 协议](./03-a2n-protocol.md)
|
||||
**版本**:1.0.0
|
||||
**用途**:多 Agent 群组协作,包含群组管理、任务拆分合并、投票、升级
|
||||
|
||||
### 5.1 实现对应关系
|
||||
|
||||
| A2N 协议功能 | SDK 方法 | 位置 |
|
||||
|------------|---------|------|
|
||||
| 创建群组 | `CreateGroup()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 加入群组 | `JoinGroup()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 离开群组 | `LeaveGroup()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 解散群组 | `DisbandGroup()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 提交群组任务 | `SubmitGroupTask()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 发送子任务结果 | `SendSubtaskResult()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 发起投票 | `InitiateVote()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 提交投票 | `SubmitVote()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 广播消息 | `Broadcast()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 升级到人类 | `EscalateToHuman()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
|
||||
### 5.2 核心数据结构
|
||||
|
||||
```go
|
||||
// Client A2N客户端
|
||||
type Client struct {
|
||||
a2aClient *a2a.Client
|
||||
groups map[string]*GroupState
|
||||
eventHandler GroupEventHandler
|
||||
}
|
||||
|
||||
// 配置
|
||||
type ClientConfig struct {
|
||||
A2AClient *a2a.Client
|
||||
EventHandler GroupEventHandler
|
||||
}
|
||||
|
||||
// 群组状态
|
||||
type GroupState struct {
|
||||
GroupID string
|
||||
Name string
|
||||
Topology types.GroupTopology
|
||||
Status types.GroupStatus
|
||||
Members []types.GroupMember
|
||||
Role string // "coordinator" | "worker"
|
||||
}
|
||||
|
||||
// 群组事件处理器接口
|
||||
type GroupEventHandler interface {
|
||||
OnGroupCreated(ctx context.Context, groupID string) error
|
||||
OnGroupDisbanded(ctx context.Context, groupID string) error
|
||||
OnMemberJoined(ctx context.Context, groupID string, member types.GroupMember) error
|
||||
OnMemberLeft(ctx context.Context, groupID string, agentID string) error
|
||||
OnTaskSplit(ctx context.Context, groupID string, originalTaskID string, subtasks []types.Subtask) error
|
||||
OnSubtaskProgress(ctx context.Context, groupID string, subtaskID string, progress int) error
|
||||
OnTaskMerged(ctx context.Context, groupID string, originalTaskID string, result any) error
|
||||
OnVoteInitiated(ctx context.Context, groupID string, vote types.GroupVotePayload) error
|
||||
OnVoteResult(ctx context.Context, groupID string, voteID string, option string, passed bool) error
|
||||
OnEscalation(ctx context.Context, groupID string, escalationID string, level int) error
|
||||
OnBroadcast(ctx context.Context, groupID string, sender types.AgentEndpoint, message []byte) error
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 使用示例
|
||||
|
||||
```go
|
||||
import (
|
||||
"dp-headless/synapse/sdk/a2n"
|
||||
"dp-headless/synapse/sdk/a2a"
|
||||
)
|
||||
|
||||
// 创建A2A客户端
|
||||
a2aClient, _ := a2a.NewClient(&a2a.ClientConfig{
|
||||
AgentEndpoint: myEndpoint,
|
||||
})
|
||||
|
||||
// 创建A2N客户端
|
||||
a2nClient, err := a2n.NewClient(&a2n.ClientConfig{
|
||||
A2AClient: a2aClient,
|
||||
EventHandler: &myGroupHandler{},
|
||||
})
|
||||
|
||||
// 创建群组
|
||||
group, err := a2nClient.CreateGroup(ctx, "视频制作组", types.GroupTopologyStar, nil, members)
|
||||
|
||||
// 作为Coordinator,提交任务
|
||||
subtasks, err := a2nClient.SubmitGroupTask(ctx, group.GroupID, "video_generation",
|
||||
map[string]any{"script": "..."},
|
||||
types.MergeAllSuccess,
|
||||
)
|
||||
|
||||
// 发起投票
|
||||
voteID, err := a2nClient.InitiateVote(ctx, group.GroupID,
|
||||
"选择哪个方案?",
|
||||
[]string{"方案A", "方案B"},
|
||||
voterIDs,
|
||||
0.5,
|
||||
5*time.Minute,
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. SDK 架构总览
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ SDK (dp-headless/synapse/sdk) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ doc.go │
|
||||
│ ├── 包文档和使用说明 │
|
||||
│ └── 指向各子包的链接 │
|
||||
├────────────────┬────────────────┬─────────────────────────┤
|
||||
│ sdk/p2a │ sdk/a2a │ sdk/a2n │
|
||||
│ │ │ │
|
||||
│ P2A协议 │ 灵隙协议 │ A2N协议 │
|
||||
│ │ │ │
|
||||
│ ├── Client │ ├── Client │ ├── Client │
|
||||
│ ├── 配置 │ ├── 配置 │ ├── 配置 │
|
||||
│ ├── 任务操作 │ ├── 消息发送 │ ├── 群组管理 │
|
||||
│ ├── 能力查询 │ ├── 任务状态 │ ├── 任务协作 │
|
||||
│ └── 回调验证 │ ├── 回调服务 │ ├── 投票 │
|
||||
│ │ └── 签名 │ └── 升级 │
|
||||
└────────────────┴────────────────┴─────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 快速开始
|
||||
|
||||
### 7.1 P2A 调用(外部调用者)
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"dp-headless/synapse/sdk/p2a"
|
||||
)
|
||||
|
||||
func main() {
|
||||
client := p2a.NewClient(p2a.ClientConfig{
|
||||
BaseURL: "https://agent.example.com",
|
||||
APIKey: "your-api-key",
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// 方式1: 轮询等待
|
||||
result, err := client.CreateAndWait(ctx, "text_generation", map[string]any{
|
||||
"prompt": "写一首诗",
|
||||
}, 5*time.Second)
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
}
|
||||
fmt.Printf("Result: %v\n", result.Result)
|
||||
|
||||
// 方式2: 回调模式
|
||||
resp, err := client.CreateWithCallback(ctx, "image_generation",
|
||||
map[string]any{"prompt": "一只猫"},
|
||||
"https://your-domain.com/callback",
|
||||
"token",
|
||||
)
|
||||
fmt.Printf("Task ID: %s\n", resp.TaskID)
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 灵隙 通信(Agent 间)
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"dp-headless/synapse/sdk/a2a"
|
||||
"dp-headless/synapse/types"
|
||||
)
|
||||
|
||||
func main() {
|
||||
client, _ := a2a.NewClient(&a2a.ClientConfig{
|
||||
AgentEndpoint: types.AgentEndpoint{
|
||||
AgentID: "agent-001",
|
||||
AgentName: "Agent 001",
|
||||
SystemID: "my-system",
|
||||
Endpoint: "http://localhost:8080/synapse/callback",
|
||||
},
|
||||
AuthSecret: "secret-key",
|
||||
CallbackPort: 8080,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
target := types.AgentEndpoint{
|
||||
AgentID: "agent-002",
|
||||
Endpoint: "http://agent-002:8080/synapse/callback",
|
||||
}
|
||||
|
||||
// 发送任务请求
|
||||
taskID, err := client.SendTaskRequest(ctx, target, "data_analysis", map[string]any{
|
||||
"dataset": "sales.csv",
|
||||
})
|
||||
fmt.Printf("Task ID: %s\n", taskID)
|
||||
|
||||
// 等待完成
|
||||
state, err := client.WaitForTask(ctx, taskID, 5*time.Minute)
|
||||
fmt.Printf("Status: %s\n", state.Status)
|
||||
}
|
||||
```
|
||||
|
||||
### 7.3 A2N 群组协作
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"dp-headless/synapse/sdk/a2n"
|
||||
"dp-headless/synapse/sdk/a2a"
|
||||
"dp-headless/synapse/types"
|
||||
)
|
||||
|
||||
func main() {
|
||||
a2aClient, _ := a2a.NewClient(&a2a.ClientConfig{
|
||||
AgentEndpoint: myEndpoint,
|
||||
})
|
||||
|
||||
a2nClient, _ := a2n.NewClient(&a2n.ClientConfig{
|
||||
A2AClient: a2aClient,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// 创建群组
|
||||
group, _ := a2nClient.CreateGroup(ctx, "协作组", types.GroupTopologyStar, nil, []types.AgentEndpoint{
|
||||
{AgentID: "member-1", Endpoint: "http://member-1:8080"},
|
||||
{AgentID: "member-2", Endpoint: "http://member-2:8080"},
|
||||
})
|
||||
|
||||
fmt.Printf("Group ID: %s\n", group.GroupID)
|
||||
|
||||
// 提交任务
|
||||
subtasks, _ := a2nClient.SubmitGroupTask(ctx, group.GroupID, "video_generation",
|
||||
map[string]any{"script": "..."},
|
||||
types.MergeAllSuccess,
|
||||
)
|
||||
|
||||
fmt.Printf("Split into %d subtasks\n", len(subtasks))
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 协议与包名对应关系
|
||||
|
||||
| 协议名称 | 协议文档 | SDK 包名 | 包路径 |
|
||||
|---------|---------|---------|--------|
|
||||
| 灵隙原始协议(Synapse Raw Protocol) | [01-synapse-raw-protocol.md](./01-synapse-raw-protocol.md) | - | 纲领性文档 |
|
||||
| A2A 协议 | [02-a2a-protocol.md](./02-a2a-protocol.md) | `a2a` | `sdk/a2a/` |
|
||||
| A2N 协议 | [03-a2n-protocol.md](./03-a2n-protocol.md) | `a2n` | `sdk/a2n/` |
|
||||
| P2A 协议 | [04-p2a-protocol.md](./04-p2a-protocol.md) | `p2a` | `sdk/p2a/` |
|
||||
|
||||
**所有协议版本:1.0.0**
|
||||
|
||||
---
|
||||
|
||||
## 9. 扩展建议
|
||||
|
||||
| 优先级 | 扩展项 | 说明 |
|
||||
|-------|-------|------|
|
||||
| 高 | gRPC 传输 | 添加 gRPC 协议支持 |
|
||||
| 高 | WebSocket | 添加 WebSocket 流式传输 |
|
||||
| 中 | 批量操作 | 批量任务提交、批量查询 |
|
||||
| 中 | 指标监控 | 内置指标收集 |
|
||||
| 低 | 多语言 SDK | Python、JavaScript 等 |
|
||||
|
||||
---
|
||||
|
||||
## 附录:相关文件索引
|
||||
|
||||
| 文件路径 | 说明 |
|
||||
|---------|------|
|
||||
| [sdk/doc.go](../sdk/doc.go) | SDK 包文档 |
|
||||
| [sdk/p2a/client.go](../sdk/p2a/client.go) | P2A 客户端实现 |
|
||||
| [sdk/a2a/client.go](../sdk/a2a/client.go) | A2A 客户端实现 |
|
||||
| [sdk/a2n/client.go](../sdk/a2n/client.go) | A2N 客户端实现 |
|
||||
| [externalapi/handler.go](../externalapi/handler.go) | P2A 协议服务端实现 |
|
||||
| [p2p/protocol.go](../p2p/protocol.go) | A2A 协议核心实现 |
|
||||
| [group/protocol.go](../group/protocol.go) | A2N 协议核心实现 |
|
||||
| [types/types.go](../types/types.go) | 公共类型定义 |
|
||||
| [transport/transport.go](../transport/transport.go) | HTTP 传输层实现 |
|
||||
@@ -1,777 +0,0 @@
|
||||
# 灵隙协议族(Synapse Protocol Suite) - 解决方案白皮书
|
||||
|
||||
> 版本:1.0.0
|
||||
> 日期:2026-06-27
|
||||
> 状态:草案
|
||||
> 前置阅读:[灵隙原始协议(Synapse Raw Protocol)](./01-a2a-raw-protocol.md)
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
1. [概述](#1-概述)
|
||||
2. [方案一:Agent 发现与广播机制](#2-方案一agent-发现与广播机制)
|
||||
3. [方案二:身份认证与信任体系](#3-方案二身份认证与信任体系)
|
||||
4. [方案三:协议缺失内容补全](#4-方案三协议缺失内容补全)
|
||||
5. [分阶段实施路线图](#5-分阶段实施路线图)
|
||||
6. [技术选型建议](#6-技术选型建议)
|
||||
|
||||
---
|
||||
|
||||
## 1. 概述
|
||||
|
||||
### 1.1 背景
|
||||
|
||||
当前灵隙协议族(Synapse Protocol Suite)定义了 Agent 之间"怎么说话"(消息格式、任务流程),但缺少:
|
||||
|
||||
- **怎么找到对方** — Agent 发现与广播机制
|
||||
- **怎么相信对方** — 身份认证与信任体系
|
||||
- **怎么规模化协作** — 路由、调度、激励等基础设施
|
||||
|
||||
本文档针对上述问题给出完整的解决方案。
|
||||
|
||||
### 1.2 设计原则
|
||||
|
||||
| 原则 | 说明 |
|
||||
|------|------|
|
||||
| **渐进式** | 从简单到复杂,每阶段都可用 |
|
||||
| **可插拔** | 组件可替换,不绑定具体实现 |
|
||||
| **传输无关** | 不依赖特定底层网络 |
|
||||
| **安全优先** | 身份认证是基础设施,不是可选功能 |
|
||||
| **向后兼容** | 不破坏现有协议,增量扩展 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 方案一:Agent 发现与广播机制
|
||||
|
||||
### 2.1 整体架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Agent 发现与广播层 │
|
||||
├─────────────────────┬─────────────────────┬─────────────────────────┤
|
||||
│ 集中式目录服务 │ 分布式 Gossip │ 组播/广播 │
|
||||
│ (Directory) │ (Gossip) │ (Multicast) │
|
||||
│ ┌───────────────┐ │ ┌───────────────┐ │ ┌────────────────┐ │
|
||||
│ │ 注册/注销 │ │ │ 消息泛洪 │ │ │ 子网内广播 │ │
|
||||
│ │ 能力索引 │ │ │ 状态同步 │ │ │ 邻居发现 │ │
|
||||
│ │ 名字解析 │ │ │ 节点发现 │ │ │ 本地心跳 │ │
|
||||
│ └───────────────┘ │ └───────────────┘ │ └────────────────┘ │
|
||||
└─────────────────────┴─────────────────────┴─────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 模式一:集中式目录服务(推荐首选)
|
||||
|
||||
#### 2.2.1 架构说明
|
||||
|
||||
引入 **Agent Directory Service(ADS)** 作为中心化的 Agent 目录服务,类似 DNS。
|
||||
|
||||
```
|
||||
Agent A ──注册──▶ ┌─────────────┐ ◀──注册── Agent B
|
||||
│ Directory │
|
||||
Agent C ──查询──▶ │ Service │ ◀──心跳── Agent D
|
||||
└─────────────┘
|
||||
│
|
||||
▼
|
||||
能力索引
|
||||
在线状态
|
||||
端点地址
|
||||
```
|
||||
|
||||
#### 2.2.2 核心接口
|
||||
|
||||
**注册协议(Agent → Directory):**
|
||||
|
||||
| 操作 | 消息类型 | 说明 |
|
||||
|------|---------|------|
|
||||
| 注册 | `dir:register` | Agent 上线时注册自身信息 |
|
||||
| 注销 | `dir:unregister` | Agent 下线时注销 |
|
||||
| 心跳 | `dir:heartbeat` | 定期上报存活状态 |
|
||||
| 更新能力 | `dir:update_capabilities` | 能力变化时更新 |
|
||||
|
||||
**查询协议(Agent → Directory):**
|
||||
|
||||
| 操作 | 消息类型 | 说明 |
|
||||
|------|---------|------|
|
||||
| 按 ID 查询 | `dir:query_by_id` | 根据 AgentID 查端点 |
|
||||
| 按能力查询 | `dir:query_by_capability` | 找具备某能力的 Agent |
|
||||
| 列表查询 | `dir:list` | 分页列出所有 Agent |
|
||||
| 批量查询 | `dir:batch_query` | 批量查询多个 Agent |
|
||||
|
||||
#### 2.2.3 注册数据结构
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_id": "agent-001",
|
||||
"agent_name": "视频生成Agent",
|
||||
"system_id": "video-system",
|
||||
"endpoint": "https://agent-001.example.com/synapse/callback",
|
||||
"version": "1.0.0",
|
||||
"capabilities": [
|
||||
{
|
||||
"type": "video_generation",
|
||||
"name": "视频生成",
|
||||
"description": "根据脚本生成视频",
|
||||
"max_concurrent": 10,
|
||||
"avg_latency_ms": 5000
|
||||
}
|
||||
],
|
||||
"transports": ["http", "grpc"],
|
||||
"status": "online",
|
||||
"last_heartbeat": "2026-06-27T10:00:00Z",
|
||||
"trust_score": 85,
|
||||
"public_key": "-----BEGIN PUBLIC KEY-----\n..."
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2.4 优缺点
|
||||
|
||||
| 优点 | 缺点 |
|
||||
|------|------|
|
||||
| 查询效率高(O(1)) | 单点故障风险 |
|
||||
| 支持复杂查询(按能力、按系统等) | 中心节点需信任 |
|
||||
| 易于管理和监控 | 扩展性有上限 |
|
||||
| 实现简单 | 存在注册/注销时序问题 |
|
||||
|
||||
---
|
||||
|
||||
### 2.3 模式二:分布式 Gossip 协议(可选扩展)
|
||||
|
||||
#### 2.3.1 架构说明
|
||||
|
||||
无中心节点,Agent 之间通过 Gossip 协议(epidemic protocol)扩散状态信息,类似比特币网络的节点发现。
|
||||
|
||||
```
|
||||
Agent A ──gossip──▶ Agent B ──gossip──▶ Agent D
|
||||
│ │
|
||||
└──gossip──▶ Agent C ──gossip──▶ Agent E
|
||||
```
|
||||
|
||||
#### 2.3.2 核心机制
|
||||
|
||||
| 机制 | 说明 |
|
||||
|------|------|
|
||||
| **Peer Sampling** | 每个 Agent 维护一个部分视图(partial view) |
|
||||
| **Active Push** | 定期随机选几个节点推送本地状态 |
|
||||
| **Passive Pull** | 收到推送后,有概率回传自身状态 |
|
||||
| **TTL** | 消息有存活跳数,防止无限扩散 |
|
||||
| **Deduplication** | 消息 ID 去重,避免重复处理 |
|
||||
|
||||
#### 2.3.3 Gossip 消息格式
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "gossip:state",
|
||||
"from": "agent-001",
|
||||
"view": [
|
||||
{"agent_id": "agent-002", "endpoint": "...", "capabilities": [...], "timestamp": "..."},
|
||||
{"agent_id": "agent-003", "endpoint": "...", "capabilities": [...], "timestamp": "..."},
|
||||
{"agent_id": "agent-004", "endpoint": "...", "capabilities": [...], "timestamp": "..."}
|
||||
],
|
||||
"ttl": 5,
|
||||
"msg_id": "msg-xxx"
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3.4 优缺点
|
||||
|
||||
| 优点 | 缺点 |
|
||||
|------|------|
|
||||
| 完全去中心化 | 查询效率低(需本地构建视图) |
|
||||
| 容错性强 | 状态最终一致,有延迟 |
|
||||
| 可无限扩展 | 实现复杂度高 |
|
||||
| 无单点故障 | 网络流量大 |
|
||||
|
||||
---
|
||||
|
||||
### 2.4 模式三:本地组播/邻居发现(局域网场景)
|
||||
|
||||
适用于同一内网/集群内的 Agent 快速发现。
|
||||
|
||||
#### 2.4.1 机制
|
||||
|
||||
| 机制 | 说明 |
|
||||
|------|------|
|
||||
| **UDP 组播** | 定期在组播地址发送广播 |
|
||||
| **mDNS/DNS-SD** | 类似 Apple Bonjour 的服务发现 |
|
||||
| **ARP/NDP** | 链路层发现(不推荐) |
|
||||
|
||||
#### 2.4.2 适用场景
|
||||
|
||||
- 边缘计算集群
|
||||
- 同一机房内的 Agent 集群
|
||||
- 本地开发测试环境
|
||||
|
||||
---
|
||||
|
||||
### 2.5 广播机制设计
|
||||
|
||||
#### 2.5.1 广播类型
|
||||
|
||||
| 广播范围 | 消息类型前缀 | 说明 |
|
||||
|---------|-------------|------|
|
||||
| 全网广播 | `global:` | 所有在线 Agent |
|
||||
| 系统广播 | `system:` | 同一 system_id 内 |
|
||||
| 群组广播 | `group:` | 同一群组内(已有) |
|
||||
| 能力组广播 | `cap:` | 具备某能力的 Agent |
|
||||
|
||||
#### 2.5.2 广播实现
|
||||
|
||||
**集中式模式下:**
|
||||
- 由 Directory Service 转发广播消息
|
||||
- 支持按条件过滤(按能力、按系统等)
|
||||
|
||||
**分布式模式下:**
|
||||
- 通过 Gossip 协议泛洪
|
||||
- TTL 控制扩散范围
|
||||
- 消息 ID 去重
|
||||
|
||||
---
|
||||
|
||||
### 2.6 心跳与存活检测
|
||||
|
||||
#### 2.6.1 心跳机制
|
||||
|
||||
```
|
||||
Agent ───heartbeat──▶ Directory
|
||||
(每30秒)
|
||||
|
||||
Directory 判定离线:
|
||||
- 连续 3 次未收到心跳 → 标记为 offline
|
||||
- 离线 24 小时 → 自动注销
|
||||
```
|
||||
|
||||
#### 2.6.2 主动探测
|
||||
|
||||
```
|
||||
Directory ───ping──▶ Agent
|
||||
◀──pong──
|
||||
```
|
||||
|
||||
用于验证 Agent 是否真的在线(防止心跳伪造)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 方案二:身份认证与信任体系
|
||||
|
||||
### 3.1 三层身份架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Layer 3: 信任评分 (Trust Score) │
|
||||
│ 声誉系统、历史行为、第三方评价 │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ Layer 2: 身份验证 (Authentication) │
|
||||
│ 签名验证、证书校验、DID 验证 │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ Layer 1: 身份标识 (Identity) │
|
||||
│ AgentID + 公钥 / 证书 / DID │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 Layer 1:身份标识方案
|
||||
|
||||
#### 3.2.1 方案 A:公钥身份(推荐基础版)
|
||||
|
||||
**核心思想**:AgentID = 公钥哈希(类似比特币地址)
|
||||
|
||||
```
|
||||
AgentID = base58(sha256(public_key)[:20])
|
||||
例:agent-1A2B3C4D5E6F...
|
||||
```
|
||||
|
||||
**优点:**
|
||||
- 自包含,不需要中心 CA
|
||||
- 公钥可验证身份
|
||||
- 实现简单
|
||||
|
||||
**缺点:**
|
||||
- 身份无法撤销(私钥泄露 = 身份被盗)
|
||||
- 没有人类可读的名字
|
||||
- 无法关联真实世界身份
|
||||
|
||||
**适用场景**:内部系统、测试环境、信任度要求不高的场景
|
||||
|
||||
#### 3.2.2 方案 B:PKI 证书体系
|
||||
|
||||
**核心思想**:引入 CA(Certificate Authority)颁发证书
|
||||
|
||||
```
|
||||
CA 根证书
|
||||
├── 系统A证书
|
||||
│ ├── Agent-001 证书
|
||||
│ └── Agent-002 证书
|
||||
└── 系统B证书
|
||||
└── Agent-003 证书
|
||||
```
|
||||
|
||||
**优点:**
|
||||
- 成熟的工业标准
|
||||
- 支持证书撤销(CRL/OCSP)
|
||||
- 有层级信任关系
|
||||
|
||||
**缺点:**
|
||||
- 需要信任中心化 CA
|
||||
- 证书管理复杂
|
||||
- 跨 CA 互操作需要桥接
|
||||
|
||||
**适用场景**:企业内部、有明确信任中心的场景
|
||||
|
||||
#### 3.2.3 方案 C:区块链 DID(去中心化身份)
|
||||
|
||||
**核心思想**:身份上链,链上存公钥和属性,类似 W3C DID 标准。
|
||||
|
||||
```
|
||||
链上存储:
|
||||
DID → {
|
||||
"publicKey": "...",
|
||||
"controller": "...",
|
||||
"authentication": [...],
|
||||
"service": [...],
|
||||
"revoked": false,
|
||||
"timestamp": "..."
|
||||
}
|
||||
```
|
||||
|
||||
**三种实现路径:**
|
||||
|
||||
| 路径 | 说明 | 适用场景 |
|
||||
|------|------|---------|
|
||||
| **公链 DID** | 使用以太坊/Solana 等公链 | 开放生态、跨组织协作 |
|
||||
| **联盟链 DID** | 多机构共建联盟链 | 行业联盟、政企合作 |
|
||||
| **私有链 DID** | 单组织内部链 | 企业内部、对性能要求高 |
|
||||
|
||||
**优点:**
|
||||
- 去中心化,无单点故障
|
||||
- 身份可验证、可追溯
|
||||
- 支持跨系统互认
|
||||
- 不可篡改的身份历史
|
||||
|
||||
**缺点:**
|
||||
- 链上操作有延迟
|
||||
- 需要支付 Gas 费(公链)
|
||||
- 实现复杂度高
|
||||
- 隐私保护需额外设计
|
||||
|
||||
---
|
||||
|
||||
### 3.3 Layer 2:身份验证方案
|
||||
|
||||
#### 3.3.1 消息签名验证
|
||||
|
||||
所有灵隙消息必须签名,签名方式:
|
||||
|
||||
```
|
||||
签名内容 = SHA256(message_id + sender + receiver + payload + timestamp)
|
||||
签名 = Sign(private_key, 签名内容)
|
||||
验证 = Verify(public_key, 签名内容, 签名)
|
||||
```
|
||||
|
||||
#### 3.3.2 签名放置位置
|
||||
|
||||
| 位置 | 说明 |
|
||||
|------|------|
|
||||
| 消息体内 | `signature` 字段 |
|
||||
| HTTP Header | `X-Synapse-Signature` |
|
||||
| gRPC Metadata | `x-synapse-signature` |
|
||||
|
||||
#### 3.3.3 防重放攻击
|
||||
|
||||
```
|
||||
每个消息有唯一 message_id + timestamp
|
||||
接收方维护已处理 message_id 缓存(如最近 1 小时)
|
||||
timestamp 与本地时间差超过 5 分钟 → 拒绝
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.4 Layer 3:信任评分体系
|
||||
|
||||
#### 3.4.1 信任分维度
|
||||
|
||||
| 维度 | 权重 | 说明 |
|
||||
|------|------|------|
|
||||
| 任务完成率 | 30% | 历史任务成功 / 总任务数 |
|
||||
| 任务质量 | 25% | 结果评分、返工率 |
|
||||
| 响应速度 | 15% | 平均响应时间 |
|
||||
| 在线稳定性 | 15% | 在线时长 / 总时长 |
|
||||
| 历史信誉 | 15% | 其他 Agent 的评价 |
|
||||
|
||||
#### 3.4.2 信任分计算
|
||||
|
||||
```
|
||||
trust_score = Σ(维度_i × 权重_i)
|
||||
|
||||
范围:0 - 100
|
||||
等级:
|
||||
90-100 S 级 高度可信
|
||||
75-89 A 级 可信
|
||||
60-74 B 级 一般可信
|
||||
40-59 C 级 谨慎使用
|
||||
0-39 D 级 不可信
|
||||
```
|
||||
|
||||
#### 3.4.3 信任传递
|
||||
|
||||
```
|
||||
A 信任 B(评分 90)
|
||||
B 信任 C(评分 85)
|
||||
A 对 C 的初始信任 = 90 × 85% × 衰减因子 = 约 68
|
||||
```
|
||||
|
||||
**注意**:信任传递有衰减,防止信任污染。
|
||||
|
||||
---
|
||||
|
||||
### 3.5 区块链适用性决策树
|
||||
|
||||
```
|
||||
是否需要跨组织协作?
|
||||
├── 否 → 用 PKI 或公钥身份(简单高效)
|
||||
└── 是 → 是否有共同信任的中心机构?
|
||||
├── 是 → 用 PKI 体系(成熟可靠)
|
||||
└── 否 → 是否需要不可篡改的审计?
|
||||
├── 是 → 考虑联盟链 DID
|
||||
└── 否 → 公钥身份 + 声誉系统可能够用
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 方案三:协议缺失内容补全
|
||||
|
||||
### 4.1 第一优先级补全(核心基础设施)
|
||||
|
||||
#### 4.1.1 Agent 发现协议(ADS - Agent Discovery Service)
|
||||
|
||||
**新增消息类型:**
|
||||
|
||||
| 消息类型 | 方向 | 说明 |
|
||||
|---------|------|------|
|
||||
| `dir:register` | Agent → Directory | 注册 Agent |
|
||||
| `dir:unregister` | Agent → Directory | 注销 Agent |
|
||||
| `dir:heartbeat` | Agent → Directory | 心跳 |
|
||||
| `dir:update_capabilities` | Agent → Directory | 更新能力 |
|
||||
| `dir:query_by_id` | Agent → Directory | 按 ID 查询 |
|
||||
| `dir:query_by_capability` | Agent → Directory | 按能力查询 |
|
||||
| `dir:list` | Agent → Directory | 列表查询 |
|
||||
| `dir:query_result` | Directory → Agent | 查询结果 |
|
||||
|
||||
**新增数据结构:** `AgentDirectoryEntry`, `CapabilityIndex`, `QueryFilter`
|
||||
|
||||
#### 4.1.2 身份认证规范
|
||||
|
||||
**新增消息类型:**
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `auth:challenge` | 身份挑战 |
|
||||
| `auth:response` | 挑战响应 |
|
||||
| `auth:cert_exchange` | 证书交换 |
|
||||
|
||||
**新增内容:**
|
||||
- 标准签名算法列表(Ed25519, ECDSA P-256, RSA 2048)
|
||||
- 消息签名格式规范
|
||||
- 防重放攻击机制
|
||||
- 身份验证流程规范
|
||||
|
||||
#### 4.1.3 心跳与存活检测
|
||||
|
||||
**新增消息类型:**
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `net:ping` | 存活探测 |
|
||||
| `net:pong` | 存活响应 |
|
||||
| `net:offline_notify` | 下线通知 |
|
||||
|
||||
**规范内容:**
|
||||
- 心跳间隔(默认 30 秒)
|
||||
- 超时判定(连续 3 次未收到 → 离线)
|
||||
- 离线通知机制
|
||||
|
||||
#### 4.1.4 统一错误码体系
|
||||
|
||||
**错误分类:**
|
||||
|
||||
| 错误码前缀 | 类别 | 示例 |
|
||||
|-----------|------|------|
|
||||
| `msg_` | 消息层 | msg_invalid_format, msg_signature_invalid |
|
||||
| `auth_` | 认证层 | auth_failed, auth_expired, auth_forbidden |
|
||||
| `task_` | 任务层 | task_not_found, task_timeout, task_cancelled |
|
||||
| `cap_` | 能力层 | cap_lacking, cap_busy, cap_not_supported |
|
||||
| `group_` | 群组层 | group_not_found, group_full, group_permission_denied |
|
||||
| `dir_` | 目录层 | dir_agent_not_found, dir_query_failed |
|
||||
| `net_` | 网络层 | net_timeout, net_offline, net_unreachable |
|
||||
| `sys_` | 系统层 | sys_internal_error, sys_not_implemented |
|
||||
|
||||
---
|
||||
|
||||
### 4.2 第二优先级补全(重要功能)
|
||||
|
||||
#### 4.2.1 能力匹配与任务路由
|
||||
|
||||
**功能:**
|
||||
- 智能选择最合适的 Agent 执行任务
|
||||
- 支持负载均衡(按并发数、响应速度、信任分)
|
||||
- 支持任务失败自动重试/转发
|
||||
|
||||
**新增消息类型:**
|
||||
- `router:submit_task` - 提交给路由器自动选择执行者
|
||||
- `router:forward` - 任务转发
|
||||
|
||||
#### 4.2.2 全网广播协议
|
||||
|
||||
**新增消息类型:**
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `global:broadcast` | 全网广播 |
|
||||
| `system:broadcast` | 系统内广播 |
|
||||
| `cap:broadcast` | 能力组广播 |
|
||||
|
||||
**规范内容:**
|
||||
- 广播范围定义
|
||||
- TTL 与去重机制
|
||||
- 广播风暴防护
|
||||
|
||||
#### 4.2.3 版本协商机制
|
||||
|
||||
**功能:**
|
||||
- Agent 之间协商使用的协议版本
|
||||
- 向后兼容处理
|
||||
- 能力降级策略
|
||||
|
||||
**新增消息类型:**
|
||||
- `meta:version_query` - 查询支持的版本
|
||||
- `meta:version_negotiate` - 版本协商
|
||||
|
||||
#### 4.2.4 幂等性保证
|
||||
|
||||
**规范内容:**
|
||||
- 幂等键(Idempotency Key)机制
|
||||
- 重复消息处理规范
|
||||
- 任务级幂等 vs 消息级幂等
|
||||
|
||||
---
|
||||
|
||||
### 4.3 第三优先级补全(增强功能)
|
||||
|
||||
#### 4.3.1 流式输出协议
|
||||
|
||||
**功能:**
|
||||
- 长任务实时进度推送
|
||||
- 生成式 AI 的 token 流式输出
|
||||
- WebSocket/SSE 传输支持
|
||||
|
||||
#### 4.3.2 批量任务协议
|
||||
|
||||
**功能:**
|
||||
- 一次性提交多个同类型任务
|
||||
- 批量状态查询
|
||||
- 批量结果收集
|
||||
|
||||
#### 4.3.3 支付与激励机制
|
||||
|
||||
**功能:**
|
||||
- 任务定价与结算
|
||||
- Token 或积分系统
|
||||
- 智能合约自动结算(区块链场景)
|
||||
|
||||
#### 4.3.4 群组发现协议
|
||||
|
||||
**功能:**
|
||||
- 按主题/任务类型查找群组
|
||||
- 推荐加入合适的群组
|
||||
- 群组热度/活跃度排名
|
||||
|
||||
---
|
||||
|
||||
### 4.4 安全相关补全
|
||||
|
||||
#### 4.4.1 端到端加密
|
||||
|
||||
**规范内容:**
|
||||
- 密钥协商(ECDH)
|
||||
- 对称加密载荷(AES-GCM)
|
||||
- 前向保密(PFS)
|
||||
|
||||
#### 4.4.2 权限委托
|
||||
|
||||
**功能:**
|
||||
- Agent A 委托 Agent B 代表自己执行任务
|
||||
- 委托令牌(Delegation Token)
|
||||
- 委托范围与时效控制
|
||||
|
||||
#### 4.4.3 审计日志标准
|
||||
|
||||
**规范内容:**
|
||||
- 统一审计日志格式
|
||||
- 关键操作必须记录
|
||||
- 日志完整性保护(哈希链)
|
||||
|
||||
---
|
||||
|
||||
## 5. 分阶段实施路线图
|
||||
|
||||
### 5.1 Phase 0:基础版(MVP) - 预计 2 周
|
||||
|
||||
**目标**:协议可用,能跑通基本流程
|
||||
|
||||
**内容:**
|
||||
- [x] 灵隙点对点任务协议(已完成)
|
||||
- [x] A2N 群组协作协议(已完成)
|
||||
- [x] P2A 外部调用协议(已完成)
|
||||
- [x] 三套协议的 SDK 实现(已完成)
|
||||
|
||||
---
|
||||
|
||||
### 5.2 Phase 1:发现与身份 - 预计 4 周
|
||||
|
||||
**目标**:Agent 能互相找到对方,能验证对方身份
|
||||
|
||||
**新增协议模块:**
|
||||
|
||||
| 模块 | 说明 | 优先级 |
|
||||
|------|------|--------|
|
||||
| Agent 目录服务协议 | 集中式 ADS | P0 |
|
||||
| 公钥身份规范 | Ed25519 签名验证 | P0 |
|
||||
| 心跳与存活检测 | 30 秒心跳,3 次超时判定离线 | P0 |
|
||||
| 统一错误码体系 | 8 大类错误码 | P0 |
|
||||
| 防重放攻击 | message_id + timestamp | P0 |
|
||||
|
||||
**输出物:**
|
||||
- 新协议文档:`06-agent-discovery.md`
|
||||
- 新协议文档:`07-authentication.md`
|
||||
- 代码:`discovery/` 包(目录服务客户端)
|
||||
- 代码:`auth/` 包(签名验证工具)
|
||||
- 更新 types 包:新增消息类型和错误码
|
||||
|
||||
---
|
||||
|
||||
### 5.3 Phase 2:路由与广播 - 预计 4 周
|
||||
|
||||
**目标**:支持智能任务路由和全网广播
|
||||
|
||||
**新增协议模块:**
|
||||
|
||||
| 模块 | 说明 | 优先级 |
|
||||
|------|------|--------|
|
||||
| 任务路由协议 | 按能力 + 负载 + 信任分选择执行者 | P1 |
|
||||
| 全网广播协议 | 基于目录服务的广播转发 | P1 |
|
||||
| 版本协商机制 | 协议版本兼容 | P1 |
|
||||
| 幂等性保证 | 幂等键 + 去重 | P1 |
|
||||
|
||||
**输出物:**
|
||||
- 新协议文档:`08-routing-and-broadcast.md`
|
||||
- 代码:`routing/` 包增强(增加能力路由)
|
||||
- 更新目录服务:支持广播转发
|
||||
|
||||
---
|
||||
|
||||
### 5.4 Phase 3:信任与激励 - 预计 6 周
|
||||
|
||||
**目标**:建立信任体系,支持规模化协作
|
||||
|
||||
**新增协议模块:**
|
||||
|
||||
| 模块 | 说明 | 优先级 |
|
||||
|------|------|--------|
|
||||
| 信任评分体系 | 5 维度信任分计算 | P2 |
|
||||
| 流式输出协议 | WebSocket/SSE 流式进度 | P2 |
|
||||
| 批量任务协议 | 批量提交/查询/收集 | P2 |
|
||||
| 群组发现协议 | 群组搜索与推荐 | P2 |
|
||||
| SLA 协议 | 服务等级承诺 | P3 |
|
||||
|
||||
**输出物:**
|
||||
- 新协议文档:`09-trust-and-reputation.md`
|
||||
- 代码:`trust/` 包(信任评分引擎)
|
||||
|
||||
---
|
||||
|
||||
### 5.5 Phase 4:高级特性 - 预计 8 周+
|
||||
|
||||
**目标**:企业级、生产级完整功能
|
||||
|
||||
**新增协议模块:**
|
||||
|
||||
| 模块 | 说明 | 优先级 |
|
||||
|------|------|--------|
|
||||
| 端到端加密 | ECDH + AES-GCM | P3 |
|
||||
| 权限委托 | 委托令牌机制 | P3 |
|
||||
| 支付与激励 | Token/积分系统 | P3 |
|
||||
| 区块链 DID 集成 | 可选模块 | P4 |
|
||||
| 审计日志标准 | 统一审计格式 | P3 |
|
||||
| Gossip 协议 | 分布式发现(可选) | P4 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 技术选型建议
|
||||
|
||||
### 6.1 目录服务存储选型
|
||||
|
||||
| 方案 | 适用场景 | 优点 | 缺点 |
|
||||
|------|---------|------|------|
|
||||
| **Redis** | 中小规模 | 高性能,支持 TTL | 数据量受限 |
|
||||
| **PostgreSQL** | 中大规模 | 复杂查询,持久化 | 需运维 |
|
||||
| **Elasticsearch** | 大规模 + 全文检索 | 强大的搜索能力 | 运维复杂 |
|
||||
|
||||
**推荐**:先用 Redis(简单快),规模大了再迁 PostgreSQL。
|
||||
|
||||
---
|
||||
|
||||
### 6.2 签名算法选型
|
||||
|
||||
| 算法 | 安全性 | 性能 | 密钥长度 | 推荐度 |
|
||||
|------|--------|------|---------|--------|
|
||||
| Ed25519 | 高 | 快 | 256 位 | ⭐⭐⭐⭐⭐ 首选 |
|
||||
| ECDSA P-256 | 高 | 中 | 256 位 | ⭐⭐⭐⭐ 兼容 |
|
||||
| RSA 2048 | 中 | 慢 | 2048 位 | ⭐⭐ 仅兼容 |
|
||||
|
||||
**推荐**:默认 Ed25519,需要兼容旧系统时支持 ECDSA。
|
||||
|
||||
---
|
||||
|
||||
### 6.3 区块链选型(如果需要)
|
||||
|
||||
| 场景 | 推荐方案 | 说明 |
|
||||
|------|---------|------|
|
||||
| 企业内部 | **私有链(Hyperledger Fabric)** | 可控,高性能,无 Gas 费 |
|
||||
| 行业联盟 | **联盟链(FISCO BCOS / ChainMaker)** | 多机构共建,合规 |
|
||||
| 开放生态 | **公链(以太坊 / Solana)** | 完全开放,需 Gas 费 |
|
||||
| 简单原型 | **模拟链(数据库模拟)** | 先验证逻辑,再上真链 |
|
||||
|
||||
**推荐**:90% 的场景用不到真区块链,先用数据库模拟验证业务逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 7. 总结与建议
|
||||
|
||||
### 7.1 核心建议
|
||||
|
||||
1. **先集中后分布**:先用集中式目录服务,简单高效;规模大了再考虑分布式
|
||||
2. **先公钥后区块链**:先用公钥身份解决 80% 的问题,真的需要了再上链
|
||||
3. **先 MVP 后完善**:先跑通核心流程,再逐步加高级功能
|
||||
4. **标准接口可替换**:目录服务、身份系统都做成可插拔的,方便未来升级
|
||||
|
||||
### 7.2 下一步行动
|
||||
|
||||
| 序号 | 行动 | 负责人 | 产出 |
|
||||
|------|------|--------|------|
|
||||
| 1 | 评审本方案,确认 Phase 1 范围 | 架构组 | 确认后的方案 |
|
||||
| 2 | 设计目录服务 API 详细规范 | 协议组 | `06-agent-discovery.md` |
|
||||
| 3 | 设计身份认证详细规范 | 协议组 | `07-authentication.md` |
|
||||
| 4 | 实现目录服务原型 | 开发组 | 可运行的 ADS 服务 |
|
||||
| 5 | 更新 SDK 支持发现和认证 | 开发组 | 新版 SDK |
|
||||
|
||||
---
|
||||
|
||||
## 附录:相关文档索引
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| [01-a2a-raw-protocol.md](./01-a2a-raw-protocol.md) | 灵隙原始协议(Synapse Raw Protocol)(纲领) |
|
||||
| [02-a2a-protocol.md](./02-a2a-protocol.md) | A2A 协议(P2P 任务) |
|
||||
| [03-a2n-protocol.md](./03-a2n-protocol.md) | A2N 协议(群组协作) |
|
||||
| [04-p2a-protocol.md](./04-p2a-protocol.md) | P2A 协议(外部调用) |
|
||||
| [05-sdk-implementation.md](./05-sdk-implementation.md) | SDK 实现说明 |
|
||||
| **本文档** | **解决方案白皮书** |
|
||||
@@ -1,414 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"synapse/sdk/group"
|
||||
"synapse/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// Group 群组测试 - 三个 Agent 之间的群组协作
|
||||
// =============================================================================
|
||||
|
||||
func main() {
|
||||
fmt.Println("=== 灵隙(Synapse)Group 群组测试 ===")
|
||||
fmt.Println("启动三个 Agent,进行群组协作验证...")
|
||||
fmt.Println()
|
||||
|
||||
// 创建群组客户端(简化版,直接在主程序模拟)
|
||||
groupClient := &simpleGroupClient{
|
||||
groups: make(map[string]*group.GroupState),
|
||||
}
|
||||
|
||||
// 启动 Agent 1 (Coordinator) - 监听 9091 端口
|
||||
go runCoordinatorAgent(groupClient)
|
||||
|
||||
// 等待 Agent 1 启动
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// 启动 Agent 2 (Worker1) - 监听 9092 端口
|
||||
go runWorkerAgent(groupClient, "worker-1", 9092)
|
||||
|
||||
// 等待 Agent 2 启动
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// 启动 Agent 3 (Worker2) - 监听 9093 端口
|
||||
go runWorkerAgent(groupClient, "worker-2", 9093)
|
||||
|
||||
// 保持主程序运行
|
||||
fmt.Println("\n=== Agent 已启动,等待群组测试 ===")
|
||||
fmt.Println("Agent 1 (Coordinator): http://localhost:9091")
|
||||
fmt.Println("Agent 2 (Worker-1): http://localhost:9092")
|
||||
fmt.Println("Agent 3 (Worker-2): http://localhost:9093")
|
||||
fmt.Println("\n按 Ctrl+C 停止...")
|
||||
|
||||
// 自动创建群组并开始测试
|
||||
go func() {
|
||||
time.Sleep(2 * time.Second)
|
||||
createGroup()
|
||||
}()
|
||||
|
||||
select {}
|
||||
}
|
||||
|
||||
// 简化的群组客户端
|
||||
type simpleGroupClient struct {
|
||||
groups map[string]*group.GroupState
|
||||
members map[string][]string // groupID -> agentIDs
|
||||
}
|
||||
|
||||
// 创建群组并开始测试
|
||||
func createGroup() {
|
||||
log.Printf("\n=== [主程序] 创建群组 ===")
|
||||
|
||||
resp, err := http.Post(
|
||||
"http://localhost:9091/api/group/create",
|
||||
"application/json",
|
||||
jsonBody(map[string]any{}),
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("[主程序] 创建群组失败: %v", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result map[string]any
|
||||
json.NewDecoder(resp.Body).Decode(&result)
|
||||
log.Printf("[主程序] 创建群组响应: %v", result)
|
||||
|
||||
// 等待任务完成
|
||||
time.Sleep(8 * time.Second)
|
||||
}
|
||||
|
||||
type agentInfo struct {
|
||||
ID string
|
||||
Name string
|
||||
Port int
|
||||
Handler interface{}
|
||||
}
|
||||
|
||||
// Coordinator Agent(协调者)
|
||||
func runCoordinatorAgent(client *simpleGroupClient) {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
// 健康检查
|
||||
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
w.Write([]byte("OK - Coordinator"))
|
||||
})
|
||||
|
||||
// 群组管理 API
|
||||
mux.HandleFunc("/api/group/create", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method not allowed", 405)
|
||||
return
|
||||
}
|
||||
|
||||
groupID := fmt.Sprintf("group-%d", time.Now().UnixNano())
|
||||
group := &group.GroupState{
|
||||
GroupID: groupID,
|
||||
Name: "视频制作组",
|
||||
Topology: types.GroupTopologyStar,
|
||||
Status: types.GroupStatusActive,
|
||||
Members: []types.GroupMember{
|
||||
{
|
||||
AgentID: "coordinator",
|
||||
AgentName: "协调者",
|
||||
Role: "coordinator",
|
||||
JoinedAt: types.NowTimestamp(),
|
||||
Status: types.GroupMemberActive,
|
||||
Capabilities: []string{"coordination"},
|
||||
LoadFactor: 0,
|
||||
},
|
||||
{
|
||||
AgentID: "worker-1",
|
||||
AgentName: "工作者1",
|
||||
Role: "worker",
|
||||
JoinedAt: types.NowTimestamp(),
|
||||
Status: types.GroupMemberActive,
|
||||
Capabilities: []string{"video_generation"},
|
||||
LoadFactor: 0,
|
||||
},
|
||||
{
|
||||
AgentID: "worker-2",
|
||||
AgentName: "工作者2",
|
||||
Role: "worker",
|
||||
JoinedAt: types.NowTimestamp(),
|
||||
Status: types.GroupMemberActive,
|
||||
Capabilities: []string{"audio_generation"},
|
||||
LoadFactor: 0,
|
||||
},
|
||||
},
|
||||
Role: "coordinator",
|
||||
}
|
||||
client.groups[groupID] = group
|
||||
|
||||
log.Printf("\n=== [Coordinator] 群组创建成功 ===")
|
||||
log.Printf("Group ID: %s", groupID)
|
||||
log.Printf("Members: coordinator, worker-1, worker-2")
|
||||
|
||||
// 模拟任务拆分
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
simulateTaskSplit(groupID, client)
|
||||
}()
|
||||
|
||||
// 模拟投票
|
||||
go func() {
|
||||
time.Sleep(3 * time.Second)
|
||||
simulateVote(groupID)
|
||||
}()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]any{
|
||||
"success": true,
|
||||
"group_id": groupID,
|
||||
"message": "群组创建成功",
|
||||
})
|
||||
})
|
||||
|
||||
// 获取群组信息
|
||||
mux.HandleFunc("/api/group/", func(w http.ResponseWriter, r *http.Request) {
|
||||
groupID := r.URL.Path[len("/api/group/"):]
|
||||
group, ok := client.groups[groupID]
|
||||
if !ok {
|
||||
http.Error(w, "group not found", 404)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]any{
|
||||
"success": true,
|
||||
"group": group,
|
||||
})
|
||||
})
|
||||
|
||||
log.Println("[Coordinator] 协调者 Agent 启动,监听端口 9091")
|
||||
if err := http.ListenAndServe(":9091", mux); err != nil {
|
||||
log.Fatalf("[Coordinator] Server error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Worker Agent(工作者)
|
||||
func runWorkerAgent(client *simpleGroupClient, workerID string, port int) {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
// 健康检查
|
||||
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
w.Write([]byte(fmt.Sprintf("OK - %s", workerID)))
|
||||
})
|
||||
|
||||
// 加入群组
|
||||
mux.HandleFunc("/api/group/join", func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
GroupID string `json:"group_id"`
|
||||
}
|
||||
json.NewDecoder(r.Body).Decode(&req)
|
||||
|
||||
log.Printf("\n=== [%s] 申请加入群组 ===", workerID)
|
||||
log.Printf("Group ID: %s", req.GroupID)
|
||||
|
||||
// 模拟处理加入请求
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]any{
|
||||
"success": true,
|
||||
"message": fmt.Sprintf("%s 已加入群组", workerID),
|
||||
"role": "worker",
|
||||
"group_id": req.GroupID,
|
||||
})
|
||||
})
|
||||
|
||||
// 接收任务
|
||||
mux.HandleFunc("/api/task/subtask", func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
GroupID string `json:"group_id"`
|
||||
Subtask string `json:"subtask"`
|
||||
TaskType string `json:"task_type"`
|
||||
Payload map[string]interface{} `json:"payload"`
|
||||
}
|
||||
json.NewDecoder(r.Body).Decode(&req)
|
||||
|
||||
log.Printf("\n=== [%s] 收到子任务 ===", workerID)
|
||||
log.Printf("Group ID: %s", req.GroupID)
|
||||
log.Printf("Subtask: %s", req.Subtask)
|
||||
log.Printf("Task Type: %s", req.TaskType)
|
||||
log.Printf("Payload: %v", req.Payload)
|
||||
|
||||
// 模拟执行任务
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
log.Printf("[%s] 子任务执行完成: %s", workerID, req.Subtask)
|
||||
|
||||
// 发送结果(这里简化处理,只是打印日志)
|
||||
log.Printf("[%s] 发送子任务结果: %s -> 完成了 %s 的工作", workerID, req.Subtask, req.TaskType)
|
||||
}()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]any{
|
||||
"success": true,
|
||||
"message": fmt.Sprintf("%s 开始执行任务", workerID),
|
||||
"subtask": req.Subtask,
|
||||
"progress": 0,
|
||||
})
|
||||
})
|
||||
|
||||
// 接收投票
|
||||
mux.HandleFunc("/api/vote", func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
GroupID string `json:"group_id"`
|
||||
VoteID string `json:"vote_id"`
|
||||
Question string `json:"question"`
|
||||
Options []string `json:"options"`
|
||||
}
|
||||
json.NewDecoder(r.Body).Decode(&req)
|
||||
|
||||
log.Printf("\n=== [%s] 收到投票请求 ===", workerID)
|
||||
log.Printf("Vote ID: %s", req.VoteID)
|
||||
log.Printf("Question: %s", req.Question)
|
||||
log.Printf("Options: %v", req.Options)
|
||||
|
||||
// 模拟投票决策(选择第一个选项)
|
||||
selectedOption := req.Options[0]
|
||||
log.Printf("[%s] 投票: %s", workerID, selectedOption)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]any{
|
||||
"success": true,
|
||||
"vote_id": req.VoteID,
|
||||
"selected_option": selectedOption,
|
||||
"reason": "看起来更合理",
|
||||
})
|
||||
})
|
||||
|
||||
log.Printf("[%s] 工作者 Agent 启动,监听端口 %d", workerID, port)
|
||||
if err := http.ListenAndServe(fmt.Sprintf(":%d", port), mux); err != nil {
|
||||
log.Fatalf("[%s] Server error: %v", workerID, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟任务拆分
|
||||
func simulateTaskSplit(groupID string, client *simpleGroupClient) {
|
||||
log.Printf("\n=== [Coordinator] 执行任务拆分 ===")
|
||||
log.Printf("Original Task: 制作视频")
|
||||
|
||||
subtasks := []struct {
|
||||
ID string
|
||||
Description string
|
||||
AssignedTo string
|
||||
}{
|
||||
{"sub-1", "生成视频脚本", "worker-1"},
|
||||
{"sub-2", "生成配音", "worker-2"},
|
||||
}
|
||||
|
||||
for _, st := range subtasks {
|
||||
log.Printf("拆分任务: %s -> %s", st.ID, st.AssignedTo)
|
||||
|
||||
// 发送任务给 worker
|
||||
port := 9092
|
||||
if st.AssignedTo == "worker-2" {
|
||||
port = 9093
|
||||
}
|
||||
|
||||
resp, err := http.Post(
|
||||
fmt.Sprintf("http://localhost:%d/api/task/subtask", port),
|
||||
"application/json",
|
||||
jsonBody(map[string]any{
|
||||
"group_id": groupID,
|
||||
"subtask": st.ID,
|
||||
"task_type": "video_generation",
|
||||
"payload": map[string]any{"description": st.Description},
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("[Coordinator] 发送任务失败: %v", err)
|
||||
continue
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟投票
|
||||
func simulateVote(groupID string) {
|
||||
log.Printf("\n=== [Coordinator] 发起投票 ===")
|
||||
|
||||
voteQuestion := "选择视频风格?"
|
||||
voteOptions := []string{"现代风格", "复古风格", "卡通风格"}
|
||||
|
||||
log.Printf("Question: %s", voteQuestion)
|
||||
log.Printf("Options: %v", voteOptions)
|
||||
|
||||
// 收集投票结果
|
||||
votes := make(map[string]int)
|
||||
for _, opt := range voteOptions {
|
||||
votes[opt] = 0
|
||||
}
|
||||
|
||||
// 向两个 worker 发送投票
|
||||
for _, port := range []int{9092, 9093} {
|
||||
resp, err := http.Post(
|
||||
fmt.Sprintf("http://localhost:%d/api/vote", port),
|
||||
"application/json",
|
||||
jsonBody(map[string]any{
|
||||
"group_id": groupID,
|
||||
"vote_id": fmt.Sprintf("vote-%d", time.Now().UnixNano()),
|
||||
"question": voteQuestion,
|
||||
"options": voteOptions,
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("[Coordinator] 投票请求失败: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
var result map[string]any
|
||||
json.NewDecoder(resp.Body).Decode(&result)
|
||||
resp.Body.Close()
|
||||
|
||||
if selected, ok := result["selected_option"].(string); ok {
|
||||
votes[selected]++
|
||||
}
|
||||
}
|
||||
|
||||
// 统计结果
|
||||
log.Printf("\n=== [Coordinator] 投票结果 ===")
|
||||
for opt, count := range votes {
|
||||
log.Printf("%s: %d 票", opt, count)
|
||||
}
|
||||
|
||||
// 确定获胜者
|
||||
var winner string
|
||||
maxVotes := 0
|
||||
for opt, count := range votes {
|
||||
if count > maxVotes {
|
||||
maxVotes = count
|
||||
winner = opt
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("\n最终决定: %s (获得 %d 票)", winner, maxVotes)
|
||||
|
||||
// 模拟结果合并
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
log.Printf("\n=== [Coordinator] 合并任务结果 ===")
|
||||
log.Printf("所有子任务已完成")
|
||||
log.Printf("最终结果: 视频制作完成,采用 %s", winner)
|
||||
}()
|
||||
}
|
||||
|
||||
func jsonBody(v map[string]any) *bytes.Buffer {
|
||||
data, _ := json.Marshal(v)
|
||||
return bytes.NewBuffer(data)
|
||||
}
|
||||
@@ -1,368 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"synapse/sdk/p2p"
|
||||
"synapse/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// P2P 通讯测试 - 两个 Agent 之间的点对点通信
|
||||
// =============================================================================
|
||||
|
||||
func main() {
|
||||
fmt.Println("=== 灵隙(Synapse)P2P 通讯测试 ===")
|
||||
fmt.Println("启动两个 Agent,进行点对点通讯验证...")
|
||||
fmt.Println()
|
||||
|
||||
// 启动 Agent 1 (Writer Agent) - 监听 8081 端口
|
||||
go runAgent1()
|
||||
|
||||
// 等待 Agent 1 启动
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// 启动 Agent 2 (Caller) - 监听 8082 端口
|
||||
go runAgent2()
|
||||
|
||||
// 保持主程序运行
|
||||
fmt.Println("\n=== Agent 已启动,等待通讯测试 ===")
|
||||
fmt.Println("Agent 1 (Writer): http://localhost:8081")
|
||||
fmt.Println("Agent 2 (Caller): http://localhost:8082")
|
||||
fmt.Println("\n按 Ctrl+C 停止...")
|
||||
|
||||
select {}
|
||||
}
|
||||
|
||||
// Agent 1 - Writer Agent(任务执行方)
|
||||
func runAgent1() {
|
||||
// 创建 P2P 客户端
|
||||
client, err := p2p.NewClient(&p2p.ClientConfig{
|
||||
AgentEndpoint: types.AgentEndpoint{
|
||||
AgentID: "writer-agent",
|
||||
AgentName: "作家 Agent",
|
||||
SystemID: "test-system",
|
||||
Endpoint: "http://localhost:8081/callback",
|
||||
},
|
||||
AuthSecret: "secret-123",
|
||||
CallbackPort: 8081,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create Agent 1: %v", err)
|
||||
}
|
||||
|
||||
// 设置消息处理器
|
||||
client.SetMessageHandler(&writerHandler{})
|
||||
|
||||
// 启动 HTTP 服务处理回调
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
|
||||
var msg types.UnifiedSynapseMessage
|
||||
if err := json.NewDecoder(r.Body).Decode(&msg); err != nil {
|
||||
http.Error(w, "bad request", 400)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := client.HandleMessage(r.Context(), &msg)
|
||||
if err != nil {
|
||||
log.Printf("[Agent1] Error handling message: %v", err)
|
||||
http.Error(w, "internal error", 500)
|
||||
return
|
||||
}
|
||||
|
||||
if resp != nil {
|
||||
respBytes, _ := json.Marshal(resp)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(respBytes)
|
||||
}
|
||||
})
|
||||
|
||||
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
w.Write([]byte("OK"))
|
||||
})
|
||||
|
||||
log.Println("[Agent1] Writer Agent 启动,监听端口 8081")
|
||||
if err := http.ListenAndServe(":8081", mux); err != nil {
|
||||
log.Fatalf("[Agent1] Server error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 2 - Caller Agent(任务请求方)
|
||||
func runAgent2() {
|
||||
// 创建 P2P 客户端
|
||||
client, err := p2p.NewClient(&p2p.ClientConfig{
|
||||
AgentEndpoint: types.AgentEndpoint{
|
||||
AgentID: "caller-agent",
|
||||
AgentName: "调用方 Agent",
|
||||
SystemID: "test-system",
|
||||
Endpoint: "http://localhost:8082/callback",
|
||||
},
|
||||
AuthSecret: "secret-123",
|
||||
CallbackPort: 8082,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create Agent 2: %v", err)
|
||||
}
|
||||
|
||||
// 设置消息处理器
|
||||
client.SetMessageHandler(&callerHandler{})
|
||||
|
||||
// 启动 HTTP 服务
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
|
||||
var msg types.UnifiedSynapseMessage
|
||||
if err := json.NewDecoder(r.Body).Decode(&msg); err != nil {
|
||||
http.Error(w, "bad request", 400)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := client.HandleMessage(r.Context(), &msg)
|
||||
if err != nil {
|
||||
log.Printf("[Agent2] Error handling message: %v", err)
|
||||
http.Error(w, "internal error", 500)
|
||||
return
|
||||
}
|
||||
|
||||
if resp != nil {
|
||||
respBytes, _ := json.Marshal(resp)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(respBytes)
|
||||
}
|
||||
})
|
||||
|
||||
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
w.Write([]byte("OK"))
|
||||
})
|
||||
|
||||
// 启动后发送测试任务
|
||||
go func() {
|
||||
time.Sleep(2 * time.Second)
|
||||
sendTestTask(client)
|
||||
}()
|
||||
|
||||
log.Println("[Agent2] Caller Agent 启动,监听端口 8082")
|
||||
if err := http.ListenAndServe(":8082", mux); err != nil {
|
||||
log.Fatalf("[Agent2] Server error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Writer Agent 的消息处理器
|
||||
type writerHandler struct{}
|
||||
|
||||
func (h *writerHandler) HandleTaskRequest(ctx context.Context, msg *types.UnifiedSynapseMessage) (*types.UnifiedSynapseMessage, error) {
|
||||
var payload types.TaskRequestPayload
|
||||
if err := json.Unmarshal(msg.Payload, &payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("\n=== [Agent1] 收到任务请求 ===")
|
||||
log.Printf("Task ID: %s", payload.Task.ID)
|
||||
log.Printf("Task Type: %s", payload.Task.Type)
|
||||
log.Printf("Payload: %v", payload.Task.Payload)
|
||||
|
||||
// 发送任务确认
|
||||
confirmPayload, _ := json.Marshal(types.TaskConfirmPayload{
|
||||
TaskID: payload.Task.ID,
|
||||
Confirmed: true,
|
||||
EstimatedDuration: 5,
|
||||
ExpectedCompletion: types.Timestamp(time.Now().Add(5 * time.Second).Format(time.RFC3339)),
|
||||
CapabilitiesUsed: []string{"text_generation"},
|
||||
})
|
||||
|
||||
confirmMsg := &types.UnifiedSynapseMessage{
|
||||
ID: types.GenerateMessageID(),
|
||||
Version: "1.0.0",
|
||||
Type: string(types.P2PTaskConfirm),
|
||||
Sender: *msg.Receiver,
|
||||
Receiver: &msg.Sender,
|
||||
Payload: confirmPayload,
|
||||
Timestamp: types.NowTimestamp(),
|
||||
}
|
||||
|
||||
log.Printf("[Agent1] 发送任务确认: %s\n", payload.Task.ID)
|
||||
|
||||
// 模拟处理,然后发送完成消息
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// 发送进度更新
|
||||
progressPayload, _ := json.Marshal(types.TaskStatusUpdatePayload{
|
||||
TaskID: payload.Task.ID,
|
||||
Status: "executing",
|
||||
Progress: 50,
|
||||
Message: "正在生成文本...",
|
||||
})
|
||||
|
||||
progressMsg := &types.UnifiedSynapseMessage{
|
||||
ID: types.GenerateMessageID(),
|
||||
Version: "1.0.0",
|
||||
Type: string(types.P2PTaskStatusUpdate),
|
||||
Sender: *msg.Receiver,
|
||||
Receiver: &msg.Sender,
|
||||
Payload: progressPayload,
|
||||
Timestamp: types.NowTimestamp(),
|
||||
}
|
||||
|
||||
log.Printf("[Agent1] 发送进度更新: 50%%")
|
||||
_ = sendMessage(progressMsg)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// 发送完成消息
|
||||
result := fmt.Sprintf("生成了一个关于「%v」的故事", payload.Task.Payload["topic"])
|
||||
completePayload, _ := json.Marshal(types.TaskCompletePayload{
|
||||
TaskID: payload.Task.ID,
|
||||
Result: struct {
|
||||
OutputType string `json:"output_type"`
|
||||
Output any `json:"output"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
}{
|
||||
OutputType: "text",
|
||||
Output: result,
|
||||
Metadata: map[string]interface{}{"tokens": len(result)},
|
||||
},
|
||||
Summary: "任务已完成",
|
||||
})
|
||||
|
||||
completeMsg := &types.UnifiedSynapseMessage{
|
||||
ID: types.GenerateMessageID(),
|
||||
Version: "1.0.0",
|
||||
Type: string(types.P2PTaskComplete),
|
||||
Sender: *msg.Receiver,
|
||||
Receiver: &msg.Sender,
|
||||
Payload: completePayload,
|
||||
Timestamp: types.NowTimestamp(),
|
||||
}
|
||||
|
||||
log.Printf("[Agent1] 发送任务完成: %s\n", result)
|
||||
_ = sendMessage(completeMsg)
|
||||
}()
|
||||
|
||||
return confirmMsg, nil
|
||||
}
|
||||
|
||||
func (h *writerHandler) HandleTaskStatusUpdate(ctx context.Context, msg *types.UnifiedSynapseMessage) error {
|
||||
log.Printf("[Agent1] 收到状态更新: %s", msg.Type)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *writerHandler) HandleTaskComplete(ctx context.Context, msg *types.UnifiedSynapseMessage) error {
|
||||
var payload types.TaskCompletePayload
|
||||
json.Unmarshal(msg.Payload, &payload)
|
||||
log.Printf("[Agent1] 任务完成: %s", payload.Summary)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *writerHandler) HandleTaskFailed(ctx context.Context, msg *types.UnifiedSynapseMessage) error {
|
||||
var payload types.TaskFailedPayload
|
||||
json.Unmarshal(msg.Payload, &payload)
|
||||
log.Printf("[Agent1] 任务失败: %s", payload.Error.Message)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *writerHandler) HandleNeedHuman(ctx context.Context, msg *types.UnifiedSynapseMessage) error {
|
||||
var payload types.TaskNeedHumanPayload
|
||||
json.Unmarshal(msg.Payload, &payload)
|
||||
log.Printf("[Agent1] 需要人类介入: %s", payload.Reason.Message)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Caller Agent 的消息处理器
|
||||
type callerHandler struct{}
|
||||
|
||||
func (h *callerHandler) HandleTaskRequest(ctx context.Context, msg *types.UnifiedSynapseMessage) (*types.UnifiedSynapseMessage, error) {
|
||||
log.Printf("[Agent2] 收到任务请求")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (h *callerHandler) HandleTaskStatusUpdate(ctx context.Context, msg *types.UnifiedSynapseMessage) error {
|
||||
var payload types.TaskStatusUpdatePayload
|
||||
json.Unmarshal(msg.Payload, &payload)
|
||||
log.Printf("\n=== [Agent2] 收到状态更新 ===")
|
||||
log.Printf("Task ID: %s", payload.TaskID)
|
||||
log.Printf("Status: %s", payload.Status)
|
||||
log.Printf("Progress: %d%%", payload.Progress)
|
||||
log.Printf("Message: %s\n", payload.Message)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *callerHandler) HandleTaskComplete(ctx context.Context, msg *types.UnifiedSynapseMessage) error {
|
||||
var payload types.TaskCompletePayload
|
||||
json.Unmarshal(msg.Payload, &payload)
|
||||
log.Printf("\n=== [Agent2] 收到任务完成 ===")
|
||||
log.Printf("Task ID: %s", payload.TaskID)
|
||||
log.Printf("Result: %v", payload.Result.Output)
|
||||
log.Printf("Summary: %s\n", payload.Summary)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *callerHandler) HandleTaskFailed(ctx context.Context, msg *types.UnifiedSynapseMessage) error {
|
||||
var payload types.TaskFailedPayload
|
||||
json.Unmarshal(msg.Payload, &payload)
|
||||
log.Printf("\n=== [Agent2] 收到任务失败 ===")
|
||||
log.Printf("Task ID: %s", payload.TaskID)
|
||||
log.Printf("Error: %s\n", payload.Error.Message)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *callerHandler) HandleNeedHuman(ctx context.Context, msg *types.UnifiedSynapseMessage) error {
|
||||
var payload types.TaskNeedHumanPayload
|
||||
json.Unmarshal(msg.Payload, &payload)
|
||||
log.Printf("\n=== [Agent2] 收到人工介入请求 ===")
|
||||
log.Printf("Task ID: %s", payload.TaskID)
|
||||
log.Printf("Reason: %s\n", payload.Reason.Message)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 发送测试任务
|
||||
func sendTestTask(client *p2p.Client) {
|
||||
log.Println("\n=== [Agent2] 发送测试任务 ===")
|
||||
|
||||
target := types.AgentEndpoint{
|
||||
AgentID: "writer-agent",
|
||||
AgentName: "作家 Agent",
|
||||
SystemID: "test-system",
|
||||
Endpoint: "http://localhost:8081/callback",
|
||||
}
|
||||
|
||||
taskID, err := client.SendTaskRequest(context.Background(), target, "text_generation", map[string]any{
|
||||
"topic": "春天",
|
||||
"style": "古诗",
|
||||
"length": "short",
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("[Agent2] 发送任务失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[Agent2] 任务已发送,Task ID: %s", taskID)
|
||||
|
||||
// 等待任务完成
|
||||
state, err := client.WaitForTask(context.Background(), taskID, 30*time.Second)
|
||||
if err != nil {
|
||||
log.Printf("[Agent2] 等待任务失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("\n=== [测试结果] ===")
|
||||
log.Printf("最终状态: %s", state.Status)
|
||||
log.Printf("结果: %v", state.Result)
|
||||
}
|
||||
|
||||
func sendMessage(msg *types.UnifiedSynapseMessage) error {
|
||||
body, _ := json.Marshal(msg)
|
||||
resp, err := http.Post(msg.Receiver.Endpoint, "application/json", bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
@@ -79,6 +79,9 @@ func (h *ExternalAPIHandler) RegisterRoutes(mux *http.ServeMux, prefix string) {
|
||||
|
||||
// 多部分表单提交
|
||||
mux.HandleFunc(prefix+"/task/multipart", h.withAuth(h.handleTaskMultipart))
|
||||
|
||||
// 能力查询
|
||||
mux.HandleFunc(prefix+"/capabilities", h.withAuth(h.handleCapabilities))
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -403,6 +406,91 @@ func (h *ExternalAPIHandler) handleTaskList(w http.ResponseWriter, r *http.Reque
|
||||
})
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 能力查询
|
||||
// =============================================================================
|
||||
|
||||
// CapabilitiesProvider 能力提供者接口
|
||||
type CapabilitiesProvider interface {
|
||||
GetCapabilities(ctx context.Context) (*CapabilitiesInfo, error)
|
||||
}
|
||||
|
||||
// CapabilitiesInfo 能力信息
|
||||
type CapabilitiesInfo struct {
|
||||
AgentID string `json:"agent_id"`
|
||||
AgentName string `json:"agent_name"`
|
||||
SystemID string `json:"system_id"`
|
||||
ProtocolVersion string `json:"protocol_version"`
|
||||
SupportedTaskTypes []SupportedTaskType `json:"supported_task_types"`
|
||||
SupportedContentTypes []string `json:"supported_content_types"`
|
||||
SupportedTransportModes []string `json:"supported_transport_modes"`
|
||||
MaxFileSize int64 `json:"max_file_size"`
|
||||
MaxConcurrentTasks int `json:"max_concurrent_tasks"`
|
||||
MaxTaskDuration int `json:"max_task_duration_seconds"`
|
||||
SupportProgressUpdates bool `json:"support_progress_updates"`
|
||||
SupportStreaming bool `json:"support_streaming"`
|
||||
SupportHiL bool `json:"support_hil"`
|
||||
SupportGroups bool `json:"support_groups"`
|
||||
}
|
||||
|
||||
// SupportedTaskType 支持的任务类型
|
||||
type SupportedTaskType struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
EstimatedTime string `json:"estimated_time"`
|
||||
InputSchema string `json:"input_schema,omitempty"`
|
||||
OutputSchema string `json:"output_schema,omitempty"`
|
||||
Capabilities []string `json:"capabilities,omitempty"`
|
||||
}
|
||||
|
||||
// CapabilitiesProviderField 能力提供者字段(可选注入)
|
||||
var CapabilitiesProviderField CapabilitiesProvider
|
||||
|
||||
func (h *ExternalAPIHandler) handleCapabilities(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
h.writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "only GET is supported")
|
||||
return
|
||||
}
|
||||
|
||||
// 如果有注入的能力提供者,使用它
|
||||
if CapabilitiesProviderField != nil {
|
||||
caps, err := CapabilitiesProviderField.GetCapabilities(r.Context())
|
||||
if err != nil {
|
||||
h.writeError(w, http.StatusInternalServerError, "capabilities_error", err.Error())
|
||||
return
|
||||
}
|
||||
h.writeJSON(w, http.StatusOK, map[string]any{
|
||||
"success": true,
|
||||
"capabilities": caps,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 默认能力信息
|
||||
caps := &CapabilitiesInfo{
|
||||
AgentID: "synapse-agent",
|
||||
AgentName: "Synapse Agent",
|
||||
SystemID: "synapse",
|
||||
ProtocolVersion: "1.0.0",
|
||||
SupportedTaskTypes: []SupportedTaskType{},
|
||||
SupportedContentTypes: []string{"application/json", "text/plain", "application/octet-stream"},
|
||||
SupportedTransportModes: []string{"callback", "polling"},
|
||||
MaxFileSize: h.MaxFileSize,
|
||||
MaxConcurrentTasks: 10,
|
||||
MaxTaskDuration: 300,
|
||||
SupportProgressUpdates: true,
|
||||
SupportStreaming: false,
|
||||
SupportHiL: true,
|
||||
SupportGroups: true,
|
||||
}
|
||||
|
||||
h.writeJSON(w, http.StatusOK, map[string]any{
|
||||
"success": true,
|
||||
"capabilities": caps,
|
||||
})
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 回调发送器
|
||||
// =============================================================================
|
||||
|
||||
@@ -1,547 +0,0 @@
|
||||
package caller
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// Synapse Caller SDK (P2A Protocol)
|
||||
// 非Agent调用方使用的客户端SDK
|
||||
// 基于 P2A 协议:非 Agent 调用方通过 REST API 调用 Agent 能力
|
||||
// =============================================================================
|
||||
|
||||
// Client 是 Synapse P2A 服务的外部调用客户端
|
||||
type Client struct {
|
||||
baseURL string
|
||||
apiKey string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// ClientConfig 客户端配置
|
||||
type ClientConfig struct {
|
||||
BaseURL string
|
||||
APIKey string
|
||||
Timeout time.Duration
|
||||
MaxRetries int
|
||||
}
|
||||
|
||||
// NewClient 创建新的 Synapse Caller 客户端
|
||||
func NewClient(config ClientConfig) *Client {
|
||||
if config.Timeout == 0 {
|
||||
config.Timeout = 30 * time.Second
|
||||
}
|
||||
return &Client{
|
||||
baseURL: config.BaseURL,
|
||||
apiKey: config.APIKey,
|
||||
httpClient: &http.Client{Timeout: config.Timeout},
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 通用响应结构
|
||||
// =============================================================================
|
||||
|
||||
// APIResponse 通用 API 响应
|
||||
type APIResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// TaskResponse 任务提交响应
|
||||
type TaskResponse struct {
|
||||
APIResponse
|
||||
TaskID string `json:"task_id"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// TaskStatusResponse 任务状态响应(简化版)
|
||||
type TaskStatusResponse struct {
|
||||
APIResponse
|
||||
TaskID string `json:"task_id"`
|
||||
Status string `json:"status"`
|
||||
Progress int `json:"progress"`
|
||||
}
|
||||
|
||||
// TaskDetailResponse 任务详情响应
|
||||
type TaskDetailResponse struct {
|
||||
APIResponse
|
||||
Task TaskInfo `json:"task"`
|
||||
}
|
||||
|
||||
// TaskListResponse 任务列表响应
|
||||
type TaskListResponse struct {
|
||||
APIResponse
|
||||
Tasks []TaskInfo `json:"tasks"`
|
||||
Total int `json:"total"`
|
||||
Limit int `json:"limit"`
|
||||
Offset int `json:"offset"`
|
||||
HasMore bool `json:"has_more"`
|
||||
}
|
||||
|
||||
// TaskInfo 任务详情信息
|
||||
type TaskInfo struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
Progress int `json:"progress"`
|
||||
Message string `json:"message,omitempty"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
CompletedAt string `json:"completed_at,omitempty"`
|
||||
ETA string `json:"eta,omitempty"`
|
||||
Result map[string]any `json:"result,omitempty"`
|
||||
Error *TaskError `json:"error,omitempty"`
|
||||
ExternalTaskID string `json:"external_task_id,omitempty"`
|
||||
}
|
||||
|
||||
// TaskError 任务错误
|
||||
type TaskError struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// TaskRequest 任务请求
|
||||
type TaskRequest struct {
|
||||
TaskType string `json:"task_type"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
ExternalTaskID string `json:"external_task_id,omitempty"`
|
||||
Priority string `json:"priority,omitempty"`
|
||||
CallbackURL string `json:"callback_url,omitempty"`
|
||||
CallbackToken string `json:"callback_token,omitempty"`
|
||||
}
|
||||
|
||||
// FileParam 上传文件参数
|
||||
type FileParam struct {
|
||||
FieldName string
|
||||
Filename string
|
||||
Data []byte
|
||||
MimeType string
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 能力查询
|
||||
// =============================================================================
|
||||
|
||||
// CapabilitiesResponse 能力查询响应
|
||||
type CapabilitiesResponse struct {
|
||||
APIResponse
|
||||
Capabilities CapabilitiesInfo `json:"capabilities"`
|
||||
}
|
||||
|
||||
// CapabilitiesInfo Agent 能力信息
|
||||
type CapabilitiesInfo struct {
|
||||
AgentID string `json:"agent_id"`
|
||||
AgentName string `json:"agent_name"`
|
||||
SystemID string `json:"system_id"`
|
||||
ProtocolVersion string `json:"protocol_version"`
|
||||
SupportedTaskTypes []SupportedTaskType `json:"supported_task_types"`
|
||||
SupportedTransportModes []string `json:"supported_transport_modes"`
|
||||
MaxFileSize int64 `json:"max_file_size"`
|
||||
MaxConcurrentTasks int `json:"max_concurrent_tasks"`
|
||||
SupportProgressUpdates bool `json:"support_progress_updates"`
|
||||
SupportStreaming bool `json:"support_streaming"`
|
||||
SupportHiL bool `json:"support_hil"`
|
||||
SupportGroups bool `json:"support_groups"`
|
||||
}
|
||||
|
||||
// SupportedTaskType 支持的任务类型
|
||||
type SupportedTaskType struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
EstimatedTime string `json:"estimated_time"`
|
||||
InputSchema string `json:"input_schema,omitempty"`
|
||||
OutputSchema string `json:"output_schema,omitempty"`
|
||||
}
|
||||
|
||||
// GetCapabilities 获取 Agent 能力清单
|
||||
func (c *Client) GetCapabilities(ctx context.Context) (*CapabilitiesResponse, error) {
|
||||
url := fmt.Sprintf("%s/api/v1/synapse/capabilities", c.baseURL)
|
||||
|
||||
httpReq, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
c.setAuthHeaders(httpReq)
|
||||
|
||||
resp, err := c.httpClient.Do(httpReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
|
||||
var result CapabilitiesResponse
|
||||
if err := json.Unmarshal(respBody, &result); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode response: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return &result, fmt.Errorf("API error: %s", result.Error)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 任务提交
|
||||
// =============================================================================
|
||||
|
||||
// SubmitTask 提交任务(JSON 方式)
|
||||
func (c *Client) SubmitTask(ctx context.Context, req *TaskRequest) (*TaskResponse, error) {
|
||||
url := fmt.Sprintf("%s/api/v1/synapse/task", c.baseURL)
|
||||
|
||||
body, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal request: %w", err)
|
||||
}
|
||||
|
||||
httpReq, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
c.setAuthHeaders(httpReq)
|
||||
|
||||
resp, err := c.httpClient.Do(httpReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
|
||||
var result TaskResponse
|
||||
if err := json.Unmarshal(respBody, &result); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode response: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return &result, fmt.Errorf("API error: %s", result.Error)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// SubmitTaskWithFiles 提交带文件的任务(multipart/form-data 方式)
|
||||
func (c *Client) SubmitTaskWithFiles(ctx context.Context, req *TaskRequest, files []FileParam) (*TaskResponse, error) {
|
||||
url := fmt.Sprintf("%s/api/v1/synapse/task/multipart", c.baseURL)
|
||||
|
||||
var buf bytes.Buffer
|
||||
writer := multipart.NewWriter(&buf)
|
||||
|
||||
_ = writer.WriteField("task_type", req.TaskType)
|
||||
|
||||
payloadJSON, _ := json.Marshal(req.Payload)
|
||||
_ = writer.WriteField("payload", string(payloadJSON))
|
||||
|
||||
if req.ExternalTaskID != "" {
|
||||
_ = writer.WriteField("external_task_id", req.ExternalTaskID)
|
||||
}
|
||||
if req.Priority != "" {
|
||||
_ = writer.WriteField("priority", req.Priority)
|
||||
}
|
||||
if req.CallbackURL != "" {
|
||||
_ = writer.WriteField("callback_url", req.CallbackURL)
|
||||
}
|
||||
if req.CallbackToken != "" {
|
||||
_ = writer.WriteField("callback_token", req.CallbackToken)
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
var part io.Writer
|
||||
var err error
|
||||
if f.MimeType != "" {
|
||||
part, err = writer.CreatePart(map[string][]string{
|
||||
"Content-Disposition": {fmt.Sprintf(`form-data; name="%s"; filename="%s"`, f.FieldName, f.Filename)},
|
||||
"Content-Type": {f.MimeType},
|
||||
})
|
||||
} else {
|
||||
part, err = writer.CreateFormFile(f.FieldName, f.Filename)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create form file: %w", err)
|
||||
}
|
||||
_, _ = part.Write(f.Data)
|
||||
}
|
||||
|
||||
_ = writer.Close()
|
||||
|
||||
httpReq, err := http.NewRequestWithContext(ctx, "POST", url, &buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
httpReq.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
c.setAuthHeaders(httpReq)
|
||||
|
||||
resp, err := c.httpClient.Do(httpReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
|
||||
var result TaskResponse
|
||||
if err := json.Unmarshal(respBody, &result); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode response: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return &result, fmt.Errorf("API error: %s", result.Error)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 任务查询
|
||||
// =============================================================================
|
||||
|
||||
// GetTask 获取任务详情
|
||||
func (c *Client) GetTask(ctx context.Context, taskID string) (*TaskDetailResponse, error) {
|
||||
url := fmt.Sprintf("%s/api/v1/synapse/task/%s", c.baseURL, taskID)
|
||||
|
||||
httpReq, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
c.setAuthHeaders(httpReq)
|
||||
|
||||
resp, err := c.httpClient.Do(httpReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
|
||||
var result TaskDetailResponse
|
||||
if err := json.Unmarshal(respBody, &result); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode response: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return &result, fmt.Errorf("API error: %s", result.Error)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// GetTaskStatus 获取任务状态(简化版)
|
||||
func (c *Client) GetTaskStatus(ctx context.Context, taskID string) (*TaskStatusResponse, error) {
|
||||
url := fmt.Sprintf("%s/api/v1/synapse/task/%s/status", c.baseURL, taskID)
|
||||
|
||||
httpReq, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
c.setAuthHeaders(httpReq)
|
||||
|
||||
resp, err := c.httpClient.Do(httpReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
|
||||
var result TaskStatusResponse
|
||||
if err := json.Unmarshal(respBody, &result); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode response: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return &result, fmt.Errorf("API error: %s", result.Error)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// ListTasks 获取任务列表
|
||||
func (c *Client) ListTasks(ctx context.Context, status string, limit, offset int) (*TaskListResponse, error) {
|
||||
if limit <= 0 {
|
||||
limit = 20
|
||||
}
|
||||
url := fmt.Sprintf("%s/api/v1/synapse/tasks?status=%s&limit=%d&offset=%d",
|
||||
c.baseURL, status, limit, offset)
|
||||
|
||||
httpReq, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
c.setAuthHeaders(httpReq)
|
||||
|
||||
resp, err := c.httpClient.Do(httpReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
|
||||
var result TaskListResponse
|
||||
if err := json.Unmarshal(respBody, &result); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode response: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return &result, fmt.Errorf("API error: %s", result.Error)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 任务取消
|
||||
// =============================================================================
|
||||
|
||||
// CancelTask 取消任务
|
||||
func (c *Client) CancelTask(ctx context.Context, taskID string) (*TaskResponse, error) {
|
||||
url := fmt.Sprintf("%s/api/v1/synapse/task/%s/cancel", c.baseURL, taskID)
|
||||
|
||||
httpReq, err := http.NewRequestWithContext(ctx, "POST", url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
c.setAuthHeaders(httpReq)
|
||||
|
||||
resp, err := c.httpClient.Do(httpReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
|
||||
var result TaskResponse
|
||||
if err := json.Unmarshal(respBody, &result); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode response: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return &result, fmt.Errorf("API error: %s", result.Error)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 等待任务完成
|
||||
// =============================================================================
|
||||
|
||||
// WaitForTask 等待任务完成(轮询方式)
|
||||
func (c *Client) WaitForTask(ctx context.Context, taskID string, interval time.Duration) (*TaskInfo, error) {
|
||||
if interval == 0 {
|
||||
interval = 5 * time.Second
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case <-ticker.C:
|
||||
resp, err := c.GetTask(ctx, taskID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
switch resp.Task.Status {
|
||||
case "completed", "failed", "cancelled", "rejected":
|
||||
return &resp.Task, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 回调验证
|
||||
// =============================================================================
|
||||
|
||||
// CallbackRequest 回调请求结构
|
||||
type CallbackRequest struct {
|
||||
TaskID string `json:"task_id"`
|
||||
Status string `json:"status"`
|
||||
Progress int `json:"progress"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Result map[string]any `json:"result,omitempty"`
|
||||
Error *TaskError `json:"error,omitempty"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}
|
||||
|
||||
// VerifyCallback 验证回调请求签名
|
||||
func (c *Client) VerifyCallback(r *http.Request, callbackToken string) bool {
|
||||
if callbackToken == "" {
|
||||
return true
|
||||
}
|
||||
token := r.Header.Get("X-Callback-Token")
|
||||
return token == callbackToken
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 高阶 API: 快速创建任务并等待
|
||||
// =============================================================================
|
||||
|
||||
// CreateAndWait 创建任务并等待完成
|
||||
func (c *Client) CreateAndWait(ctx context.Context, taskType string, payload map[string]any, interval time.Duration) (*TaskInfo, error) {
|
||||
req := &TaskRequest{
|
||||
TaskType: taskType,
|
||||
Payload: payload,
|
||||
Priority: "normal",
|
||||
}
|
||||
|
||||
resp, err := c.SubmitTask(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return nil, fmt.Errorf("task submission failed: %s", resp.Message)
|
||||
}
|
||||
|
||||
return c.WaitForTask(ctx, resp.TaskID, interval)
|
||||
}
|
||||
|
||||
// CreateWithCallback 创建任务并指定回调
|
||||
func (c *Client) CreateWithCallback(ctx context.Context, taskType string, payload map[string]any, callbackURL, callbackToken string) (*TaskResponse, error) {
|
||||
req := &TaskRequest{
|
||||
TaskType: taskType,
|
||||
Payload: payload,
|
||||
Priority: "normal",
|
||||
CallbackURL: callbackURL,
|
||||
CallbackToken: callbackToken,
|
||||
}
|
||||
|
||||
return c.SubmitTask(ctx, req)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 辅助方法
|
||||
// =============================================================================
|
||||
|
||||
func (c *Client) setAuthHeaders(req *http.Request) {
|
||||
if c.apiKey != "" {
|
||||
req.Header.Set("X-API-Key", c.apiKey)
|
||||
}
|
||||
req.Header.Set("Accept", "application/json")
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
//
|
||||
// 灵隙协议族(Synapse Protocol Suite)Go 语言 SDK,包含三个子包:
|
||||
//
|
||||
// - sdk/caller: P2A 协议 - 非 Agent 调用方通过 REST API 调用 Agent 能力
|
||||
// - sdk/p2p: A2A 协议 - Agent 之间的点对点任务协作通信
|
||||
// - sdk/group: A2N 协议 - 多 Agent 群组协作(群组管理、任务拆分合并、投票)
|
||||
// - sdk/p2a: P2A 协议 - 非 Agent 调用方通过 REST API 调用 Agent 能力
|
||||
// - sdk/a2a: A2A 协议 - Agent 之间的点对点任务协作通信
|
||||
// - sdk/a2n: A2N 协议 - 多 Agent 群组协作(群组管理、任务拆分合并、投票)
|
||||
//
|
||||
// 协议版本:1.0.0
|
||||
package sdk
|
||||
|
||||
@@ -1,346 +0,0 @@
|
||||
package group
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"synapse/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// Synapse Group SDK (A2N Protocol)
|
||||
// 多 Agent 群组协作客户端 SDK
|
||||
// 基于 A2N 协议:多个 Agent 组成群组进行协同工作
|
||||
// =============================================================================
|
||||
|
||||
// GroupEventHandler 群组事件处理器接口
|
||||
type GroupEventHandler interface {
|
||||
OnGroupCreated(ctx context.Context, groupID string) error
|
||||
OnGroupDisbanded(ctx context.Context, groupID string) error
|
||||
OnMemberJoined(ctx context.Context, groupID string, member types.GroupMember) error
|
||||
OnMemberLeft(ctx context.Context, groupID, agentID string) error
|
||||
OnTaskSplit(ctx context.Context, groupID, originalTaskID string, subtasks []types.Subtask) error
|
||||
OnSubtaskProgress(ctx context.Context, groupID, subtaskID string, progress int) error
|
||||
OnTaskMerged(ctx context.Context, groupID, originalTaskID string, result any) error
|
||||
OnVoteInitiated(ctx context.Context, groupID string, vote types.GroupVotePayload) error
|
||||
OnVoteResult(ctx context.Context, groupID, voteID, option string, passed bool) error
|
||||
OnEscalation(ctx context.Context, groupID, escalationID string, level int) error
|
||||
OnBroadcast(ctx context.Context, groupID string, sender types.AgentEndpoint, message []byte) error
|
||||
}
|
||||
|
||||
// Client A2N 群组协作客户端
|
||||
type Client struct {
|
||||
p2pClient *p2pClient
|
||||
groups map[string]*GroupState
|
||||
groupsMu sync.RWMutex
|
||||
eventHandler GroupEventHandler
|
||||
}
|
||||
|
||||
// p2pClient 是 P2P 客户端的简化接口
|
||||
type p2pClient interface {
|
||||
SendMessage(ctx context.Context, target types.AgentEndpoint, msg *types.UnifiedSynapseMessage) error
|
||||
AgentEndpoint() types.AgentEndpoint
|
||||
}
|
||||
|
||||
// ClientConfig A2N 客户端配置
|
||||
type ClientConfig struct {
|
||||
P2PClient *p2pClient
|
||||
EventHandler GroupEventHandler
|
||||
}
|
||||
|
||||
// GroupState 群组状态
|
||||
type GroupState struct {
|
||||
GroupID string
|
||||
Name string
|
||||
Topology types.GroupTopology
|
||||
Status types.GroupStatus
|
||||
Members []types.GroupMember
|
||||
Role string
|
||||
}
|
||||
|
||||
// NewClient 创建新的 A2N 群组客户端
|
||||
func NewClient(p2pClient interface{}, eventHandler GroupEventHandler) (*Client, error) {
|
||||
return &Client{
|
||||
groups: make(map[string]*GroupState),
|
||||
eventHandler: eventHandler,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetEventHandler 设置群组事件处理器
|
||||
func (c *Client) SetEventHandler(handler GroupEventHandler) {
|
||||
c.eventHandler = handler
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 群组管理
|
||||
// =============================================================================
|
||||
|
||||
// CreateGroup 创建群组
|
||||
func (c *Client) CreateGroup(ctx context.Context, name string, topology types.GroupTopology, config *types.GroupConfig, initialMembers []types.AgentEndpoint) (*GroupState, error) {
|
||||
groupID := fmt.Sprintf("group-%d", time.Now().UnixNano())
|
||||
|
||||
if config == nil {
|
||||
config = &types.GroupConfig{
|
||||
AllowSelfLeave: true,
|
||||
RequiresApproval: false,
|
||||
ConsensusRequired: false,
|
||||
ConsensusThreshold: 0.5,
|
||||
BroadcastMode: "all",
|
||||
}
|
||||
}
|
||||
|
||||
members := make([]types.GroupMember, 0, len(initialMembers)+1)
|
||||
for _, m := range initialMembers {
|
||||
members = append(members, types.GroupMember{
|
||||
AgentID: m.AgentID,
|
||||
AgentName: m.AgentName,
|
||||
Role: "worker",
|
||||
JoinedAt: types.NowTimestamp(),
|
||||
Status: types.GroupMemberActive,
|
||||
Capabilities: []string{},
|
||||
LoadFactor: 0,
|
||||
})
|
||||
}
|
||||
|
||||
groupState := &GroupState{
|
||||
GroupID: groupID,
|
||||
Name: name,
|
||||
Topology: topology,
|
||||
Status: types.GroupStatusActive,
|
||||
Members: members,
|
||||
Role: "coordinator",
|
||||
}
|
||||
|
||||
c.groupsMu.Lock()
|
||||
c.groups[groupID] = groupState
|
||||
c.groupsMu.Unlock()
|
||||
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnGroupCreated(ctx, groupID)
|
||||
}
|
||||
|
||||
return groupState, nil
|
||||
}
|
||||
|
||||
// JoinGroup 加入群组
|
||||
func (c *Client) JoinGroup(ctx context.Context, groupID string) error {
|
||||
c.groupsMu.Lock()
|
||||
defer c.groupsMu.Unlock()
|
||||
|
||||
if _, ok := c.groups[groupID]; !ok {
|
||||
c.groups[groupID] = &GroupState{
|
||||
GroupID: groupID,
|
||||
Status: types.GroupStatusActive,
|
||||
Role: "worker",
|
||||
}
|
||||
}
|
||||
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnMemberJoined(ctx, groupID, types.GroupMember{})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LeaveGroup 离开群组
|
||||
func (c *Client) LeaveGroup(ctx context.Context, groupID string) error {
|
||||
c.groupsMu.Lock()
|
||||
delete(c.groups, groupID)
|
||||
c.groupsMu.Unlock()
|
||||
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnMemberLeft(ctx, groupID, "")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisbandGroup 解散群组
|
||||
func (c *Client) DisbandGroup(ctx context.Context, groupID string) error {
|
||||
c.groupsMu.Lock()
|
||||
if group, ok := c.groups[groupID]; ok {
|
||||
group.Status = types.GroupStatusDisbanded
|
||||
}
|
||||
c.groupsMu.Unlock()
|
||||
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnGroupDisbanded(ctx, groupID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetGroup 获取群组信息
|
||||
func (c *Client) GetGroup(groupID string) (*GroupState, bool) {
|
||||
c.groupsMu.RLock()
|
||||
defer c.groupsMu.RUnlock()
|
||||
group, ok := c.groups[groupID]
|
||||
return group, ok
|
||||
}
|
||||
|
||||
// ListGroups 列出所有群组
|
||||
func (c *Client) ListGroups() []*GroupState {
|
||||
c.groupsMu.RLock()
|
||||
defer c.groupsMu.RUnlock()
|
||||
result := make([]*GroupState, 0, len(c.groups))
|
||||
for _, g := range c.groups {
|
||||
result = append(result, g)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 任务协作
|
||||
// =============================================================================
|
||||
|
||||
// SubmitGroupTask 提交群组任务(作为 coordinator 拆分任务)
|
||||
func (c *Client) SubmitGroupTask(ctx context.Context, groupID, taskType string, requirements map[string]any, mergeStrategy types.MergeStrategy) ([]types.Subtask, error) {
|
||||
subtasks := []types.Subtask{
|
||||
{
|
||||
ID: fmt.Sprintf("sub-%d-1", time.Now().UnixNano()),
|
||||
Description: "子任务 1",
|
||||
AssignedTo: "",
|
||||
Dependencies: []string{},
|
||||
EstimatedDuration: 60,
|
||||
Priority: 1,
|
||||
},
|
||||
}
|
||||
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnTaskSplit(ctx, groupID, "", subtasks)
|
||||
}
|
||||
|
||||
return subtasks, nil
|
||||
}
|
||||
|
||||
// SendSubtaskResult 发送子任务结果
|
||||
func (c *Client) SendSubtaskResult(ctx context.Context, groupID, subtaskID string, result any, err error) error {
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnTaskMerged(ctx, groupID, "", result)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 投票与共识
|
||||
// =============================================================================
|
||||
|
||||
// InitiateVote 发起投票
|
||||
func (c *Client) InitiateVote(ctx context.Context, groupID, question string, options []string, voterIDs []string, threshold float64, deadline time.Duration) (string, error) {
|
||||
voteID := fmt.Sprintf("vote-%d", time.Now().UnixNano())
|
||||
|
||||
vote := types.GroupVotePayload{
|
||||
VoteID: voteID,
|
||||
Question: question,
|
||||
Options: options,
|
||||
Voters: voterIDs,
|
||||
Deadline: types.Timestamp(time.Now().Add(deadline).UTC().Format(time.RFC3339)),
|
||||
Threshold: threshold,
|
||||
Status: "open",
|
||||
}
|
||||
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnVoteInitiated(ctx, groupID, vote)
|
||||
}
|
||||
|
||||
return voteID, nil
|
||||
}
|
||||
|
||||
// SubmitVote 提交投票
|
||||
func (c *Client) SubmitVote(ctx context.Context, groupID, voteID, option, reason string) error {
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnVoteResult(ctx, groupID, voteID, option, true)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 广播与升级
|
||||
// =============================================================================
|
||||
|
||||
// Broadcast 广播消息
|
||||
func (c *Client) Broadcast(ctx context.Context, groupID string, message []byte) error {
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnBroadcast(ctx, groupID, types.AgentEndpoint{}, message)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EscalateToHuman 升级到人类决策
|
||||
func (c *Client) EscalateToHuman(ctx context.Context, groupID string, level int, reason string) (string, error) {
|
||||
escalationID := fmt.Sprintf("esc-%d", time.Now().UnixNano())
|
||||
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnEscalation(ctx, groupID, escalationID, level)
|
||||
}
|
||||
|
||||
return escalationID, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 消息处理
|
||||
// =============================================================================
|
||||
|
||||
// HandleMessage 处理群组消息
|
||||
func (c *Client) HandleMessage(ctx context.Context, msg *types.UnifiedSynapseMessage) (*types.UnifiedSynapseMessage, error) {
|
||||
groupID := msg.GroupID
|
||||
|
||||
switch types.GroupMessageType(msg.Type) {
|
||||
case types.GroupCreate:
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnGroupCreated(ctx, groupID)
|
||||
}
|
||||
|
||||
case types.GroupDisband:
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnGroupDisbanded(ctx, groupID)
|
||||
}
|
||||
|
||||
case types.GroupMemberUpdate:
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnMemberJoined(ctx, groupID, types.GroupMember{})
|
||||
}
|
||||
|
||||
case types.GroupTaskSplit:
|
||||
var payload types.GroupTaskSplitPayload
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnTaskSplit(ctx, groupID, payload.OriginalTask.ID, payload.Subtasks)
|
||||
}
|
||||
|
||||
case types.GroupTaskProgress:
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnSubtaskProgress(ctx, groupID, "", 0)
|
||||
}
|
||||
|
||||
case types.GroupTaskMerge:
|
||||
var payload types.GroupTaskMergePayload
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnTaskMerged(ctx, groupID, payload.OriginalTaskID, payload.MergedResult)
|
||||
}
|
||||
|
||||
case types.GroupVote:
|
||||
var payload types.GroupVotePayload
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnVoteInitiated(ctx, groupID, payload)
|
||||
}
|
||||
|
||||
case types.GroupConsensus:
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnVoteResult(ctx, groupID, "", "", true)
|
||||
}
|
||||
|
||||
case types.GroupConsensusEscalate, types.GroupSubtaskEscalate:
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnEscalation(ctx, groupID, "", 1)
|
||||
}
|
||||
|
||||
case types.GroupBroadcast:
|
||||
if c.eventHandler != nil {
|
||||
_ = c.eventHandler.OnBroadcast(ctx, groupID, msg.Sender, msg.Payload)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,459 +0,0 @@
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"synapse/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// Synapse P2P SDK (A2A Protocol)
|
||||
// Agent 间点对点通信客户端 SDK
|
||||
// 基于 A2A 协议:两个 Agent 之间进行点对点任务协作
|
||||
// =============================================================================
|
||||
|
||||
// MessageCallbackHandler 消息回调处理器接口
|
||||
type MessageCallbackHandler interface {
|
||||
HandleTaskRequest(ctx context.Context, msg *types.UnifiedSynapseMessage) (*types.UnifiedSynapseMessage, error)
|
||||
HandleTaskStatusUpdate(ctx context.Context, msg *types.UnifiedSynapseMessage) error
|
||||
HandleTaskComplete(ctx context.Context, msg *types.UnifiedSynapseMessage) error
|
||||
HandleTaskFailed(ctx context.Context, msg *types.UnifiedSynapseMessage) error
|
||||
HandleNeedHuman(ctx context.Context, msg *types.UnifiedSynapseMessage) error
|
||||
}
|
||||
|
||||
// Client A2A 点对点通信客户端
|
||||
type Client struct {
|
||||
agentEndpoint types.AgentEndpoint
|
||||
authSecret string
|
||||
callbackHandler MessageCallbackHandler
|
||||
tasks map[string]*P2PTaskState
|
||||
tasksMu sync.RWMutex
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// ClientConfig A2A 客户端配置
|
||||
type ClientConfig struct {
|
||||
AgentEndpoint types.AgentEndpoint
|
||||
AuthSecret string
|
||||
CallbackPort int
|
||||
Timeout time.Duration
|
||||
MaxRetries int
|
||||
}
|
||||
|
||||
// P2PTaskState P2P 任务状态
|
||||
type P2PTaskState struct {
|
||||
TaskID string
|
||||
Status types.P2PTaskStatus
|
||||
Progress int
|
||||
Message string
|
||||
Result any
|
||||
Error *types.SynapseError
|
||||
}
|
||||
|
||||
// NewClient 创建新的 A2A 客户端
|
||||
func NewClient(config *ClientConfig) (*Client, error) {
|
||||
if config.Timeout == 0 {
|
||||
config.Timeout = 30 * time.Second
|
||||
}
|
||||
|
||||
return &Client{
|
||||
agentEndpoint: config.AgentEndpoint,
|
||||
authSecret: config.AuthSecret,
|
||||
tasks: make(map[string]*P2PTaskState),
|
||||
httpClient: &http.Client{Timeout: config.Timeout},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetMessageHandler 设置消息回调处理器
|
||||
func (c *Client) SetMessageHandler(handler MessageCallbackHandler) {
|
||||
c.callbackHandler = handler
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 任务状态管理
|
||||
// =============================================================================
|
||||
|
||||
// GetTaskState 获取任务状态
|
||||
func (c *Client) GetTaskState(taskID string) (*P2PTaskState, bool) {
|
||||
c.tasksMu.RLock()
|
||||
defer c.tasksMu.RUnlock()
|
||||
state, ok := c.tasks[taskID]
|
||||
return state, ok
|
||||
}
|
||||
|
||||
// ListTasks 列出所有任务
|
||||
func (c *Client) ListTasks() map[string]*P2PTaskState {
|
||||
c.tasksMu.RLock()
|
||||
defer c.tasksMu.RUnlock()
|
||||
result := make(map[string]*P2PTaskState, len(c.tasks))
|
||||
for k, v := range c.tasks {
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *Client) setTaskState(taskID string, status types.P2PTaskStatus, progress int, message string) {
|
||||
c.tasksMu.Lock()
|
||||
defer c.tasksMu.Unlock()
|
||||
if _, ok := c.tasks[taskID]; !ok {
|
||||
c.tasks[taskID] = &P2PTaskState{TaskID: taskID}
|
||||
}
|
||||
c.tasks[taskID].Status = status
|
||||
c.tasks[taskID].Progress = progress
|
||||
c.tasks[taskID].Message = message
|
||||
}
|
||||
|
||||
func (c *Client) setTaskResult(taskID string, result any) {
|
||||
c.tasksMu.Lock()
|
||||
defer c.tasksMu.Unlock()
|
||||
if state, ok := c.tasks[taskID]; ok {
|
||||
state.Result = result
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) setTaskError(taskID string, err *types.SynapseError) {
|
||||
c.tasksMu.Lock()
|
||||
defer c.tasksMu.Unlock()
|
||||
if state, ok := c.tasks[taskID]; ok {
|
||||
state.Error = err
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 消息发送
|
||||
// =============================================================================
|
||||
|
||||
// SendTaskRequest 发送任务请求
|
||||
func (c *Client) SendTaskRequest(ctx context.Context, target types.AgentEndpoint, taskType string, payload map[string]any) (string, error) {
|
||||
taskID := types.GenerateTaskID()
|
||||
|
||||
taskReq := types.TaskRequestPayload{}
|
||||
taskReq.Task.ID = taskID
|
||||
taskReq.Task.Type = taskType
|
||||
taskReq.Task.Priority = types.PriorityNormal
|
||||
taskReq.Task.Payload = payload
|
||||
|
||||
payloadBytes, _ := json.Marshal(taskReq)
|
||||
|
||||
msg := &types.UnifiedSynapseMessage{
|
||||
ID: types.GenerateMessageID(),
|
||||
Version: "1.0.0",
|
||||
Type: string(types.P2PTaskRequest),
|
||||
Sender: c.agentEndpoint,
|
||||
Receiver: &target,
|
||||
Payload: payloadBytes,
|
||||
Priority: types.PriorityNormal,
|
||||
Timestamp: types.NowTimestamp(),
|
||||
}
|
||||
|
||||
c.setTaskState(taskID, types.P2PStatusPending, 0, "任务已发送")
|
||||
|
||||
err := c.sendMessage(ctx, target.Endpoint, msg)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to send task request: %w", err)
|
||||
}
|
||||
|
||||
return taskID, nil
|
||||
}
|
||||
|
||||
// SendTaskCancel 发送任务取消
|
||||
func (c *Client) SendTaskCancel(ctx context.Context, target types.AgentEndpoint, taskID, reason string) error {
|
||||
cancelPayload := types.TaskCancelPayload{
|
||||
TaskID: taskID,
|
||||
Reason: reason,
|
||||
CancelledBy: c.agentEndpoint.AgentID,
|
||||
}
|
||||
|
||||
payloadBytes, _ := json.Marshal(cancelPayload)
|
||||
|
||||
msg := &types.UnifiedSynapseMessage{
|
||||
ID: types.GenerateMessageID(),
|
||||
Version: "1.0.0",
|
||||
Type: string(types.P2PTaskCancel),
|
||||
Sender: c.agentEndpoint,
|
||||
Receiver: &target,
|
||||
Payload: payloadBytes,
|
||||
Timestamp: types.NowTimestamp(),
|
||||
}
|
||||
|
||||
c.setTaskState(taskID, types.P2PStatusCancelled, 0, "任务已取消")
|
||||
|
||||
return c.sendMessage(ctx, target.Endpoint, msg)
|
||||
}
|
||||
|
||||
// SendHumanDecision 发送人类决策结果
|
||||
func (c *Client) SendHumanDecision(ctx context.Context, target types.AgentEndpoint, taskID string, decision types.HumanDecisionPayload) error {
|
||||
payloadBytes, _ := json.Marshal(decision)
|
||||
|
||||
msg := &types.UnifiedSynapseMessage{
|
||||
ID: types.GenerateMessageID(),
|
||||
Version: "1.0.0",
|
||||
Type: string(types.P2PHumanDecision),
|
||||
Sender: c.agentEndpoint,
|
||||
Receiver: &target,
|
||||
Payload: payloadBytes,
|
||||
Timestamp: types.NowTimestamp(),
|
||||
}
|
||||
|
||||
return c.sendMessage(ctx, target.Endpoint, msg)
|
||||
}
|
||||
|
||||
// SendCapabilityQuery 查询对方能力
|
||||
func (c *Client) SendCapabilityQuery(ctx context.Context, target types.AgentEndpoint) ([]string, error) {
|
||||
msg := &types.UnifiedSynapseMessage{
|
||||
ID: types.GenerateMessageID(),
|
||||
Version: "1.0.0",
|
||||
Type: string(types.P2PCapabilityQuery),
|
||||
Sender: c.agentEndpoint,
|
||||
Receiver: &target,
|
||||
Payload: []byte("{}"),
|
||||
Timestamp: types.NowTimestamp(),
|
||||
}
|
||||
|
||||
respMsg, err := c.sendRequest(ctx, target.Endpoint, msg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query capabilities: %w", err)
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
Capabilities []string `json:"capabilities"`
|
||||
}
|
||||
if err := json.Unmarshal(respMsg.Payload, &resp); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse capabilities: %w", err)
|
||||
}
|
||||
|
||||
return resp.Capabilities, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 消息签名与验证
|
||||
// =============================================================================
|
||||
|
||||
func (c *Client) signMessage(msg *types.UnifiedSynapseMessage) string {
|
||||
if c.authSecret == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
h := hmac.New(sha256.New, []byte(c.authSecret))
|
||||
h.Write([]byte(msg.ID + "." + string(msg.Payload)))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// VerifySignature 验证消息签名
|
||||
func (c *Client) VerifySignature(msg *types.UnifiedSynapseMessage, secret string) bool {
|
||||
if secret == "" {
|
||||
return true
|
||||
}
|
||||
if msg.Signature == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
h := hmac.New(sha256.New, []byte(secret))
|
||||
h.Write([]byte(msg.ID + "." + string(msg.Payload)))
|
||||
expected := hex.EncodeToString(h.Sum(nil))
|
||||
return hmac.Equal([]byte(msg.Signature), []byte(expected))
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 消息发送底层
|
||||
// =============================================================================
|
||||
|
||||
func (c *Client) sendMessage(ctx context.Context, endpoint string, msg *types.UnifiedSynapseMessage) error {
|
||||
msg.Signature = c.signMessage(msg)
|
||||
|
||||
body, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal message: %w", err)
|
||||
}
|
||||
|
||||
httpReq, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
httpReq.Header.Set("X-Synapse-Signature", msg.Signature)
|
||||
|
||||
resp, err := c.httpClient.Do(httpReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("remote error: %s", string(respBody))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) sendRequest(ctx context.Context, endpoint string, msg *types.UnifiedSynapseMessage) (*types.UnifiedSynapseMessage, error) {
|
||||
msg.Signature = c.signMessage(msg)
|
||||
|
||||
body, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal message: %w", err)
|
||||
}
|
||||
|
||||
httpReq, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
httpReq.Header.Set("X-Synapse-Signature", msg.Signature)
|
||||
|
||||
resp, err := c.httpClient.Do(httpReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return nil, fmt.Errorf("remote error: %s", string(respBody))
|
||||
}
|
||||
|
||||
var respMsg types.UnifiedSynapseMessage
|
||||
if err := json.Unmarshal(respBody, &respMsg); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse response: %w", err)
|
||||
}
|
||||
|
||||
return &respMsg, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 等待任务完成
|
||||
// =============================================================================
|
||||
|
||||
// WaitForTask 等待任务完成(轮询方式,通过回调更新状态)
|
||||
func (c *Client) WaitForTask(ctx context.Context, taskID string, timeout time.Duration) (*P2PTaskState, error) {
|
||||
deadline := time.Now().Add(timeout)
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case <-ticker.C:
|
||||
if time.Now().After(deadline) {
|
||||
return nil, fmt.Errorf("task timeout")
|
||||
}
|
||||
|
||||
state, ok := c.GetTaskState(taskID)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch state.Status {
|
||||
case types.P2PStatusCompleted, types.P2PStatusFailed, types.P2PStatusCancelled, types.P2PStatusRejected:
|
||||
return state, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 回调处理
|
||||
// =============================================================================
|
||||
|
||||
// HandleMessage 处理收到的消息(供 HTTP 服务调用)
|
||||
func (c *Client) HandleMessage(ctx context.Context, msg *types.UnifiedSynapseMessage) (*types.UnifiedSynapseMessage, error) {
|
||||
if c.authSecret != "" && !c.VerifySignature(msg, c.authSecret) {
|
||||
return nil, fmt.Errorf("invalid signature")
|
||||
}
|
||||
|
||||
switch types.P2PMessageType(msg.Type) {
|
||||
case types.P2PTaskRequest:
|
||||
if c.callbackHandler != nil {
|
||||
return c.callbackHandler.HandleTaskRequest(ctx, msg)
|
||||
}
|
||||
return c.ackMessage(msg), nil
|
||||
|
||||
case types.P2PTaskConfirm:
|
||||
var payload types.TaskConfirmPayload
|
||||
_ = json.Unmarshal(msg.Payload, &payload)
|
||||
c.setTaskState(payload.TaskID, types.P2PStatusConfirmed, 0, "任务已确认")
|
||||
return c.ackMessage(msg), nil
|
||||
|
||||
case types.P2PTaskReject:
|
||||
var payload types.TaskRejectPayload
|
||||
_ = json.Unmarshal(msg.Payload, &payload)
|
||||
c.setTaskState(payload.TaskID, types.P2PStatusRejected, 0, payload.Reason.Message)
|
||||
return c.ackMessage(msg), nil
|
||||
|
||||
case types.P2PTaskStatusUpdate:
|
||||
var payload types.TaskStatusUpdatePayload
|
||||
_ = json.Unmarshal(msg.Payload, &payload)
|
||||
c.setTaskState(payload.TaskID, types.P2PTaskStatus(payload.Status), payload.Progress, payload.Message)
|
||||
if c.callbackHandler != nil {
|
||||
_ = c.callbackHandler.HandleTaskStatusUpdate(ctx, msg)
|
||||
}
|
||||
return c.ackMessage(msg), nil
|
||||
|
||||
case types.P2PTaskComplete:
|
||||
var payload types.TaskCompletePayload
|
||||
_ = json.Unmarshal(msg.Payload, &payload)
|
||||
c.setTaskState(payload.TaskID, types.P2PStatusCompleted, 100, payload.Summary)
|
||||
c.setTaskResult(payload.TaskID, payload.Result.Output)
|
||||
if c.callbackHandler != nil {
|
||||
_ = c.callbackHandler.HandleTaskComplete(ctx, msg)
|
||||
}
|
||||
return c.ackMessage(msg), nil
|
||||
|
||||
case types.P2PTaskFailed:
|
||||
var payload types.TaskFailedPayload
|
||||
_ = json.Unmarshal(msg.Payload, &payload)
|
||||
c.setTaskState(payload.TaskID, types.P2PStatusFailed, 0, payload.Error.Message)
|
||||
c.setTaskError(payload.TaskID, types.NewSynapseError(
|
||||
types.SynapseErrorCode(0),
|
||||
payload.Error.Message,
|
||||
nil,
|
||||
))
|
||||
if c.callbackHandler != nil {
|
||||
_ = c.callbackHandler.HandleTaskFailed(ctx, msg)
|
||||
}
|
||||
return c.ackMessage(msg), nil
|
||||
|
||||
case types.P2PTaskNeedHuman:
|
||||
var payload types.TaskNeedHumanPayload
|
||||
_ = json.Unmarshal(msg.Payload, &payload)
|
||||
c.setTaskState(payload.TaskID, types.P2PStatusNeedHuman, 0, payload.Reason.Message)
|
||||
if c.callbackHandler != nil {
|
||||
_ = c.callbackHandler.HandleNeedHuman(ctx, msg)
|
||||
}
|
||||
return c.ackMessage(msg), nil
|
||||
|
||||
case types.P2PCapabilityQuery:
|
||||
return nil, nil
|
||||
|
||||
default:
|
||||
return c.ackMessage(msg), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) ackMessage(msg *types.UnifiedSynapseMessage) *types.UnifiedSynapseMessage {
|
||||
return &types.UnifiedSynapseMessage{
|
||||
ID: types.GenerateMessageID(),
|
||||
Version: msg.Version,
|
||||
Type: string(types.P2PAck),
|
||||
Sender: c.agentEndpoint,
|
||||
Receiver: &msg.Sender,
|
||||
ReplyTo: msg.ID,
|
||||
Payload: []byte("{}"),
|
||||
Timestamp: types.NowTimestamp(),
|
||||
}
|
||||
}
|
||||
@@ -1,328 +0,0 @@
|
||||
# 灵隙原始协议(Synapse Raw Protocol)
|
||||
|
||||
> 版本:1.0.0
|
||||
> 状态:草案
|
||||
> 类型:纲领性文档
|
||||
|
||||
---
|
||||
|
||||
## 1. 协议愿景
|
||||
|
||||
灵隙(Synapse,原 Agent-to-Agent)协议旨在定义一套通用的、可扩展的智能体间通信标准,使不同系统、不同厂商、不同能力的智能体能够在统一的协议框架下进行协作、交易与能力组合。
|
||||
|
||||
本协议是灵隙协议族(Synapse Protocol Suite)的原始纲领,定义核心设计理念、架构原则与术语体系,为所有衍生协议提供顶层指导。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心设计理念
|
||||
|
||||
### 2.1 去中心化对等(Peer-to-Peer)
|
||||
- 所有 Agent 在协议层面地位平等,无中心化控制节点
|
||||
- 每个 Agent 既是服务提供者,也是服务消费者
|
||||
- Agent 间直接通信,不依赖中间 broker(传输层可选择不同实现)
|
||||
|
||||
### 2.2 能力驱动(Capability-Driven)
|
||||
- 通信以能力为核心,而非以身份为核心
|
||||
- Agent 声明自身能力,按需发现与调用
|
||||
- 能力可组合、可分解、可升级
|
||||
|
||||
### 2.3 任务导向(Task-Oriented)
|
||||
- 所有交互围绕"任务"展开
|
||||
- 任务有明确的生命周期、状态机与超时机制
|
||||
- 任务支持拆分、合并、委派与协作
|
||||
|
||||
### 2.4 人类可介入(Human-in-the-Loop)
|
||||
- 协议原生支持人类介入决策
|
||||
- Agent 可主动请求人类判断
|
||||
- 人类决策优先于 Agent 自动决策
|
||||
- 支持多级升级(escalation)机制
|
||||
|
||||
### 2.5 群组协作(Group Collaboration)
|
||||
- 支持多 Agent 组成临时或永久群组
|
||||
- 群组内有角色分工与权限控制
|
||||
- 支持任务拆分-执行-合并的 MapReduce 模式
|
||||
- 支持投票、共识等群体决策机制
|
||||
|
||||
### 2.6 传输无关(Transport Agnostic)
|
||||
- 协议语义与底层传输分离
|
||||
- 支持 HTTP/2、gRPC、WebSocket、消息队列等多种传输
|
||||
- 支持回调(Callback)、轮询(Polling)、同步(Sync)、混合(Hybrid)等多种交互模式
|
||||
|
||||
### 2.7 安全可信(Secure & Trustworthy)
|
||||
- 消息级签名与验证
|
||||
- Agent 身份认证与信任等级
|
||||
- 支持加密传输
|
||||
- 可审计、可追溯
|
||||
|
||||
---
|
||||
|
||||
## 3. 协议分层架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 应用层 (Application) │
|
||||
│ 业务逻辑、工作流、领域特定逻辑 │
|
||||
├─────────────────────────────────────────┤
|
||||
│ 协议层 (Protocol) │
|
||||
├─────────────┬───────────────────────────┤
|
||||
│ A2A 协议 │ A2N 组群协议 │ ← 本协议族
|
||||
│ (P2P) │ (Group) │
|
||||
├─────────────┴───────────────────────────┤
|
||||
│ 传输层 (Transport) │
|
||||
│ HTTP / gRPC / WebSocket / MQ ... │
|
||||
├─────────────────────────────────────────┤
|
||||
│ 网络层 (Network) │
|
||||
│ TCP / UDP / QUIC ... │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.1 协议族构成
|
||||
|
||||
| 协议名称 | 缩写 | 描述 | 适用场景 |
|
||||
|---------|------|------|---------|
|
||||
| 灵隙原始协议(Synapse Raw Protocol) | A2A-Raw | 顶层纲领,定义理念、术语、原则 | 指导所有协议设计 |
|
||||
| A2A 协议 | A2A-Base | 点对点任务协作协议 | 两个 Agent 间的任务交互 |
|
||||
| A2N 组群协议 | A2N-Group | 多 Agent 群组协作协议 | 多个 Agent 协同完成任务 |
|
||||
| P2A 调用协议 | P2A-Caller | 非 Agent 调用者接入协议 | 普通服务/前端调用 Agent 能力 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 核心概念与术语
|
||||
|
||||
### 4.1 Agent(智能体)
|
||||
能够自主感知环境、做出决策并执行动作的计算实体。
|
||||
|
||||
**属性:**
|
||||
- `agent_id`:Agent 唯一标识
|
||||
- `system_id`:所属系统标识
|
||||
- `endpoint`:通信端点地址
|
||||
- `capabilities`:能力清单
|
||||
- `trust_level`:信任等级
|
||||
|
||||
### 4.2 Task(任务)
|
||||
Agent 间协作的基本单位,具有明确的输入、输出与生命周期。
|
||||
|
||||
**属性:**
|
||||
- `task_id`:任务唯一标识
|
||||
- `task_type`:任务类型
|
||||
- `payload`:任务载荷
|
||||
- `status`:任务状态
|
||||
- `priority`:优先级
|
||||
- `deadline`:截止时间
|
||||
|
||||
### 4.3 Message(消息)
|
||||
Agent 间通信的基本单位。
|
||||
|
||||
**属性:**
|
||||
- `message_id`:消息唯一标识
|
||||
- `type`:消息类型
|
||||
- `sender`:发送方
|
||||
- `receiver`:接收方(P2P)
|
||||
- `group_id`:群组标识(群组)
|
||||
- `payload`:消息载荷
|
||||
- `correlation_id`:关联标识(用于请求-响应关联)
|
||||
- `timestamp`:时间戳
|
||||
- `signature`:签名
|
||||
|
||||
### 4.4 Capability(能力)
|
||||
Agent 能够执行的任务类型的声明。
|
||||
|
||||
**属性:**
|
||||
- `type`:能力对应的任务类型
|
||||
- `name`:能力名称
|
||||
- `description`:能力描述
|
||||
- `input_schema`:输入参数 schema
|
||||
- `output_schema`:输出结果 schema
|
||||
- `estimated_time`:预估耗时
|
||||
|
||||
### 4.5 Group(群组)
|
||||
由多个 Agent 组成的协作单元。
|
||||
|
||||
**属性:**
|
||||
- `group_id`:群组唯一标识
|
||||
- `name`:群组名称
|
||||
- `topology`:拓扑结构
|
||||
- `members`:成员列表
|
||||
- `roles`:角色定义
|
||||
- `status`:群组状态
|
||||
|
||||
### 4.6 Human-in-the-Loop(人在回路)
|
||||
在 Agent 协作过程中,人类可介入进行决策的机制。
|
||||
|
||||
**关键概念:**
|
||||
- `escalation`:升级(将问题提交给人类)
|
||||
- `decision`:决策(人类做出的判断)
|
||||
- `resume`:恢复(人类决策后继续执行)
|
||||
|
||||
---
|
||||
|
||||
## 5. 设计原则
|
||||
|
||||
### 5.1 简约性(Simplicity)
|
||||
- 核心机制保持简单
|
||||
- 复杂功能通过组合基础机制实现
|
||||
- 可选功能通过能力声明来协商
|
||||
|
||||
### 5.2 可扩展性(Extensibility)
|
||||
- 消息类型可扩展
|
||||
- 能力类型可扩展
|
||||
- 传输方式可扩展
|
||||
- 通过版本号与能力协商保证向后兼容
|
||||
|
||||
### 5.3 容错性(Fault Tolerance)
|
||||
- 网络不可靠是常态
|
||||
- 消息可能丢失、重复、延迟
|
||||
- Agent 可能故障、离线
|
||||
- 协议必须有重试、超时、降级机制
|
||||
|
||||
### 5.4 可观测性(Observability)
|
||||
- 所有交互可追踪
|
||||
- 任务状态可查询
|
||||
- 支持日志、指标、链路追踪
|
||||
|
||||
### 5.5 互操作性(Interoperability)
|
||||
- 不同语言、不同框架的 Agent 可互操作
|
||||
- 协议规范优先于具体实现
|
||||
- 提供标准测试用例验证兼容性
|
||||
|
||||
---
|
||||
|
||||
## 6. 消息模型总则
|
||||
|
||||
### 6.1 统一消息信封
|
||||
所有协议共享统一的消息信封结构,具体载荷由消息类型决定。
|
||||
|
||||
```
|
||||
UnifiedSynapseMessage
|
||||
├── id // 消息ID
|
||||
├── version // 协议版本
|
||||
├── type // 消息类型
|
||||
├── sender // 发送方 AgentEndpoint
|
||||
├── receiver? // 接收方(P2P 时)
|
||||
├── group_id? // 群组ID(群组时)
|
||||
├── correlation_id // 关联ID
|
||||
├── reply_to? // 回复的消息ID
|
||||
├── payload // 消息载荷(JSON)
|
||||
├── priority? // 优先级
|
||||
├── signature? // 签名
|
||||
├── timestamp // 发送时间
|
||||
└── expires_at? // 过期时间
|
||||
```
|
||||
|
||||
### 6.2 消息命名规范
|
||||
- 使用 `领域:动作` 格式
|
||||
- 全小写,用下划线分隔
|
||||
- 示例:`p2p:task_request`, `group:create`, `hil:decision`
|
||||
|
||||
### 6.3 请求-响应模式
|
||||
- 请求消息必须携带唯一 `id`
|
||||
- 响应消息通过 `reply_to` 关联请求
|
||||
- 支持 ACK/NACK 机制
|
||||
- 超时未收到响应应重试或报错
|
||||
|
||||
---
|
||||
|
||||
## 7. 任务生命周期总则
|
||||
|
||||
### 7.1 状态机
|
||||
|
||||
```
|
||||
┌──────────┐
|
||||
│ pending │ 待确认
|
||||
└────┬─────┘
|
||||
│ 确认/拒绝
|
||||
┌─────┴──────┐
|
||||
▼ ▼
|
||||
┌─────────┐ ┌──────────┐
|
||||
│confirmed│ │ rejected │ 已拒绝(终态)
|
||||
└────┬────┘ └──────────┘
|
||||
│ 开始执行
|
||||
▼
|
||||
┌──────────┐
|
||||
│executing │ 执行中
|
||||
└────┬─────┘
|
||||
│ 信息缺失
|
||||
┌────▼─────┐
|
||||
│info_needed│ 需要信息
|
||||
└────┬─────┘
|
||||
│ 信息补充
|
||||
▼
|
||||
回到 executing
|
||||
│
|
||||
├──────────────┐
|
||||
│ 需人类决策 │
|
||||
▼ ▼
|
||||
┌──────────┐ ┌───────────┐
|
||||
│need_human│ │ 各种终态 │
|
||||
└────┬─────┘ └───────────┘
|
||||
│ 人类决策
|
||||
▼
|
||||
回到 executing
|
||||
│
|
||||
┌────┴────┬─────────┐
|
||||
▼ ▼ ▼
|
||||
completed failed cancelled
|
||||
(完成) (失败) (取消)
|
||||
```
|
||||
|
||||
### 7.2 终态
|
||||
- `completed`:成功完成
|
||||
- `failed`:执行失败
|
||||
- `cancelled`:被取消
|
||||
- `rejected`:被拒绝
|
||||
|
||||
---
|
||||
|
||||
## 8. 安全模型总则
|
||||
|
||||
### 8.1 身份认证
|
||||
- 每个 Agent 有唯一身份
|
||||
- 支持 HMAC、JWT、OAuth2 等多种认证方式
|
||||
- 信任等级机制
|
||||
|
||||
### 8.2 消息完整性
|
||||
- 消息签名机制
|
||||
- 防止篡改与伪造
|
||||
|
||||
### 8.3 机密性
|
||||
- 支持端到端加密
|
||||
- 敏感载荷加密传输
|
||||
|
||||
### 8.4 访问控制
|
||||
- 基于信任等级的权限控制
|
||||
- 群组内基于角色的权限控制
|
||||
|
||||
---
|
||||
|
||||
## 9. 非目标
|
||||
|
||||
以下内容**不在**本原始协议的讨论范围内:
|
||||
|
||||
1. **具体传输协议的实现细节** - 由各传输层实现定义
|
||||
2. **特定领域的任务类型** - 由应用层或领域扩展定义
|
||||
3. **Agent 内部实现架构** - 仅定义对外接口与行为
|
||||
4. **具体编程语言绑定** - 语言无关,可多语言实现
|
||||
5. **UI/UX 设计** - 人机界面不在协议范围内
|
||||
|
||||
---
|
||||
|
||||
## 10. 参考实现
|
||||
|
||||
本协议族的参考实现位于:`dp-headless/synapse/`
|
||||
|
||||
| 模块 | 对应协议 | 文件路径 |
|
||||
|------|---------|---------|
|
||||
| types 包 | 公共类型定义 | `types/` |
|
||||
| p2p 包 | A2A 协议 | `p2p/` |
|
||||
| group 包 | A2N 组群协议 | `group/` |
|
||||
| externalapi 包 | P2A 调用协议 | `externalapi/` |
|
||||
| sdk 包 | P2A 客户端 SDK | `sdk/` |
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:协议演进
|
||||
|
||||
本原始协议作为顶层指导,保持相对稳定。各具体协议可独立版本化演进,但需遵循本协议定义的核心原则。
|
||||
|
||||
**版本历史:**
|
||||
- v1.0.0:初始版本,定义核心概念与设计原则
|
||||
@@ -1,465 +0,0 @@
|
||||
# A2A 协议(Agent-to-Agent Protocol)
|
||||
|
||||
> 版本:1.0.0
|
||||
> 状态:草案
|
||||
> 类型:实现规范
|
||||
> 基于:[灵隙原始协议(Synapse Raw Protocol) v1.0.0](./01-synapse-raw-protocol.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 概述
|
||||
|
||||
A2A 协议定义了两个 Agent 之间进行点对点(P2P)任务协作的完整规范,包括消息类型、交互流程、状态机、接口定义与错误处理。
|
||||
|
||||
### 1.1 适用场景
|
||||
- 单个任务请求与响应
|
||||
- 任务状态实时同步
|
||||
- 任务取消
|
||||
- 信息补全交互
|
||||
- 人类介入决策
|
||||
|
||||
### 1.2 非适用场景
|
||||
- 多 Agent 群组协作(参见 A2N 组群协议)
|
||||
- 非 Agent 调用方接入(参见 P2A 调用协议)
|
||||
|
||||
---
|
||||
|
||||
## 2. 消息类型
|
||||
|
||||
### 2.1 消息类型清单
|
||||
|
||||
所有消息类型前缀:`p2p:`
|
||||
|
||||
| 消息类型 | 方向 | 说明 |
|
||||
|---------|------|------|
|
||||
| `p2p:task_request` | 请求方 → 执行方 | 提交任务请求 |
|
||||
| `p2p:task_confirm` | 执行方 → 请求方 | 确认接受任务 |
|
||||
| `p2p:task_reject` | 执行方 → 请求方 | 拒绝任务 |
|
||||
| `p2p:task_need_info` | 执行方 → 请求方 | 请求补充信息 |
|
||||
| `p2p:task_info_provided` | 请求方 → 执行方 | 提供补充信息 |
|
||||
| `p2p:task_status_update` | 执行方 → 请求方 | 任务进度更新 |
|
||||
| `p2p:task_complete` | 执行方 → 请求方 | 任务完成 |
|
||||
| `p2p:task_failed` | 执行方 → 请求方 | 任务失败 |
|
||||
| `p2p:task_cancel` | 请求方 → 执行方 | 取消任务 |
|
||||
| `p2p:task_ack` | 双向 | 任务级确认 |
|
||||
| `p2p:ack` | 双向 | 通用确认 |
|
||||
| `p2p:nack` | 双向 | 通用否定确认(错误) |
|
||||
| `p2p:heartbeat` | 双向 | 心跳保活 |
|
||||
| `p2p:capability_query` | 双向 | 查询对方能力 |
|
||||
| `p2p:capability_response` | 双向 | 返回能力清单 |
|
||||
| `p2p:task_need_human` | 执行方 → 请求方 | 请求人类介入 |
|
||||
| `p2p:human_decision` | 请求方 → 执行方 | 人类决策结果 |
|
||||
|
||||
### 2.2 消息类型完整定义
|
||||
|
||||
参见代码:[types/types.go](../types/types.go)
|
||||
|
||||
---
|
||||
|
||||
## 3. 任务状态机
|
||||
|
||||
### 3.1 状态定义
|
||||
|
||||
| 状态 | 说明 |
|
||||
|------|------|
|
||||
| `idle` | 空闲(初始状态) |
|
||||
| `pending` | 待确认(已发送请求,等待确认) |
|
||||
| `confirmed` | 已确认(对方接受任务) |
|
||||
| `executing` | 执行中 |
|
||||
| `info_needed` | 需要信息 |
|
||||
| `info_provided` | 信息已提供 |
|
||||
| `need_human` | 需要人类介入 |
|
||||
| `human_deciding` | 人类决策中 |
|
||||
| `human_approved` | 人类已批准 |
|
||||
| `human_rejected` | 人类已拒绝 |
|
||||
| `completed` | 已完成(终态) |
|
||||
| `failed` | 已失败(终态) |
|
||||
| `cancelled` | 已取消(终态) |
|
||||
| `rejected` | 已拒绝(终态) |
|
||||
|
||||
### 3.2 状态流转图
|
||||
|
||||
```
|
||||
请求方视角:
|
||||
|
||||
idle → pending → confirmed → executing → completed
|
||||
↓ ↓ → failed
|
||||
rejected info_needed → cancelled
|
||||
↑↓
|
||||
info_provided
|
||||
↓
|
||||
need_human
|
||||
↑↓
|
||||
human_decision
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 核心交互流程
|
||||
|
||||
### 4.1 正常任务提交流程
|
||||
|
||||
```
|
||||
请求方 Agent 执行方 Agent
|
||||
| |
|
||||
|-------- task_request ------------->| 1. 提交任务
|
||||
| |
|
||||
|<------- task_confirm / task_reject -| 2. 确认/拒绝
|
||||
| |
|
||||
|<------- task_status_update --------| 3. 进度更新(多次)
|
||||
| |
|
||||
|<------- task_complete -------------| 4. 完成通知
|
||||
| |
|
||||
|-------- task_ack ----------------->| 5. 确认收到
|
||||
| |
|
||||
```
|
||||
|
||||
### 4.2 信息补全流程
|
||||
|
||||
```
|
||||
请求方 Agent 执行方 Agent
|
||||
| |
|
||||
|-------- task_request ------------->|
|
||||
| |
|
||||
|<------- task_need_info ------------| 发现信息缺失
|
||||
| |
|
||||
|-------- task_info_provided ------->| 补充信息
|
||||
| |
|
||||
|<------- task_status_update --------| 继续执行
|
||||
| |
|
||||
```
|
||||
|
||||
### 4.3 人类介入流程
|
||||
|
||||
```
|
||||
请求方 Agent 执行方 Agent
|
||||
| |
|
||||
|-------- task_request ------------->|
|
||||
| |
|
||||
|<------- task_need_human -----------| 需要人类决策
|
||||
| |
|
||||
| [人类做出决策] |
|
||||
| |
|
||||
|-------- human_decision ----------->| 决策结果
|
||||
| |
|
||||
|<------- task_status_update --------| 继续执行
|
||||
| |
|
||||
```
|
||||
|
||||
### 4.4 任务取消流程
|
||||
|
||||
```
|
||||
请求方 Agent 执行方 Agent
|
||||
| |
|
||||
|-------- task_request ------------->|
|
||||
| |
|
||||
|-------- task_cancel -------------->| 取消任务
|
||||
| |
|
||||
|<------- task_ack ------------------| 确认取消
|
||||
| |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 接口规范
|
||||
|
||||
### 5.1 P2P 协议处理器接口
|
||||
|
||||
**接口定义:**
|
||||
|
||||
```go
|
||||
type P2PProtocol interface {
|
||||
// 处理收到的消息
|
||||
HandleMessage(ctx context.Context, msg *UnifiedSynapseMessage) (*UnifiedSynapseMessage, error)
|
||||
|
||||
// 设置任务处理器
|
||||
SetTaskHandler(h TaskHandler)
|
||||
|
||||
// 设置回调发送器
|
||||
SetCallbackSender(s CallbackSender)
|
||||
|
||||
// 获取 Agent 端点
|
||||
AgentEndpoint() AgentEndpoint
|
||||
|
||||
// 查询任务状态
|
||||
GetTaskStatus(taskID string) (P2PTaskStatus, bool)
|
||||
|
||||
// 列出所有任务
|
||||
ListTasks() map[string]P2PTaskStatus
|
||||
}
|
||||
```
|
||||
|
||||
**参考实现:** [p2p/protocol.go](../p2p/protocol.go)
|
||||
|
||||
### 5.2 任务处理器接口
|
||||
|
||||
```go
|
||||
type TaskHandler interface {
|
||||
// 执行任务并返回结果
|
||||
ExecuteTask(ctx context.Context, taskType string, payload map[string]interface{}) (any, error)
|
||||
|
||||
// 验证任务是否可执行,返回缺失字段列表和是否可处理
|
||||
ValidateTask(taskType string, payload map[string]interface{}) ([]MissingField, bool)
|
||||
|
||||
// 获取能力列表
|
||||
GetCapabilities() []string
|
||||
}
|
||||
```
|
||||
|
||||
**说明:**
|
||||
- `ExecuteTask`:实际执行业务逻辑的入口
|
||||
- `ValidateTask`:在任务确认前调用,用于判断是否接受任务或需要补充信息
|
||||
- `GetCapabilities`:返回 Agent 支持的任务类型列表
|
||||
|
||||
### 5.3 回调发送器接口
|
||||
|
||||
```go
|
||||
type CallbackSender interface {
|
||||
// 向远程 Agent 发送消息
|
||||
SendCallback(ctx context.Context, endpoint string, msg *UnifiedSynapseMessage) error
|
||||
}
|
||||
```
|
||||
|
||||
**说明:**
|
||||
- 传输层实现此接口
|
||||
- 支持多种传输方式(HTTP、gRPC、WebSocket 等)
|
||||
|
||||
### 5.4 配置项
|
||||
|
||||
```go
|
||||
type P2PConfig struct {
|
||||
MaxRetries int // 最大重试次数(默认 5)
|
||||
RetryBaseDelay time.Duration // 重试基础延迟(默认 2s,指数退避)
|
||||
TaskTimeout time.Duration // 任务执行超时(默认 5 分钟)
|
||||
HeartbeatInterval time.Duration // 心跳间隔(默认 30 秒)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 载荷格式
|
||||
|
||||
### 6.1 TaskRequestPayload(任务请求)
|
||||
|
||||
```json
|
||||
{
|
||||
"task": {
|
||||
"id": "task-xxx",
|
||||
"type": "text_generation",
|
||||
"priority": "normal",
|
||||
"deadline": "2026-01-01T00:00:00Z",
|
||||
"payload": {
|
||||
"prompt": "写一首诗",
|
||||
"style": "古典"
|
||||
}
|
||||
},
|
||||
"context": {
|
||||
"session_id": "sess-xxx",
|
||||
"parent_task_id": "parent-xxx",
|
||||
"correlation_id": "corr-xxx",
|
||||
"metadata": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 TaskConfirmPayload(任务确认)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"confirmed": true,
|
||||
"estimated_duration": 120,
|
||||
"expected_completion": "2026-01-01T00:02:00Z",
|
||||
"capabilities_used": ["text_generation"],
|
||||
"constraints": {
|
||||
"max_retries": 3,
|
||||
"fallback_enabled": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 TaskRejectPayload(任务拒绝)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"reason": {
|
||||
"code": "capability_lacking",
|
||||
"message": "不支持该任务类型",
|
||||
"details": null
|
||||
},
|
||||
"alternatives": {
|
||||
"suggested_agent_id": "other-agent",
|
||||
"retry_after": 300,
|
||||
"partial_support": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.4 TaskStatusUpdatePayload(状态更新)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"status": "executing",
|
||||
"progress": 50,
|
||||
"message": "正在生成内容",
|
||||
"intermediate_result": null
|
||||
}
|
||||
```
|
||||
|
||||
### 6.5 TaskCompletePayload(任务完成)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"result": {
|
||||
"output_type": "text",
|
||||
"output": "生成的文本内容...",
|
||||
"metadata": {
|
||||
"tokens": 1024,
|
||||
"model": "gpt-4"
|
||||
}
|
||||
},
|
||||
"summary": "任务已完成"
|
||||
}
|
||||
```
|
||||
|
||||
### 6.6 TaskFailedPayload(任务失败)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"error": {
|
||||
"code": "execution_error",
|
||||
"message": "模型调用超时",
|
||||
"recoverable": true,
|
||||
"details": null
|
||||
},
|
||||
"partial_result": null
|
||||
}
|
||||
```
|
||||
|
||||
### 6.7 TaskNeedInfoPayload(需要信息)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"missing_fields": [
|
||||
{
|
||||
"field": "style",
|
||||
"reason": "未指定生成风格",
|
||||
"suggested_value": "古典",
|
||||
"alternatives": ["现代", "浪漫"]
|
||||
}
|
||||
],
|
||||
"question": "请选择生成风格",
|
||||
"required_by": "2026-01-01T00:01:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### 6.8 TaskNeedHumanPayload(需要人类)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"reason": {
|
||||
"code": "ethical_decision",
|
||||
"message": "涉及伦理决策需要人工审核"
|
||||
},
|
||||
"context": {
|
||||
"task_summary": "生成一张人物肖像",
|
||||
"current_progress": 30,
|
||||
"blocked_reason": "需要确认肖像使用授权"
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"field": "authorized",
|
||||
"value": true,
|
||||
"pros_cons": "继续生成,假设已授权",
|
||||
"confidence": 70
|
||||
}
|
||||
],
|
||||
"escalation_level": 1,
|
||||
"deadline": "2026-01-01T00:05:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### 6.9 HumanDecisionPayload(人类决策)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task-xxx",
|
||||
"decision_id": "dec-xxx",
|
||||
"reply_to": "msg-xxx",
|
||||
"decided_by": {
|
||||
"user_id": "user-123",
|
||||
"user_name": "张三",
|
||||
"role": "reviewer"
|
||||
},
|
||||
"decision": {
|
||||
"action": "approve",
|
||||
"approved_fields": {
|
||||
"authorized": true
|
||||
},
|
||||
"rejection_reason": ""
|
||||
},
|
||||
"notes": "已确认授权",
|
||||
"timestamp": "2026-01-01T00:03:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**完整载荷定义:** [types/types.go](../types/types.go)
|
||||
|
||||
---
|
||||
|
||||
## 7. 错误码
|
||||
|
||||
| 错误码 | 说明 | HTTP 状态码(如适用) |
|
||||
|--------|------|---------------------|
|
||||
| `message_format_invalid` | 消息格式无效 | 400 |
|
||||
| `capability_lacking` | 能力不足 | 403 |
|
||||
| `agent_not_ready` | Agent 未就绪 | 503 |
|
||||
| `task_not_found` | 任务不存在 | 404 |
|
||||
| `task_already_completed` | 任务已完成 | 409 |
|
||||
| `execution_timeout` | 执行超时 | 504 |
|
||||
| `execution_failed` | 执行失败 | 500 |
|
||||
| `permission_denied` | 权限不足 | 403 |
|
||||
| `invalid_params` | 参数无效 | 400 |
|
||||
| `rate_limited` | 限流 | 429 |
|
||||
|
||||
**完整错误定义:** [types/errors.go](../types/errors.go)
|
||||
|
||||
---
|
||||
|
||||
## 8. 传输要求
|
||||
|
||||
### 8.1 HTTP 传输
|
||||
- Content-Type: `application/json`
|
||||
- 请求方法:`POST`
|
||||
- 端点路径:由 Agent 自行定义,建议 `/synapse/callback`
|
||||
- 超时:建议 30 秒
|
||||
|
||||
### 8.2 签名验证(可选)
|
||||
- 使用 HMAC-SHA256 签名
|
||||
- 签名放在 Header:`X-Synapse-Signature`
|
||||
- 或放在消息体:`signature` 字段
|
||||
|
||||
---
|
||||
|
||||
## 9. 参考实现
|
||||
|
||||
本协议的参考实现位于 `p2p/` 目录:
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| [p2p/protocol.go](../p2p/protocol.go) | P2P 协议核心实现 |
|
||||
| [types/types.go](../types/types.go) | 数据类型定义 |
|
||||
| [types/errors.go](../types/errors.go) | 错误定义 |
|
||||
| [transport/transport.go](../transport/transport.go) | HTTP 传输实现 |
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:能力声明格式
|
||||
|
||||
参见 [灵隙原始协议(Synapse Raw Protocol) - Capability](./01-synapse-raw-protocol.md#44-capability能力)
|
||||
@@ -1,545 +0,0 @@
|
||||
# A2N 协议(Agent-to-N Protocol)
|
||||
|
||||
> 版本:1.0.0
|
||||
> 状态:草案
|
||||
> 类型:实现规范
|
||||
> 基于:[灵隙原始协议(Synapse Raw Protocol) v1.0.0](./01-synapse-raw-protocol.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 概述
|
||||
|
||||
A2N(Agent-to-N)组群协议定义了多个 Agent 组成群组进行协同工作的完整规范,包括群组管理、任务拆分与合并、群体决策、角色与权限等。
|
||||
|
||||
### 1.1 适用场景
|
||||
- 复杂任务的拆分与并行执行(MapReduce 模式)
|
||||
- 多 Agent 协同完成单一目标
|
||||
- 群体投票与共识决策
|
||||
- 临时或永久的 Agent 工作组
|
||||
- 分级升级(escalation)机制
|
||||
|
||||
### 1.2 依赖
|
||||
- 依赖 A2A 协议的点对点通信能力
|
||||
- 群组内成员间的单播通信使用 A2A 协议
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心概念
|
||||
|
||||
### 2.1 Group(群组)
|
||||
由多个 Agent 组成的协作单元,具有明确的拓扑结构、角色分工与协作规则。
|
||||
|
||||
### 2.2 Topology(拓扑结构)
|
||||
|
||||
| 拓扑类型 | 说明 | 适用场景 |
|
||||
|---------|------|---------|
|
||||
| `star` | 星型,中心 coordinator 协调 | 简单任务分发 |
|
||||
| `mesh` | 网状,成员间可直接通信 | 对等协作 |
|
||||
| `chain` | 链式,流水线模式 | 顺序处理 |
|
||||
| `hierarchical` | 层级型,多层管理 | 大型复杂任务 |
|
||||
| `fullmesh` | 全连接网状 | 高可靠协作 |
|
||||
|
||||
### 2.3 Role(角色)
|
||||
|
||||
| 角色 | 说明 | 权限 |
|
||||
|------|------|------|
|
||||
| `coordinator` | 协调者 | 创建/解散群组、任务拆分、发起投票 |
|
||||
| `worker` | 工作者 | 执行子任务、提交结果 |
|
||||
| `reviewer` | 审核者 | 审核结果、发起投票 |
|
||||
| `observer` | 观察者 | 只读,查看进度 |
|
||||
|
||||
### 2.4 GroupStatus(群组状态)
|
||||
|
||||
| 状态 | 说明 |
|
||||
|------|------|
|
||||
| `creating` | 创建中 |
|
||||
| `active` | 活跃 |
|
||||
| `frozen` | 冻结(不可执行新任务) |
|
||||
| `disbanded` | 已解散(终态) |
|
||||
|
||||
---
|
||||
|
||||
## 3. 消息类型
|
||||
|
||||
### 3.1 消息类型清单
|
||||
|
||||
所有消息类型前缀:`group:` 或 `inter_group:`
|
||||
|
||||
#### 群组管理
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `group:create` | 创建群组 |
|
||||
| `group:disband` | 解散群组 |
|
||||
| `group:join` | 加入群组 |
|
||||
| `group:leave` | 离开群组 |
|
||||
| `group:invite` | 邀请加入 |
|
||||
| `group:accept_invite` | 接受邀请 |
|
||||
| `group:reject_invite` | 拒绝邀请 |
|
||||
| `group:member_update` | 成员更新通知 |
|
||||
| `group:role_change` | 角色变更 |
|
||||
|
||||
#### 群组通信
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `group:broadcast` | 广播(所有成员) |
|
||||
| `group:multicast` | 组播(指定成员) |
|
||||
| `group:direct` | 群内单播 |
|
||||
| `group:relay` | 消息中继 |
|
||||
|
||||
#### 任务协作
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `group:task_split` | 任务拆分 |
|
||||
| `group:task_assign` | 任务分配 |
|
||||
| `group:task_merge` | 结果合并 |
|
||||
| `group:task_progress` | 子任务进度 |
|
||||
|
||||
#### 共识与投票
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `group:vote` | 发起投票 |
|
||||
| `group:vote_submit` | 提交投票 |
|
||||
| `group:consensus` | 达成共识 |
|
||||
|
||||
#### 人在回路(HiL)
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `group:consensus_escalate` | 共识僵局升级给人类 |
|
||||
| `group:subtask_escalate` | 子任务阻塞升级给人类 |
|
||||
| `group:human_decision` | 人类决策结果 |
|
||||
| `inter_group:escalate` | 跨群组升级 |
|
||||
|
||||
**完整定义:** [types/types.go](../types/types.go)
|
||||
|
||||
---
|
||||
|
||||
## 4. 群组生命周期
|
||||
|
||||
### 4.1 状态机
|
||||
|
||||
```
|
||||
creating → active → frozen → disbanded
|
||||
↑ ↓
|
||||
└─────────┘
|
||||
解冻
|
||||
```
|
||||
|
||||
### 4.2 群组创建流程
|
||||
|
||||
```
|
||||
创建者 被邀请成员
|
||||
| |
|
||||
|------ group:invite -------------->| 发送邀请
|
||||
| |
|
||||
|<----- group:accept_invite --------| 接受邀请
|
||||
| |
|
||||
|------ group:member_update ------->| 通知成员更新
|
||||
| |
|
||||
[群组创建完成,状态变为 active] |
|
||||
```
|
||||
|
||||
### 4.3 群组解散流程
|
||||
|
||||
```
|
||||
coordinator 所有成员
|
||||
| |
|
||||
|------ group:disband ------------->| 通知解散
|
||||
| |
|
||||
[群组状态变为 disbanded] |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 任务协作流程
|
||||
|
||||
### 5.1 MapReduce 模式(拆分-执行-合并)
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ coordinator │
|
||||
└──────┬───────┘
|
||||
│
|
||||
group:task_split(拆分任务)
|
||||
│
|
||||
┌────────────────┼────────────────┐
|
||||
▼ ▼ ▼
|
||||
┌─────────┐ ┌─────────┐ ┌─────────┐
|
||||
│ worker1 │ │ worker2 │ │ worker3 │
|
||||
└────┬────┘ └────┬────┘ └────┬────┘
|
||||
│ │ │
|
||||
│ 执行子任务 │ 执行子任务 │ 执行子任务
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
子任务结果 子任务结果 子任务结果
|
||||
└────────────────┼────────────────┘
|
||||
│
|
||||
group:task_merge(合并结果)
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 最终结果 │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
### 5.2 详细流程
|
||||
|
||||
1. **任务拆分阶段**
|
||||
- coordinator 接收原始任务
|
||||
- 将任务拆分为多个子任务(subtask)
|
||||
- 为每个子任务指定执行的 worker
|
||||
- 定义合并策略(merge strategy)
|
||||
|
||||
2. **并行执行阶段**
|
||||
- 各 worker 并行执行子任务
|
||||
- 定期上报进度(`group:task_progress`)
|
||||
- 子任务完成后上报结果
|
||||
|
||||
3. **结果合并阶段**
|
||||
- 所有子任务完成后,coordinator 合并结果
|
||||
- 根据合并策略决定最终结果
|
||||
- 输出最终结果
|
||||
|
||||
---
|
||||
|
||||
## 6. 合并策略(Merge Strategy)
|
||||
|
||||
| 策略 | 说明 |
|
||||
|------|------|
|
||||
| `all_success` | 全部成功才认为成功,否则失败 |
|
||||
| `majority` | 多数子任务成功即可,取多数结果 |
|
||||
| `any_complete` | 任意一个完成即返回结果 |
|
||||
| `priority` | 按优先级,高优先级子任务结果优先 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 投票与共识
|
||||
|
||||
### 7.1 投票流程
|
||||
|
||||
```
|
||||
发起者 投票者
|
||||
| |
|
||||
|-------- group:vote -------------->| 发起投票
|
||||
| |
|
||||
|<------- group:vote_submit --------| 提交投票
|
||||
| |
|
||||
[计票,判断是否达到阈值] |
|
||||
| |
|
||||
|-------- group:consensus --------->| 公布结果
|
||||
| |
|
||||
```
|
||||
|
||||
### 7.2 投票规则
|
||||
|
||||
- `threshold`:通过阈值(0-1),如 0.5 表示过半
|
||||
- `deadline`:投票截止时间
|
||||
- `voters`:有投票权的成员列表
|
||||
- 超时未投票视为弃权
|
||||
|
||||
### 7.3 共识僵局(Consensus Deadlock)
|
||||
|
||||
当投票无法达成共识(如平票、未达阈值且超时)时,可升级给人类决策。
|
||||
|
||||
---
|
||||
|
||||
## 8. 人在回路(HiL)
|
||||
|
||||
群组协作中有两种升级场景:
|
||||
|
||||
### 8.1 共识僵局升级
|
||||
投票无法达成共识时,升级给人类裁决。
|
||||
|
||||
**消息:** `group:consensus_escalate`
|
||||
|
||||
### 8.2 子任务阻塞升级
|
||||
某个子任务遇到无法自动解决的问题时,升级给人类。
|
||||
|
||||
**消息:** `group:subtask_escalate`
|
||||
|
||||
### 8.3 跨群组升级
|
||||
问题超出当前群组能力时,可升级到上级群组或人类。
|
||||
|
||||
**消息:** `inter_group:escalate`
|
||||
|
||||
---
|
||||
|
||||
## 9. 接口规范
|
||||
|
||||
### 9.1 GroupProtocol 接口
|
||||
|
||||
```go
|
||||
type GroupProtocol interface {
|
||||
// 处理群组消息
|
||||
HandleMessage(ctx context.Context, msg *UnifiedSynapseMessage) (*UnifiedSynapseMessage, error)
|
||||
|
||||
// 设置任务处理器
|
||||
SetTaskHandler(h GroupTaskHandler)
|
||||
|
||||
// 查询群组列表
|
||||
GetGroups() []*AgentGroup
|
||||
|
||||
// 查询特定群组
|
||||
GetGroup(groupID string) (*AgentGroup, bool)
|
||||
}
|
||||
```
|
||||
|
||||
**参考实现:** [group/protocol.go](../group/protocol.go)
|
||||
|
||||
### 9.2 GroupTaskHandler 接口
|
||||
|
||||
```go
|
||||
type GroupTaskHandler interface {
|
||||
// 执行子任务
|
||||
ExecuteSubtask(ctx context.Context, subtaskID string, payload map[string]interface{}) (any, error)
|
||||
|
||||
// 合并多个子任务结果
|
||||
MergeResults(results []SubtaskResult, strategy MergeStrategy) (any, error)
|
||||
|
||||
// 验证任务是否可拆分,返回拆分方案
|
||||
ValidateTaskSplit(taskType string, requirements map[string]interface{}) ([]Subtask, bool)
|
||||
}
|
||||
```
|
||||
|
||||
**说明:**
|
||||
- `ExecuteSubtask`:执行单个子任务的业务逻辑
|
||||
- `MergeResults`:根据策略合并多个子任务的结果
|
||||
- `ValidateTaskSplit`:验证任务是否可拆分,并给出拆分方案
|
||||
|
||||
### 9.3 配置项
|
||||
|
||||
```go
|
||||
type GroupConfig struct {
|
||||
MaxGroupSize int // 最大群组人数(默认 20)
|
||||
MaxSubtasks int // 最大子任务数(默认 50)
|
||||
ConsensusTimeout time.Duration // 共识超时(默认 5 分钟)
|
||||
TaskTimeout time.Duration // 任务超时(默认 10 分钟)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 载荷格式
|
||||
|
||||
### 10.1 GroupCreatePayload(创建群组)
|
||||
|
||||
```json
|
||||
{
|
||||
"group": {
|
||||
"name": "视频制作组",
|
||||
"topology": "star",
|
||||
"config": {
|
||||
"allow_self_leave": true,
|
||||
"requires_approval": false,
|
||||
"consensus_required": false,
|
||||
"consensus_threshold": 0.5,
|
||||
"broadcast_mode": "all"
|
||||
},
|
||||
"initial_members": [
|
||||
{
|
||||
"agent_id": "worker-1",
|
||||
"system_id": "sys-a",
|
||||
"endpoint": "http://worker1/synapse"
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
{
|
||||
"id": "coordinator",
|
||||
"name": "协调者",
|
||||
"permissions": ["create", "disband", "split", "vote"],
|
||||
"max_tasks": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 GroupTaskSplitPayload(任务拆分)
|
||||
|
||||
```json
|
||||
{
|
||||
"original_task": {
|
||||
"id": "task-xxx",
|
||||
"type": "video_generation",
|
||||
"requirements": {
|
||||
"script": "xxx",
|
||||
"duration": 60
|
||||
},
|
||||
"deadline": "2026-01-01T00:10:00Z"
|
||||
},
|
||||
"subtasks": [
|
||||
{
|
||||
"id": "sub-1",
|
||||
"description": "生成脚本",
|
||||
"assigned_to": "writer-agent",
|
||||
"dependencies": [],
|
||||
"estimated_duration": 120,
|
||||
"priority": 1
|
||||
},
|
||||
{
|
||||
"id": "sub-2",
|
||||
"description": "生成配音",
|
||||
"assigned_to": "voice-agent",
|
||||
"dependencies": ["sub-1"],
|
||||
"estimated_duration": 180,
|
||||
"priority": 2
|
||||
}
|
||||
],
|
||||
"merge_strategy": "all_success"
|
||||
}
|
||||
```
|
||||
|
||||
### 10.3 GroupTaskMergePayload(结果合并)
|
||||
|
||||
```json
|
||||
{
|
||||
"original_task_id": "task-xxx",
|
||||
"subtask_results": [
|
||||
{
|
||||
"subtask_id": "sub-1",
|
||||
"assigned_to": "writer-agent",
|
||||
"status": "success",
|
||||
"result": { "script": "..." },
|
||||
"completed_at": "2026-01-01T00:02:00Z"
|
||||
}
|
||||
],
|
||||
"merge_strategy_used": "all_success",
|
||||
"merged_result": {
|
||||
"video_url": "http://..."
|
||||
},
|
||||
"consensus_achieved": true
|
||||
}
|
||||
```
|
||||
|
||||
### 10.4 GroupVotePayload(投票)
|
||||
|
||||
```json
|
||||
{
|
||||
"vote_id": "vote-xxx",
|
||||
"question": "是否采用方案A?",
|
||||
"options": ["方案A", "方案B", "方案C"],
|
||||
"voters": ["agent-1", "agent-2", "agent-3"],
|
||||
"deadline": "2026-01-01T00:05:00Z",
|
||||
"threshold": 0.66,
|
||||
"status": "open",
|
||||
"votes": []
|
||||
}
|
||||
```
|
||||
|
||||
### 10.5 GroupConsensusEscalatePayload(共识升级)
|
||||
|
||||
```json
|
||||
{
|
||||
"vote_id": "vote-xxx",
|
||||
"escalation_id": "esc-xxx",
|
||||
"question": "是否采用方案A?",
|
||||
"votes": [
|
||||
{
|
||||
"voter_id": "agent-1",
|
||||
"voter_role": "reviewer",
|
||||
"option": "方案A",
|
||||
"reason": "质量更好"
|
||||
}
|
||||
],
|
||||
"consensus_status": {
|
||||
"required_threshold": 0.66,
|
||||
"actual_threshold": 0.33,
|
||||
"leading_option": "方案A",
|
||||
"options_tied": false
|
||||
},
|
||||
"escalate_reason": {
|
||||
"code": "no_consensus",
|
||||
"message": "无法达成共识"
|
||||
},
|
||||
"escalation_level": 2,
|
||||
"deadline": "2026-01-01T00:10:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**完整载荷定义:** [types/types.go](../types/types.go)
|
||||
|
||||
---
|
||||
|
||||
## 11. 数据结构
|
||||
|
||||
### 11.1 AgentGroup(群组)
|
||||
|
||||
```go
|
||||
type AgentGroup struct {
|
||||
ID string
|
||||
Name string
|
||||
Topology GroupTopology
|
||||
CreatedBy string
|
||||
CreatedAt Timestamp
|
||||
Config GroupConfig
|
||||
Members []GroupMember
|
||||
Roles []GroupRole
|
||||
Status GroupStatus
|
||||
}
|
||||
```
|
||||
|
||||
### 11.2 GroupMember(群组成员)
|
||||
|
||||
```go
|
||||
type GroupMember struct {
|
||||
AgentID string
|
||||
AgentName string
|
||||
Role string
|
||||
JoinedAt Timestamp
|
||||
Status GroupMemberStatus
|
||||
Capabilities []string
|
||||
LoadFactor float64 // 负载因子 0-1,用于任务分配
|
||||
}
|
||||
```
|
||||
|
||||
### 11.3 Subtask(子任务)
|
||||
|
||||
```go
|
||||
type Subtask struct {
|
||||
ID string
|
||||
Description string
|
||||
AssignedTo string
|
||||
Dependencies []string
|
||||
EstimatedDuration int
|
||||
Priority int
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 错误码
|
||||
|
||||
群组协议特有错误码:
|
||||
|
||||
| 错误码 | 说明 |
|
||||
|--------|------|
|
||||
| `group_not_found` | 群组不存在 |
|
||||
| `group_not_active` | 群组不活跃 |
|
||||
| `group_full` | 群组已满 |
|
||||
| `already_member` | 已是成员 |
|
||||
| `not_group_member` | 不是群组成员 |
|
||||
| `member_not_found` | 成员不存在 |
|
||||
| `permission_denied` | 权限不足 |
|
||||
| `invalid_params` | 参数无效 |
|
||||
|
||||
---
|
||||
|
||||
## 13. 参考实现
|
||||
|
||||
本协议的参考实现位于 `group/` 目录:
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| [group/protocol.go](../group/protocol.go) | 群组协议核心实现 |
|
||||
| [types/types.go](../types/types.go) | 群组相关数据类型 |
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:与 A2A 协议的关系
|
||||
|
||||
- 群组内的点对点通信使用 A2A 协议
|
||||
- 群组协议在 A2A 协议之上提供群组管理、任务拆分合并、共识投票等能力
|
||||
- 群组消息同样使用统一消息信封(`UnifiedSynapseMessage`),通过 `group_id` 字段标识群组上下文
|
||||
@@ -1,503 +0,0 @@
|
||||
# P2A 协议(Person/Program-to-Agent Protocol)
|
||||
|
||||
> 版本:1.0.0
|
||||
> 状态:草案
|
||||
> 类型:实现规范
|
||||
> 基于:[灵隙原始协议(Synapse Raw Protocol) v1.0.0](./01-synapse-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 | 简化验证 |
|
||||
@@ -1,519 +0,0 @@
|
||||
# SDK 实现说明
|
||||
|
||||
> 版本:1.0.0
|
||||
> 更新日期:2026-06-27
|
||||
> 协议版本:1.0.0
|
||||
> 关联协议:
|
||||
> - [A2A 协议](./02-a2a-protocol.md)
|
||||
> - [A2N 协议](./03-a2n-protocol.md)
|
||||
> - [P2A 协议](./04-p2a-protocol.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 概述
|
||||
|
||||
SDK 按照灵隙协议族(Synapse Protocol Suite)的三个子协议进行了模块化设计,分为三个子包,命名与协议名保持一致:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ SDK (sdk/) │
|
||||
├──────────────┬───────────────────┬───────────────────────────┤
|
||||
│ sdk/p2a │ sdk/a2a │ sdk/a2n │
|
||||
│ P2A 协议 │ A2A 协议 │ A2N 协议 │
|
||||
│ (外部调用) │ (Agent间通信) │ (群组协作) │
|
||||
│ ✅ 完整实现 │ ✅ 完整实现 │ ✅ 完整实现 │
|
||||
└──────────────┴───────────────────┴───────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. SDK 目录结构
|
||||
|
||||
```
|
||||
sdk/
|
||||
├── doc.go # SDK 包文档(使用说明)
|
||||
├── p2a/
|
||||
│ └── client.go # P2A 协议 - 外部调用客户端
|
||||
├── a2a/
|
||||
│ └── client.go # A2A 协议 - Agent间通信客户端
|
||||
└── a2n/
|
||||
└── client.go # A2N 协议 - 群组协作客户端
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 第一部分:sdk/p2a - P2A 协议
|
||||
|
||||
**协议对应**:[P2A 协议](./04-p2a-protocol.md)
|
||||
**版本**:1.0.0
|
||||
**用途**:非 Agent 调用方(前端、后端服务、第三方应用)通过 REST API 调用 Agent 能力
|
||||
|
||||
### 3.1 实现对应关系
|
||||
|
||||
| P2A 协议功能 | SDK 方法 | 位置 |
|
||||
|-------------|---------|------|
|
||||
| 认证方式 | `setHeaders()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 提交任务(JSON) | `SubmitTask()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 提交任务(Multipart) | `SubmitTaskWithFiles()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 获取任务详情 | `GetTask()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 获取任务状态(简化版) | `GetTaskStatus()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 获取任务列表 | `ListTasks()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 取消任务 | `CancelTask()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 能力查询 | `GetCapabilities()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 回调验证 | `VerifyCallback()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
| 轮询等待 | `WaitForTask()` | [sdk/p2a/client.go](../sdk/p2a/client.go) |
|
||||
|
||||
### 3.2 核心数据结构
|
||||
|
||||
```go
|
||||
// Client 客户端
|
||||
type Client struct {
|
||||
baseURL string
|
||||
apiKey string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// 配置
|
||||
type ClientConfig struct {
|
||||
BaseURL string
|
||||
APIKey string
|
||||
Timeout time.Duration
|
||||
MaxRetries int
|
||||
}
|
||||
|
||||
// 任务请求
|
||||
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
|
||||
Result map[string]any
|
||||
Error *TaskError
|
||||
}
|
||||
|
||||
// 文件上传参数
|
||||
type FileParam struct {
|
||||
FieldName string
|
||||
Filename string
|
||||
Data []byte
|
||||
MimeType string
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 使用示例
|
||||
|
||||
```go
|
||||
import "dp-headless/synapse/sdk/p2a"
|
||||
|
||||
client := p2a.NewClient(p2a.ClientConfig{
|
||||
BaseURL: "https://agent-api.example.com",
|
||||
APIKey: "your-api-key",
|
||||
})
|
||||
|
||||
// 提交任务并等待完成
|
||||
taskInfo, err := client.CreateAndWait(ctx, "text_generation", map[string]any{
|
||||
"prompt": "写一首诗",
|
||||
"style": "古典",
|
||||
}, 5*time.Second)
|
||||
|
||||
// 使用回调模式
|
||||
resp, err := client.CreateWithCallback(ctx, "image_generation", payload,
|
||||
"https://your-domain.com/callback",
|
||||
"callback-token",
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 第二部分:sdk/a2a - A2A 协议
|
||||
|
||||
**协议对应**:[A2A 协议](./02-a2a-protocol.md)
|
||||
**版本**:1.0.0
|
||||
**用途**:Agent 之间的点对点通信,包含消息发送、签名验证、回调处理
|
||||
|
||||
### 4.1 实现对应关系
|
||||
|
||||
| A2A 协议功能 | SDK 方法 | 位置 |
|
||||
|------------|---------|------|
|
||||
| 发送任务请求 | `SendTaskRequest()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
| 发送任务取消 | `SendTaskCancel()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
| 发送人类决策 | `SendHumanDecision()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
| 查询能力 | `SendCapabilityQuery()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
| 消息签名 | `signMessage()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
| 签名验证 | `verifySignature()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
| 任务状态管理 | `GetTaskState()`, `ListTasks()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
| 等待任务完成 | `WaitForTask()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
| 回调服务 | `startCallbackServer()` | [sdk/a2a/client.go](../sdk/a2a/client.go) |
|
||||
|
||||
### 4.2 核心数据结构
|
||||
|
||||
```go
|
||||
// Client A2A客户端
|
||||
type Client struct {
|
||||
agentEndpoint types.AgentEndpoint
|
||||
authSecret string
|
||||
callbackHandler MessageCallbackHandler
|
||||
tasks map[string]*A2ATaskState
|
||||
}
|
||||
|
||||
// 配置
|
||||
type ClientConfig struct {
|
||||
AgentEndpoint types.AgentEndpoint
|
||||
AuthSecret string
|
||||
CallbackPort int
|
||||
Timeout time.Duration
|
||||
MaxRetries int
|
||||
}
|
||||
|
||||
// 消息回调处理器接口
|
||||
type MessageCallbackHandler interface {
|
||||
HandleTaskRequest(ctx context.Context, msg *types.UnifiedSynapseMessage) (*types.UnifiedSynapseMessage, error)
|
||||
HandleTaskStatusUpdate(ctx context.Context, msg *types.UnifiedSynapseMessage) error
|
||||
HandleTaskComplete(ctx context.Context, msg *types.UnifiedSynapseMessage) error
|
||||
HandleTaskFailed(ctx context.Context, msg *types.UnifiedSynapseMessage) error
|
||||
HandleNeedHuman(ctx context.Context, msg *types.UnifiedSynapseMessage) error
|
||||
}
|
||||
|
||||
// 任务状态
|
||||
type A2ATaskState struct {
|
||||
TaskID string
|
||||
Status types.P2PTaskStatus
|
||||
Progress int
|
||||
Message string
|
||||
Result any
|
||||
Error *types.SynapseError
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 使用示例
|
||||
|
||||
```go
|
||||
import "dp-headless/synapse/sdk/a2a"
|
||||
|
||||
a2aClient, err := a2a.NewClient(&a2a.ClientConfig{
|
||||
AgentEndpoint: types.AgentEndpoint{
|
||||
AgentID: "my-agent",
|
||||
AgentName: "我的Agent",
|
||||
SystemID: "my-system",
|
||||
Endpoint: "http://localhost:8080/synapse/callback",
|
||||
},
|
||||
AuthSecret: "my-hmac-secret",
|
||||
CallbackPort: 8080,
|
||||
})
|
||||
|
||||
// 设置消息处理器
|
||||
a2aClient.SetMessageHandler(&myHandler{})
|
||||
|
||||
// 发送任务请求给其他Agent
|
||||
taskID, err := a2aClient.SendTaskRequest(ctx, targetAgent, "video_generation", map[string]any{
|
||||
"prompt": "生成一段视频",
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 第三部分:sdk/a2n - A2N 协议
|
||||
|
||||
**协议对应**:[A2N 协议](./03-a2n-protocol.md)
|
||||
**版本**:1.0.0
|
||||
**用途**:多 Agent 群组协作,包含群组管理、任务拆分合并、投票、升级
|
||||
|
||||
### 5.1 实现对应关系
|
||||
|
||||
| A2N 协议功能 | SDK 方法 | 位置 |
|
||||
|------------|---------|------|
|
||||
| 创建群组 | `CreateGroup()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 加入群组 | `JoinGroup()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 离开群组 | `LeaveGroup()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 解散群组 | `DisbandGroup()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 提交群组任务 | `SubmitGroupTask()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 发送子任务结果 | `SendSubtaskResult()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 发起投票 | `InitiateVote()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 提交投票 | `SubmitVote()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 广播消息 | `Broadcast()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
| 升级到人类 | `EscalateToHuman()` | [sdk/a2n/client.go](../sdk/a2n/client.go) |
|
||||
|
||||
### 5.2 核心数据结构
|
||||
|
||||
```go
|
||||
// Client A2N客户端
|
||||
type Client struct {
|
||||
a2aClient *a2a.Client
|
||||
groups map[string]*GroupState
|
||||
eventHandler GroupEventHandler
|
||||
}
|
||||
|
||||
// 配置
|
||||
type ClientConfig struct {
|
||||
A2AClient *a2a.Client
|
||||
EventHandler GroupEventHandler
|
||||
}
|
||||
|
||||
// 群组状态
|
||||
type GroupState struct {
|
||||
GroupID string
|
||||
Name string
|
||||
Topology types.GroupTopology
|
||||
Status types.GroupStatus
|
||||
Members []types.GroupMember
|
||||
Role string // "coordinator" | "worker"
|
||||
}
|
||||
|
||||
// 群组事件处理器接口
|
||||
type GroupEventHandler interface {
|
||||
OnGroupCreated(ctx context.Context, groupID string) error
|
||||
OnGroupDisbanded(ctx context.Context, groupID string) error
|
||||
OnMemberJoined(ctx context.Context, groupID string, member types.GroupMember) error
|
||||
OnMemberLeft(ctx context.Context, groupID string, agentID string) error
|
||||
OnTaskSplit(ctx context.Context, groupID string, originalTaskID string, subtasks []types.Subtask) error
|
||||
OnSubtaskProgress(ctx context.Context, groupID string, subtaskID string, progress int) error
|
||||
OnTaskMerged(ctx context.Context, groupID string, originalTaskID string, result any) error
|
||||
OnVoteInitiated(ctx context.Context, groupID string, vote types.GroupVotePayload) error
|
||||
OnVoteResult(ctx context.Context, groupID string, voteID string, option string, passed bool) error
|
||||
OnEscalation(ctx context.Context, groupID string, escalationID string, level int) error
|
||||
OnBroadcast(ctx context.Context, groupID string, sender types.AgentEndpoint, message []byte) error
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 使用示例
|
||||
|
||||
```go
|
||||
import (
|
||||
"dp-headless/synapse/sdk/a2n"
|
||||
"dp-headless/synapse/sdk/a2a"
|
||||
)
|
||||
|
||||
// 创建A2A客户端
|
||||
a2aClient, _ := a2a.NewClient(&a2a.ClientConfig{
|
||||
AgentEndpoint: myEndpoint,
|
||||
})
|
||||
|
||||
// 创建A2N客户端
|
||||
a2nClient, err := a2n.NewClient(&a2n.ClientConfig{
|
||||
A2AClient: a2aClient,
|
||||
EventHandler: &myGroupHandler{},
|
||||
})
|
||||
|
||||
// 创建群组
|
||||
group, err := a2nClient.CreateGroup(ctx, "视频制作组", types.GroupTopologyStar, nil, members)
|
||||
|
||||
// 作为Coordinator,提交任务
|
||||
subtasks, err := a2nClient.SubmitGroupTask(ctx, group.GroupID, "video_generation",
|
||||
map[string]any{"script": "..."},
|
||||
types.MergeAllSuccess,
|
||||
)
|
||||
|
||||
// 发起投票
|
||||
voteID, err := a2nClient.InitiateVote(ctx, group.GroupID,
|
||||
"选择哪个方案?",
|
||||
[]string{"方案A", "方案B"},
|
||||
voterIDs,
|
||||
0.5,
|
||||
5*time.Minute,
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. SDK 架构总览
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ SDK (dp-headless/synapse/sdk) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ doc.go │
|
||||
│ ├── 包文档和使用说明 │
|
||||
│ └── 指向各子包的链接 │
|
||||
├────────────────┬────────────────┬─────────────────────────┤
|
||||
│ sdk/p2a │ sdk/a2a │ sdk/a2n │
|
||||
│ │ │ │
|
||||
│ P2A协议 │ 灵隙协议 │ A2N协议 │
|
||||
│ │ │ │
|
||||
│ ├── Client │ ├── Client │ ├── Client │
|
||||
│ ├── 配置 │ ├── 配置 │ ├── 配置 │
|
||||
│ ├── 任务操作 │ ├── 消息发送 │ ├── 群组管理 │
|
||||
│ ├── 能力查询 │ ├── 任务状态 │ ├── 任务协作 │
|
||||
│ └── 回调验证 │ ├── 回调服务 │ ├── 投票 │
|
||||
│ │ └── 签名 │ └── 升级 │
|
||||
└────────────────┴────────────────┴─────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 快速开始
|
||||
|
||||
### 7.1 P2A 调用(外部调用者)
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"dp-headless/synapse/sdk/p2a"
|
||||
)
|
||||
|
||||
func main() {
|
||||
client := p2a.NewClient(p2a.ClientConfig{
|
||||
BaseURL: "https://agent.example.com",
|
||||
APIKey: "your-api-key",
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// 方式1: 轮询等待
|
||||
result, err := client.CreateAndWait(ctx, "text_generation", map[string]any{
|
||||
"prompt": "写一首诗",
|
||||
}, 5*time.Second)
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
}
|
||||
fmt.Printf("Result: %v\n", result.Result)
|
||||
|
||||
// 方式2: 回调模式
|
||||
resp, err := client.CreateWithCallback(ctx, "image_generation",
|
||||
map[string]any{"prompt": "一只猫"},
|
||||
"https://your-domain.com/callback",
|
||||
"token",
|
||||
)
|
||||
fmt.Printf("Task ID: %s\n", resp.TaskID)
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 灵隙 通信(Agent 间)
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"dp-headless/synapse/sdk/a2a"
|
||||
"dp-headless/synapse/types"
|
||||
)
|
||||
|
||||
func main() {
|
||||
client, _ := a2a.NewClient(&a2a.ClientConfig{
|
||||
AgentEndpoint: types.AgentEndpoint{
|
||||
AgentID: "agent-001",
|
||||
AgentName: "Agent 001",
|
||||
SystemID: "my-system",
|
||||
Endpoint: "http://localhost:8080/synapse/callback",
|
||||
},
|
||||
AuthSecret: "secret-key",
|
||||
CallbackPort: 8080,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
target := types.AgentEndpoint{
|
||||
AgentID: "agent-002",
|
||||
Endpoint: "http://agent-002:8080/synapse/callback",
|
||||
}
|
||||
|
||||
// 发送任务请求
|
||||
taskID, err := client.SendTaskRequest(ctx, target, "data_analysis", map[string]any{
|
||||
"dataset": "sales.csv",
|
||||
})
|
||||
fmt.Printf("Task ID: %s\n", taskID)
|
||||
|
||||
// 等待完成
|
||||
state, err := client.WaitForTask(ctx, taskID, 5*time.Minute)
|
||||
fmt.Printf("Status: %s\n", state.Status)
|
||||
}
|
||||
```
|
||||
|
||||
### 7.3 A2N 群组协作
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"dp-headless/synapse/sdk/a2n"
|
||||
"dp-headless/synapse/sdk/a2a"
|
||||
"dp-headless/synapse/types"
|
||||
)
|
||||
|
||||
func main() {
|
||||
a2aClient, _ := a2a.NewClient(&a2a.ClientConfig{
|
||||
AgentEndpoint: myEndpoint,
|
||||
})
|
||||
|
||||
a2nClient, _ := a2n.NewClient(&a2n.ClientConfig{
|
||||
A2AClient: a2aClient,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// 创建群组
|
||||
group, _ := a2nClient.CreateGroup(ctx, "协作组", types.GroupTopologyStar, nil, []types.AgentEndpoint{
|
||||
{AgentID: "member-1", Endpoint: "http://member-1:8080"},
|
||||
{AgentID: "member-2", Endpoint: "http://member-2:8080"},
|
||||
})
|
||||
|
||||
fmt.Printf("Group ID: %s\n", group.GroupID)
|
||||
|
||||
// 提交任务
|
||||
subtasks, _ := a2nClient.SubmitGroupTask(ctx, group.GroupID, "video_generation",
|
||||
map[string]any{"script": "..."},
|
||||
types.MergeAllSuccess,
|
||||
)
|
||||
|
||||
fmt.Printf("Split into %d subtasks\n", len(subtasks))
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 协议与包名对应关系
|
||||
|
||||
| 协议名称 | 协议文档 | SDK 包名 | 包路径 |
|
||||
|---------|---------|---------|--------|
|
||||
| 灵隙原始协议(Synapse Raw Protocol) | [01-synapse-raw-protocol.md](./01-synapse-raw-protocol.md) | - | 纲领性文档 |
|
||||
| A2A 协议 | [02-a2a-protocol.md](./02-a2a-protocol.md) | `a2a` | `sdk/a2a/` |
|
||||
| A2N 协议 | [03-a2n-protocol.md](./03-a2n-protocol.md) | `a2n` | `sdk/a2n/` |
|
||||
| P2A 协议 | [04-p2a-protocol.md](./04-p2a-protocol.md) | `p2a` | `sdk/p2a/` |
|
||||
|
||||
**所有协议版本:1.0.0**
|
||||
|
||||
---
|
||||
|
||||
## 9. 扩展建议
|
||||
|
||||
| 优先级 | 扩展项 | 说明 |
|
||||
|-------|-------|------|
|
||||
| 高 | gRPC 传输 | 添加 gRPC 协议支持 |
|
||||
| 高 | WebSocket | 添加 WebSocket 流式传输 |
|
||||
| 中 | 批量操作 | 批量任务提交、批量查询 |
|
||||
| 中 | 指标监控 | 内置指标收集 |
|
||||
| 低 | 多语言 SDK | Python、JavaScript 等 |
|
||||
|
||||
---
|
||||
|
||||
## 附录:相关文件索引
|
||||
|
||||
| 文件路径 | 说明 |
|
||||
|---------|------|
|
||||
| [sdk/doc.go](../sdk/doc.go) | SDK 包文档 |
|
||||
| [sdk/p2a/client.go](../sdk/p2a/client.go) | P2A 客户端实现 |
|
||||
| [sdk/a2a/client.go](../sdk/a2a/client.go) | A2A 客户端实现 |
|
||||
| [sdk/a2n/client.go](../sdk/a2n/client.go) | A2N 客户端实现 |
|
||||
| [externalapi/handler.go](../externalapi/handler.go) | P2A 协议服务端实现 |
|
||||
| [p2p/protocol.go](../p2p/protocol.go) | A2A 协议核心实现 |
|
||||
| [group/protocol.go](../group/protocol.go) | A2N 协议核心实现 |
|
||||
| [types/types.go](../types/types.go) | 公共类型定义 |
|
||||
| [transport/transport.go](../transport/transport.go) | HTTP 传输层实现 |
|
||||
@@ -1,777 +0,0 @@
|
||||
# 灵隙协议族(Synapse Protocol Suite) - 解决方案白皮书
|
||||
|
||||
> 版本:1.0.0
|
||||
> 日期:2026-06-27
|
||||
> 状态:草案
|
||||
> 前置阅读:[灵隙原始协议(Synapse Raw Protocol)](./01-synapse-raw-protocol.md)
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
1. [概述](#1-概述)
|
||||
2. [方案一:Agent 发现与广播机制](#2-方案一agent-发现与广播机制)
|
||||
3. [方案二:身份认证与信任体系](#3-方案二身份认证与信任体系)
|
||||
4. [方案三:协议缺失内容补全](#4-方案三协议缺失内容补全)
|
||||
5. [分阶段实施路线图](#5-分阶段实施路线图)
|
||||
6. [技术选型建议](#6-技术选型建议)
|
||||
|
||||
---
|
||||
|
||||
## 1. 概述
|
||||
|
||||
### 1.1 背景
|
||||
|
||||
当前灵隙协议族(Synapse Protocol Suite)定义了 Agent 之间"怎么说话"(消息格式、任务流程),但缺少:
|
||||
|
||||
- **怎么找到对方** — Agent 发现与广播机制
|
||||
- **怎么相信对方** — 身份认证与信任体系
|
||||
- **怎么规模化协作** — 路由、调度、激励等基础设施
|
||||
|
||||
本文档针对上述问题给出完整的解决方案。
|
||||
|
||||
### 1.2 设计原则
|
||||
|
||||
| 原则 | 说明 |
|
||||
|------|------|
|
||||
| **渐进式** | 从简单到复杂,每阶段都可用 |
|
||||
| **可插拔** | 组件可替换,不绑定具体实现 |
|
||||
| **传输无关** | 不依赖特定底层网络 |
|
||||
| **安全优先** | 身份认证是基础设施,不是可选功能 |
|
||||
| **向后兼容** | 不破坏现有协议,增量扩展 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 方案一:Agent 发现与广播机制
|
||||
|
||||
### 2.1 整体架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Agent 发现与广播层 │
|
||||
├─────────────────────┬─────────────────────┬─────────────────────────┤
|
||||
│ 集中式目录服务 │ 分布式 Gossip │ 组播/广播 │
|
||||
│ (Directory) │ (Gossip) │ (Multicast) │
|
||||
│ ┌───────────────┐ │ ┌───────────────┐ │ ┌────────────────┐ │
|
||||
│ │ 注册/注销 │ │ │ 消息泛洪 │ │ │ 子网内广播 │ │
|
||||
│ │ 能力索引 │ │ │ 状态同步 │ │ │ 邻居发现 │ │
|
||||
│ │ 名字解析 │ │ │ 节点发现 │ │ │ 本地心跳 │ │
|
||||
│ └───────────────┘ │ └───────────────┘ │ └────────────────┘ │
|
||||
└─────────────────────┴─────────────────────┴─────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 模式一:集中式目录服务(推荐首选)
|
||||
|
||||
#### 2.2.1 架构说明
|
||||
|
||||
引入 **Agent Directory Service(ADS)** 作为中心化的 Agent 目录服务,类似 DNS。
|
||||
|
||||
```
|
||||
Agent A ──注册──▶ ┌─────────────┐ ◀──注册── Agent B
|
||||
│ Directory │
|
||||
Agent C ──查询──▶ │ Service │ ◀──心跳── Agent D
|
||||
└─────────────┘
|
||||
│
|
||||
▼
|
||||
能力索引
|
||||
在线状态
|
||||
端点地址
|
||||
```
|
||||
|
||||
#### 2.2.2 核心接口
|
||||
|
||||
**注册协议(Agent → Directory):**
|
||||
|
||||
| 操作 | 消息类型 | 说明 |
|
||||
|------|---------|------|
|
||||
| 注册 | `dir:register` | Agent 上线时注册自身信息 |
|
||||
| 注销 | `dir:unregister` | Agent 下线时注销 |
|
||||
| 心跳 | `dir:heartbeat` | 定期上报存活状态 |
|
||||
| 更新能力 | `dir:update_capabilities` | 能力变化时更新 |
|
||||
|
||||
**查询协议(Agent → Directory):**
|
||||
|
||||
| 操作 | 消息类型 | 说明 |
|
||||
|------|---------|------|
|
||||
| 按 ID 查询 | `dir:query_by_id` | 根据 AgentID 查端点 |
|
||||
| 按能力查询 | `dir:query_by_capability` | 找具备某能力的 Agent |
|
||||
| 列表查询 | `dir:list` | 分页列出所有 Agent |
|
||||
| 批量查询 | `dir:batch_query` | 批量查询多个 Agent |
|
||||
|
||||
#### 2.2.3 注册数据结构
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_id": "agent-001",
|
||||
"agent_name": "视频生成Agent",
|
||||
"system_id": "video-system",
|
||||
"endpoint": "https://agent-001.example.com/synapse/callback",
|
||||
"version": "1.0.0",
|
||||
"capabilities": [
|
||||
{
|
||||
"type": "video_generation",
|
||||
"name": "视频生成",
|
||||
"description": "根据脚本生成视频",
|
||||
"max_concurrent": 10,
|
||||
"avg_latency_ms": 5000
|
||||
}
|
||||
],
|
||||
"transports": ["http", "grpc"],
|
||||
"status": "online",
|
||||
"last_heartbeat": "2026-06-27T10:00:00Z",
|
||||
"trust_score": 85,
|
||||
"public_key": "-----BEGIN PUBLIC KEY-----\n..."
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2.4 优缺点
|
||||
|
||||
| 优点 | 缺点 |
|
||||
|------|------|
|
||||
| 查询效率高(O(1)) | 单点故障风险 |
|
||||
| 支持复杂查询(按能力、按系统等) | 中心节点需信任 |
|
||||
| 易于管理和监控 | 扩展性有上限 |
|
||||
| 实现简单 | 存在注册/注销时序问题 |
|
||||
|
||||
---
|
||||
|
||||
### 2.3 模式二:分布式 Gossip 协议(可选扩展)
|
||||
|
||||
#### 2.3.1 架构说明
|
||||
|
||||
无中心节点,Agent 之间通过 Gossip 协议(epidemic protocol)扩散状态信息,类似比特币网络的节点发现。
|
||||
|
||||
```
|
||||
Agent A ──gossip──▶ Agent B ──gossip──▶ Agent D
|
||||
│ │
|
||||
└──gossip──▶ Agent C ──gossip──▶ Agent E
|
||||
```
|
||||
|
||||
#### 2.3.2 核心机制
|
||||
|
||||
| 机制 | 说明 |
|
||||
|------|------|
|
||||
| **Peer Sampling** | 每个 Agent 维护一个部分视图(partial view) |
|
||||
| **Active Push** | 定期随机选几个节点推送本地状态 |
|
||||
| **Passive Pull** | 收到推送后,有概率回传自身状态 |
|
||||
| **TTL** | 消息有存活跳数,防止无限扩散 |
|
||||
| **Deduplication** | 消息 ID 去重,避免重复处理 |
|
||||
|
||||
#### 2.3.3 Gossip 消息格式
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "gossip:state",
|
||||
"from": "agent-001",
|
||||
"view": [
|
||||
{"agent_id": "agent-002", "endpoint": "...", "capabilities": [...], "timestamp": "..."},
|
||||
{"agent_id": "agent-003", "endpoint": "...", "capabilities": [...], "timestamp": "..."},
|
||||
{"agent_id": "agent-004", "endpoint": "...", "capabilities": [...], "timestamp": "..."}
|
||||
],
|
||||
"ttl": 5,
|
||||
"msg_id": "msg-xxx"
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3.4 优缺点
|
||||
|
||||
| 优点 | 缺点 |
|
||||
|------|------|
|
||||
| 完全去中心化 | 查询效率低(需本地构建视图) |
|
||||
| 容错性强 | 状态最终一致,有延迟 |
|
||||
| 可无限扩展 | 实现复杂度高 |
|
||||
| 无单点故障 | 网络流量大 |
|
||||
|
||||
---
|
||||
|
||||
### 2.4 模式三:本地组播/邻居发现(局域网场景)
|
||||
|
||||
适用于同一内网/集群内的 Agent 快速发现。
|
||||
|
||||
#### 2.4.1 机制
|
||||
|
||||
| 机制 | 说明 |
|
||||
|------|------|
|
||||
| **UDP 组播** | 定期在组播地址发送广播 |
|
||||
| **mDNS/DNS-SD** | 类似 Apple Bonjour 的服务发现 |
|
||||
| **ARP/NDP** | 链路层发现(不推荐) |
|
||||
|
||||
#### 2.4.2 适用场景
|
||||
|
||||
- 边缘计算集群
|
||||
- 同一机房内的 Agent 集群
|
||||
- 本地开发测试环境
|
||||
|
||||
---
|
||||
|
||||
### 2.5 广播机制设计
|
||||
|
||||
#### 2.5.1 广播类型
|
||||
|
||||
| 广播范围 | 消息类型前缀 | 说明 |
|
||||
|---------|-------------|------|
|
||||
| 全网广播 | `global:` | 所有在线 Agent |
|
||||
| 系统广播 | `system:` | 同一 system_id 内 |
|
||||
| 群组广播 | `group:` | 同一群组内(已有) |
|
||||
| 能力组广播 | `cap:` | 具备某能力的 Agent |
|
||||
|
||||
#### 2.5.2 广播实现
|
||||
|
||||
**集中式模式下:**
|
||||
- 由 Directory Service 转发广播消息
|
||||
- 支持按条件过滤(按能力、按系统等)
|
||||
|
||||
**分布式模式下:**
|
||||
- 通过 Gossip 协议泛洪
|
||||
- TTL 控制扩散范围
|
||||
- 消息 ID 去重
|
||||
|
||||
---
|
||||
|
||||
### 2.6 心跳与存活检测
|
||||
|
||||
#### 2.6.1 心跳机制
|
||||
|
||||
```
|
||||
Agent ───heartbeat──▶ Directory
|
||||
(每30秒)
|
||||
|
||||
Directory 判定离线:
|
||||
- 连续 3 次未收到心跳 → 标记为 offline
|
||||
- 离线 24 小时 → 自动注销
|
||||
```
|
||||
|
||||
#### 2.6.2 主动探测
|
||||
|
||||
```
|
||||
Directory ───ping──▶ Agent
|
||||
◀──pong──
|
||||
```
|
||||
|
||||
用于验证 Agent 是否真的在线(防止心跳伪造)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 方案二:身份认证与信任体系
|
||||
|
||||
### 3.1 三层身份架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Layer 3: 信任评分 (Trust Score) │
|
||||
│ 声誉系统、历史行为、第三方评价 │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ Layer 2: 身份验证 (Authentication) │
|
||||
│ 签名验证、证书校验、DID 验证 │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ Layer 1: 身份标识 (Identity) │
|
||||
│ AgentID + 公钥 / 证书 / DID │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 Layer 1:身份标识方案
|
||||
|
||||
#### 3.2.1 方案 A:公钥身份(推荐基础版)
|
||||
|
||||
**核心思想**:AgentID = 公钥哈希(类似比特币地址)
|
||||
|
||||
```
|
||||
AgentID = base58(sha256(public_key)[:20])
|
||||
例:agent-1A2B3C4D5E6F...
|
||||
```
|
||||
|
||||
**优点:**
|
||||
- 自包含,不需要中心 CA
|
||||
- 公钥可验证身份
|
||||
- 实现简单
|
||||
|
||||
**缺点:**
|
||||
- 身份无法撤销(私钥泄露 = 身份被盗)
|
||||
- 没有人类可读的名字
|
||||
- 无法关联真实世界身份
|
||||
|
||||
**适用场景**:内部系统、测试环境、信任度要求不高的场景
|
||||
|
||||
#### 3.2.2 方案 B:PKI 证书体系
|
||||
|
||||
**核心思想**:引入 CA(Certificate Authority)颁发证书
|
||||
|
||||
```
|
||||
CA 根证书
|
||||
├── 系统A证书
|
||||
│ ├── Agent-001 证书
|
||||
│ └── Agent-002 证书
|
||||
└── 系统B证书
|
||||
└── Agent-003 证书
|
||||
```
|
||||
|
||||
**优点:**
|
||||
- 成熟的工业标准
|
||||
- 支持证书撤销(CRL/OCSP)
|
||||
- 有层级信任关系
|
||||
|
||||
**缺点:**
|
||||
- 需要信任中心化 CA
|
||||
- 证书管理复杂
|
||||
- 跨 CA 互操作需要桥接
|
||||
|
||||
**适用场景**:企业内部、有明确信任中心的场景
|
||||
|
||||
#### 3.2.3 方案 C:区块链 DID(去中心化身份)
|
||||
|
||||
**核心思想**:身份上链,链上存公钥和属性,类似 W3C DID 标准。
|
||||
|
||||
```
|
||||
链上存储:
|
||||
DID → {
|
||||
"publicKey": "...",
|
||||
"controller": "...",
|
||||
"authentication": [...],
|
||||
"service": [...],
|
||||
"revoked": false,
|
||||
"timestamp": "..."
|
||||
}
|
||||
```
|
||||
|
||||
**三种实现路径:**
|
||||
|
||||
| 路径 | 说明 | 适用场景 |
|
||||
|------|------|---------|
|
||||
| **公链 DID** | 使用以太坊/Solana 等公链 | 开放生态、跨组织协作 |
|
||||
| **联盟链 DID** | 多机构共建联盟链 | 行业联盟、政企合作 |
|
||||
| **私有链 DID** | 单组织内部链 | 企业内部、对性能要求高 |
|
||||
|
||||
**优点:**
|
||||
- 去中心化,无单点故障
|
||||
- 身份可验证、可追溯
|
||||
- 支持跨系统互认
|
||||
- 不可篡改的身份历史
|
||||
|
||||
**缺点:**
|
||||
- 链上操作有延迟
|
||||
- 需要支付 Gas 费(公链)
|
||||
- 实现复杂度高
|
||||
- 隐私保护需额外设计
|
||||
|
||||
---
|
||||
|
||||
### 3.3 Layer 2:身份验证方案
|
||||
|
||||
#### 3.3.1 消息签名验证
|
||||
|
||||
所有灵隙消息必须签名,签名方式:
|
||||
|
||||
```
|
||||
签名内容 = SHA256(message_id + sender + receiver + payload + timestamp)
|
||||
签名 = Sign(private_key, 签名内容)
|
||||
验证 = Verify(public_key, 签名内容, 签名)
|
||||
```
|
||||
|
||||
#### 3.3.2 签名放置位置
|
||||
|
||||
| 位置 | 说明 |
|
||||
|------|------|
|
||||
| 消息体内 | `signature` 字段 |
|
||||
| HTTP Header | `X-Synapse-Signature` |
|
||||
| gRPC Metadata | `x-synapse-signature` |
|
||||
|
||||
#### 3.3.3 防重放攻击
|
||||
|
||||
```
|
||||
每个消息有唯一 message_id + timestamp
|
||||
接收方维护已处理 message_id 缓存(如最近 1 小时)
|
||||
timestamp 与本地时间差超过 5 分钟 → 拒绝
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.4 Layer 3:信任评分体系
|
||||
|
||||
#### 3.4.1 信任分维度
|
||||
|
||||
| 维度 | 权重 | 说明 |
|
||||
|------|------|------|
|
||||
| 任务完成率 | 30% | 历史任务成功 / 总任务数 |
|
||||
| 任务质量 | 25% | 结果评分、返工率 |
|
||||
| 响应速度 | 15% | 平均响应时间 |
|
||||
| 在线稳定性 | 15% | 在线时长 / 总时长 |
|
||||
| 历史信誉 | 15% | 其他 Agent 的评价 |
|
||||
|
||||
#### 3.4.2 信任分计算
|
||||
|
||||
```
|
||||
trust_score = Σ(维度_i × 权重_i)
|
||||
|
||||
范围:0 - 100
|
||||
等级:
|
||||
90-100 S 级 高度可信
|
||||
75-89 A 级 可信
|
||||
60-74 B 级 一般可信
|
||||
40-59 C 级 谨慎使用
|
||||
0-39 D 级 不可信
|
||||
```
|
||||
|
||||
#### 3.4.3 信任传递
|
||||
|
||||
```
|
||||
A 信任 B(评分 90)
|
||||
B 信任 C(评分 85)
|
||||
A 对 C 的初始信任 = 90 × 85% × 衰减因子 = 约 68
|
||||
```
|
||||
|
||||
**注意**:信任传递有衰减,防止信任污染。
|
||||
|
||||
---
|
||||
|
||||
### 3.5 区块链适用性决策树
|
||||
|
||||
```
|
||||
是否需要跨组织协作?
|
||||
├── 否 → 用 PKI 或公钥身份(简单高效)
|
||||
└── 是 → 是否有共同信任的中心机构?
|
||||
├── 是 → 用 PKI 体系(成熟可靠)
|
||||
└── 否 → 是否需要不可篡改的审计?
|
||||
├── 是 → 考虑联盟链 DID
|
||||
└── 否 → 公钥身份 + 声誉系统可能够用
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 方案三:协议缺失内容补全
|
||||
|
||||
### 4.1 第一优先级补全(核心基础设施)
|
||||
|
||||
#### 4.1.1 Agent 发现协议(ADS - Agent Discovery Service)
|
||||
|
||||
**新增消息类型:**
|
||||
|
||||
| 消息类型 | 方向 | 说明 |
|
||||
|---------|------|------|
|
||||
| `dir:register` | Agent → Directory | 注册 Agent |
|
||||
| `dir:unregister` | Agent → Directory | 注销 Agent |
|
||||
| `dir:heartbeat` | Agent → Directory | 心跳 |
|
||||
| `dir:update_capabilities` | Agent → Directory | 更新能力 |
|
||||
| `dir:query_by_id` | Agent → Directory | 按 ID 查询 |
|
||||
| `dir:query_by_capability` | Agent → Directory | 按能力查询 |
|
||||
| `dir:list` | Agent → Directory | 列表查询 |
|
||||
| `dir:query_result` | Directory → Agent | 查询结果 |
|
||||
|
||||
**新增数据结构:** `AgentDirectoryEntry`, `CapabilityIndex`, `QueryFilter`
|
||||
|
||||
#### 4.1.2 身份认证规范
|
||||
|
||||
**新增消息类型:**
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `auth:challenge` | 身份挑战 |
|
||||
| `auth:response` | 挑战响应 |
|
||||
| `auth:cert_exchange` | 证书交换 |
|
||||
|
||||
**新增内容:**
|
||||
- 标准签名算法列表(Ed25519, ECDSA P-256, RSA 2048)
|
||||
- 消息签名格式规范
|
||||
- 防重放攻击机制
|
||||
- 身份验证流程规范
|
||||
|
||||
#### 4.1.3 心跳与存活检测
|
||||
|
||||
**新增消息类型:**
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `net:ping` | 存活探测 |
|
||||
| `net:pong` | 存活响应 |
|
||||
| `net:offline_notify` | 下线通知 |
|
||||
|
||||
**规范内容:**
|
||||
- 心跳间隔(默认 30 秒)
|
||||
- 超时判定(连续 3 次未收到 → 离线)
|
||||
- 离线通知机制
|
||||
|
||||
#### 4.1.4 统一错误码体系
|
||||
|
||||
**错误分类:**
|
||||
|
||||
| 错误码前缀 | 类别 | 示例 |
|
||||
|-----------|------|------|
|
||||
| `msg_` | 消息层 | msg_invalid_format, msg_signature_invalid |
|
||||
| `auth_` | 认证层 | auth_failed, auth_expired, auth_forbidden |
|
||||
| `task_` | 任务层 | task_not_found, task_timeout, task_cancelled |
|
||||
| `cap_` | 能力层 | cap_lacking, cap_busy, cap_not_supported |
|
||||
| `group_` | 群组层 | group_not_found, group_full, group_permission_denied |
|
||||
| `dir_` | 目录层 | dir_agent_not_found, dir_query_failed |
|
||||
| `net_` | 网络层 | net_timeout, net_offline, net_unreachable |
|
||||
| `sys_` | 系统层 | sys_internal_error, sys_not_implemented |
|
||||
|
||||
---
|
||||
|
||||
### 4.2 第二优先级补全(重要功能)
|
||||
|
||||
#### 4.2.1 能力匹配与任务路由
|
||||
|
||||
**功能:**
|
||||
- 智能选择最合适的 Agent 执行任务
|
||||
- 支持负载均衡(按并发数、响应速度、信任分)
|
||||
- 支持任务失败自动重试/转发
|
||||
|
||||
**新增消息类型:**
|
||||
- `router:submit_task` - 提交给路由器自动选择执行者
|
||||
- `router:forward` - 任务转发
|
||||
|
||||
#### 4.2.2 全网广播协议
|
||||
|
||||
**新增消息类型:**
|
||||
|
||||
| 消息类型 | 说明 |
|
||||
|---------|------|
|
||||
| `global:broadcast` | 全网广播 |
|
||||
| `system:broadcast` | 系统内广播 |
|
||||
| `cap:broadcast` | 能力组广播 |
|
||||
|
||||
**规范内容:**
|
||||
- 广播范围定义
|
||||
- TTL 与去重机制
|
||||
- 广播风暴防护
|
||||
|
||||
#### 4.2.3 版本协商机制
|
||||
|
||||
**功能:**
|
||||
- Agent 之间协商使用的协议版本
|
||||
- 向后兼容处理
|
||||
- 能力降级策略
|
||||
|
||||
**新增消息类型:**
|
||||
- `meta:version_query` - 查询支持的版本
|
||||
- `meta:version_negotiate` - 版本协商
|
||||
|
||||
#### 4.2.4 幂等性保证
|
||||
|
||||
**规范内容:**
|
||||
- 幂等键(Idempotency Key)机制
|
||||
- 重复消息处理规范
|
||||
- 任务级幂等 vs 消息级幂等
|
||||
|
||||
---
|
||||
|
||||
### 4.3 第三优先级补全(增强功能)
|
||||
|
||||
#### 4.3.1 流式输出协议
|
||||
|
||||
**功能:**
|
||||
- 长任务实时进度推送
|
||||
- 生成式 AI 的 token 流式输出
|
||||
- WebSocket/SSE 传输支持
|
||||
|
||||
#### 4.3.2 批量任务协议
|
||||
|
||||
**功能:**
|
||||
- 一次性提交多个同类型任务
|
||||
- 批量状态查询
|
||||
- 批量结果收集
|
||||
|
||||
#### 4.3.3 支付与激励机制
|
||||
|
||||
**功能:**
|
||||
- 任务定价与结算
|
||||
- Token 或积分系统
|
||||
- 智能合约自动结算(区块链场景)
|
||||
|
||||
#### 4.3.4 群组发现协议
|
||||
|
||||
**功能:**
|
||||
- 按主题/任务类型查找群组
|
||||
- 推荐加入合适的群组
|
||||
- 群组热度/活跃度排名
|
||||
|
||||
---
|
||||
|
||||
### 4.4 安全相关补全
|
||||
|
||||
#### 4.4.1 端到端加密
|
||||
|
||||
**规范内容:**
|
||||
- 密钥协商(ECDH)
|
||||
- 对称加密载荷(AES-GCM)
|
||||
- 前向保密(PFS)
|
||||
|
||||
#### 4.4.2 权限委托
|
||||
|
||||
**功能:**
|
||||
- Agent A 委托 Agent B 代表自己执行任务
|
||||
- 委托令牌(Delegation Token)
|
||||
- 委托范围与时效控制
|
||||
|
||||
#### 4.4.3 审计日志标准
|
||||
|
||||
**规范内容:**
|
||||
- 统一审计日志格式
|
||||
- 关键操作必须记录
|
||||
- 日志完整性保护(哈希链)
|
||||
|
||||
---
|
||||
|
||||
## 5. 分阶段实施路线图
|
||||
|
||||
### 5.1 Phase 0:基础版(MVP) - 预计 2 周
|
||||
|
||||
**目标**:协议可用,能跑通基本流程
|
||||
|
||||
**内容:**
|
||||
- [x] 灵隙点对点任务协议(已完成)
|
||||
- [x] A2N 群组协作协议(已完成)
|
||||
- [x] P2A 外部调用协议(已完成)
|
||||
- [x] 三套协议的 SDK 实现(已完成)
|
||||
|
||||
---
|
||||
|
||||
### 5.2 Phase 1:发现与身份 - 预计 4 周
|
||||
|
||||
**目标**:Agent 能互相找到对方,能验证对方身份
|
||||
|
||||
**新增协议模块:**
|
||||
|
||||
| 模块 | 说明 | 优先级 |
|
||||
|------|------|--------|
|
||||
| Agent 目录服务协议 | 集中式 ADS | P0 |
|
||||
| 公钥身份规范 | Ed25519 签名验证 | P0 |
|
||||
| 心跳与存活检测 | 30 秒心跳,3 次超时判定离线 | P0 |
|
||||
| 统一错误码体系 | 8 大类错误码 | P0 |
|
||||
| 防重放攻击 | message_id + timestamp | P0 |
|
||||
|
||||
**输出物:**
|
||||
- 新协议文档:`06-agent-discovery.md`
|
||||
- 新协议文档:`07-authentication.md`
|
||||
- 代码:`discovery/` 包(目录服务客户端)
|
||||
- 代码:`auth/` 包(签名验证工具)
|
||||
- 更新 types 包:新增消息类型和错误码
|
||||
|
||||
---
|
||||
|
||||
### 5.3 Phase 2:路由与广播 - 预计 4 周
|
||||
|
||||
**目标**:支持智能任务路由和全网广播
|
||||
|
||||
**新增协议模块:**
|
||||
|
||||
| 模块 | 说明 | 优先级 |
|
||||
|------|------|--------|
|
||||
| 任务路由协议 | 按能力 + 负载 + 信任分选择执行者 | P1 |
|
||||
| 全网广播协议 | 基于目录服务的广播转发 | P1 |
|
||||
| 版本协商机制 | 协议版本兼容 | P1 |
|
||||
| 幂等性保证 | 幂等键 + 去重 | P1 |
|
||||
|
||||
**输出物:**
|
||||
- 新协议文档:`08-routing-and-broadcast.md`
|
||||
- 代码:`routing/` 包增强(增加能力路由)
|
||||
- 更新目录服务:支持广播转发
|
||||
|
||||
---
|
||||
|
||||
### 5.4 Phase 3:信任与激励 - 预计 6 周
|
||||
|
||||
**目标**:建立信任体系,支持规模化协作
|
||||
|
||||
**新增协议模块:**
|
||||
|
||||
| 模块 | 说明 | 优先级 |
|
||||
|------|------|--------|
|
||||
| 信任评分体系 | 5 维度信任分计算 | P2 |
|
||||
| 流式输出协议 | WebSocket/SSE 流式进度 | P2 |
|
||||
| 批量任务协议 | 批量提交/查询/收集 | P2 |
|
||||
| 群组发现协议 | 群组搜索与推荐 | P2 |
|
||||
| SLA 协议 | 服务等级承诺 | P3 |
|
||||
|
||||
**输出物:**
|
||||
- 新协议文档:`09-trust-and-reputation.md`
|
||||
- 代码:`trust/` 包(信任评分引擎)
|
||||
|
||||
---
|
||||
|
||||
### 5.5 Phase 4:高级特性 - 预计 8 周+
|
||||
|
||||
**目标**:企业级、生产级完整功能
|
||||
|
||||
**新增协议模块:**
|
||||
|
||||
| 模块 | 说明 | 优先级 |
|
||||
|------|------|--------|
|
||||
| 端到端加密 | ECDH + AES-GCM | P3 |
|
||||
| 权限委托 | 委托令牌机制 | P3 |
|
||||
| 支付与激励 | Token/积分系统 | P3 |
|
||||
| 区块链 DID 集成 | 可选模块 | P4 |
|
||||
| 审计日志标准 | 统一审计格式 | P3 |
|
||||
| Gossip 协议 | 分布式发现(可选) | P4 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 技术选型建议
|
||||
|
||||
### 6.1 目录服务存储选型
|
||||
|
||||
| 方案 | 适用场景 | 优点 | 缺点 |
|
||||
|------|---------|------|------|
|
||||
| **Redis** | 中小规模 | 高性能,支持 TTL | 数据量受限 |
|
||||
| **PostgreSQL** | 中大规模 | 复杂查询,持久化 | 需运维 |
|
||||
| **Elasticsearch** | 大规模 + 全文检索 | 强大的搜索能力 | 运维复杂 |
|
||||
|
||||
**推荐**:先用 Redis(简单快),规模大了再迁 PostgreSQL。
|
||||
|
||||
---
|
||||
|
||||
### 6.2 签名算法选型
|
||||
|
||||
| 算法 | 安全性 | 性能 | 密钥长度 | 推荐度 |
|
||||
|------|--------|------|---------|--------|
|
||||
| Ed25519 | 高 | 快 | 256 位 | ⭐⭐⭐⭐⭐ 首选 |
|
||||
| ECDSA P-256 | 高 | 中 | 256 位 | ⭐⭐⭐⭐ 兼容 |
|
||||
| RSA 2048 | 中 | 慢 | 2048 位 | ⭐⭐ 仅兼容 |
|
||||
|
||||
**推荐**:默认 Ed25519,需要兼容旧系统时支持 ECDSA。
|
||||
|
||||
---
|
||||
|
||||
### 6.3 区块链选型(如果需要)
|
||||
|
||||
| 场景 | 推荐方案 | 说明 |
|
||||
|------|---------|------|
|
||||
| 企业内部 | **私有链(Hyperledger Fabric)** | 可控,高性能,无 Gas 费 |
|
||||
| 行业联盟 | **联盟链(FISCO BCOS / ChainMaker)** | 多机构共建,合规 |
|
||||
| 开放生态 | **公链(以太坊 / Solana)** | 完全开放,需 Gas 费 |
|
||||
| 简单原型 | **模拟链(数据库模拟)** | 先验证逻辑,再上真链 |
|
||||
|
||||
**推荐**:90% 的场景用不到真区块链,先用数据库模拟验证业务逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 7. 总结与建议
|
||||
|
||||
### 7.1 核心建议
|
||||
|
||||
1. **先集中后分布**:先用集中式目录服务,简单高效;规模大了再考虑分布式
|
||||
2. **先公钥后区块链**:先用公钥身份解决 80% 的问题,真的需要了再上链
|
||||
3. **先 MVP 后完善**:先跑通核心流程,再逐步加高级功能
|
||||
4. **标准接口可替换**:目录服务、身份系统都做成可插拔的,方便未来升级
|
||||
|
||||
### 7.2 下一步行动
|
||||
|
||||
| 序号 | 行动 | 负责人 | 产出 |
|
||||
|------|------|--------|------|
|
||||
| 1 | 评审本方案,确认 Phase 1 范围 | 架构组 | 确认后的方案 |
|
||||
| 2 | 设计目录服务 API 详细规范 | 协议组 | `06-agent-discovery.md` |
|
||||
| 3 | 设计身份认证详细规范 | 协议组 | `07-authentication.md` |
|
||||
| 4 | 实现目录服务原型 | 开发组 | 可运行的 ADS 服务 |
|
||||
| 5 | 更新 SDK 支持发现和认证 | 开发组 | 新版 SDK |
|
||||
|
||||
---
|
||||
|
||||
## 附录:相关文档索引
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| [01-synapse-raw-protocol.md](./01-synapse-raw-protocol.md) | 灵隙原始协议(Synapse Raw Protocol)(纲领) |
|
||||
| [02-a2a-protocol.md](./02-a2a-protocol.md) | A2A 协议(P2P 任务) |
|
||||
| [03-a2n-protocol.md](./03-a2n-protocol.md) | A2N 协议(群组协作) |
|
||||
| [04-p2a-protocol.md](./04-p2a-protocol.md) | P2A 协议(外部调用) |
|
||||
| [05-sdk-implementation.md](./05-sdk-implementation.md) | SDK 实现说明 |
|
||||
| **本文档** | **解决方案白皮书** |
|
||||
@@ -164,6 +164,18 @@ const (
|
||||
GroupMemberBusy GroupMemberStatus = "busy"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// Signature Algorithm
|
||||
// =============================================================================
|
||||
|
||||
// SignatureAlgorithm defines supported signature algorithms
|
||||
type SignatureAlgorithm string
|
||||
|
||||
const (
|
||||
AlgHMACSHA256 SignatureAlgorithm = "HMAC-SHA256" // 默认
|
||||
AlgHMACSHA512 SignatureAlgorithm = "HMAC-SHA512"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// Priority
|
||||
// =============================================================================
|
||||
@@ -175,6 +187,7 @@ const (
|
||||
PriorityHigh Priority = "high"
|
||||
PriorityNormal Priority = "normal"
|
||||
PriorityLow Priority = "low"
|
||||
PriorityUrgent Priority = "urgent"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -213,8 +226,9 @@ type UnifiedSynapseMessage struct {
|
||||
Broadcast bool `json:"broadcast,omitempty"`
|
||||
|
||||
// Security
|
||||
Signature string `json:"signature,omitempty"`
|
||||
Encrypt bool `json:"encrypt,omitempty"`
|
||||
Signature string `json:"signature,omitempty"`
|
||||
SignatureAlgorithm string `json:"signature_algorithm,omitempty"`
|
||||
Encrypt bool `json:"encrypt,omitempty"`
|
||||
|
||||
// Timestamps
|
||||
Timestamp Timestamp `json:"timestamp"`
|
||||
|
||||
Reference in New Issue
Block a user