灵隙(synapse) v1.0.0 版本
This commit is contained in:
21
CHANGELOG.md
Normal file
21
CHANGELOG.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# 变更记录
|
||||
|
||||
## [1.0.0] - 2026-06-27
|
||||
|
||||
### Feat
|
||||
|
||||
- 初始化灵隙 Synapse 协议栈项目
|
||||
- 实现 A2A(点对点)、A2N(群组协作)、P2A(外部调用)三大子协议
|
||||
- 实现七大组件层:灵脉、灵玺、灵鉴、灵泊、灵瞰、灵衡、灵枢
|
||||
- 实现完整 SDK(p2a/a2a/a2n 三合一)
|
||||
- 实现 gRPC 双传输协议
|
||||
- 实现 HiL 三级升级机制
|
||||
- 实现数字分段错误码系统
|
||||
- 协议文档 v1 重构(抽象层 + 实现层分离)
|
||||
|
||||
### Refactor
|
||||
|
||||
- 项目结构按标准化规范重组(cmd/internal/pkg/docs/configs/test)
|
||||
- SDK 三个子包合并为单包,通过 Protocol 参数区分
|
||||
- 协议文档移除编号前缀
|
||||
- 所有 import 路径对齐新目录结构
|
||||
0
configs/.gitkeep
Normal file
0
configs/.gitkeep
Normal file
@@ -1,113 +0,0 @@
|
||||
# 灵隙 Synapse 协议栈 v1
|
||||
|
||||
> 版本:1.0.0
|
||||
> 更新日期:2026-06-27
|
||||
|
||||
---
|
||||
|
||||
## 协议结构
|
||||
|
||||
本版本协议按抽象层设计,只定义"实现什么功能"和"数据格式",不涉及具体实现细节。
|
||||
|
||||
```
|
||||
docs/v1/
|
||||
├── 00-index.md # 本文件(协议索引)
|
||||
├── 01-raw-protocol.md # 原始协议(基础层)
|
||||
├── 02-a2a-protocol.md # A2A 协议(点对点)
|
||||
├── 03-a2n-protocol.md # A2N 协议(群组协作)
|
||||
└── 04-p2a-protocol.md # P2A 协议(外部调用)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 协议概览
|
||||
|
||||
### 层级关系
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ P2A 协议(外部调用入口) │
|
||||
│ 04-p2a-protocol.md │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
│ 封装
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ A2A 协议(点对点任务协作) │
|
||||
│ 02-a2a-protocol.md │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ 复用单播
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ A2N 协议(群组协作) │
|
||||
│ 03-a2n-protocol.md │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ 继承
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ 原始协议(公共基础) │
|
||||
│ 01-raw-protocol.md │
|
||||
│ - 统一消息信封 │
|
||||
│ - 身份标识 │
|
||||
│ - 签名机制 │
|
||||
│ - 时间格式 │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 协议对比
|
||||
|
||||
| 协议 | 代号 | 参与方 | 核心功能 |
|
||||
|------|------|--------|---------|
|
||||
| 原始协议 | Raw | - | 消息信封、身份、签名 |
|
||||
| A2A 协议 | P2P | Agent ↔ Agent | 任务提交、进度同步、信息补全、HiL |
|
||||
| A2N 协议 | Group | 1 Coordinator + N Worker | 群组管理、任务拆分合并、投票共识 |
|
||||
| P2A 协议 | Caller | 外部 → Agent | REST API、回调/轮询 |
|
||||
|
||||
---
|
||||
|
||||
## 消息类型前缀
|
||||
|
||||
| 前缀 | 协议 | 说明 |
|
||||
|------|------|------|
|
||||
| `p2p:` | A2A | 点对点消息(17 种) |
|
||||
| `group:` | A2N | 群组内消息(20+ 种) |
|
||||
| `inter_group:` | A2N | 跨群组消息(升级) |
|
||||
| `caller:` | P2A | 外部调用消息(内部转换) |
|
||||
|
||||
---
|
||||
|
||||
## 与 v0 版本的差异
|
||||
|
||||
| 对比项 | v0 版本 | v1 版本 |
|
||||
|--------|---------|---------|
|
||||
| 内容定位 | 混合抽象层和实现层 | 纯抽象层 |
|
||||
| 数据格式 | 嵌入大量 JSON 示例 | 表格化字段定义 |
|
||||
| 实现细节 | 包含 Go struct、算法实现 | 仅定义接口要求 |
|
||||
| 七大组件 | 无协议规范 | 待补充独立文档 |
|
||||
| 文件数量 | 8 个 | 4 个核心协议 |
|
||||
|
||||
---
|
||||
|
||||
## 后续补充
|
||||
|
||||
以下内容待补充为独立实现文档(不属于协议抽象层):
|
||||
|
||||
- 七大组件详细规范(灵脉、灵玺、灵鉴、灵泊、灵瞰、灵衡、灵枢)
|
||||
- 内容类型系统(TextContent、FileContent 等)
|
||||
- 能力声明系统
|
||||
- gRPC 传输协议
|
||||
- HiL 三级升级机制
|
||||
- 数字分段错误码完整定义
|
||||
|
||||
---
|
||||
|
||||
## 版本历史
|
||||
|
||||
| 版本 | 日期 | 变更说明 |
|
||||
|------|------|---------|
|
||||
| v1.0.0 | 2026-06-27 | 重构为抽象层协议,分离实现细节 |
|
||||
| v0.x | 2026-06-27 | 初始版本(混合抽象层与实现层) |
|
||||
107
docs/v1/00_index.md
Normal file
107
docs/v1/00_index.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# 灵隙 Synapse 协议栈 v1
|
||||
|
||||
> 版本:1.0.0
|
||||
> 更新日期:2026-06-27
|
||||
|
||||
---
|
||||
|
||||
## 协议结构
|
||||
|
||||
本版本协议按抽象层设计,只定义"实现什么功能"和"数据格式",不涉及具体实现细节。
|
||||
|
||||
```
|
||||
docs/v1/
|
||||
├── index.md # 本文件(协议索引)
|
||||
├── raw-protocol.md # 原始协议(基础层)
|
||||
├── a2a-protocol.md # A2A 协议(点对点)
|
||||
├── a2n-protocol.md # A2N 协议(群组协作)
|
||||
├── p2a-protocol.md # P2A 协议(外部调用)
|
||||
├── identity-protocol-draft.md # 去中心化身份与寻址(草案)
|
||||
│
|
||||
├── components.md # 七大组件层(灵脉/灵玺/灵鉴/灵泊/灵瞰/灵衡/灵枢)
|
||||
├── content-system.md # 内容类型系统
|
||||
├── capabilities-system.md # 能力声明系统
|
||||
├── grpc-transport.md # gRPC 传输协议
|
||||
├── hil-mechanism.md # HiL 三级升级机制
|
||||
├── error-codes.md # 数字分段错误码系统
|
||||
├── message-extensions.md # 消息扩展字段
|
||||
└── transport-modes.md # 传输模式定义
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 文档分类
|
||||
|
||||
v1 版本文档分为三层:
|
||||
|
||||
### 协议层(抽象层)
|
||||
|
||||
定义"实现什么功能"和"数据格式",不涉及具体实现细节:
|
||||
|
||||
| 编号 | 文档 | 说明 |
|
||||
|----|----------------------------------------------------------------|---------------|
|
||||
| 00 | [00_index.md](00_index.md) | 协议索引 |
|
||||
| 01 | [01_raw-protocol.md](01_raw-protocol.md) | 原始协议(公共基础) |
|
||||
| 02 | [02_a2a-protocol.md](02_a2a-protocol.md) | A2A 协议(点对点) |
|
||||
| 03 | [03_a2n-protocol.md](03_a2n-protocol.md) | A2N 协议(群组协作) |
|
||||
| 04 | [04_p2a-protocol.md](04_p2a-protocol.md) | P2A 协议(外部调用) |
|
||||
| 05 | [05_identity-protocol-draft.md](05_identity-protocol-draft.md) | 去中心化身份与寻址(草案) |
|
||||
|
||||
### 实现层
|
||||
|
||||
每个文档对应一个独立的功能模块:
|
||||
|
||||
| 文档 | 对应模块 |
|
||||
|--------------------------------------------------|-----------------------------|
|
||||
| [components.md](components.md) | 七大组件层(灵脉/灵玺/灵鉴/灵泊/灵瞰/灵衡/灵枢) |
|
||||
| [content-system.md](content-system.md) | 内容类型系统 |
|
||||
| [capabilities-system.md](capabilities-system.md) | 能力声明系统 |
|
||||
| [grpc-transport.md](grpc-transport.md) | gRPC 传输协议 |
|
||||
| [hil-mechanism.md](hil-mechanism.md) | HiL 三级升级机制 |
|
||||
| [error-codes.md](error-codes.md) | 数字分段错误码系统 |
|
||||
| [message-extensions.md](message-extensions.md) | 消息扩展字段 |
|
||||
| [transport-modes.md](transport-modes.md) | 传输模式定义 |
|
||||
|
||||
---
|
||||
|
||||
## 协议对比
|
||||
|
||||
| 协议 | 代号 | 参与方 | 核心功能 |
|
||||
|--------|--------|--------------------------|--------------------|
|
||||
| 原始协议 | Raw | - | 消息信封、身份、签名 |
|
||||
| A2A 协议 | P2P | Agent ↔ Agent | 任务提交、进度同步、信息补全、HiL |
|
||||
| A2N 协议 | Group | 1 Coordinator + N Worker | 群组管理、任务拆分合并、投票共识 |
|
||||
| P2A 协议 | Caller | 外部 → Agent | REST API、回调/轮询 |
|
||||
|
||||
---
|
||||
|
||||
## 消息类型前缀
|
||||
|
||||
| 前缀 | 协议 | 说明 |
|
||||
|----------------|-----|--------------|
|
||||
| `p2p:` | A2A | 点对点消息(17 种) |
|
||||
| `group:` | A2N | 群组内消息(20+ 种) |
|
||||
| `inter_group:` | A2N | 跨群组消息(升级) |
|
||||
| `caller:` | P2A | 外部调用消息(内部转换) |
|
||||
|
||||
---
|
||||
|
||||
## 与 v0 版本的差异
|
||||
|
||||
| 对比项 | v0 版本 | v1 版本 |
|
||||
|------|-------------------|--------------------------|
|
||||
| 内容定位 | 混合抽象层和实现层 | 协议层 + 独立实现层 |
|
||||
| 数据格式 | 嵌入大量 JSON 示例 | 表格化字段定义 |
|
||||
| 实现细节 | 包含 Go struct、算法实现 | 独立实现层文档 |
|
||||
| 七大组件 | 一个文档混合包含 | 独立为组件层规范 |
|
||||
| SDK | 三个子包(p2a/a2a/a2n) | 单包,通过 Protocol 参数区分 |
|
||||
| 文件数量 | 8 个 | 14 个(5 协议 + 8 实现 + 1 草案) |
|
||||
|
||||
---
|
||||
|
||||
## 版本历史
|
||||
|
||||
| 版本 | 日期 | 变更说明 |
|
||||
|--------|------------|-------------------------------|
|
||||
| v1.0.0 | 2026-06-27 | 重构为抽象层协议 + 独立实现层文档,继承 v0 全部内容 |
|
||||
| v0.x | 2026-06-27 | 初始版本(混合抽象层与实现层) |
|
||||
@@ -3,7 +3,7 @@
|
||||
> 版本:1.0.0
|
||||
> 状态:规范
|
||||
> 层级:子协议层
|
||||
> 基于:[灵隙原始协议](01-raw-protocol.md)
|
||||
> 基于:[灵隙原始协议](01_raw-protocol.md)
|
||||
> 前缀:`p2p:`
|
||||
|
||||
---
|
||||
@@ -3,7 +3,7 @@
|
||||
> 版本:1.0.0
|
||||
> 状态:规范
|
||||
> 层级:子协议层
|
||||
> 基于:[灵隙原始协议](01-raw-protocol.md)、[A2A 协议](02-a2a-protocol.md)
|
||||
> 基于:[灵隙原始协议](01_raw-protocol.md)、[A2A 协议](02_a2a-protocol.md)
|
||||
> 前缀:`group:` / `inter_group:`
|
||||
|
||||
---
|
||||
@@ -3,7 +3,7 @@
|
||||
> 版本:1.0.0
|
||||
> 状态:规范
|
||||
> 层级:子协议层
|
||||
> 基于:[灵隙原始协议](01-raw-protocol.md)
|
||||
> 基于:[灵隙原始协议](01_raw-protocol.md)
|
||||
> 前缀:`caller:`(内部转换)
|
||||
|
||||
---
|
||||
@@ -3,7 +3,7 @@
|
||||
> 版本:0.1(草案)
|
||||
> 状态:草案
|
||||
> 层级:基础设施层
|
||||
> 依赖:[灵隙原始协议](01-raw-protocol.md)
|
||||
> 依赖:[灵隙原始协议](01_raw-protocol.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -782,7 +782,7 @@ func (a *AntiReplay) CheckAndAdd(nonce string, timestamp int64) bool {
|
||||
|
||||
### 8.1 与原始协议的整合
|
||||
|
||||
本草案扩展 [01-raw-protocol.md](01-raw-protocol.md) 中的 `AgentEndpoint`:
|
||||
本草案扩展 [raw-protocol.md](01_raw-protocol.md) 中的 `AgentEndpoint`:
|
||||
|
||||
```go
|
||||
type AgentEndpoint struct {
|
||||
480
docs/v1/capabilities-system.md
Normal file
480
docs/v1/capabilities-system.md
Normal file
@@ -0,0 +1,480 @@
|
||||
# 灵隙协议实现规范:能力声明系统
|
||||
|
||||
> 版本:1.0.0 | 层级:实现层
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
1. [核心接口 CapabilitiesProvider](#1-核心接口-capabilitiesprovider)
|
||||
2. [能力描述结构 AgentCapabilities](#2-能力描述结构-agentcapabilities)
|
||||
3. [SupportedTaskType 结构](#3-supportedtasktype-结构)
|
||||
4. [接口分类](#4-接口分类)
|
||||
5. [合规级别](#5-合规级别)
|
||||
6. [标准接口注册表](#6-标准接口注册表)
|
||||
|
||||
---
|
||||
|
||||
## 1. 核心接口 CapabilitiesProvider
|
||||
|
||||
每个 Agent 必须实现 `CapabilitiesProvider` 接口,以便其他 Agent 查询其能力。该接口是所有能力声明系统的入口点,定义了三个核心方法:
|
||||
|
||||
### 1.1 接口定义
|
||||
|
||||
```go
|
||||
type CapabilitiesProvider interface {
|
||||
GetCapabilities(ctx context.Context) (*AgentCapabilities, error)
|
||||
GetSupportedInterfaces() []ImplementedInterface
|
||||
GetTaskTypeCapabilities(taskType string) (*SupportedTaskType, error)
|
||||
}
|
||||
```
|
||||
|
||||
### 1.2 方法说明
|
||||
|
||||
| 方法 | 返回值 | 说明 |
|
||||
|------|--------|------|
|
||||
| `GetCapabilities(ctx)` | `*AgentCapabilities` | 返回 Agent 的完整能力描述,包括身份信息、支持的任务类型、内容类型、传输模式、资源限制以及各项特性支持标志。这是能力查询的核心方法。 |
|
||||
| `GetSupportedInterfaces()` | `[]ImplementedInterface` | 返回 Agent 实现的标准接口列表。每个条目包含接口名称、分类、合规级别和版本号,用于快速判断 Agent 是否支持某项协议功能。 |
|
||||
| `GetTaskTypeCapabilities(taskType)` | `*SupportedTaskType` | 返回指定任务类型的详细能力描述,包括输入输出 Schema、预估耗时、关联能力标签等。若 Agent 不支持该任务类型,应返回 `nil`。 |
|
||||
|
||||
### 1.3 ImplementedInterface 结构
|
||||
|
||||
```go
|
||||
type ImplementedInterface struct {
|
||||
Name string `json:"name"`
|
||||
Category InterfaceCategory `json:"category"`
|
||||
ComplianceLevel Compliance `json:"compliance_level"`
|
||||
Version string `json:"version"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
### 1.4 调用约定
|
||||
|
||||
- `GetCapabilities` 应当在 Agent 启动时被注册中心调用,用于填充全局能力目录。
|
||||
- `GetSupportedInterfaces` 应当在 P2P 握手阶段被对端 Agent 调用,用于协商交互协议。
|
||||
- `GetTaskTypeCapabilities` 应当在任务提交前被调用,用于验证任务类型是否受支持以及获取输入输出约束。
|
||||
- 所有方法必须保持幂等性,多次调用应返回一致的结果。
|
||||
|
||||
---
|
||||
|
||||
## 2. 能力描述结构 AgentCapabilities
|
||||
|
||||
`AgentCapabilities` 是 Agent 能力声明的核心数据结构,包含了 Agent 的身份信息、支持的任务类型、内容类型、传输模式以及资源限制和特性支持标志。
|
||||
|
||||
### 2.1 完整结构定义
|
||||
|
||||
```go
|
||||
type AgentCapabilities 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 []TransportMode `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"`
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 字段详解
|
||||
|
||||
#### 身份信息
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `AgentID` | `string` | Agent 唯一标识符。在整个生态系统中必须全局唯一,通常由系统 ID 和 Agent 名称组合生成。格式示例:`sys_alpha_agent_01`。 |
|
||||
| `AgentName` | `string` | Agent 的人类可读名称。用于日志和界面展示,如"文本生成助手"。 |
|
||||
| `SystemID` | `string` | 所属系统 ID。标识该 Agent 归属于哪个系统或组织,用于跨系统路由和权限控制。 |
|
||||
| `ProtocolVersion` | `string` | 协议版本号。遵循语义化版本规范,格式为 `major.minor.patch`,如 `"1.0.0"`。用于协议兼容性协商。 |
|
||||
|
||||
#### 能力清单
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `SupportedTaskTypes` | `[]SupportedTaskType` | 支持的任务类型列表。每个条目描述一种任务类型的详细能力(见第 3 章)。 |
|
||||
| `SupportedContentTypes` | `[]string` | 支持的内容类型列表。使用 MIME 类型标识,如 `["text/plain", "image/png", "application/json"]`。 |
|
||||
| `SupportedTransportModes` | `[]TransportMode` | 支持的传输模式列表。详见 `TransportMode` 枚举定义。 |
|
||||
|
||||
##### TransportMode 枚举
|
||||
|
||||
```go
|
||||
type TransportMode string
|
||||
|
||||
const (
|
||||
TransportModeCallback TransportMode = "callback" // 异步回调(Webhook)
|
||||
TransportModePolling TransportMode = "polling" // 异步轮询
|
||||
TransportModeSync TransportMode = "sync" // 同步等待
|
||||
TransportModeHybrid TransportMode = "hybrid" // 回调 + 轮询兜底
|
||||
)
|
||||
```
|
||||
|
||||
#### 资源限制
|
||||
|
||||
| 字段 | 类型 | 说明 | 默认值 |
|
||||
|------|------|------|--------|
|
||||
| `MaxFileSize` | `int64` | 单文件最大字节数。超过此大小的文件应当使用 URL 或流式传输。 | 10MB(10485760) |
|
||||
| `MaxConcurrentTasks` | `int` | 最大并发任务数。用于流量控制和负载均衡,超过此数的任务请求将被拒绝(错误码 4004)。 | 10 |
|
||||
| `MaxTaskDuration` | `int` | 单个任务最大持续时间(秒)。超过此时间的任务将被自动终止(错误码 2004)。 | 3600(1 小时) |
|
||||
|
||||
#### 特性支持标志
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `SupportProgressUpdates` | `bool` | 是否支持进度更新推送。若为 `true`,Agent 将在任务执行过程中发送 `TaskEvent` 类型为 `progress` 的更新。 |
|
||||
| `SupportStreaming` | `bool` | 是否支持流式传输。若为 `true`,Agent 可以接收和发送 `StreamChunk` 格式的流数据。 |
|
||||
| `SupportHiL` | `bool` | 是否支持人工介入(Human-in-the-Loop)。若为 `true`,Agent 可以在必要时发起 HiL 升级请求(见第 5 章)。 |
|
||||
| `SupportGroups` | `bool` | 是否支持群组协作。若为 `true`,Agent 可以参与群组任务、投票和消息广播。 |
|
||||
|
||||
### 2.3 默认值工厂函数
|
||||
|
||||
```go
|
||||
func DefaultAgentCapabilities() *AgentCapabilities {
|
||||
return &AgentCapabilities{
|
||||
ProtocolVersion: "1.0.0",
|
||||
MaxFileSize: 10 * 1024 * 1024, // 10MB
|
||||
MaxConcurrentTasks: 10,
|
||||
MaxTaskDuration: 3600,
|
||||
SupportProgressUpdates: true,
|
||||
SupportStreaming: false,
|
||||
SupportHiL: false,
|
||||
SupportGroups: false,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 序列化示例
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_id": "agent_text_gen_01",
|
||||
"agent_name": "文本生成助手",
|
||||
"system_id": "system_alpha",
|
||||
"protocol_version": "1.0.0",
|
||||
"supported_task_types": [
|
||||
{
|
||||
"type": "text_generation",
|
||||
"name": "文本生成",
|
||||
"description": "根据输入提示生成自然语言文本",
|
||||
"estimated_time": "5s-30s",
|
||||
"input_schema": "...",
|
||||
"output_schema": "...",
|
||||
"capabilities": ["creative_writing", "summarization"]
|
||||
}
|
||||
],
|
||||
"supported_content_types": [
|
||||
"text/plain",
|
||||
"text/markdown",
|
||||
"application/json"
|
||||
],
|
||||
"supported_transport_modes": [
|
||||
"callback",
|
||||
"polling",
|
||||
"sync"
|
||||
],
|
||||
"max_file_size": 10485760,
|
||||
"max_concurrent_tasks": 10,
|
||||
"max_task_duration_seconds": 3600,
|
||||
"support_progress_updates": true,
|
||||
"support_streaming": false,
|
||||
"support_hil": true,
|
||||
"support_groups": false
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. SupportedTaskType 结构
|
||||
|
||||
`SupportedTaskType` 描述 Agent 支持的单一任务类型的详细信息,包括类型标识、名称、描述、预估耗时、输入输出 Schema 以及关联能力标签。
|
||||
|
||||
### 3.1 结构定义
|
||||
|
||||
```go
|
||||
type SupportedTaskType struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
EstimatedTime string `json:"estimated_time"` // 如 "30s-2min"
|
||||
InputSchema string `json:"input_schema,omitempty"`
|
||||
OutputSchema string `json:"output_schema,omitempty"`
|
||||
Capabilities []string `json:"capabilities,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 字段说明
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `Type` | `string` | 是 | 任务类型标识符。采用蛇形命名法(snake_case),如 `text_generation`、`image_analysis`、`code_review`。该标识在协议消息的 `TaskType` 字段中使用。 |
|
||||
| `Name` | `string` | 是 | 任务类型的人类可读名称,如"文本生成"、"图片分析"、"代码审查"。用于界面展示。 |
|
||||
| `Description` | `string` | 是 | 任务类型的详细描述,说明该任务的输入、输出和适用场景。 |
|
||||
| `EstimatedTime` | `string` | 是 | 预估完成时间范围。格式为字符串,如 `"5s-30s"`、`"30s-2min"`、`"1min-5min"`。用于任务调度和超时设置参考。 |
|
||||
| `InputSchema` | `string` | 否 | 输入数据的 JSON Schema。用于验证任务提交时的 payload 格式。格式为 JSON Schema Draft-07 字符串。 |
|
||||
| `OutputSchema` | `string` | 否 | 输出数据的 JSON Schema。用于解析任务结果时的格式校验。 |
|
||||
| `Capabilities` | `[]string` | 否 | 该任务类型所依赖的细粒度能力标签列表。用于按能力搜索 Agent。 |
|
||||
|
||||
### 3.3 预定义任务类型
|
||||
|
||||
| 类型标识 | 名称 | 说明 |
|
||||
|---------|------|------|
|
||||
| `text_generation` | 文本生成 | 根据提示生成自然语言文本 |
|
||||
| `text_analysis` | 文本分析 | 对文本进行分类、情感分析、实体识别等 |
|
||||
| `summarization` | 摘要生成 | 将长文本总结为简短摘要 |
|
||||
| `translation` | 翻译 | 在不同语言之间进行翻译 |
|
||||
| `code_generation` | 代码生成 | 根据需求生成源代码 |
|
||||
| `code_review` | 代码审查 | 对代码进行审查、发现潜在问题 |
|
||||
| `image_generation` | 图片生成 | 根据文本描述生成图片 |
|
||||
| `image_analysis` | 图片分析 | 对图片进行内容识别和分析 |
|
||||
| `audio_transcription` | 语音转文字 | 将音频转换为文本 |
|
||||
| `audio_generation` | 语音合成 | 将文本合成为语音 |
|
||||
| `video_generation` | 视频生成 | 根据描述生成视频内容 |
|
||||
| `data_analysis` | 数据分析 | 对结构化数据进行分析和可视化 |
|
||||
| `search_retrieval` | 搜索检索 | 从知识库或互联网检索信息 |
|
||||
| `planning` | 任务规划 | 将复杂任务分解为可执行的子任务 |
|
||||
| `reasoning` | 逻辑推理 | 执行多步逻辑推理和决策 |
|
||||
|
||||
### 3.4 示例
|
||||
|
||||
```go
|
||||
textGenTaskType := &SupportedTaskType{
|
||||
Type: "text_generation",
|
||||
Name: "文本生成",
|
||||
Description: "根据输入提示生成自然语言文本,支持创意写作、商业文案、技术文档等",
|
||||
EstimatedTime: "5s-30s",
|
||||
InputSchema: `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"prompt": {"type": "string", "description": "输入提示"},
|
||||
"max_tokens": {"type": "integer", "minimum": 1, "maximum": 4096},
|
||||
"temperature": {"type": "number", "minimum": 0, "maximum": 2}
|
||||
},
|
||||
"required": ["prompt"]
|
||||
}`,
|
||||
OutputSchema: `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {"type": "string", "description": "生成的文本"},
|
||||
"tokens_used": {"type": "integer", "description": "使用的 token 数量"}
|
||||
},
|
||||
"required": ["text"]
|
||||
}`,
|
||||
Capabilities: []string{"creative_writing", "summarization", "paraphrasing"},
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 接口分类
|
||||
|
||||
系统中的标准接口按照功能领域划分为七大分类。每个分类代表协议栈中的一个特定功能维度。
|
||||
|
||||
### 4.1 分类枚举
|
||||
|
||||
```go
|
||||
type InterfaceCategory string
|
||||
|
||||
const (
|
||||
CategoryCore InterfaceCategory = "core" // 核心接口
|
||||
CategoryP2P InterfaceCategory = "p2p" // P2P 接口
|
||||
CategoryGroup InterfaceCategory = "group" // 群组接口
|
||||
CategoryHiL InterfaceCategory = "hil" // HiL 接口
|
||||
CategoryTransport InterfaceCategory = "transport" // 传输接口
|
||||
CategorySecurity InterfaceCategory = "security" // 安全接口
|
||||
CategoryManagement InterfaceCategory = "management" // 管理接口
|
||||
)
|
||||
```
|
||||
|
||||
### 4.2 分类详解
|
||||
|
||||
| 分类 | 枚举值 | 定位 | 职责范围 | 典型接口 |
|
||||
|------|--------|------|----------|---------|
|
||||
| 核心接口 | `core` | 协议基石 | Agent 身份标识、消息收发、能力声明等所有 Agent 最基本的生存能力 | AgentIdentity、MessageHandler、CapabilitiesProvider |
|
||||
| P2P 接口 | `p2p` | 点对点协作 | Agent 之间的任务提交、处理、回调与状态查询 | P2PTaskHandler、CallbackSender、CallbackReceiver、TaskStatusProvider |
|
||||
| 群组接口 | `group` | 群组协同 | 多 Agent 组成群组后的任务分发、投票决策与管理 | GroupTaskHandler、GroupManager、VotingHandler |
|
||||
| HiL 接口 | `hil` | 人工介入 | 将决策升级到人类用户,处理人工审批、驳回与修改 | HILHandler、EscalationHandler |
|
||||
| 传输接口 | `transport` | 数据传输 | 定义 Agent 间数据传输的网络协议和模式 | HTTPTransport、MultipartTransport、HybridTransport、StreamingTransport |
|
||||
| 安全接口 | `security` | 安全保障 | 身份认证、消息签名、幂等性保障 | Authenticator、IdempotencyHandler |
|
||||
| 管理接口 | `management` | 运维管理 | Agent 配置、健康监控、日志查询 | ConfigProvider、HealthReporter、MetricCollector |
|
||||
|
||||
### 4.3 分类依赖关系
|
||||
|
||||
```
|
||||
core ──→ 所有分类的前置依赖,任何 Agent 必须实现
|
||||
│
|
||||
├──→ security ──→ transport ──→ p2p ──→ group
|
||||
│ │
|
||||
│ └──→ hil
|
||||
│
|
||||
└──→ management(可选,与所有分类正交)
|
||||
```
|
||||
|
||||
- `core` 是基础层,所有其他分类都依赖 `core` 接口。
|
||||
- `security` 在 `transport` 之上提供安全层。
|
||||
- `p2p` 依赖 `transport` 和 `security`。
|
||||
- `group` 和 `hil` 在 `p2p` 基础上构建更高级的交互模式。
|
||||
- `management` 与所有分类正交,提供运维支持。
|
||||
|
||||
---
|
||||
|
||||
## 5. 合规级别
|
||||
|
||||
每个标准接口定义了一个合规级别,用于标识该接口在协议实现中的重要性等级。
|
||||
|
||||
### 5.1 级别枚举
|
||||
|
||||
```go
|
||||
const (
|
||||
ComplianceBasic Compliance = "basic" // 基本级 - 必须实现
|
||||
ComplianceComplete Compliance = "complete" // 完整级 - 推荐实现
|
||||
ComplianceAdvanced Compliance = "advanced" // 进阶级 - 可选实现
|
||||
)
|
||||
```
|
||||
|
||||
### 5.2 级别说明
|
||||
|
||||
| 级别 | 约束 | 说明 |
|
||||
|------|------|------|
|
||||
| **basic(基本级)** | **必须实现** | 所有 Agent 都必须实现此接口,否则无法完成基本的协议握手与通信。这是协议兼容性的最低门槛。未实现 basic 级别接口的 Agent 将被注册中心拒绝注册。 |
|
||||
| **complete(完整级)** | **推荐实现** | 建议 Agent 实现此接口以获得完整的协议功能体验。未实现时,Agent 仍然可以正常工作,但部分高级交互模式(如异步回调、进度更新)将不可用。 |
|
||||
| **advanced(进阶级)** | **可选实现** | Agent 可根据自身场景和能力选择实现。这些接口通常对应特定场景的高级功能(如流式传输、群组协作)。未实现不影响基本通信能力。 |
|
||||
|
||||
### 5.3 合规矩阵
|
||||
|
||||
| 场景 | Basic 接口 | Complete 接口 | Advanced 接口 |
|
||||
|------|-----------|--------------|--------------|
|
||||
| 最小可行 Agent | 全部实现 | 可不实现 | 可不实现 |
|
||||
| 标准 P2P Agent | 全部实现 | 推荐全部实现 | 按需实现 |
|
||||
| 群组协作 Agent | 全部实现 | 全部实现 | 推荐全部实现 |
|
||||
| 全功能 Agent | 全部实现 | 全部实现 | 全部实现 |
|
||||
|
||||
### 5.4 合规级别声明
|
||||
|
||||
Agent 通过 `GetSupportedInterfaces()` 方法声明其实现的接口及对应的合规级别。注册中心可以根据这些信息对 Agent 进行合规性评估:
|
||||
|
||||
```go
|
||||
type ComplianceEvaluation struct {
|
||||
AgentID string `json:"agent_id"`
|
||||
BasicImplemented int `json:"basic_implemented"` // 已实现的 basic 接口数
|
||||
BasicRequired int `json:"basic_required"` // 需要实现的 basic 接口数
|
||||
CompleteImplemented int `json:"complete_implemented"` // 已实现的 complete 接口数
|
||||
CompleteTotal int `json:"complete_total"` // complete 接口总数
|
||||
AdvancedImplemented int `json:"advanced_implemented"` // 已实现的 advanced 接口数
|
||||
AdvancedTotal int `json:"advanced_total"` // advanced 接口总数
|
||||
Compliant bool `json:"compliant"` // 是否合规(所有 basic 接口均已实现)
|
||||
MissingBasics []string `json:"missing_basics,omitempty"` // 缺失的 basic 接口列表
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 标准接口注册表
|
||||
|
||||
系统预定义了 18 个标准接口,涵盖协议栈的所有功能维度。以下表格详细列出每个接口的名称、分类、合规级别和说明。
|
||||
|
||||
### 6.1 接口注册表
|
||||
|
||||
| 序号 | 接口名 | 分类 | 合规级别 | 说明 |
|
||||
|------|--------|------|---------|------|
|
||||
| 1 | `AgentIdentity` | `core` | basic | Agent 身份信息。提供 Agent 的 ID、名称、公钥等基础身份数据。是所有交互的起点,注册中心以此识别 Agent。 |
|
||||
| 2 | `MessageHandler` | `core` | basic | 消息处理。接收并处理来自其他 Agent 或外部调用方的 `UnifiedSynapseMessage` 消息。Agent 之间通信的核心入口。 |
|
||||
| 3 | `CapabilitiesProvider` | `core` | basic | 能力声明。提供 Agent 的能力描述(AgentCapabilities),供注册中心和其他 Agent 查询。 |
|
||||
| 4 | `P2PTaskHandler` | `p2p` | basic | P2P 任务处理。接收并处理来自其他 Agent 的点对点任务请求,执行任务并返回结果。 |
|
||||
| 5 | `CallbackReceiver` | `p2p` | basic | 回调接收端点。对外暴露回调 URL,用于接收其他 Agent 异步执行完成后的任务结果回调。 |
|
||||
| 6 | `CallbackSender` | `p2p` | complete | 回调发送。在异步任务完成后,主动向请求方注册的回调端点发送结果。 |
|
||||
| 7 | `TaskStatusProvider` | `p2p` | complete | 任务状态查询。提供任务执行状态的查询接口,支持按任务 ID 查询当前进度和状态。用于轮询模式。 |
|
||||
| 8 | `GroupTaskHandler` | `group` | advanced | 群组任务处理。接收群组内分发的子任务,执行并将结果报告给群组协调者。 |
|
||||
| 9 | `GroupManager` | `group` | advanced | 群组管理。创建、解散群组,管理成员加入/离开,维护群组角色和权限。 |
|
||||
| 10 | `VotingHandler` | `group` | advanced | 投票处理。发起投票、提交投票、统计结果,支持多种共识类型(多数、绝对多数、一致同意、加权、排序)。 |
|
||||
| 11 | `HILHandler` | `hil` | complete | HiL 决策处理。接收人工介入请求,将决策上下文呈现给人类用户,处理用户的批准/驳回/修改响应。 |
|
||||
| 12 | `EscalationHandler` | `hil` | advanced | 升级处理。在 HiL 超时或 Level 1 无法解决时,自动将决策升级到更高级别(Level 2/3)。 |
|
||||
| 13 | `HTTPTransport` | `transport` | basic | HTTP 传输。支持通过 HTTP/HTTPS 协议进行同步请求-响应通信。所有 Agent 必须支持 HTTP 作为基础传输方式。 |
|
||||
| 14 | `MultipartTransport` | `transport` | complete | Multipart 文件传输。支持通过 multipart/form-data 进行文件上传,适用于大文件传输场景。 |
|
||||
| 15 | `HybridTransport` | `transport` | complete | 混合传输。同时支持回调(Webhook)和轮询两种异步传输模式,以回调为主、轮询为兜底。 |
|
||||
| 16 | `StreamingTransport` | `transport` | advanced | 流式传输。支持 Server-Sent Events(SSE)或 WebSocket 流式数据传输,适用于实时性要求高的场景。 |
|
||||
| 17 | `Authenticator` | `security` | basic | 认证签名。提供消息签名、验签、令牌颁发与验证功能。所有消息必须经过签名认证。 |
|
||||
| 18 | `IdempotencyHandler` | `security` | basic | 幂等性处理。通过幂等键(Idempotency-Key)确保任务提交和消息发送的幂等性,防止重复处理。 |
|
||||
|
||||
### 6.2 按合规级别汇总
|
||||
|
||||
| 合规级别 | 接口数量 | 接口列表 |
|
||||
|---------|---------|---------|
|
||||
| **basic(必须实现)** | 7 | AgentIdentity、MessageHandler、CapabilitiesProvider、P2PTaskHandler、CallbackReceiver、HTTPTransport、Authenticator、IdempotencyHandler |
|
||||
| **complete(推荐实现)** | 4 | CallbackSender、TaskStatusProvider、HILHandler、MultipartTransport、HybridTransport |
|
||||
| **advanced(可选实现)** | 5 | GroupTaskHandler、GroupManager、VotingHandler、EscalationHandler、StreamingTransport |
|
||||
|
||||
> 注:basic 接口共 8 个,complete 共 5 个,advanced 共 5 个,总计 18 个标准接口。
|
||||
|
||||
### 6.3 按分类汇总
|
||||
|
||||
| 分类 | 接口数量 | 接口列表 |
|
||||
|------|---------|---------|
|
||||
| core | 3 | AgentIdentity、MessageHandler、CapabilitiesProvider |
|
||||
| p2p | 4 | P2PTaskHandler、CallbackReceiver、CallbackSender、TaskStatusProvider |
|
||||
| group | 3 | GroupTaskHandler、GroupManager、VotingHandler |
|
||||
| hil | 2 | HILHandler、EscalationHandler |
|
||||
| transport | 4 | HTTPTransport、MultipartTransport、HybridTransport、StreamingTransport |
|
||||
| security | 2 | Authenticator、IdempotencyHandler |
|
||||
| management | 0 | (由具体实现自行定义,协议层面不强制) |
|
||||
|
||||
### 6.4 接口注册与发现流程
|
||||
|
||||
```
|
||||
请求方 Agent 注册中心 目标 Agent
|
||||
│ │ │
|
||||
│ 1. 注册并上报能力 │ │
|
||||
│ ────────────────────────────→ │ │
|
||||
│ │ 2. 存储能力信息到目录 │
|
||||
│ │ ──────────────── │
|
||||
│ │ │
|
||||
│ 3. 按能力查询 Agent │ │
|
||||
│ ────────────────────────────→ │ │
|
||||
│ │ 4. 返回匹配的 Agent 列表 │
|
||||
│ ←──────────────────────────── │ │
|
||||
│ │ │
|
||||
│ 5. P2P 握手 │ │
|
||||
│ ──────────────────────────────────────────────────────────→ │
|
||||
│ │ 6. 返回支持接口列表 │
|
||||
│ ←────────────────────────────────────────────────────────── │
|
||||
│ │ │
|
||||
│ 7. 按协商接口通信 │ │
|
||||
│ ──────────────────────────────────────────────────────────→ │
|
||||
```
|
||||
|
||||
**流程说明**:
|
||||
|
||||
1. Agent 启动时向注册中心注册,并上报 `AgentCapabilities`。
|
||||
2. 注册中心将能力信息存入全局目录,验证 basic 接口合规性。
|
||||
3. 请求方通过 `FindByCapability` 查询具备特定能力的 Agent。
|
||||
4. 注册中心返回匹配的 Agent 端点列表。
|
||||
5. 请求方与目标 Agent 直接建立 P2P 连接,进行握手。
|
||||
6. 目标 Agent 返回 `GetSupportedInterfaces()` 结果,协商接口兼容性。
|
||||
7. 双方按协商结果使用对应接口进行通信。
|
||||
|
||||
### 6.5 接口版本兼容规则
|
||||
|
||||
- Agent 必须实现当前协议版本所要求的所有 basic 接口。
|
||||
- 接口版本遵循语义化版本规范:
|
||||
- **MAJOR** 版本不匹配时,接口不兼容,无法通信。
|
||||
- **MINOR** 版本不同时,以较低版本为兼容基准。
|
||||
- **PATCH** 版本不同时,完全兼容。
|
||||
- 新增接口字段必须使用 `omitempty` 标记,确保向后兼容。
|
||||
|
||||
---
|
||||
|
||||
> 本文档基于 v0 实现细节文档第 3 章扩展编写,是灵隙协议能力声明系统的完整实现规范。
|
||||
> 配套文档:(接口实现指南)
|
||||
517
docs/v1/components.md
Normal file
517
docs/v1/components.md
Normal file
@@ -0,0 +1,517 @@
|
||||
# 灵隙协议组件实现规范:七大组件层
|
||||
|
||||
> 版本:1.0.0 | 层级:实现层 | 依赖:v1 各核心协议
|
||||
|
||||
根据 [v0/06-implementation-details.md](file:///e:/code/go/synapse/docs/v0/06-implementation-details.md) 的第1章内容,分别创建以下7个组件的规范(每组包含:定位说明、核心功能表、数据结构定义)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 灵脉 Meridian - 寻址与路由层
|
||||
|
||||
### 定位说明
|
||||
|
||||
Agent 之间虽有空隙(灵隙),但暗中有能量流动的轨迹(脉)。此层负责维护全局路由表。
|
||||
|
||||
### 核心功能
|
||||
|
||||
| 功能 | 方法 | 说明 |
|
||||
|-----|------|------|
|
||||
| Agent 注册 | `Register(endpoint)` | Agent "通脉"入网 |
|
||||
| Agent 注销 | `Unregister(agentID)` | Agent "断脉"离场 |
|
||||
| Agent 查询 | `GetAgent(agentID)` | 获取 Agent 端点信息 |
|
||||
| Agent 列表 | `ListAgents()` | 列出所有活跃 Agent |
|
||||
| 心跳保活 | `Heartbeat(agentID)` | 刷新 Agent TTL |
|
||||
| 按能力查找 | `FindByCapability(capability)` | 查找具备特定能力的 Agent |
|
||||
|
||||
### 数据结构定义
|
||||
|
||||
```go
|
||||
// AgentEndpoint - Agent 端点信息
|
||||
type AgentEndpoint struct {
|
||||
AgentID string // Agent 唯一标识
|
||||
AgentName string // Agent 名称
|
||||
SystemID string // 所属系统 ID
|
||||
Endpoint string // 回调端点 URL
|
||||
Version string // Agent 版本
|
||||
Capabilities []string // 能力标签列表
|
||||
Metadata map[string]interface{} // 扩展元数据
|
||||
}
|
||||
|
||||
// RegistryConfig - 注册表配置
|
||||
type RegistryConfig struct {
|
||||
DefaultTTL time.Duration // 默认存活时间(5分钟)
|
||||
}
|
||||
```
|
||||
|
||||
### 拓扑管理
|
||||
|
||||
```go
|
||||
// 拓扑类型
|
||||
type TopologyType string
|
||||
const (
|
||||
TopologyStar = "star" // 星型拓扑(中心协调)
|
||||
TopologyMesh = "mesh" // 网状拓扑(全互联)
|
||||
TopologyRing = "ring" // 环形拓扑(环形传递)
|
||||
TopologyTree = "tree" // 树形拓扑(层级管理)
|
||||
TopologyHybrid = "hybrid" // 混合拓扑
|
||||
)
|
||||
|
||||
// 拓扑管理接口
|
||||
type TopologyManager interface {
|
||||
CreateTopology(id, name string, topologyType TopologyType) *NetworkTopology
|
||||
AddNode(topologyID, nodeID string) error
|
||||
AddEdge(topologyID, from, to string) error
|
||||
GetNeighbors(topologyID, nodeID string) ([]string, error)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 灵玺 Sigil - 身份与凭证层
|
||||
|
||||
### 定位说明
|
||||
|
||||
皇帝的玉玺是最高凭证。每个 Agent 的初始身份都需经过"铸玺"过程,每一次交互都要"验玺"。
|
||||
|
||||
### 核心功能
|
||||
|
||||
| 功能 | 方法 | 说明 |
|
||||
|-----|------|------|
|
||||
| 创建身份 | `CreateIdentity(agentID, ...)` | "铸玺" - 创建身份凭证 |
|
||||
| 获取身份 | `GetIdentity(agentID)` | 获取 Agent 身份信息 |
|
||||
| 撤销身份 | `RevokeIdentity(agentID)` | "废玺" - 撤销身份 |
|
||||
| 检查撤销 | `IsRevoked(agentID)` | 检查身份是否已撤销 |
|
||||
| 签名消息 | `SignMessage(msg)` | 对消息签名 |
|
||||
| 验证签名 | `VerifySignature(msg)` | 验证消息签名 |
|
||||
| 颁发令牌 | `IssueToken(agentID, ...)` | 颁发认证令牌 |
|
||||
| 验证令牌 | `ValidateToken(signedToken)` | 验证令牌有效性 |
|
||||
|
||||
### 数据结构定义
|
||||
|
||||
```go
|
||||
// AgentIdentity - Agent 身份凭证
|
||||
type AgentIdentity struct {
|
||||
AgentID string // Agent 唯一标识
|
||||
AgentName string // Agent 名称
|
||||
SystemID string // 所属系统 ID
|
||||
PublicKey string // 公钥(可选)
|
||||
PrivateKey string // 私钥(本地保存)
|
||||
AuthSecret string // HMAC 密钥
|
||||
Roles []string // 角色列表
|
||||
Permissions []string // 权限列表
|
||||
CreatedAt time.Time // 创建时间
|
||||
ExpiresAt *time.Time // 过期时间
|
||||
RevokedAt *time.Time // 撤销时间
|
||||
}
|
||||
|
||||
// AuthToken - 认证令牌
|
||||
type AuthToken struct {
|
||||
TokenID string // 令牌 ID
|
||||
AgentID string // 所属 Agent
|
||||
SystemID string // 所属系统
|
||||
Roles []string // 角色列表
|
||||
IssuedAt time.Time // 颁发时间
|
||||
ExpiresAt time.Time // 过期时间
|
||||
}
|
||||
```
|
||||
|
||||
### 签名算法
|
||||
|
||||
```go
|
||||
// 支持的签名算法
|
||||
type SignatureAlgorithm string
|
||||
const (
|
||||
AlgHMACSHA256 SignatureAlgorithm = "HMAC-SHA256" // 默认
|
||||
AlgHMACSHA512 SignatureAlgorithm = "HMAC-SHA512" // 可选
|
||||
)
|
||||
|
||||
// 签名内容格式
|
||||
// signature_content = sender_id | receiver_id | timestamp | payload_json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 灵鉴 Speculum - 共识与裁决层
|
||||
|
||||
### 定位说明
|
||||
|
||||
古有"以史为鉴",今有"以智为鉴"。此层通过对比推理链的严密性来达成共识,如青天明镜。
|
||||
|
||||
### 核心功能
|
||||
|
||||
| 功能 | 方法 | 说明 |
|
||||
|-----|------|------|
|
||||
| 发起投票 | `InitiateVote(...)` | 创建投票议题 |
|
||||
| 提交投票 | `SubmitVote(voteID, voterID, optionID, reasoning)` | 提交投票 |
|
||||
| 关闭投票 | `CloseVote(voteID)` | 关闭并统计结果 |
|
||||
| 获取投票 | `GetVote(voteID)` | 获取投票信息 |
|
||||
| 仲裁争议 | `Arbitrate(ctx, dispute)` | 仲裁争议 |
|
||||
| 提交推理链 | `SubmitReasoning(chain)` | 提交推理链到镜面 |
|
||||
| 比较推理链 | `CompareReasonings(chainIDs)` | 比较推理链严密性 |
|
||||
|
||||
### 共识类型
|
||||
|
||||
```go
|
||||
type ConsensusType string
|
||||
const (
|
||||
ConsensusMajority = "majority" // 多数投票(>50%)
|
||||
ConsensusSupermajority = "supermajority" // 绝对多数(>67%)
|
||||
ConsensusUnanimity = "unanimity" // 一致同意(100%)
|
||||
ConsensusWeighted = "weighted" // 加权投票
|
||||
ConsensusRanked = "ranked" // 排序投票
|
||||
)
|
||||
```
|
||||
|
||||
### 数据结构定义
|
||||
|
||||
```go
|
||||
// Vote - 投票
|
||||
type Vote struct {
|
||||
VoteID string // 投票 ID
|
||||
Question string // 投票问题
|
||||
Options []VoteOption // 投票选项
|
||||
Type ConsensusType // 共识类型
|
||||
Initiator string // 发起者
|
||||
Participants []string // 参与者列表
|
||||
Votes map[string]string // voter_id -> option_id
|
||||
Weights map[string]float64 // voter_id -> 权重
|
||||
Status VoteStatus // 投票状态
|
||||
Deadline time.Time // 截止时间
|
||||
Result *VoteResult // 投票结果
|
||||
Reasoning map[string]string // voter_id -> 推理理由
|
||||
}
|
||||
|
||||
// VoteResult - 投票结果
|
||||
type VoteResult struct {
|
||||
Winner string // 获胜选项
|
||||
WinOption VoteOption // 获胜选项详情
|
||||
VoteCounts map[string]int // 选项 -> 票数
|
||||
Passed bool // 是否通过
|
||||
Margin float64 // 得票率
|
||||
}
|
||||
```
|
||||
|
||||
### 推理镜面
|
||||
|
||||
```go
|
||||
// ReasoningChain - 推理链
|
||||
type ReasoningChain struct {
|
||||
ChainID string // 推理链 ID
|
||||
AgentID string // Agent ID
|
||||
Topic string // 推理主题
|
||||
Steps []ReasoningStep // 推理步骤
|
||||
Score float64 // 推理得分
|
||||
SubmittedAt time.Time // 提交时间
|
||||
}
|
||||
|
||||
// ReasoningStep - 推理步骤
|
||||
type ReasoningStep struct {
|
||||
StepID string // 步骤 ID
|
||||
Description string // 步骤描述
|
||||
Evidence []string // 证据列表
|
||||
Confidence float64 // 置信度(0-1)
|
||||
Next []string // 下一步 ID 列表
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 灵泊 Harbor - 异步队列与暂存层
|
||||
|
||||
### 定位说明
|
||||
|
||||
"泊"为船舶停靠。数据包遇风浪(网络抖动)时暂泊于此,待风平浪静再启航。
|
||||
|
||||
### 核心功能
|
||||
|
||||
| 功能 | 方法 | 说明 |
|
||||
|-----|------|------|
|
||||
| 消息入队 | `Enqueue(msg, target, priority)` | 消息入队(支持优先级) |
|
||||
| 消息出队 | `Dequeue()` | 消息出队 |
|
||||
| 标记送达 | `MarkDelivered(msgID)` | 标记消息已送达 |
|
||||
| 标记失败 | `MarkFailed(msgID, err)` | 标记消息失败(触发重试) |
|
||||
| 缓冲消息 | `BufferMessage(agentID, msg)` | 为离线 Agent 缓冲消息 |
|
||||
| 清空缓冲 | `FlushBuffer(agentID)` | Agent 上线后清空缓冲 |
|
||||
| 添加重试 | `AddRetry(msg, target)` | 添加重试条目 |
|
||||
|
||||
### 数据结构定义
|
||||
|
||||
```go
|
||||
// QueueConfig - 队列配置
|
||||
type QueueConfig struct {
|
||||
MaxSize int // 最大队列大小(10000)
|
||||
DefaultMaxRetries int // 默认最大重试次数(5)
|
||||
DefaultRetryDelay time.Duration // 默认重试延迟(30s)
|
||||
}
|
||||
|
||||
// 队列消息状态
|
||||
type QueuedStatus string
|
||||
const (
|
||||
QueuedPending = "pending" // 待处理
|
||||
QueuedProcessing = "processing" // 处理中
|
||||
QueuedDelivered = "delivered" // 已送达
|
||||
QueuedFailed = "failed" // 失败
|
||||
QueuedDead = "dead" // 死信
|
||||
)
|
||||
```
|
||||
|
||||
### 重试机制
|
||||
|
||||
```go
|
||||
// RetryConfig - 重试配置
|
||||
type RetryConfig struct {
|
||||
MaxRetries int // 最大重试次数(5)
|
||||
BaseDelay time.Duration // 基础延迟(1s)
|
||||
MaxDelay time.Duration // 最大延迟(5min)
|
||||
JitterFactor float64 // 抖动因子(0.1)
|
||||
}
|
||||
|
||||
// 重试延迟算法:指数退避 + 抖动
|
||||
// delay = min(BaseDelay * 2^(retryCount-1), MaxDelay) * (1 ± JitterFactor)
|
||||
```
|
||||
|
||||
### 缓冲配置
|
||||
|
||||
```go
|
||||
// BufferConfig - 缓冲配置
|
||||
type BufferConfig struct {
|
||||
MaxPerAgent int // 每个 Agent 最大缓冲数(1000)
|
||||
MaxAge time.Duration // 消息最大存活时间(24h)
|
||||
CleanupInterval time.Duration // 清理间隔(1h)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 灵瞰 Panorama - 可观测与度量层
|
||||
|
||||
### 定位说明
|
||||
|
||||
居高临下,俯瞰全局。如"天眼"般记录每一帧交互的耗时、置信度与资源消耗。
|
||||
|
||||
### 核心功能
|
||||
|
||||
| 功能 | 方法 | 说明 |
|
||||
|-----|------|------|
|
||||
| 开始追踪 | `StartTrace(traceID, name, agentID)` | 创建追踪链路 |
|
||||
| 开始跨度 | `StartSpan(traceID, parentSpanID, name, agentID)` | 创建子跨度 |
|
||||
| 结束跨度 | `EndSpan(traceID, spanID, status, err)` | 结束跨度 |
|
||||
| 添加日志 | `AddSpanLog(traceID, spanID, message, fields)` | 添加跨度日志 |
|
||||
| 记录指标 | `RecordMetric(name, type, value, labels)` | 记录指标 |
|
||||
| 递增计数 | `IncrementCounter(name, labels, delta)` | 递增计数器 |
|
||||
| 设置计量 | `SetGauge(name, labels, value)` | 设置计量值 |
|
||||
| 健康检查 | `CheckAll(ctx)` | 检查所有组件健康状态 |
|
||||
|
||||
### 数据结构定义
|
||||
|
||||
```go
|
||||
// Trace - 追踪链路
|
||||
type Trace struct {
|
||||
TraceID string // 追踪 ID
|
||||
RootSpan *Span // 根跨度
|
||||
Spans map[string]*Span // 跨度集合
|
||||
StartTime time.Time // 开始时间
|
||||
EndTime *time.Time // 结束时间
|
||||
Metadata map[string]interface{} // 元数据
|
||||
}
|
||||
|
||||
// Span - 追踪跨度
|
||||
type Span struct {
|
||||
SpanID string // 跨度 ID
|
||||
TraceID string // 所属追踪 ID
|
||||
ParentID string // 父跨度 ID
|
||||
Name string // 跨度名称
|
||||
Service string // 服务名
|
||||
AgentID string // Agent ID
|
||||
StartTime time.Time // 开始时间
|
||||
EndTime *time.Time // 结束时间
|
||||
Duration time.Duration // 持续时间
|
||||
Status SpanStatus // 跨度状态
|
||||
Tags map[string]string // 标签
|
||||
Logs []SpanLog // 日志
|
||||
Attributes map[string]interface{} // 属性
|
||||
}
|
||||
|
||||
// SpanStatus - 跨度状态
|
||||
type SpanStatus string
|
||||
const (
|
||||
SpanStatusPending = "pending" // 待定
|
||||
SpanStatusRunning = "running" // 运行中
|
||||
SpanStatusSuccess = "success" // 成功
|
||||
SpanStatusFailed = "failed" // 失败
|
||||
SpanStatusCancelled = "cancelled" // 已取消
|
||||
)
|
||||
```
|
||||
|
||||
### 指标类型
|
||||
|
||||
```go
|
||||
type MetricType string
|
||||
const (
|
||||
MetricCounter = "counter" // 计数器(累加)
|
||||
MetricGauge = "gauge" // 计量器(瞬时值)
|
||||
MetricHistogram = "histogram" // 直方图(分布)
|
||||
)
|
||||
```
|
||||
|
||||
### 健康状态
|
||||
|
||||
```go
|
||||
type HealthStatus string
|
||||
const (
|
||||
HealthStatusHealthy = "healthy" // 健康
|
||||
HealthStatusDegraded = "degraded" // 降级
|
||||
HealthStatusUnhealthy = "unhealthy" // 不健康
|
||||
HealthStatusUnknown = "unknown" // 未知
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 灵衡 Libra - 价值结算与激励层
|
||||
|
||||
### 定位说明
|
||||
|
||||
权衡利弊,计量公平。将 Agent 间的"智力贡献"与"资源消耗"通证化,动态定价。
|
||||
|
||||
### 核心功能
|
||||
|
||||
| 功能 | 方法 | 说明 |
|
||||
|-----|------|------|
|
||||
| 创建账户 | `CreateAccount(agentID)` | 为 Agent 创建账户 |
|
||||
| 获取余额 | `GetBalance(agentID)` | 查询账户余额 |
|
||||
| 转账 | `Transfer(from, to, amount, ...)` | 执行转账 |
|
||||
| 设置价格 | `SetPrice(taskType, capability, price)` | 设置任务价格 |
|
||||
| 获取价格 | `GetPrice(taskType)` | 获取任务价格 |
|
||||
| 计算成本 | `CalculateCost(taskType, complexity, duration)` | 计算任务成本 |
|
||||
| 结算任务 | `SettleTask(taskID, ...)` | 结算单个任务 |
|
||||
| 结算群组任务 | `SettleGroupTask(taskID, ...)` | 结算群组任务(分摊费用) |
|
||||
| 奖励 | `Reward(agentID, amount, reason)` | 发放奖励 |
|
||||
|
||||
### 数据结构定义
|
||||
|
||||
```go
|
||||
// Account - 账户
|
||||
type Account struct {
|
||||
AgentID string // Agent ID
|
||||
Balance map[string]float64 // 余额(currency -> amount)
|
||||
CreatedAt time.Time // 创建时间
|
||||
Metadata map[string]interface{} // 元数据
|
||||
}
|
||||
|
||||
// Transaction - 交易记录
|
||||
type Transaction struct {
|
||||
TxID string // 交易 ID
|
||||
Type TransactionType // 交易类型
|
||||
FromAgentID string // 发送方
|
||||
ToAgentID string // 接收方
|
||||
Amount float64 // 金额
|
||||
Currency string // 货币("SYN")
|
||||
Description string // 描述
|
||||
TaskID string // 关联任务 ID
|
||||
Timestamp time.Time // 时间戳
|
||||
Status TransactionStatus // 交易状态
|
||||
}
|
||||
|
||||
// TransactionType - 交易类型
|
||||
type TransactionType string
|
||||
const (
|
||||
TxTaskExecution = "task_execution" // 任务执行
|
||||
TxDataSharing = "data_sharing" // 数据共享
|
||||
TxAPIInvocation = "api_invocation" // API 调用
|
||||
TxGroupContribution = "group_contribution" // 群组贡献
|
||||
TxDeposit = "deposit" // 充值
|
||||
TxWithdraw = "withdraw" // 提现
|
||||
TxReward = "reward" // 奖励
|
||||
TxPenalty = "penalty" // 惩罚
|
||||
)
|
||||
```
|
||||
|
||||
### 定价规则
|
||||
|
||||
```go
|
||||
// 基础价格(默认)
|
||||
var BasePrice = map[string]float64{
|
||||
"text_generation": 1.0,
|
||||
"image_generation": 5.0,
|
||||
"video_generation": 20.0,
|
||||
"audio_generation": 3.0,
|
||||
"analysis": 2.0,
|
||||
"translation": 1.5,
|
||||
}
|
||||
|
||||
// 成本计算公式
|
||||
// cost = basePrice * complexityFactor + durationFactor
|
||||
// complexityFactor = complexity * 0.5
|
||||
// durationFactor = duration_seconds * 0.01
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 灵枢 Pivot - 紧急制动与治理层
|
||||
|
||||
### 定位说明
|
||||
|
||||
取自《灵枢经》。这是整个协议的"总闸"与"方向盘",不轻易触发,但触发时能以最小代价重置网络状态。
|
||||
|
||||
### 核心功能
|
||||
|
||||
| 功能 | 方法 | 说明 |
|
||||
|-----|------|------|
|
||||
| 允许请求 | `Allow()` | 检查熔断器是否允许请求 |
|
||||
| 记录成功 | `RecordSuccess()` | 记录成功(用于恢复) |
|
||||
| 记录失败 | `RecordFailure()` | 记录失败(触发熔断) |
|
||||
| 获取状态 | `State()` | 获取熔断器状态 |
|
||||
| 重置 | `Reset()` | 重置熔断器 |
|
||||
| 设置治理级别 | `SetLevel(level)` | 设置治理级别 |
|
||||
| 封禁 Agent | `BanAgent(agentID, reason)` | 封禁 Agent |
|
||||
| 解封 Agent | `UnbanAgent(agentID)` | 解封 Agent |
|
||||
| 紧急停止 | `EmergencyStop(reason)` | 一键熔断所有 |
|
||||
| 恢复正常 | `Resume()` | 恢复正常模式 |
|
||||
| 应用升级 | `ApplyUpgrade(version)` | 应用协议升级 |
|
||||
| 回滚升级 | `RollbackUpgrade(version)` | 回滚协议升级 |
|
||||
|
||||
### 数据结构定义
|
||||
|
||||
```go
|
||||
// CircuitBreaker - 熔断器
|
||||
type CircuitBreaker struct {
|
||||
name string // 熔断器名称
|
||||
state CircuitState // 状态
|
||||
failureCount int // 失败计数
|
||||
successCount int // 成功计数(半开状态)
|
||||
threshold int // 失败阈值(5)
|
||||
timeout time.Duration // 熔断超时(30s)
|
||||
}
|
||||
|
||||
// CircuitState - 熔断器状态
|
||||
type CircuitState string
|
||||
const (
|
||||
CircuitClosed = "closed" // 关闭:正常流通
|
||||
CircuitOpen = "open" // 打开:熔断,拒绝请求
|
||||
CircuitHalfOpen = "half_open" // 半开:尝试恢复
|
||||
)
|
||||
|
||||
// 状态转换规则:
|
||||
// Closed -> Open :失败次数 >= threshold
|
||||
// Open -> HalfOpen :超过 timeout 时间
|
||||
// HalfOpen -> Closed:连续成功 3 次
|
||||
// HalfOpen -> Open :再次失败
|
||||
```
|
||||
|
||||
### 治理级别
|
||||
|
||||
```go
|
||||
type GovernanceLevel int
|
||||
const (
|
||||
GovLevelNormal = iota // 正常运行
|
||||
GovLevelWatch // 观察模式(记录但不限制)
|
||||
GovLevelRestricted // 受限模式(限流)
|
||||
GovLevelEmergency // 紧急模式(仅白名单)
|
||||
GovLevelShutdown // 停机模式(全部拒绝)
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> 本文档对应 [v0/06-implementation-details.md](file:///e:/code/go/synapse/docs/v0/06-implementation-details.md) 第1章"七大组件层详细规范",是 v1 协议栈组件实现的参考依据。
|
||||
427
docs/v1/content-system.md
Normal file
427
docs/v1/content-system.md
Normal file
@@ -0,0 +1,427 @@
|
||||
# 灵隙协议实现规范:内容类型系统
|
||||
|
||||
> 版本:1.0.0 | 层级:实现层
|
||||
|
||||
根据 [v0/06-implementation-details.md](../v0/06-implementation-details.md) 的第2章内容,本文档定义 Synapse 协议内容类型系统的完整规范,涵盖传输模式、内容编码、结构化内容类型定义及 MIME 类型常量。
|
||||
|
||||
---
|
||||
|
||||
## 1. 传输模式
|
||||
|
||||
传输模式决定内容数据在 Agent 之间的传递方式,根据数据大小和实时性需求选择最佳模式。
|
||||
|
||||
```go
|
||||
type TransferMode string
|
||||
|
||||
const (
|
||||
TransferInline TransferMode = "inline" // 内嵌传输(base64 编码)
|
||||
TransferURL TransferMode = "url" // URL 引用传输
|
||||
TransferStreaming TransferMode = "streaming" // 流式传输
|
||||
TransferMultipart TransferMode = "multipart" // Multipart 表单传输
|
||||
)
|
||||
```
|
||||
|
||||
### 1.1 文件大小阈值
|
||||
|
||||
| 模式 | 适用范围 | 说明 |
|
||||
|------|---------|------|
|
||||
| `inline` | ≤ 1MB | 数据直接嵌入消息体,适用于小文件 |
|
||||
| `url` | > 1MB | 通过 URL 引用下载,适用于中等文件 |
|
||||
| `streaming` | > 1MB | 流式分片传输,适用于大文件或实时数据流 |
|
||||
| `multipart` | 任意大小 | 表单形式上传,适用于文件上传场景 |
|
||||
|
||||
```
|
||||
// 文件大小阈值判定逻辑
|
||||
// size <= 1MB → inline
|
||||
// size > 1MB → url 或 streaming
|
||||
```
|
||||
|
||||
### 1.2 ContentBase
|
||||
|
||||
所有内容类型共享的基础结构:
|
||||
|
||||
```go
|
||||
type ContentBase struct {
|
||||
Type TransferMode `json:"type"`
|
||||
ContentType string `json:"content_type"` // MIME 类型
|
||||
Encoding ContentEncoding `json:"encoding,omitempty"` // 内容编码
|
||||
Size int64 `json:"size,omitempty"` // 原始大小(字节)
|
||||
EncodedSize int64 `json:"encoded_size,omitempty"` // 编码后大小(字节)
|
||||
Checksum string `json:"checksum,omitempty"` // SHA-256 校验和
|
||||
Metadata map[string]any `json:"metadata,omitempty"` // 扩展元数据
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 内容编码
|
||||
|
||||
内容编码定义了原始数据在传输前的编码/压缩方式。
|
||||
|
||||
```go
|
||||
type ContentEncoding string
|
||||
|
||||
const (
|
||||
EncodingNone ContentEncoding = "none" // 无编码
|
||||
EncodingBase64 ContentEncoding = "base64" // Base64 编码
|
||||
EncodingGzip ContentEncoding = "gzip" // Gzip 压缩
|
||||
EncodingDeflate ContentEncoding = "deflate" // Deflate 压缩
|
||||
)
|
||||
```
|
||||
|
||||
| 编码 | 适用场景 | 说明 |
|
||||
|------|---------|------|
|
||||
| `none` | 纯文本、JSON | 无需额外编码,直接传输原始数据 |
|
||||
| `base64` | 二进制数据 inline 传输 | 将二进制数据编码为 ASCII 字符串 |
|
||||
| `gzip` | 大文本、代码、JSON | 高压缩比,适合网络传输 |
|
||||
| `deflate` | 通用压缩场景 | 基于 DEFLATE 算法,兼容性好 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 内容类型定义
|
||||
|
||||
### 3.1 文本内容(TextContent)
|
||||
|
||||
纯文本内容,支持字符集和语言声明。
|
||||
|
||||
```go
|
||||
type TextContent struct {
|
||||
ContentBase
|
||||
Charset string `json:"charset"` // 字符集(默认 "utf-8")
|
||||
Language string `json:"language,omitempty"` // 语言代码(如 "zh-CN", "en-US")
|
||||
Data string `json:"data"` // 文本数据
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|:----:|------|
|
||||
| `charset` | string | ✓ | 字符集编码,默认 `"utf-8"` |
|
||||
| `language` | string | - | 语言代码,遵循 BCP 47 标准 |
|
||||
| `data` | string | ✓ | 文本内容数据 |
|
||||
|
||||
**示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "inline",
|
||||
"content_type": "text/plain",
|
||||
"encoding": "none",
|
||||
"charset": "utf-8",
|
||||
"language": "zh-CN",
|
||||
"data": "你好,世界!"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.2 JSON 内容(JSONContent)
|
||||
|
||||
结构化 JSON 数据,支持 Schema 验证。
|
||||
|
||||
```go
|
||||
type JSONContent struct {
|
||||
ContentBase
|
||||
Schema string `json:"schema,omitempty"` // Schema 标识(如 JSON Schema URI)
|
||||
SchemaURL string `json:"schema_url,omitempty"` // Schema 外部引用 URL
|
||||
Data any `json:"data"` // JSON 数据(任意合法 JSON 值)
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|:----:|------|
|
||||
| `schema` | string | - | Schema 标识符,用于接收方识别数据结构 |
|
||||
| `schema_url` | string | - | Schema 的外部访问地址,可在线获取 Schema 定义 |
|
||||
| `data` | any | ✓ | 任意合法的 JSON 值(object、array、string、number、boolean) |
|
||||
|
||||
**示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "inline",
|
||||
"content_type": "application/json",
|
||||
"encoding": "none",
|
||||
"schema": "https://schema.synapse.com/v1/agent-info",
|
||||
"data": {
|
||||
"agent_id": "agent-001",
|
||||
"name": "智能助手",
|
||||
"version": "1.2.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.3 代码内容(CodeContent)
|
||||
|
||||
源代码或脚本内容,支持多文件依赖声明。
|
||||
|
||||
```go
|
||||
type CodeContent struct {
|
||||
ContentBase
|
||||
Language string `json:"language"` // 编程语言(如 "go", "python", "javascript")
|
||||
Version string `json:"version,omitempty"` // 语言版本(如 "3.11", "1.21")
|
||||
Dependencies []string `json:"dependencies,omitempty"` // 依赖列表(包名或模块名)
|
||||
Data string `json:"data"` // 源代码内容
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|:----:|------|
|
||||
| `language` | string | ✓ | 编程语言标识 |
|
||||
| `version` | string | - | 语言运行时版本 |
|
||||
| `dependencies` | array[string] | - | 外部依赖列表,辅助接收方环境准备 |
|
||||
| `data` | string | ✓ | 源代码文本 |
|
||||
|
||||
**示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "inline",
|
||||
"content_type": "text/plain",
|
||||
"encoding": "none",
|
||||
"language": "python",
|
||||
"version": "3.11",
|
||||
"dependencies": ["requests>=2.28", "pydantic>=2.0"],
|
||||
"data": "def hello(name: str) -> str:\n return f\"Hello, {name}!\""
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.4 文件内容(FileContent)
|
||||
|
||||
通用文件内容,支持 inline、url、streaming 三种传输模式。
|
||||
|
||||
```go
|
||||
type FileContent struct {
|
||||
ContentBase
|
||||
|
||||
// 文件标识
|
||||
Name string `json:"name"` // 文件名(含扩展名)
|
||||
Role string `json:"role,omitempty"` // 角色("reference", "result", "attachment")
|
||||
Description string `json:"description,omitempty"` // 文件描述
|
||||
|
||||
// Inline 传输(≤ 1MB)
|
||||
Data string `json:"data,omitempty"` // base64 编码的文件数据
|
||||
|
||||
// URL 传输(> 1MB)
|
||||
URL string `json:"url,omitempty"` // 文件下载 URL
|
||||
URLExpiresAt *Timestamp `json:"url_expires_at,omitempty"` // URL 过期时间
|
||||
DownloadMethod string `json:"download_method,omitempty"` // 下载方法(GET / POST / signed_url)
|
||||
RangeSupported bool `json:"range_supported,omitempty"` // 是否支持 HTTP Range 请求
|
||||
|
||||
// 流式传输
|
||||
StreamingSessionID string `json:"streaming_session_id,omitempty"` // 流式会话 ID
|
||||
TotalChunks int64 `json:"total_chunks,omitempty"` // 总块数
|
||||
ChunkSize int64 `json:"chunk_size,omitempty"` // 每块大小
|
||||
|
||||
// 元数据
|
||||
Width int `json:"width,omitempty"` // 宽度(图片/视频,单位:像素)
|
||||
Height int `json:"height,omitempty"` // 高度(图片/视频,单位:像素)
|
||||
Duration float64 `json:"duration,omitempty"` // 时长(音视频,单位:秒)
|
||||
Resolution string `json:"resolution,omitempty"` // 分辨率描述(如 "1920x1080")
|
||||
ThumbnailURL string `json:"thumbnail_url,omitempty"` // 缩略图 URL
|
||||
}
|
||||
```
|
||||
|
||||
**角色定义**:
|
||||
|
||||
| 角色 | 说明 |
|
||||
|------|------|
|
||||
| `reference` | 参考材料,辅助执行方理解上下文 |
|
||||
| `result` | 执行结果输出文件 |
|
||||
| `attachment` | 附件,与任务相关但不参与核心逻辑的文件 |
|
||||
|
||||
**传输模式选择**:
|
||||
|
||||
| 模式 | 条件 | 填充字段 |
|
||||
|------|------|---------|
|
||||
| `inline` | 文件大小 ≤ 1MB | `data`(base64 编码) |
|
||||
| `url` | 文件大小 > 1MB | `url` + `url_expires_at` + `download_method` + `range_supported` |
|
||||
| `streaming` | 大文件或实时流 | `streaming_session_id` + `total_chunks` + `chunk_size` |
|
||||
|
||||
**示例(inline 传输)**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "inline",
|
||||
"content_type": "image/png",
|
||||
"encoding": "base64",
|
||||
"size": 524288,
|
||||
"name": "screenshot.png",
|
||||
"role": "reference",
|
||||
"data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
|
||||
}
|
||||
```
|
||||
|
||||
**示例(url 传输)**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "url",
|
||||
"content_type": "video/mp4",
|
||||
"encoding": "none",
|
||||
"size": 15728640,
|
||||
"name": "demo.mp4",
|
||||
"role": "result",
|
||||
"url": "https://storage.synapse.com/files/demo.mp4",
|
||||
"url_expires_at": "2026-06-30T23:59:59Z",
|
||||
"download_method": "GET",
|
||||
"range_supported": true,
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"duration": 30.5,
|
||||
"resolution": "1920x1080"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.5 内容包(ContentBundle + ContentBundleItem)
|
||||
|
||||
用于传输多个相关联的内容对象,支持分组和角色标注。
|
||||
|
||||
```go
|
||||
type ContentBundle struct {
|
||||
ID string `json:"id"` // 包唯一标识
|
||||
Name string `json:"name,omitempty"` // 包名称
|
||||
Description string `json:"description,omitempty"` // 包描述
|
||||
Items []ContentBundleItem `json:"items"` // 内容项列表
|
||||
TotalSize int64 `json:"total_size,omitempty"` // 总大小(字节)
|
||||
CreatedAt Timestamp `json:"created_at"` // 创建时间
|
||||
}
|
||||
|
||||
type ContentBundleItem struct {
|
||||
ID string `json:"id"` // 项唯一标识
|
||||
Role string `json:"role"` // 角色("main", "reference", "attachment", "thumbnail")
|
||||
Content any `json:"content"` // 内容对象(TextContent / JSONContent / CodeContent / FileContent 之一)
|
||||
}
|
||||
```
|
||||
|
||||
**项角色定义**:
|
||||
|
||||
| 角色 | 说明 |
|
||||
|------|------|
|
||||
| `main` | 主要内容项,通常为任务的主输出 |
|
||||
| `reference` | 参考材料项,辅助理解上下文 |
|
||||
| `attachment` | 附件项,补充信息 |
|
||||
| `thumbnail` | 缩略图项,用于预览展示 |
|
||||
|
||||
**示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "bundle-001",
|
||||
"name": "分析报告",
|
||||
"description": "包含分析结果、图表和原始数据",
|
||||
"items": [
|
||||
{
|
||||
"id": "item-001",
|
||||
"role": "main",
|
||||
"content": {
|
||||
"type": "inline",
|
||||
"content_type": "text/markdown",
|
||||
"encoding": "none",
|
||||
"charset": "utf-8",
|
||||
"language": "zh-CN",
|
||||
"data": "# 分析报告\n\n本次分析共发现 **3** 个关键问题..."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "item-002",
|
||||
"role": "reference",
|
||||
"content": {
|
||||
"type": "url",
|
||||
"content_type": "image/png",
|
||||
"encoding": "none",
|
||||
"size": 204800,
|
||||
"name": "chart.png",
|
||||
"role": "reference",
|
||||
"url": "https://storage.synapse.com/reports/chart.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "item-003",
|
||||
"role": "attachment",
|
||||
"content": {
|
||||
"type": "url",
|
||||
"content_type": "application/json",
|
||||
"encoding": "gzip",
|
||||
"size": 1048576,
|
||||
"name": "raw_data.json.gz",
|
||||
"role": "attachment",
|
||||
"url": "https://storage.synapse.com/reports/raw_data.json.gz"
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_size": 1253376,
|
||||
"created_at": "2026-06-28T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. MIME 类型常量表
|
||||
|
||||
```go
|
||||
const (
|
||||
// 文本类型
|
||||
MIMETextPlain = "text/plain" // 纯文本
|
||||
MIMETextMarkdown = "text/markdown" // Markdown 文档
|
||||
MIMETextHTML = "text/html" // HTML 文档
|
||||
MIMETextCSV = "text/csv" // CSV 表格数据
|
||||
MIMETextCSS = "text/css" // 样式表
|
||||
MIMETextXML = "text/xml" // XML 文本
|
||||
|
||||
// 应用程序类型
|
||||
MIMEJSON = "application/json" // JSON 数据
|
||||
MIMEXML = "application/xml" // XML 数据
|
||||
MIMEPDF = "application/pdf" // PDF 文档
|
||||
MIMEZip = "application/zip" // ZIP 压缩包
|
||||
MIMEGZip = "application/gzip" // GZip 压缩文件
|
||||
MIMEOctetStream = "application/octet-stream" // 二进制流(通用)
|
||||
MIMEMultipartForm = "multipart/form-data" // Multipart 表单数据
|
||||
|
||||
// 图片类型
|
||||
MIMEImageJPEG = "image/jpeg" // JPEG 图片
|
||||
MIMEImagePNG = "image/png" // PNG 图片
|
||||
MIMEImageWebP = "image/webp" // WebP 图片
|
||||
MIMEImageGIF = "image/gif" // GIF 图片
|
||||
MIMEImageSVG = "image/svg+xml" // SVG 矢量图
|
||||
MIMEImageBMP = "image/bmp" // BMP 位图
|
||||
MIMEImageTIFF = "image/tiff" // TIFF 图片
|
||||
MIMEImageICO = "image/x-icon" // 图标文件
|
||||
|
||||
// 视频类型
|
||||
MIMEVideoMP4 = "video/mp4" // MP4 视频
|
||||
MIMEVideoWebM = "video/webm" // WebM 视频
|
||||
MIMEVideoAVI = "video/x-msvideo" // AVI 视频
|
||||
MIMEVideoMOV = "video/quicktime" // MOV 视频
|
||||
MIMEVideoMKV = "video/x-matroska" // MKV 视频
|
||||
|
||||
// 音频类型
|
||||
MIMEAudioMP3 = "audio/mpeg" // MP3 音频
|
||||
MIMEAudioWAV = "audio/wav" // WAV 音频
|
||||
MIMEAudioAAC = "audio/aac" // AAC 音频
|
||||
MIMEAudioOGG = "audio/ogg" // OGG 音频
|
||||
MIMEAudioFLAC = "audio/flac" // FLAC 无损音频
|
||||
MIMEAudioWebM = "audio/webm" // WebM 音频
|
||||
)
|
||||
```
|
||||
|
||||
### 分类汇总
|
||||
|
||||
| 类别 | 数量 | 常量列表 |
|
||||
|------|:----:|---------|
|
||||
| 文本类型 | 6 | `MIMETextPlain`, `MIMETextMarkdown`, `MIMETextHTML`, `MIMETextCSV`, `MIMETextCSS`, `MIMETextXML` |
|
||||
| 应用程序类型 | 7 | `MIMEJSON`, `MIMEXML`, `MIMEPDF`, `MIMEZip`, `MIMEGZip`, `MIMEOctetStream`, `MIMEMultipartForm` |
|
||||
| 图片类型 | 8 | `MIMEImageJPEG`, `MIMEImagePNG`, `MIMEImageWebP`, `MIMEImageGIF`, `MIMEImageSVG`, `MIMEImageBMP`, `MIMEImageTIFF`, `MIMEImageICO` |
|
||||
| 视频类型 | 5 | `MIMEVideoMP4`, `MIMEVideoWebM`, `MIMEVideoAVI`, `MIMEVideoMOV`, `MIMEVideoMKV` |
|
||||
| 音频类型 | 6 | `MIMEAudioMP3`, `MIMEAudioWAV`, `MIMEAudioAAC`, `MIMEAudioOGG`, `MIMEAudioFLAC`, `MIMEAudioWebM` |
|
||||
|
||||
---
|
||||
|
||||
**文档结束**
|
||||
|
||||
> 本文档对应 v0/06-implementation-details.md 第2章「内容类型系统」,
|
||||
> 是 Synapse 协议内容子系统在 v1 层级中的独立规范。
|
||||
408
docs/v1/error-codes.md
Normal file
408
docs/v1/error-codes.md
Normal file
@@ -0,0 +1,408 @@
|
||||
# 灵隙协议实现规范:数字分段错误码系统
|
||||
|
||||
> 版本:1.0.0 | 层级:实现层
|
||||
|
||||
---
|
||||
|
||||
## 1. 错误码分段总览
|
||||
|
||||
灵隙协议将错误码按照功能领域划分为 9 个分段,每个分段占用 1000 个编码空间(即一个十进制千位段),便于程序化路由、国际化映射和监控告警分类。
|
||||
|
||||
| 分段范围 | 类别 | 常量标识 | 说明 |
|
||||
|---------|------|---------|------|
|
||||
| 1000-1999 | SYSTEM_ERROR | 系统错误 | 服务端基础设施类错误,包括内部错误、维护状态等 |
|
||||
| 2000-2999 | TASK_ERROR | 任务错误 | 任务生命周期相关错误,包括查找、取消、超时等 |
|
||||
| 3000-3999 | INFO_ERROR | 信息错误 | 请求信息/数据类错误,包括字段缺失、格式错误等 |
|
||||
| 4000-4999 | CAPABILITY_ERROR | 能力错误 | Agent 能力相关错误,包括不支持的任务类型、资源繁忙等 |
|
||||
| 5000-5999 | SECURITY_ERROR | 安全错误 | 认证与授权类错误,包括签名验证、权限不足等 |
|
||||
| 6000-6999 | COMMUNICATION_ERROR | 通信错误 | 网络通信类错误,包括端点不可达、回调失败等 |
|
||||
| 7000-7999 | GROUP_ERROR | 群组错误 | 群组协作类错误,包括成员管理、共识失败等 |
|
||||
| 8000-8999 | HIL_ERROR | HiL 错误 | 人工介入(Human-in-the-Loop)相关错误 |
|
||||
| 9000-9999 | VALIDATION_ERROR | 验证错误 | 通用参数验证错误 |
|
||||
|
||||
分类映射逻辑由 `SynapseErrorCode.Category()` 方法实现,通过错误码范围自动推导所属类别。
|
||||
|
||||
---
|
||||
|
||||
## 2. 系统错误(1000-1999)
|
||||
|
||||
系统错误表示服务端基础设施层面的异常,通常需要运维人员介入处理。
|
||||
|
||||
| 错误码 | 常量名称 | 说明 |
|
||||
|-------|---------|------|
|
||||
| 1000 | ErrSystemError | 通用系统错误,无法归类的系统级异常 |
|
||||
| 1001 | ErrInternalError | 内部错误,服务端内部处理逻辑异常 |
|
||||
| 1002 | ErrMaintenance | 系统维护中,服务暂不可用 |
|
||||
|
||||
**适用场景**:
|
||||
- 数据库连接失败、磁盘写入异常等基础设施故障
|
||||
- 未捕获的运行时异常
|
||||
- 计划内维护窗口或突发性服务不可用
|
||||
|
||||
---
|
||||
|
||||
## 3. 任务错误(2000-2999)
|
||||
|
||||
任务错误表示与任务生命周期管理相关的异常,涵盖任务的创建、执行、完成和取消等阶段。
|
||||
|
||||
| 错误码 | 常量名称 | 说明 |
|
||||
|-------|---------|------|
|
||||
| 2001 | ErrTaskNotFound | 任务不存在,指定的任务 ID 无效或已过期 |
|
||||
| 2002 | ErrTaskAlreadyCompleted | 任务已完成,无法对已完成的任务执行变更操作 |
|
||||
| 2003 | ErrTaskCancelled | 任务已取消,任务已被发起方或系统取消 |
|
||||
| 2004 | ErrTaskTimeout | 任务超时,执行时长超过最大允许时间 |
|
||||
| 2005 | ErrTaskInvalidParams | 任务参数无效,提交任务时提供的参数不符合要求 |
|
||||
|
||||
**适用场景**:
|
||||
- 查询不存在的任务状态
|
||||
- 对已结束的任务发起取消或修改
|
||||
- 任务执行超过最大持续时间阈值
|
||||
- 提交任务时缺少必要参数或参数格式错误
|
||||
|
||||
---
|
||||
|
||||
## 4. 信息错误(3000-3999)
|
||||
|
||||
信息错误表示请求中携带的信息不完整或不符合规范,通常由调用方修正后重试。
|
||||
|
||||
| 错误码 | 常量名称 | 说明 |
|
||||
|-------|---------|------|
|
||||
| 3001 | ErrRequiredFieldMissing | 必填字段缺失,请求中缺少协议要求的必需字段 |
|
||||
| 3002 | ErrInvalidFieldFormat | 字段格式无效,字段值不符合预期的格式规范 |
|
||||
| 3003 | ErrInfoNotProvided | 信息未提供,需要的信息未被提供 |
|
||||
|
||||
**适用场景**:
|
||||
- 消息中缺少 `sender`、`receiver` 等必填字段
|
||||
- 邮箱、URL、时间戳等字段格式校验失败
|
||||
- 上下文信息不足以完成任务处理
|
||||
|
||||
---
|
||||
|
||||
## 5. 能力错误(4000-4999)
|
||||
|
||||
能力错误表示接收方 Agent 无法处理请求所要求的能力或资源不可用。
|
||||
|
||||
| 错误码 | 常量名称 | 说明 |
|
||||
|-------|---------|------|
|
||||
| 4001 | ErrUnsupportedTaskType | 不支持的任务类型,Agent 未声明支持该类型的任务 |
|
||||
| 4002 | ErrCapabilityLacking | 能力不足,Agent 不具备执行请求所需的某项能力 |
|
||||
| 4003 | ErrResourceBusy | 资源繁忙,Agent 当前正在处理其他任务,无法接受新请求 |
|
||||
| 4004 | ErrQuotaExceeded | 配额超限,请求超出 Agent 设定的资源配额限制 |
|
||||
|
||||
**适用场景**:
|
||||
- 向只支持文本生成的 Agent 提交图像生成任务
|
||||
- 调用未在 `AgentCapabilities` 中声明的能力
|
||||
- Agent 已达到 `MaxConcurrentTasks` 上限
|
||||
- 文件大小超过 `MaxFileSize` 或任务时长超过 `MaxTaskDuration`
|
||||
|
||||
---
|
||||
|
||||
## 6. 安全错误(5000-5999)
|
||||
|
||||
安全错误表示认证、授权或安全策略相关的异常,涉及身份验证和访问控制。
|
||||
|
||||
| 错误码 | 常量名称 | 说明 |
|
||||
|-------|---------|------|
|
||||
| 5001 | ErrAuthFailed | 认证失败,身份凭证验证未通过 |
|
||||
| 5002 | ErrSignatureInvalid | 签名无效,消息签名验证失败,内容可能被篡改 |
|
||||
| 5003 | ErrPermissionDenied | 权限不足,当前身份不具备执行操作所需的权限 |
|
||||
| 5004 | ErrPolicyViolation | 策略违规,请求违反系统安全策略或合规要求 |
|
||||
|
||||
**适用场景**:
|
||||
- 令牌过期、密钥错误等认证失败
|
||||
- HMAC 签名不匹配或签名算法不一致
|
||||
- 角色(Role)不具备操作所需的权限(Permission)
|
||||
- 违反数据访问策略、IP 白名单限制等安全规则
|
||||
|
||||
---
|
||||
|
||||
## 7. 通信错误(6000-6999)
|
||||
|
||||
通信错误表示网络传输层面的异常,通常涉及 Agent 之间的消息投递和回调机制。
|
||||
|
||||
| 错误码 | 常量名称 | 说明 |
|
||||
|-------|---------|------|
|
||||
| 6001 | ErrEndpointUnreachable | 端点不可达,目标 Agent 的回调端点无法连接 |
|
||||
| 6002 | ErrCallbackFailed | 回调失败,回调请求发送成功但返回错误响应 |
|
||||
| 6003 | ErrMessageFormatInvalid | 消息格式无效,接收到的消息不符合协议格式要求 |
|
||||
| 6004 | ErrTTLExpired | TTL 过期,消息的存活时间已过期,已被丢弃 |
|
||||
|
||||
**适用场景**:
|
||||
- 目标 Agent 离线或网络不可达
|
||||
- 回调端点返回非 2xx 状态码
|
||||
- 消息结构解析失败、JSON 格式错误
|
||||
- 消息在灵泊(Harbor)队列中停留超过 TTL
|
||||
|
||||
---
|
||||
|
||||
## 8. 群组错误(7000-7999)
|
||||
|
||||
群组错误表示群组协作场景中的异常,涵盖群组生命周期管理和成员操作。
|
||||
|
||||
| 错误码 | 常量名称 | 说明 |
|
||||
|-------|---------|------|
|
||||
| 7001 | ErrGroupNotFound | 群组不存在,指定的群组 ID 无效或已解散 |
|
||||
| 7002 | ErrGroupNotActive | 群组未激活,群组当前处于非活跃状态 |
|
||||
| 7003 | ErrMemberNotFound | 成员不存在,指定的成员不在群组中 |
|
||||
| 7004 | ErrNotGroupMember | 不是群组成员,当前 Agent 不属于该群组 |
|
||||
| 7005 | ErrConsensusNotReached | 未达成共识,群组投票未达到共识阈值 |
|
||||
| 7006 | ErrRoleNotFound | 角色不存在,指定的角色在群组中未定义 |
|
||||
| 7007 | ErrAlreadyMember | 已是成员,Agent 已经加入该群组 |
|
||||
| 7008 | ErrGroupFull | 群组已满,群组成员数量已达上限 |
|
||||
|
||||
**适用场景**:
|
||||
- 查询或操作不存在的群组
|
||||
- 向未激活的群组发送消息
|
||||
- 查询群组中不存在的成员信息
|
||||
- 非群组成员尝试执行仅成员可用的操作
|
||||
- 投票结果未达到 `ConsensusMajority` 或 `ConsensusSupermajority` 阈值
|
||||
- 重复加入同一群组
|
||||
- 群组人数超过 `MaxMembers` 限制
|
||||
|
||||
---
|
||||
|
||||
## 9. HiL 错误(8000-8999)
|
||||
|
||||
HiL(Human-in-the-Loop)错误表示人工介入流程中的异常情况,涵盖人工决策超时、拒绝和升级级别错误。
|
||||
|
||||
| 错误码 | 常量名称 | 说明 |
|
||||
|-------|---------|------|
|
||||
| 8001 | ErrHumanTimeout | 人工决策超时,在规定时间内未收到人工响应 |
|
||||
| 8002 | ErrHumanDeclined | 人工拒绝,人工决策者明确拒绝了请求 |
|
||||
| 8003 | ErrEscalationLevel | 升级级别错误,指定的升级级别无效或不适用 |
|
||||
|
||||
**适用场景**:
|
||||
- Level 1/2/3 超时未响应(超时时间依据级别分别为 24h/12h/72h)
|
||||
- 人工批复结果为 `rejected`
|
||||
- 升级级别超出定义范围(1-3)或与升级原因不匹配
|
||||
|
||||
**级别超时默认值**:
|
||||
|
||||
| 级别 | 默认超时 | 紧急超时 |
|
||||
|-----|---------|---------|
|
||||
| Level 1 | 24 小时 | — |
|
||||
| Level 2 | 12 小时 | 2 小时 |
|
||||
| Level 3 | 72 小时 | — |
|
||||
|
||||
---
|
||||
|
||||
## 10. 验证错误(9000-9999)
|
||||
|
||||
验证错误表示通用参数验证未通过,适用于多个层级的输入校验。
|
||||
|
||||
| 错误码 | 常量名称 | 说明 |
|
||||
|-------|---------|------|
|
||||
| 9001 | ErrInvalidParams | 参数无效,通用参数校验失败 |
|
||||
|
||||
**适用场景**:
|
||||
- 请求参数类型不匹配
|
||||
- 枚举值超出允许范围
|
||||
- 数值超出允许的上下界
|
||||
- 字符串长度超出限制
|
||||
|
||||
---
|
||||
|
||||
## 11. 错误响应结构
|
||||
|
||||
灵隙协议定义了两层错误响应结构:`SynapseError` 承载单一错误的完整信息,`ErrorResponse` 作为统一的 API 响应信封。
|
||||
|
||||
### 11.1 SynapseError
|
||||
|
||||
`SynapseError` 是协议中所有错误的标准载体,包含错误码、类别、人类可读消息以及扩展信息。
|
||||
|
||||
```go
|
||||
type SynapseError struct {
|
||||
Code SynapseErrorCode `json:"code"` // 数字错误码,如 1000、2001
|
||||
Category string `json:"category"` // 错误类别,如 "SYSTEM_ERROR"、"TASK_ERROR"
|
||||
Message string `json:"message"` // 错误描述,人类可读的英文消息
|
||||
Details any `json:"details,omitempty"` // 错误详情,可选,可包含结构化补充信息
|
||||
RetryAfter *int `json:"retry_after,omitempty"` // 建议重试等待时间(秒),可选
|
||||
Timestamp Timestamp `json:"timestamp"` // 错误发生时间戳
|
||||
}
|
||||
```
|
||||
|
||||
**字段说明**:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|-----|------|-----|------|
|
||||
| `code` | SynapseErrorCode | 是 | 数字错误码,属于预定义的 9 个分段之一 |
|
||||
| `category` | string | 是 | 由 `SynapseErrorCode.Category()` 自动推导的类别常量 |
|
||||
| `message` | string | 是 | 面向开发者的英文错误描述,便于日志和调试 |
|
||||
| `details` | any | 否 | 可选的扩展信息,可包含字段级别的验证错误等结构化数据 |
|
||||
| `retry_after` | *int | 否 | 对可重试错误,建议调用方等待的秒数(如资源繁忙时) |
|
||||
| `timestamp` | Timestamp | 是 | 错误生成的时间戳,用于追踪和时序分析 |
|
||||
|
||||
### 11.2 ErrorResponse
|
||||
|
||||
`ErrorResponse` 是所有 API 响应的统一错误信封,通过 `success` 字段标识请求处理状态。
|
||||
|
||||
```go
|
||||
type ErrorResponse struct {
|
||||
Success bool `json:"success"` // 固定为 false
|
||||
Error *SynapseError `json:"error"` // 错误详情
|
||||
}
|
||||
```
|
||||
|
||||
**JSON 序列化示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": {
|
||||
"code": 4003,
|
||||
"category": "CAPABILITY_ERROR",
|
||||
"message": "Agent is currently busy processing other tasks",
|
||||
"details": {
|
||||
"current_tasks": 5,
|
||||
"max_concurrent": 5
|
||||
},
|
||||
"retry_after": 30,
|
||||
"timestamp": "2026-06-28T10:30:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 11.3 ToMap() 方法
|
||||
|
||||
`SynapseError` 提供 `ToMap() map[string]any` 方法,将错误结构转换为扁平化的 map 结构,便于在不同上下文中进行 JSON 序列化或格式转换。
|
||||
|
||||
**转换规则**:
|
||||
|
||||
| 源字段 | map 中的 key | 条件 |
|
||||
|-------|-------------|------|
|
||||
| `Code` | `"code"` | 始终包含 |
|
||||
| `Category` | `"category"` | 始终包含 |
|
||||
| `Message` | `"message"` | 始终包含 |
|
||||
| `Timestamp` | `"timestamp"` | 始终包含 |
|
||||
| `Details` | `"details"` | 仅当 `Details != nil` 时包含 |
|
||||
| `RetryAfter` | `"retry_after"` | 仅当 `RetryAfter != nil` 时包含 |
|
||||
|
||||
**方法签名**:
|
||||
|
||||
```go
|
||||
func (e *SynapseError) ToMap() map[string]any
|
||||
```
|
||||
|
||||
**Go 实现**:
|
||||
|
||||
```go
|
||||
func (e *SynapseError) ToMap() map[string]any {
|
||||
m := map[string]any{
|
||||
"code": e.Code,
|
||||
"category": e.Category,
|
||||
"message": e.Message,
|
||||
"timestamp": e.Timestamp,
|
||||
}
|
||||
if e.Details != nil {
|
||||
m["details"] = e.Details
|
||||
}
|
||||
if e.RetryAfter != nil {
|
||||
m["retry_after"] = *e.RetryAfter
|
||||
}
|
||||
return m
|
||||
}
|
||||
```
|
||||
|
||||
### 11.4 工厂函数
|
||||
|
||||
协议提供两个工厂函数用于构造 `SynapseError`:
|
||||
|
||||
| 函数 | 说明 |
|
||||
|-----|------|
|
||||
| `NewSynapseError(code, message, details)` | 创建标准错误,自动填充 `Category` 和 `Timestamp` |
|
||||
| `NewSynapseErrorWithRetry(code, message, details, retryAfter)` | 创建带重试提示的错误 |
|
||||
|
||||
### 11.5 预定义错误实例
|
||||
|
||||
`types/errors.go` 中定义了一系列预构建的错误实例,供实现直接引用:
|
||||
|
||||
| 变量名 | 对应错误码 | 默认消息 |
|
||||
|-------|-----------|---------|
|
||||
| `ErrTaskNotFoundError` | 2001 | "Task not found" |
|
||||
| `ErrTaskAlreadyCompletedError` | 2002 | "Task has already been completed" |
|
||||
| `ErrTaskCancelledError` | 2003 | "Task has been cancelled" |
|
||||
| `ErrTaskTimeoutError` | 2004 | "Task execution timed out" |
|
||||
| `ErrRequiredFieldMissingError` | 3001 | "Required field is missing" |
|
||||
| `ErrUnsupportedTaskTypeError` | 4001 | "Task type is not supported by this agent" |
|
||||
| `ErrCapabilityLackingError` | 4002 | "Agent lacks the required capability" |
|
||||
| `ErrResourceBusyError` | 4003 | "Agent is currently busy processing other tasks" |
|
||||
| `ErrAuthFailedError` | 5001 | "Authentication failed" |
|
||||
| `ErrSignatureInvalidError` | 5002 | "Message signature verification failed" |
|
||||
| `ErrPermissionDeniedError` | 5003 | "Permission denied" |
|
||||
| `ErrEndpointUnreachableError` | 6001 | "Callback endpoint is unreachable" |
|
||||
| `ErrMessageFormatInvalidError` | 6003 | "Message format is invalid" |
|
||||
| `ErrGroupNotFoundError` | 7001 | "Group not found" |
|
||||
| `ErrNotGroupMemberError` | 7004 | "Agent is not a member of this group" |
|
||||
| `ErrGroupNotActiveError` | 7002 | "Group is not active" |
|
||||
| `ErrAlreadyMemberError` | 7007 | "Agent is already a member of this group" |
|
||||
| `ErrGroupFullError` | 7008 | "Group has reached maximum capacity" |
|
||||
| `ErrMemberNotFoundError` | 7003 | "Member not found in group" |
|
||||
| `ErrInvalidParamsError` | 9001 | "Invalid parameters" |
|
||||
| `ErrHumanTimeoutError` | 8001 | "Human decision timed out" |
|
||||
|
||||
---
|
||||
|
||||
## 附录:错误码常量定义
|
||||
|
||||
```go
|
||||
type SynapseErrorCode int
|
||||
|
||||
// 系统错误
|
||||
const ErrSystemError SynapseErrorCode = 1000
|
||||
const ErrInternalError SynapseErrorCode = 1001
|
||||
const ErrMaintenance SynapseErrorCode = 1002
|
||||
|
||||
// 任务错误
|
||||
const ErrTaskNotFound SynapseErrorCode = 2001
|
||||
const ErrTaskAlreadyCompleted SynapseErrorCode = 2002
|
||||
const ErrTaskCancelled SynapseErrorCode = 2003
|
||||
const ErrTaskTimeout SynapseErrorCode = 2004
|
||||
const ErrTaskInvalidParams SynapseErrorCode = 2005
|
||||
|
||||
// 信息错误
|
||||
const ErrRequiredFieldMissing SynapseErrorCode = 3001
|
||||
const ErrInvalidFieldFormat SynapseErrorCode = 3002
|
||||
const ErrInfoNotProvided SynapseErrorCode = 3003
|
||||
|
||||
// 能力错误
|
||||
const ErrUnsupportedTaskType SynapseErrorCode = 4001
|
||||
const ErrCapabilityLacking SynapseErrorCode = 4002
|
||||
const ErrResourceBusy SynapseErrorCode = 4003
|
||||
const ErrQuotaExceeded SynapseErrorCode = 4004
|
||||
|
||||
// 安全错误
|
||||
const ErrAuthFailed SynapseErrorCode = 5001
|
||||
const ErrSignatureInvalid SynapseErrorCode = 5002
|
||||
const ErrPermissionDenied SynapseErrorCode = 5003
|
||||
const ErrPolicyViolation SynapseErrorCode = 5004
|
||||
|
||||
// 通信错误
|
||||
const ErrEndpointUnreachable SynapseErrorCode = 6001
|
||||
const ErrCallbackFailed SynapseErrorCode = 6002
|
||||
const ErrMessageFormatInvalid SynapseErrorCode = 6003
|
||||
const ErrTTLExpired SynapseErrorCode = 6004
|
||||
|
||||
// 群组错误
|
||||
const ErrGroupNotFound SynapseErrorCode = 7001
|
||||
const ErrGroupNotActive SynapseErrorCode = 7002
|
||||
const ErrMemberNotFound SynapseErrorCode = 7003
|
||||
const ErrNotGroupMember SynapseErrorCode = 7004
|
||||
const ErrConsensusNotReached SynapseErrorCode = 7005
|
||||
const ErrRoleNotFound SynapseErrorCode = 7006
|
||||
const ErrAlreadyMember SynapseErrorCode = 7007
|
||||
const ErrGroupFull SynapseErrorCode = 7008
|
||||
|
||||
// HiL 错误
|
||||
const ErrHumanTimeout SynapseErrorCode = 8001
|
||||
const ErrHumanDeclined SynapseErrorCode = 8002
|
||||
const ErrEscalationLevel SynapseErrorCode = 8003
|
||||
|
||||
// 验证错误
|
||||
const ErrInvalidParams SynapseErrorCode = 9001
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> 本文档对应源码:[types/errors.go](file:///e:/code/go/synapse/types/errors.go)
|
||||
>
|
||||
> 参考文档:[06-implementation-details.md](file:///e:/code/go/synapse/docs/v0/06-implementation-details.md) 第 6 章
|
||||
521
docs/v1/grpc-transport.md
Normal file
521
docs/v1/grpc-transport.md
Normal file
@@ -0,0 +1,521 @@
|
||||
# 灵隙协议实现规范:gRPC 传输协议
|
||||
|
||||
> 版本:1.0.0 | 层级:实现层
|
||||
|
||||
---
|
||||
|
||||
## 1. Proto 定义位置说明
|
||||
|
||||
Synapse 协议的 gRPC 服务定义统一存放于以下位置:
|
||||
|
||||
```
|
||||
proto/
|
||||
├── synapse.proto # 主 proto 文件,包含所有服务与消息定义
|
||||
├── synapse_common.proto # 公共消息类型(可选拆分)
|
||||
├── google/
|
||||
│ └── api/
|
||||
│ ├── annotations.proto # Google HTTP 注解依赖
|
||||
│ └── http.proto
|
||||
└── protoc-gen-openapiv2/
|
||||
└── options/
|
||||
└── annotations.proto # OpenAPI v2 注解依赖(可选)
|
||||
```
|
||||
|
||||
主定义文件 [`synapse.proto`](file:///proto/synapse.proto) 包含:
|
||||
|
||||
- 5 个 gRPC 服务(TaskService、SynapseMessageService、CapabilitiesService、GroupService、HealthService)
|
||||
- 20 个 RPC 方法
|
||||
- 对应的请求/响应消息类型
|
||||
- `google.api.http` 注解,用于通过 gRPC Gateway 生成 HTTP REST 端点
|
||||
|
||||
通过 `buf` 或 `protoc` 工具链编译生成:
|
||||
|
||||
| 生成产物 | 说明 |
|
||||
|---------|------|
|
||||
| `synapse.pb.go` | gRPC 服务端/客户端桩代码 |
|
||||
| `synapse.pb.gw.go` | gRPC Gateway 反向代理代码 |
|
||||
| `synapse.swagger.json` | OpenAPI v2 规范文档(可选) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 服务定义
|
||||
|
||||
### 2.1 TaskService(任务服务)
|
||||
|
||||
任务服务是协议核心,负责任务的提交、查询、取消与状态流式推送。
|
||||
|
||||
```protobuf
|
||||
service TaskService {
|
||||
// 提交任务(JSON payload)
|
||||
rpc SubmitTask(SubmitTaskRequest) returns (SubmitTaskResponse);
|
||||
|
||||
// 提交任务(含文件附件,Multipart)
|
||||
rpc SubmitTaskWithFiles(SubmitTaskWithFilesRequest) returns (SubmitTaskResponse);
|
||||
|
||||
// 获取任务完整信息
|
||||
rpc GetTask(GetTaskRequest) returns (TaskStatusResponse);
|
||||
|
||||
// 获取任务状态(轻量查询)
|
||||
rpc GetTaskStatus(GetTaskRequest) returns (TaskStatusResponse);
|
||||
|
||||
// 获取任务列表(支持分页和过滤)
|
||||
rpc ListTasks(ListTasksRequest) returns (ListTasksResponse);
|
||||
|
||||
// 取消任务
|
||||
rpc CancelTask(CancelTaskRequest) returns (CancelTaskResponse);
|
||||
|
||||
// 流式监听任务状态变化(双向流)
|
||||
rpc StreamTaskStatus(stream TaskStatusStreamRequest) returns (stream TaskStatusUpdate);
|
||||
}
|
||||
```
|
||||
|
||||
**RPC 方法列表:**
|
||||
|
||||
| # | 方法名 | 请求类型 | 响应类型 | 流式 |
|
||||
|---|--------|---------|---------|------|
|
||||
| 1 | `SubmitTask` | `SubmitTaskRequest` | `SubmitTaskResponse` | 否 |
|
||||
| 2 | `SubmitTaskWithFiles` | `SubmitTaskWithFilesRequest` | `SubmitTaskResponse` | 否 |
|
||||
| 3 | `GetTask` | `GetTaskRequest` | `TaskStatusResponse` | 否 |
|
||||
| 4 | `GetTaskStatus` | `GetTaskRequest` | `TaskStatusResponse` | 否 |
|
||||
| 5 | `ListTasks` | `ListTasksRequest` | `ListTasksResponse` | 否 |
|
||||
| 6 | `CancelTask` | `CancelTaskRequest` | `CancelTaskResponse` | 否 |
|
||||
| 7 | `StreamTaskStatus` | `stream TaskStatusStreamRequest` | `stream TaskStatusUpdate` | 双向流 |
|
||||
|
||||
### 2.2 SynapseMessageService(消息服务)
|
||||
|
||||
消息服务负责 Agent 间的异步消息传递、流式对话以及回调推送。
|
||||
|
||||
```protobuf
|
||||
service SynapseMessageService {
|
||||
// 发送单条消息
|
||||
rpc SendMessage(SendMessageRequest) returns (SendMessageResponse);
|
||||
|
||||
// 流式消息交换(双向流,适用于多轮对话)
|
||||
rpc StreamMessages(stream SynapseMessage) returns (stream SynapseMessage);
|
||||
|
||||
// 接收服务端推送的回调消息(服务器推送流)
|
||||
rpc ReceiveCallback(google.protobuf.Empty) returns (stream SynapseMessage);
|
||||
}
|
||||
```
|
||||
|
||||
**RPC 方法列表:**
|
||||
|
||||
| # | 方法名 | 请求类型 | 响应类型 | 流式 |
|
||||
|---|--------|---------|---------|------|
|
||||
| 8 | `SendMessage` | `SendMessageRequest` | `SendMessageResponse` | 否 |
|
||||
| 9 | `StreamMessages` | `stream SynapseMessage` | `stream SynapseMessage` | 双向流 |
|
||||
| 10 | `ReceiveCallback` | `google.protobuf.Empty` | `stream SynapseMessage` | 服务端流 |
|
||||
|
||||
### 2.3 CapabilitiesService(能力服务)
|
||||
|
||||
能力服务提供 Agent 能力查询与发现机制。
|
||||
|
||||
```protobuf
|
||||
service CapabilitiesService {
|
||||
// 获取当前 Agent 自身的能力声明
|
||||
rpc GetCapabilities(google.protobuf.Empty) returns (CapabilitiesResponse);
|
||||
|
||||
// 查询指定 Agent 的能力
|
||||
rpc QueryAgent(QueryAgentRequest) returns (CapabilitiesResponse);
|
||||
|
||||
// 列出网络中所有活跃 Agent
|
||||
rpc ListAgents(google.protobuf.Empty) returns (ListAgentsResponse);
|
||||
}
|
||||
```
|
||||
|
||||
**RPC 方法列表:**
|
||||
|
||||
| # | 方法名 | 请求类型 | 响应类型 | 流式 |
|
||||
|---|--------|---------|---------|------|
|
||||
| 11 | `GetCapabilities` | `google.protobuf.Empty` | `CapabilitiesResponse` | 否 |
|
||||
| 12 | `QueryAgent` | `QueryAgentRequest` | `CapabilitiesResponse` | 否 |
|
||||
| 13 | `ListAgents` | `google.protobuf.Empty` | `ListAgentsResponse` | 否 |
|
||||
|
||||
### 2.4 GroupService(群组服务)
|
||||
|
||||
群组服务支持 Agent 群的创建、成员管理与群内消息流。
|
||||
|
||||
```protobuf
|
||||
service GroupService {
|
||||
// 创建群组
|
||||
rpc CreateGroup(CreateGroupRequest) returns (CreateGroupResponse);
|
||||
|
||||
// 加入群组
|
||||
rpc JoinGroup(JoinGroupRequest) returns (JoinGroupResponse);
|
||||
|
||||
// 离开群组
|
||||
rpc LeaveGroup(LeaveGroupRequest) returns (LeaveGroupResponse);
|
||||
|
||||
// 解散群组(仅群主/管理员)
|
||||
rpc DisbandGroup(DisbandGroupRequest) returns (DisbandGroupResponse);
|
||||
|
||||
// 列出群组成员
|
||||
rpc ListGroupMembers(ListGroupMembersRequest) returns (ListGroupMembersResponse);
|
||||
|
||||
// 流式收发群组消息(双向流)
|
||||
rpc StreamGroupMessages(stream SynapseMessage) returns (stream SynapseMessage);
|
||||
}
|
||||
```
|
||||
|
||||
**RPC 方法列表:**
|
||||
|
||||
| # | 方法名 | 请求类型 | 响应类型 | 流式 |
|
||||
|---|--------|---------|---------|------|
|
||||
| 14 | `CreateGroup` | `CreateGroupRequest` | `CreateGroupResponse` | 否 |
|
||||
| 15 | `JoinGroup` | `JoinGroupRequest` | `JoinGroupResponse` | 否 |
|
||||
| 16 | `LeaveGroup` | `LeaveGroupRequest` | `LeaveGroupResponse` | 否 |
|
||||
| 17 | `DisbandGroup` | `DisbandGroupRequest` | `DisbandGroupResponse` | 否 |
|
||||
| 18 | `ListGroupMembers` | `ListGroupMembersRequest` | `ListGroupMembersResponse` | 否 |
|
||||
| 19 | `StreamGroupMessages` | `stream SynapseMessage` | `stream SynapseMessage` | 双向流 |
|
||||
|
||||
### 2.5 HealthService(健康服务)
|
||||
|
||||
健康服务提供存活探针与健康状态检查。
|
||||
|
||||
```protobuf
|
||||
service HealthService {
|
||||
// 健康检查
|
||||
rpc HealthCheck(google.protobuf.Empty) returns (HealthCheckResponse);
|
||||
}
|
||||
```
|
||||
|
||||
**RPC 方法列表:**
|
||||
|
||||
| # | 方法名 | 请求类型 | 响应类型 | 流式 |
|
||||
|---|--------|---------|---------|------|
|
||||
| 20 | `HealthCheck` | `google.protobuf.Empty` | `HealthCheckResponse` | 否 |
|
||||
|
||||
---
|
||||
|
||||
## 3. HTTP 路由映射表
|
||||
|
||||
通过 `google.api.http` 注解,每个 gRPC 方法映射为对应的 HTTP REST 端点。gRPC Gateway 在启动时读取这些注解,自动生成反向代理,使同一套后端可同时服务于 gRPC 和 HTTP 客户端。
|
||||
|
||||
### 3.1 完整映射表
|
||||
|
||||
| # | gRPC 方法 | HTTP 方法 | HTTP 路径 | 说明 |
|
||||
|---|-----------|----------|----------|------|
|
||||
| 1 | `SubmitTask` | `POST` | `/api/v1/synapse/task` | 提交任务 |
|
||||
| 2 | `SubmitTaskWithFiles` | `POST` | `/api/v1/synapse/task/multipart` | 提交含附件的任务 |
|
||||
| 3 | `GetTask` | `GET` | `/api/v1/synapse/task/{task_id}` | 获取任务详情 |
|
||||
| 4 | `GetTaskStatus` | `GET` | `/api/v1/synapse/task/{task_id}/status` | 获取任务状态 |
|
||||
| 5 | `ListTasks` | `GET` | `/api/v1/synapse/tasks` | 列出任务(支持分页) |
|
||||
| 6 | `CancelTask` | `POST` | `/api/v1/synapse/task/{task_id}/cancel` | 取消任务 |
|
||||
| 7 | `StreamTaskStatus` | `GET` | `/api/v1/synapse/task/{task_id}/stream` | SSE 流式监听任务状态 |
|
||||
| 8 | `SendMessage` | `POST` | `/api/v1/synapse/message` | 发送消息 |
|
||||
| 9 | `StreamMessages` | `GET` | `/api/v1/synapse/messages/stream` | WebSocket/SSE 流式消息交换 |
|
||||
| 10 | `ReceiveCallback` | `GET` | `/api/v1/synapse/messages/callback` | SSE 接收服务端回调推送 |
|
||||
| 11 | `GetCapabilities` | `GET` | `/api/v1/synapse/capabilities` | 获取自身能力声明 |
|
||||
| 12 | `QueryAgent` | `GET` | `/api/v1/synapse/agents/{agent_id}` | 查询指定 Agent 能力 |
|
||||
| 13 | `ListAgents` | `GET` | `/api/v1/synapse/agents` | 列出所有活跃 Agent |
|
||||
| 14 | `CreateGroup` | `POST` | `/api/v1/synapse/group` | 创建群组 |
|
||||
| 15 | `JoinGroup` | `POST` | `/api/v1/synapse/group/{group_id}/join` | 加入群组 |
|
||||
| 16 | `LeaveGroup` | `POST` | `/api/v1/synapse/group/{group_id}/leave` | 离开群组 |
|
||||
| 17 | `DisbandGroup` | `POST` | `/api/v1/synapse/group/{group_id}/disband` | 解散群组 |
|
||||
| 18 | `ListGroupMembers` | `GET` | `/api/v1/synapse/group/{group_id}/members` | 列出群组成员 |
|
||||
| 19 | `StreamGroupMessages` | `GET` | `/api/v1/synapse/group/{group_id}/messages/stream` | WebSocket/SSE 流式群组消息 |
|
||||
| 20 | `HealthCheck` | `GET` | `/api/v1/synapse/health` | 健康检查 |
|
||||
|
||||
### 3.2 路由特性说明
|
||||
|
||||
**路径参数绑定**:
|
||||
|
||||
| 路径参数 | 绑定字段 | 示例值 |
|
||||
|---------|---------|-------|
|
||||
| `{task_id}` | `GetTaskRequest.task_id` | `"task-abc123"` |
|
||||
| `{agent_id}` | `QueryAgentRequest.agent_id` | `"agent-xyz789"` |
|
||||
| `{group_id}` | `JoinGroupRequest.group_id` | `"group-demo-001"` |
|
||||
|
||||
**请求体映射规则**:
|
||||
|
||||
- **POST 请求**:除路径参数外,剩余字段映射为 HTTP 请求体(JSON)
|
||||
- **GET 请求**:非路径参数字段映射为 URL 查询参数(query string)
|
||||
|
||||
**流式端点说明**:
|
||||
|
||||
| 流式 RPC | HTTP 协议映射 | 说明 |
|
||||
|---------|-------------|------|
|
||||
| `StreamTaskStatus` | SSE(Server-Sent Events) | 服务端单向推送任务状态变更 |
|
||||
| `StreamMessages` | WebSocket | 全双工流式消息交换 |
|
||||
| `ReceiveCallback` | SSE(Server-Sent Events) | 服务端单向推送回调消息 |
|
||||
| `StreamGroupMessages` | WebSocket | 全双工群组消息流 |
|
||||
|
||||
对于双向流 RPC(`StreamMessages`、`StreamGroupMessages`),HTTP REST 映射通过 WebSocket Upgrade 实现;对于服务端流 RPC(`StreamTaskStatus`、`ReceiveCallback`),通过 SSE(`text/event-stream`)实现。
|
||||
|
||||
### 3.3 路由前缀约定
|
||||
|
||||
所有 Synapse 协议 REST API 遵循以下 URL 前缀约定:
|
||||
|
||||
```
|
||||
/api/v1/synapse/
|
||||
```
|
||||
|
||||
- `api`:标识为 API 请求
|
||||
- `v1`:API 版本号
|
||||
- `synapse`:命名空间,区分 Synapse 协议与其他业务 API
|
||||
|
||||
版本升级时(如 v1 → v2),路径前缀同步更新为 `/api/v2/synapse/`,新旧版本可并行部署。
|
||||
|
||||
### 3.4 HTTP 状态码与 gRPC 状态码映射
|
||||
|
||||
| HTTP 状态码 | gRPC 状态码 | 说明 |
|
||||
|------------|-------------|------|
|
||||
| `200 OK` | `OK` | 请求成功 |
|
||||
| `201 Created` | `OK` | 资源创建成功(SubmitTask、CreateGroup) |
|
||||
| `400 Bad Request` | `InvalidArgument` | 请求参数无效 |
|
||||
| `401 Unauthorized` | `Unauthenticated` | 认证失败 |
|
||||
| `403 Forbidden` | `PermissionDenied` | 权限不足 |
|
||||
| `404 Not Found` | `NotFound` | 资源不存在 |
|
||||
| `409 Conflict` | `AlreadyExists` / `Aborted` | 资源冲突 |
|
||||
| `429 Too Many Requests` | `ResourceExhausted` | 限流触发 |
|
||||
| `500 Internal Server Error` | `Internal` | 服务端内部错误 |
|
||||
| `503 Service Unavailable` | `Unavailable` | 服务不可用 |
|
||||
|
||||
gRPC Gateway 自动完成上述状态码转换,客户端无需额外处理。
|
||||
|
||||
---
|
||||
|
||||
## 4. 协议缓冲区消息结构概览
|
||||
|
||||
以下列出各服务核心 RPC 的请求与响应消息结构(仅示意字段分类,完整定义以 `.proto` 文件为准):
|
||||
|
||||
### 4.1 任务相关消息
|
||||
|
||||
```protobuf
|
||||
// SubmitTask 请求
|
||||
message SubmitTaskRequest {
|
||||
string task_type = 1; // 任务类型
|
||||
string payload_json = 2; // JSON 负载
|
||||
string callback_url = 3; // 回调地址(可选)
|
||||
int32 ttl_seconds = 4; // 任务 TTL(可选)
|
||||
string idempotency_key = 5; // 幂等键(可选)
|
||||
}
|
||||
|
||||
// SubmitTask 响应
|
||||
message SubmitTaskResponse {
|
||||
string task_id = 1; // 任务 ID
|
||||
string status = 2; // 初始状态
|
||||
string message = 3; // 附加信息
|
||||
}
|
||||
|
||||
// 任务状态查询响应(通用)
|
||||
message TaskStatusResponse {
|
||||
string task_id = 1;
|
||||
string status = 2;
|
||||
int32 progress = 3; // 0-100
|
||||
string result_json = 4; // 执行结果(JSON)
|
||||
string error_message = 5;
|
||||
string created_at = 6;
|
||||
string updated_at = 7;
|
||||
string etag = 8; // 用于增量查询
|
||||
}
|
||||
|
||||
// 任务流式更新
|
||||
message TaskStatusUpdate {
|
||||
string task_id = 1;
|
||||
string event_type = 2; // status_update / progress / log / error / complete
|
||||
string data_json = 3;
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 消息相关消息
|
||||
|
||||
```protobuf
|
||||
// SendMessage 请求
|
||||
message SendMessageRequest {
|
||||
string receiver_id = 1;
|
||||
string content_type = 2; // text / json / code / file
|
||||
string payload_json = 3;
|
||||
string correlation_id = 4; // 关联 ID(可选)
|
||||
string conversation_id = 5; // 会话 ID(可选)
|
||||
}
|
||||
|
||||
// SendMessage 响应
|
||||
message SendMessageResponse {
|
||||
string message_id = 1;
|
||||
string status = 2;
|
||||
}
|
||||
|
||||
// 统一消息信封(用于流式传输)
|
||||
message SynapseMessage {
|
||||
string id = 1;
|
||||
string sender_id = 2;
|
||||
string receiver_id = 3;
|
||||
string content_type = 4;
|
||||
string payload_json = 5;
|
||||
string correlation_id = 6;
|
||||
string conversation_id = 7;
|
||||
int64 timestamp = 8;
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 能力相关消息
|
||||
|
||||
```protobuf
|
||||
message CapabilitiesResponse {
|
||||
string agent_id = 1;
|
||||
string agent_name = 2;
|
||||
string protocol_version = 3;
|
||||
repeated string supported_task_types = 4;
|
||||
repeated string supported_content_types = 5;
|
||||
int64 max_file_size = 6;
|
||||
int32 max_concurrent_tasks = 7;
|
||||
bool support_streaming = 8;
|
||||
bool support_hil = 9;
|
||||
bool support_groups = 10;
|
||||
}
|
||||
|
||||
message QueryAgentRequest {
|
||||
string agent_id = 1;
|
||||
}
|
||||
|
||||
message ListAgentsResponse {
|
||||
repeated AgentInfo agents = 1;
|
||||
int32 total = 2;
|
||||
}
|
||||
|
||||
message AgentInfo {
|
||||
string agent_id = 1;
|
||||
string agent_name = 2;
|
||||
string endpoint = 3;
|
||||
repeated string capabilities = 4;
|
||||
string status = 5; // online / offline / busy
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 群组相关消息
|
||||
|
||||
```protobuf
|
||||
message CreateGroupRequest {
|
||||
string name = 1;
|
||||
string description = 2;
|
||||
repeated string initial_members = 3;
|
||||
}
|
||||
|
||||
message CreateGroupResponse {
|
||||
string group_id = 1;
|
||||
string status = 2;
|
||||
}
|
||||
|
||||
message JoinGroupRequest {
|
||||
string group_id = 1;
|
||||
string agent_id = 2;
|
||||
}
|
||||
|
||||
message LeaveGroupRequest {
|
||||
string group_id = 1;
|
||||
string agent_id = 2;
|
||||
}
|
||||
|
||||
message DisbandGroupRequest {
|
||||
string group_id = 1;
|
||||
string agent_id = 2; // 需群主或管理员权限
|
||||
}
|
||||
|
||||
message ListGroupMembersRequest {
|
||||
string group_id = 1;
|
||||
}
|
||||
|
||||
message ListGroupMembersResponse {
|
||||
string group_id = 1;
|
||||
repeated GroupMember members = 2;
|
||||
}
|
||||
|
||||
message GroupMember {
|
||||
string agent_id = 1;
|
||||
string role = 2; // owner / admin / member
|
||||
string joined_at = 3;
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 健康检查消息
|
||||
|
||||
```protobuf
|
||||
message HealthCheckResponse {
|
||||
string status = 1; // SERVING / NOT_SERVING
|
||||
string version = 2;
|
||||
int64 uptime_seconds = 3;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 传输配置与使用指引
|
||||
|
||||
### 5.1 gRPC 客户端连接配置
|
||||
|
||||
```go
|
||||
// 推荐 gRPC 客户端配置
|
||||
conn, err := grpc.Dial(
|
||||
target,
|
||||
grpc.WithInsecure(), // 开发环境;生产环境使用 TLS
|
||||
grpc.WithDefaultCallOptions(
|
||||
grpc.MaxCallRecvMsgSize(10*1024*1024), // 最大接收消息 10MB
|
||||
grpc.MaxCallSendMsgSize(10*1024*1024), // 最大发送消息 10MB
|
||||
),
|
||||
grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
||||
Time: 30 * time.Second, // 每 30s 发送心跳
|
||||
Timeout: 10 * time.Second, // 心跳超时 10s
|
||||
PermitWithoutStream: true, // 无活跃流时也发送心跳
|
||||
}),
|
||||
)
|
||||
```
|
||||
|
||||
### 5.2 HTTP REST 客户端调用示例
|
||||
|
||||
```bash
|
||||
# SubmitTask
|
||||
curl -X POST http://localhost:8080/api/v1/synapse/task \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"task_type":"text_generation","payload_json":"...","callback_url":"..."}'
|
||||
|
||||
# GetTask
|
||||
curl http://localhost:8080/api/v1/synapse/task/task-abc123
|
||||
|
||||
# 流式监听任务状态(SSE)
|
||||
curl -N http://localhost:8080/api/v1/synapse/task/task-abc123/stream
|
||||
|
||||
# HealthCheck
|
||||
curl http://localhost:8080/api/v1/synapse/health
|
||||
```
|
||||
|
||||
### 5.3 幂等性支持
|
||||
|
||||
对于 `SubmitTask` 和 `CreateGroup` 等写操作,建议通过 `idempotency_key` 字段实现幂等:
|
||||
|
||||
- 客户端为每个写操作生成唯一幂等键(如 UUID v4)
|
||||
- 服务端缓存幂等键及其响应,相同键的重复请求返回缓存结果
|
||||
- 幂等键缓存 TTL 默认为 24 小时
|
||||
|
||||
---
|
||||
|
||||
## 附录:gRPC Gateway 集成方式
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ 外部客户端 │
|
||||
│ gRPC Client HTTP Client (REST) │
|
||||
└────────┬───────────────────────────────┬────────────┘
|
||||
│ gRPC (h2) │ HTTP/1.1
|
||||
▼ ▼
|
||||
┌─────────────────┐ ┌──────────────────────┐
|
||||
│ gRPC Server │ │ gRPC Gateway │
|
||||
│ (直接服务) │◄────────►│ (反向代理 / 协议转换) │
|
||||
└────────┬────────┘ └──────────────────────┘
|
||||
│ 内部 gRPC 调用
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Synapse 业务逻辑层 │
|
||||
│ TaskService / MessageService / Capabilities ... │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
推荐集成方式:
|
||||
|
||||
1. 在 Go 服务端使用 `grpc-gateway` 库,通过一行代码注册反向代理
|
||||
2. 启动时同时监听两个端口(例:gRPC 端口 `9090`,HTTP 端口 `8080`)
|
||||
3. HTTP 端口同时提供 REST API 和 Swagger UI(开发环境)
|
||||
|
||||
---
|
||||
|
||||
**文档结束**
|
||||
1010
docs/v1/hil-mechanism.md
Normal file
1010
docs/v1/hil-mechanism.md
Normal file
File diff suppressed because it is too large
Load Diff
201
docs/v1/message-extensions.md
Normal file
201
docs/v1/message-extensions.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# 灵隙协议实现规范:消息扩展字段
|
||||
|
||||
> 版本:1.0.0 | 层级:实现层
|
||||
|
||||
---
|
||||
|
||||
## 1. 消息扩展字段总览
|
||||
|
||||
统一消息信封 `UnifiedSynapseMessage` 在原始协议基础字段之上,扩充了以下扩展字段,用于支持会话关联、传输控制、群组精准投递及安全声明等高级功能。
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `ConversationID` | `string` | 会话 ID,用于多轮对话关联 |
|
||||
| `TTL` | `int` | 消息存活时间,单位秒;超时后由灵泊(Harbor)自动清理 |
|
||||
| `Broadcast` | `bool` | 是否广播;为 `true` 时消息将发送至群组所有成员 |
|
||||
| `Encrypt` | `bool` | 标识 payload 是否已加密;接收方据此决定是否解密 |
|
||||
| `TargetType` | `GroupTargetType` | 群组目标类型,定义消息在群组内的投递范围 |
|
||||
| `TargetIDs` | `[]string` | 目标 ID 列表,与 `TargetType` 配合使用 |
|
||||
| `SignatureAlgorithm` | `string` | 签名算法声明,接收方据此选择验证方式 |
|
||||
|
||||
### 代码定义
|
||||
|
||||
```go
|
||||
type UnifiedSynapseMessage struct {
|
||||
// 基础字段(协议定义)
|
||||
ID string `json:"id"`
|
||||
Version string `json:"version"`
|
||||
Type string `json:"type"`
|
||||
Sender AgentEndpoint `json:"sender"`
|
||||
Receiver *AgentEndpoint `json:"receiver,omitempty"`
|
||||
CorrelationID string `json:"correlation_id,omitempty"`
|
||||
ReplyTo string `json:"reply_to,omitempty"`
|
||||
GroupID string `json:"group_id,omitempty"`
|
||||
Payload json.RawMessage `json:"payload"`
|
||||
Timestamp Timestamp `json:"timestamp"`
|
||||
Signature string `json:"signature,omitempty"`
|
||||
|
||||
// 扩展字段(实现补充)
|
||||
ConversationID string `json:"conversation_id,omitempty"`
|
||||
TTL int `json:"ttl,omitempty"`
|
||||
Broadcast bool `json:"broadcast,omitempty"`
|
||||
Encrypt bool `json:"encrypt,omitempty"`
|
||||
TargetType GroupTargetType `json:"target_type,omitempty"`
|
||||
TargetIDs []string `json:"target_ids,omitempty"`
|
||||
SignatureAlgorithm string `json:"signature_algorithm,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 群组目标类型
|
||||
|
||||
`GroupTargetType` 枚举定义了消息在群组内的三种目标选择策略,用于精确控制消息的投递范围,避免向无关成员发送无关消息。
|
||||
|
||||
```go
|
||||
type GroupTargetType string
|
||||
|
||||
const (
|
||||
TargetTypeAll GroupTargetType = "all" // 所有成员
|
||||
TargetTypeRole GroupTargetType = "role" // 按角色
|
||||
TargetTypeSpecific GroupTargetType = "specific" // 指定成员
|
||||
)
|
||||
```
|
||||
|
||||
### 字段说明表
|
||||
|
||||
| 值 | 常量名 | 含义 | TargetIDs 用法 |
|
||||
|----|--------|------|----------------|
|
||||
| `"all"` | `TargetTypeAll` | 发送给群组内所有成员 | 忽略 `TargetIDs`,即使有值也不参与路由 |
|
||||
| `"role"` | `TargetTypeRole` | 按角色筛选成员 | `TargetIDs` 填入角色名称列表(如 `["admin","operator"]`),仅匹配这些角色的成员会收到消息 |
|
||||
| `"specific"` | `TargetTypeSpecific` | 发送给指定的若干成员 | `TargetIDs` 填入成员 AgentID 列表,精确投递 |
|
||||
|
||||
### 使用约束
|
||||
|
||||
- 仅在 `GroupID` 非空时生效。若 `GroupID` 为空,`TargetType` 和 `TargetIDs` 将被忽略。
|
||||
- 默认值为空字符串,此时行为等同于 `"all"`(全量投递)。
|
||||
- `TargetType` 与 `Broadcast` 的关系:当 `Broadcast` 为 `true` 时,`TargetType` 仍可进一步缩小广播范围;两者协同工作而非互斥。
|
||||
|
||||
---
|
||||
|
||||
## 3. 各字段用途说明表
|
||||
|
||||
| 字段名 | 类型 | 用途 | 使用场景 | 示例值 |
|
||||
|--------|------|------|----------|--------|
|
||||
| `ConversationID` | `string` | 关联同一会话的多条消息,形成对话上下文 | 多轮问答、连续任务编排、长对话跟踪 | `"conv-a1b2c3d4"` |
|
||||
| `TTL` | `int` | 消息的存活时长,超时后由灵泊组件自动丢弃 | 临时通知、时效性指令、过期任务取消 | `300`(5 分钟) |
|
||||
| `Broadcast` | `bool` | 标记消息是否为广播,指示接收方是否需要扩散 | 群组公告、系统通知、状态广播 | `true` |
|
||||
| `Encrypt` | `bool` | 标记 payload 是否已加密,接收方据此触发解密流程 | 包含敏感数据的消息、跨信任域通信 | `true` |
|
||||
| `TargetType` | `GroupTargetType` | 群组内的目标选择策略 | 按角色下发命令、定向通知指定成员 | `"role"` |
|
||||
| `TargetIDs` | `[]string` | 配合 `TargetType` 提供目标标识列表 | 指定角色名列表或成员 ID 列表 | `["admin","operator"]` |
|
||||
| `SignatureAlgorithm` | `string` | 声明消息签名所使用的算法,接收方选择对应验证逻辑 | 多算法共存场景、算法升级过渡期 | `"HMAC-SHA256"` |
|
||||
|
||||
---
|
||||
|
||||
## 4. 扩展字段与基础字段关系图
|
||||
|
||||
下图展示了包含扩展字段在内的完整 `UnifiedSynapseMessage` 结构层次。
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ UnifiedSynapseMessage │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────┐│
|
||||
│ │ 基础字段 ││
|
||||
│ │ ││
|
||||
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ ││
|
||||
│ │ │ ID │ │ Version │ │ Type │ ││
|
||||
│ │ └──────────┘ └──────────┘ └──────────────────┘ ││
|
||||
│ │ ││
|
||||
│ │ ┌────────────────────────────────────────────────┐ ││
|
||||
│ │ │ Sender (AgentEndpoint) │ ││
|
||||
│ │ │ ┌─────────┐ ┌──────────┐ ┌────────────────┐ │ ││
|
||||
│ │ │ │AgentID │ │AgentName │ │ Endpoint │ │ ││
|
||||
│ │ │ └─────────┘ └──────────┘ └────────────────┘ │ ││
|
||||
│ │ │ ┌─────────┐ ┌──────────┐ ┌────────────────┐ │ ││
|
||||
│ │ │ │SystemID │ │Version │ │ Capabilities │ │ ││
|
||||
│ │ │ └─────────┘ └──────────┘ └────────────────┘ │ ││
|
||||
│ │ └────────────────────────────────────────────────┘ ││
|
||||
│ │ ││
|
||||
│ │ ┌──────────┐ ┌──────────────┐ ┌──────────────┐ ││
|
||||
│ │ │ Receiver │ │CorrelationID │ │ ReplyTo │ ││
|
||||
│ │ │(optional)│ │ │ │ │ ││
|
||||
│ │ └──────────┘ └──────────────┘ └──────────────┘ ││
|
||||
│ │ ││
|
||||
│ │ ┌──────────┐ ┌──────────────────────────────────┐ ││
|
||||
│ │ │ GroupID │ │ Payload │ ││
|
||||
│ │ │ │ │ (json.RawMessage) │ ││
|
||||
│ │ └──────────┘ └──────────────────────────────────┘ ││
|
||||
│ │ ││
|
||||
│ │ ┌──────────┐ ┌──────────┐ ││
|
||||
│ │ │Timestamp │ │Signature │ ││
|
||||
│ │ └──────────┘ └──────────┘ ││
|
||||
│ └─────────────────────────────────────────────────────┘│
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────┐│
|
||||
│ │ 扩展字段 ││
|
||||
│ │ ││
|
||||
│ │ ┌────────────────┐ ┌──────┐ ┌──────────┐ ││
|
||||
│ │ │ ConversationID │ │ TTL │ │ Broadcast│ ││
|
||||
│ │ └────────────────┘ └──────┘ └──────────┘ ││
|
||||
│ │ ││
|
||||
│ │ ┌──────────┐ ┌──────────────────────────────────┐ ││
|
||||
│ │ │ Encrypt │ │ TargetType + TargetIDs │ ││
|
||||
│ │ │ │ │ (群组目标选择) │ ││
|
||||
│ │ └──────────┘ └──────────────────────────────────┘ ││
|
||||
│ │ ││
|
||||
│ │ ┌────────────────────────────────────────────────┐ ││
|
||||
│ │ │ SignatureAlgorithm │ ││
|
||||
│ │ │ (签名算法声明) │ ││
|
||||
│ │ └────────────────────────────────────────────────┘ ││
|
||||
│ └─────────────────────────────────────────────────────┘│
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 层次说明
|
||||
|
||||
| 层级 | 包含字段 | 职责 |
|
||||
|------|----------|------|
|
||||
| **标识层** | `ID`, `Version`, `Type` | 消息唯一标识与协议版本声明 |
|
||||
| **源端层** | `Sender` (AgentEndpoint) | 发送方端点信息,含身份、能力与路由地址 |
|
||||
| **目标层** | `Receiver`, `GroupID`, `TargetType`, `TargetIDs` | 点对点或群组目标寻址 |
|
||||
| **控制层** | `CorrelationID`, `ReplyTo`, `ConversationID` | 消息关联、回复与会话跟踪 |
|
||||
| **数据层** | `Payload` | 业务负载,JSON 格式 |
|
||||
| **传输层** | `TTL`, `Broadcast`, `Priority`, `ExpiresAt` | 传输行为控制与生命周期管理 |
|
||||
| **安全层** | `Signature`, `SignatureAlgorithm`, `Encrypt` | 完整性保护、算法声明与负载加密 |
|
||||
| **时间层** | `Timestamp` | 消息产生时间,ISO 8601 格式 |
|
||||
|
||||
### 扩展字段与基础字段的协作关系
|
||||
|
||||
```
|
||||
发送流程 接收流程
|
||||
───────── ─────────
|
||||
|
||||
发方构造消息 收方解析消息
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ 填写基础字段 │ │ 校验基础字段 │
|
||||
│ ID, Sender, │─────────▶ │ ID, Type, │
|
||||
│ Type, Payload│ │ Signature │
|
||||
└──────────────┘ └──────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ 设置扩展字段 │ │ 读取扩展字段 │
|
||||
│ │ │ │
|
||||
│ • ConversationID ──────▶ │ • 恢复会话 │
|
||||
│ • TTL ──────▶ │ • 设置超时 │
|
||||
│ • Broadcast ──────▶ │ • 判断扩散 │
|
||||
│ • Encrypt ──────▶ │ • 触发解密 │
|
||||
│ • TargetType ──────▶ │ • 筛选成员 │
|
||||
│ • TargetIDs ──────▶ │ • 精确投递 │
|
||||
│ • SigAlgo ──────▶ │ • 选择验证 │
|
||||
└──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**参考来源**
|
||||
- [v0 实现细节 - 第 7 章:消息扩展字段](file:///e:/code/go/synapse/docs/v0/06-implementation-details.md#1088)
|
||||
- [UnifiedSynapseMessage 类型定义](file:///e:/code/go/synapse/pkg/types/types.go#L197-L236)
|
||||
579
docs/v1/transport-modes.md
Normal file
579
docs/v1/transport-modes.md
Normal file
@@ -0,0 +1,579 @@
|
||||
# 灵隙协议实现规范:传输模式定义
|
||||
|
||||
> 版本:1.0.0 | 层级:实现层
|
||||
|
||||
---
|
||||
|
||||
## 1. 传输模式枚举
|
||||
|
||||
传输模式(TransportMode)定义了任务通信的传输方式。Synapse 协议支持四种传输模式,适配不同的网络环境和业务场景。
|
||||
|
||||
### 1.1 模式定义
|
||||
|
||||
```go
|
||||
// TransportMode 定义任务通信的传输模式
|
||||
type TransportMode string
|
||||
|
||||
const (
|
||||
// TransportModeCallback - 异步回调模式
|
||||
// 任务完成后通过 Webhook 回调通知结果
|
||||
TransportModeCallback TransportMode = "callback"
|
||||
|
||||
// TransportModePolling - 异步轮询模式
|
||||
// 调用方主动轮询获取任务状态和结果
|
||||
TransportModePolling TransportMode = "polling"
|
||||
|
||||
// TransportModeSync - 同步模式
|
||||
// 调用方阻塞等待任务完成,直接返回结果
|
||||
TransportModeSync TransportMode = "sync"
|
||||
|
||||
// TransportModeHybrid - 混合模式
|
||||
// 优先使用回调,回调失败时启用轮询作为兜底
|
||||
TransportModeHybrid TransportMode = "hybrid"
|
||||
)
|
||||
```
|
||||
|
||||
### 1.2 模式对比
|
||||
|
||||
| 模式 | 枚举值 | 通信方向 | 实时性 | 适用场景 |
|
||||
|------|--------|----------|--------|----------|
|
||||
| 回调(Callback) | `callback` | 服务端 → 调用方 | 高 | 网络可达、期望实时通知 |
|
||||
| 轮询(Polling) | `polling` | 调用方 → 服务端 | 中 | 调用方主动拉取、服务端不主动推送 |
|
||||
| 同步(Sync) | `sync` | 双向阻塞 | 最高 | 短耗时任务、需要即时响应 |
|
||||
| 混合(Hybrid) | `hybrid` | 双向 + 兜底 | 高 | 生产环境、容错要求高的场景 |
|
||||
|
||||
### 1.3 模式选择建议
|
||||
|
||||
| 条件 | 推荐模式 |
|
||||
|------|----------|
|
||||
| 任务耗时 < 5s | `sync` — 直接同步等待 |
|
||||
| 任务耗时 5s~5min,回调端点可达 | `callback` — 异步回调 |
|
||||
| 任务耗时 5s~5min,回调端点不可达 | `polling` — 轮询拉取 |
|
||||
| 生产环境,需要高可靠性 | `hybrid` — 回调 + 轮询兜底 |
|
||||
| 任务耗时 > 5min | `polling` 或 `hybrid` — 长时间异步 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 传输偏好配置
|
||||
|
||||
### 2.1 传输偏好结构
|
||||
|
||||
```go
|
||||
// TransportPreferences 定义任务的传输偏好配置
|
||||
type TransportPreferences struct {
|
||||
// Preferred - 首选传输模式
|
||||
Preferred TransportMode `json:"preferred"`
|
||||
|
||||
// Fallback - 兜底传输模式,当首选模式不可用时使用
|
||||
Fallback TransportMode `json:"fallback,omitempty"`
|
||||
|
||||
// CallbackEndpoint - 回调端点 URL
|
||||
// 仅当 Preferred 或 Fallback 为 callback/hybrid 时生效
|
||||
CallbackEndpoint string `json:"callback_endpoint,omitempty"`
|
||||
|
||||
// PollingInterval - 轮询间隔(秒)
|
||||
// 仅当 Preferred 或 Fallback 为 polling/hybrid 时生效
|
||||
PollingInterval int `json:"polling_interval,omitempty"`
|
||||
|
||||
// ProgressUpdates - 是否接收进度更新
|
||||
// 为 true 时,服务端会在任务执行过程中推送进度事件
|
||||
ProgressUpdates bool `json:"progress_updates"`
|
||||
|
||||
// ProgressInterval - 进度更新最小间隔(秒)
|
||||
// 控制进度通知的频率,避免频繁推送
|
||||
ProgressInterval int `json:"progress_interval,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 字段说明
|
||||
|
||||
| 字段 | 类型 | 必需 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| `preferred` | `TransportMode` | 是 | `callback` | 首选传输模式 |
|
||||
| `fallback` | `TransportMode` | 否 | `polling` | 兜底模式,首选不可用时回退 |
|
||||
| `callback_endpoint` | `string` | 条件必填 | `""` | 回调 URL,callback/hybrid 模式必需 |
|
||||
| `polling_interval` | `int` | 否 | `5` | 轮询间隔(秒),最小 1 |
|
||||
| `progress_updates` | `bool` | 否 | `true` | 是否启用进度推送 |
|
||||
| `progress_interval` | `int` | 否 | `10` | 进度更新间隔(秒) |
|
||||
|
||||
### 2.3 默认配置
|
||||
|
||||
```go
|
||||
// DefaultTransportPreferences 返回默认传输偏好配置
|
||||
//
|
||||
// 默认值说明:
|
||||
// - 首选回调模式(callback),兜底轮询(polling)
|
||||
// - 轮询间隔 5 秒
|
||||
// - 启用进度更新,最小间隔 10 秒
|
||||
func DefaultTransportPreferences() *TransportPreferences {
|
||||
return &TransportPreferences{
|
||||
Preferred: TransportModeCallback,
|
||||
Fallback: TransportModePolling,
|
||||
PollingInterval: 5,
|
||||
ProgressUpdates: true,
|
||||
ProgressInterval: 10,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 配置示例
|
||||
|
||||
**纯回调模式:**
|
||||
|
||||
```go
|
||||
prefs := &TransportPreferences{
|
||||
Preferred: TransportModeCallback,
|
||||
CallbackEndpoint: "https://my-service.com/callback",
|
||||
ProgressUpdates: true,
|
||||
ProgressInterval: 5,
|
||||
}
|
||||
```
|
||||
|
||||
**纯轮询模式:**
|
||||
|
||||
```go
|
||||
prefs := &TransportPreferences{
|
||||
Preferred: TransportModePolling,
|
||||
PollingInterval: 3,
|
||||
ProgressUpdates: false,
|
||||
}
|
||||
```
|
||||
|
||||
**混合模式(生产推荐):**
|
||||
|
||||
```go
|
||||
prefs := &TransportPreferences{
|
||||
Preferred: TransportModeCallback,
|
||||
Fallback: TransportModePolling,
|
||||
CallbackEndpoint: "https://my-service.com/callback",
|
||||
PollingInterval: 5,
|
||||
ProgressUpdates: true,
|
||||
ProgressInterval: 10,
|
||||
}
|
||||
```
|
||||
|
||||
**同步模式:**
|
||||
|
||||
```go
|
||||
prefs := &TransportPreferences{
|
||||
Preferred: TransportModeSync,
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 轮询 API 类型
|
||||
|
||||
轮询 API 为调用方提供主动拉取任务状态的能力。所有 Agent 端必须实现以下类型以支持轮询传输模式。
|
||||
|
||||
### 3.1 任务状态响应
|
||||
|
||||
```go
|
||||
// TaskStatusResponse 任务状态轮询响应
|
||||
//
|
||||
// 调用方通过此结构获取单个任务的当前状态。
|
||||
// 成功时 Success 为 true,Task 字段包含任务详情;
|
||||
// 失败时 Success 为 false,Error 字段包含错误信息。
|
||||
type TaskStatusResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Task *TaskInfo `json:"task,omitempty"`
|
||||
Error *SynapseError `json:"error,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 任务信息
|
||||
|
||||
```go
|
||||
// TaskInfo 任务详细信息
|
||||
//
|
||||
// 描述一个异步任务的完整状态,包括进度、结果和
|
||||
// 错误信息。ETag 字段用于增量查询,避免重复传输
|
||||
// 未变更的数据。
|
||||
type TaskInfo struct {
|
||||
// ID - 任务唯一标识
|
||||
ID string `json:"id"`
|
||||
|
||||
// Type - 任务类型,如 "text_generation", "image_analysis"
|
||||
Type string `json:"type"`
|
||||
|
||||
// Status - 任务状态
|
||||
// 可选值: pending, running, completed, failed, cancelled
|
||||
Status string `json:"status"`
|
||||
|
||||
// Progress - 任务进度百分比(0-100)
|
||||
Progress int `json:"progress"`
|
||||
|
||||
// Message - 当前状态的人类可读描述
|
||||
Message string `json:"message,omitempty"`
|
||||
|
||||
// CreatedAt - 任务创建时间
|
||||
CreatedAt Timestamp `json:"created_at"`
|
||||
|
||||
// UpdatedAt - 任务最后更新时间
|
||||
UpdatedAt Timestamp `json:"updated_at"`
|
||||
|
||||
// ETA - 预计完成时间(可选)
|
||||
ETA *Timestamp `json:"eta,omitempty"`
|
||||
|
||||
// Result - 任务执行结果
|
||||
// 当 Status 为 completed 时此字段有效
|
||||
Result any `json:"result,omitempty"`
|
||||
|
||||
// Error - 任务执行错误
|
||||
// 当 Status 为 failed 时此字段有效
|
||||
Error *SynapseError `json:"error,omitempty"`
|
||||
|
||||
// ETag - 实体标签,用于增量查询和缓存
|
||||
// 客户端可在后续请求中携带此值以获取变更数据
|
||||
ETag string `json:"etag,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 任务状态枚举
|
||||
|
||||
| 状态值 | 说明 | 可转换至 |
|
||||
|--------|------|----------|
|
||||
| `pending` | 任务已提交,等待执行 | `running` |
|
||||
| `running` | 任务正在执行中 | `completed`, `failed`, `cancelled` |
|
||||
| `completed` | 任务执行成功,Result 可用 | —(终态) |
|
||||
| `failed` | 任务执行失败,Error 可用 | —(终态) |
|
||||
| `cancelled` | 任务已被取消 | —(终态) |
|
||||
|
||||
### 3.4 任务列表响应
|
||||
|
||||
```go
|
||||
// TaskListResponse 任务列表响应
|
||||
//
|
||||
// 支持分页查询,调用方可使用 Limit 和 Offset
|
||||
// 参数控制返回结果的数量和偏移量。
|
||||
type TaskListResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Tasks []TaskInfo `json:"tasks"`
|
||||
Total int `json:"total"`
|
||||
Limit int `json:"limit"`
|
||||
Offset int `json:"offset"`
|
||||
HasMore bool `json:"has_more"`
|
||||
}
|
||||
```
|
||||
|
||||
**分页参数说明:**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `total` | `int` | 符合条件的任务总数 |
|
||||
| `limit` | `int` | 本次请求的限制数量 |
|
||||
| `offset` | `int` | 本次请求的偏移量 |
|
||||
| `has_more` | `bool` | 是否还有更多数据 |
|
||||
|
||||
### 3.5 任务事件
|
||||
|
||||
```go
|
||||
// TaskEvent 任务事件
|
||||
//
|
||||
// 表示任务执行过程中产生的事件,用于事件流推送
|
||||
// 和增量状态更新。
|
||||
type TaskEvent struct {
|
||||
// ID - 事件唯一标识
|
||||
ID string `json:"id"`
|
||||
|
||||
// Type - 事件类型
|
||||
// 可选值: status_update, progress, log, error, complete
|
||||
Type string `json:"type"`
|
||||
|
||||
// Timestamp - 事件发生时间
|
||||
Timestamp Timestamp `json:"timestamp"`
|
||||
|
||||
// Data - 事件数据
|
||||
// 不同类型的事件携带不同的数据结构
|
||||
Data any `json:"data"`
|
||||
}
|
||||
```
|
||||
|
||||
### 3.6 事件类型说明
|
||||
|
||||
| 事件类型 | 触发时机 | Data 内容 |
|
||||
|----------|----------|-----------|
|
||||
| `status_update` | 任务状态变更 | 包含新状态和状态变更原因 |
|
||||
| `progress` | 进度更新 | 包含当前进度值(0-100)和描述 |
|
||||
| `log` | 任务产生日志输出 | 日志文本内容 |
|
||||
| `error` | 任务执行出错 | 错误代码和错误消息 |
|
||||
| `complete` | 任务完成 | 任务执行结果 |
|
||||
|
||||
### 3.7 任务事件流响应
|
||||
|
||||
```go
|
||||
// TaskEventsResponse 任务事件流响应
|
||||
//
|
||||
// 支持基于游标的分页,调用方可使用 Next 游标
|
||||
// 持续拉取后续事件。
|
||||
type TaskEventsResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Since string `json:"since"`
|
||||
Events []TaskEvent `json:"events"`
|
||||
HasMore bool `json:"has_more"`
|
||||
Next string `json:"next_cursor,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
**游标使用流程:**
|
||||
|
||||
1. 首次请求不带 `since` 参数,获取当前所有事件
|
||||
2. 响应中返回 `next_cursor` 作为下一页游标
|
||||
3. 后续请求携带 `since=next_cursor` 获取后续事件
|
||||
4. 当 `has_more` 为 `false` 时,表示已无新事件
|
||||
|
||||
### 3.8 轮询 API 端点约定
|
||||
|
||||
| 端点 | 方法 | 请求参数 | 响应类型 | 说明 |
|
||||
|------|------|----------|----------|------|
|
||||
| `/v1/tasks/{taskID}` | GET | — | `TaskStatusResponse` | 查询单个任务状态 |
|
||||
| `/v1/tasks` | GET | `limit, offset, status` | `TaskListResponse` | 查询任务列表 |
|
||||
| `/v1/tasks/{taskID}/events` | GET | `since` | `TaskEventsResponse` | 查询任务事件流 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 流式传输类型
|
||||
|
||||
流式传输用于实时传输大文件或持续生成的数据,如音频流、视频流、长文本生成等场景。
|
||||
|
||||
### 4.1 流数据块
|
||||
|
||||
```go
|
||||
// StreamChunk 流式传输数据块
|
||||
//
|
||||
// 每次传输一个数据块,Sequence 字段保证数据块
|
||||
// 的顺序。当 Final 为 true 时,表示流传输结束,
|
||||
// 客户端可开始组装完整数据。
|
||||
type StreamChunk struct {
|
||||
// TaskID - 任务 ID,标识所属任务
|
||||
TaskID string `json:"task_id"`
|
||||
|
||||
// Sequence - 块序列号,从 1 开始递增
|
||||
// 用于客户端排序和校验丢包
|
||||
Sequence int64 `json:"sequence"`
|
||||
|
||||
// ChunkType - 数据块类型
|
||||
// 可选值: text, binary, audio, video
|
||||
ChunkType string `json:"chunk_type"`
|
||||
|
||||
// Data - 数据块内容
|
||||
// 二进制数据使用 base64 编码传输
|
||||
Data string `json:"data"`
|
||||
|
||||
// Final - 是否为最后一个数据块
|
||||
// 为 true 时表示流传输结束
|
||||
Final bool `json:"final"`
|
||||
|
||||
// Timestamp - 数据块时间戳
|
||||
Timestamp Timestamp `json:"timestamp"`
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 数据块类型说明
|
||||
|
||||
| ChunkType | 说明 | Data 编码 | 适用场景 |
|
||||
|-----------|------|-----------|----------|
|
||||
| `text` | 文本数据 | UTF-8 字符串 | 流式文本生成、日志输出 |
|
||||
| `binary` | 二进制数据 | Base64 | 文件传输、二进制协议数据 |
|
||||
| `audio` | 音频数据 | Base64 | 实时语音、音频流处理 |
|
||||
| `video` | 视频数据 | Base64 | 实时视频、视频流处理 |
|
||||
|
||||
### 4.3 流完成通知
|
||||
|
||||
```go
|
||||
// StreamComplete 流传输完成通知
|
||||
//
|
||||
// 当所有数据块传输完毕后发送,包含完整数据的
|
||||
// 校验和,供客户端验证数据完整性。
|
||||
type StreamComplete struct {
|
||||
// TaskID - 任务 ID
|
||||
TaskID string `json:"task_id"`
|
||||
|
||||
// TotalChunks - 总数据块数
|
||||
// 客户端可据此验证是否收到所有数据块
|
||||
TotalChunks int64 `json:"total_chunks"`
|
||||
|
||||
// FullContentChecksum - 完整数据校验和
|
||||
// 格式: "sha256:<hex>",用于完整性校验
|
||||
FullContentChecksum string `json:"full_content_checksum"`
|
||||
|
||||
// Result - 任务执行结果(可选)
|
||||
// 流传输完成后携带的最终结果
|
||||
Result any `json:"result,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 流式传输生命周期
|
||||
|
||||
```
|
||||
调用方 服务端
|
||||
| |
|
||||
|-- POST /v1/tasks/submit --->|
|
||||
| (TransportMode: sync) | 启动异步任务
|
||||
| |
|
||||
|<--- StreamChunk{seq:1} -----| 数据块 1
|
||||
|<--- StreamChunk{seq:2} -----| 数据块 2
|
||||
|<--- StreamChunk{seq:3} -----| 数据块 3
|
||||
|<--- StreamChunk{seq:4} -----| 数据块 4(Final=true)
|
||||
|<--- StreamComplete ---------| 完成通知
|
||||
| |
|
||||
|
||||
客户端收到 StreamComplete 后:
|
||||
1. 确认 TotalChunks == 实际收到的块数
|
||||
2. 计算完整数据的 SHA256 校验和
|
||||
3. 对比 FullContentChecksum 验证完整性
|
||||
4. 读取 Result 获取任务执行结果
|
||||
```
|
||||
|
||||
### 4.5 校验和验证
|
||||
|
||||
```go
|
||||
// ValidateStreamChecksum 验证流式传输数据的完整性
|
||||
//
|
||||
// 客户端在收到 StreamComplete 后应调用此函数
|
||||
// 验证收到的数据是否完整。
|
||||
func ValidateStreamChecksum(receivedChecksum, computedChecksum string) bool {
|
||||
return receivedChecksum == computedChecksum
|
||||
}
|
||||
```
|
||||
|
||||
### 4.6 传输模式与流式传输的组合规则
|
||||
|
||||
| 传输模式 | 是否支持流式 | 说明 |
|
||||
|----------|-------------|------|
|
||||
| `callback` | 否 | 回调模式仅通知结果,不承载流数据 |
|
||||
| `polling` | 否 | 轮询模式通过分块获取,非实时流 |
|
||||
| `sync` | 是 | 同步模式下可实时流式返回数据 |
|
||||
| `hybrid` | 是(回调阶段) | 混合模式中回调路径支持流式 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 传输模式交互流程
|
||||
|
||||
### 5.1 回调模式(Callback)
|
||||
|
||||
```
|
||||
调用方 Agent
|
||||
| |
|
||||
|--- SubmitTask ------------>| 提交任务
|
||||
| (callback_endpoint: URL) |
|
||||
| | Agent 处理任务
|
||||
| |---> HTTP POST --->| 调用方回调端点
|
||||
| (异步回调) |<--- 200 OK -------| 回调响应
|
||||
| |
|
||||
| [处理完成] |
|
||||
```
|
||||
|
||||
### 5.2 轮询模式(Polling)
|
||||
|
||||
```
|
||||
调用方 Agent
|
||||
| |
|
||||
|--- SubmitTask ------------>| 提交任务
|
||||
| |
|
||||
|--- GET /v1/tasks/{id} ---->| 轮询状态(间隔 5s)
|
||||
|<--- TaskInfo(status=running)|
|
||||
|--- GET /v1/tasks/{id} ---->|
|
||||
|<--- TaskInfo(status=running)|
|
||||
|--- GET /v1/tasks/{id} ---->|
|
||||
|<--- TaskInfo(status=completed, result=...)
|
||||
| |
|
||||
```
|
||||
|
||||
### 5.3 同步模式(Sync)
|
||||
|
||||
```
|
||||
调用方 Agent
|
||||
| |
|
||||
|--- SubmitTask + Sync ----->| 提交任务
|
||||
| | Agent 立即处理
|
||||
|<-- Result (同步返回) -------| 直接返回结果
|
||||
| |
|
||||
```
|
||||
|
||||
### 5.4 混合模式(Hybrid)
|
||||
|
||||
```
|
||||
调用方 Agent
|
||||
| |
|
||||
|--- SubmitTask ------------>| 提交任务
|
||||
| (preferred: callback, |
|
||||
| fallback: polling) |
|
||||
| | Agent 处理任务
|
||||
| |
|
||||
| [回调成功] |
|
||||
|<-- AsyncCallback -----------| 回调通知结果
|
||||
| |
|
||||
| [回调失败] |
|
||||
|--- GET /v1/tasks/{id} ---->| 轮询兜底
|
||||
|<-- TaskInfo(result=...) ----| 获取结果
|
||||
| |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 错误处理
|
||||
|
||||
### 6.1 SynapseError 结构
|
||||
|
||||
```go
|
||||
// SynapseError 协议错误信息
|
||||
type SynapseError struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 传输模式相关错误码
|
||||
|
||||
| 错误码 | 名称 | 说明 |
|
||||
|--------|------|------|
|
||||
| `4001` | `CALLBACK_FAILED` | 回调端点不可达或返回非 2xx |
|
||||
| `4002` | `POLLING_TIMEOUT` | 轮询超时,任务未在预期时间内完成 |
|
||||
| `4003` | `INVALID_CALLBACK_ENDPOINT` | 回调端点 URL 格式无效 |
|
||||
| `4004` | `MODE_NOT_SUPPORTED` | Agent 不支持请求的传输模式 |
|
||||
| `4005` | `STREAM_SEQUENCE_GAP` | 流式传输出现序列号跳变 |
|
||||
|
||||
---
|
||||
|
||||
## 附录:Go 类型汇总
|
||||
|
||||
```go
|
||||
// 传输模式枚举
|
||||
type TransportMode string
|
||||
const (
|
||||
TransportModeCallback TransportMode = "callback"
|
||||
TransportModePolling TransportMode = "polling"
|
||||
TransportModeSync TransportMode = "sync"
|
||||
TransportModeHybrid TransportMode = "hybrid"
|
||||
)
|
||||
|
||||
// 传输偏好配置
|
||||
type TransportPreferences struct {
|
||||
Preferred TransportMode `json:"preferred"`
|
||||
Fallback TransportMode `json:"fallback,omitempty"`
|
||||
CallbackEndpoint string `json:"callback_endpoint,omitempty"`
|
||||
PollingInterval int `json:"polling_interval,omitempty"`
|
||||
ProgressUpdates bool `json:"progress_updates"`
|
||||
ProgressInterval int `json:"progress_interval,omitempty"`
|
||||
}
|
||||
|
||||
func DefaultTransportPreferences() *TransportPreferences { ... }
|
||||
|
||||
// 轮询 API 类型
|
||||
type TaskStatusResponse struct { ... }
|
||||
type TaskInfo struct { ... }
|
||||
type TaskListResponse struct { ... }
|
||||
type TaskEvent struct { ... }
|
||||
type TaskEventsResponse struct { ... }
|
||||
|
||||
// 流式传输类型
|
||||
type StreamChunk struct { ... }
|
||||
type StreamComplete struct { ... }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> 本文档对应代码实现在 [types/content.go](file:///e:/code/go/synapse/types/content.go) 中。
|
||||
>
|
||||
> 参考源文档:[06-implementation-details.md §8](file:///e:/code/go/synapse/docs/v0/06-implementation-details.md#L1144)
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"synapse/types"
|
||||
"synapse/pkg/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"synapse/types"
|
||||
"synapse/pkg/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"synapse/types"
|
||||
"synapse/pkg/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"synapse/types"
|
||||
"synapse/pkg/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"synapse/types"
|
||||
"synapse/pkg/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"synapse/types"
|
||||
"synapse/pkg/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"synapse/types"
|
||||
"synapse/pkg/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"synapse/types"
|
||||
"synapse/pkg/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"synapse/types"
|
||||
"synapse/pkg/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"synapse/types"
|
||||
"synapse/pkg/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"synapse/types"
|
||||
"synapse/pkg/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"synapse/types"
|
||||
"synapse/pkg/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"synapse/types"
|
||||
"synapse/pkg/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"synapse/group"
|
||||
"synapse/hil"
|
||||
"synapse/p2p"
|
||||
"synapse/types"
|
||||
"synapse/internal/agent/group"
|
||||
"synapse/internal/agent/p2p"
|
||||
"synapse/internal/hil"
|
||||
"synapse/pkg/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"synapse/types"
|
||||
"synapse/pkg/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"synapse/types"
|
||||
"synapse/pkg/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
1250
pkg/sdk/client.go
Normal file
1250
pkg/sdk/client.go
Normal file
File diff suppressed because it is too large
Load Diff
21
pkg/sdk/doc.go
Normal file
21
pkg/sdk/doc.go
Normal file
@@ -0,0 +1,21 @@
|
||||
// Package sdk 灵隙(Synapse)协议 SDK
|
||||
//
|
||||
// 单包 SDK,通过 Protocol 类型区分三种协议客户端:
|
||||
//
|
||||
// - ProtoP2A(协议前缀 "P2A"):外部调用方,使用 REST API
|
||||
// - ProtoA2A(协议前缀 "A2A"):Agent 点对点通信,消息级交互
|
||||
// - ProtoA2N(协议前缀 "A2N"):Agent 群组协作,管理群组与任务
|
||||
//
|
||||
// 用法示例:
|
||||
//
|
||||
// p2a := sdk.NewClient(sdk.ProtoP2A, sdk.Config{BaseURL: "http://...", APIKey: "..."})
|
||||
// task, err := p2a.SubmitTask(ctx, req)
|
||||
//
|
||||
// a2a := sdk.NewClient(sdk.ProtoA2A, sdk.Config{...})
|
||||
// taskID, err := a2a.SendTaskRequest(ctx, target, "text_gen", payload)
|
||||
//
|
||||
// a2n := sdk.NewClient(sdk.ProtoA2N, sdk.Config{...})
|
||||
// group, err := a2n.CreateGroup(ctx, name, topology, nil, members)
|
||||
//
|
||||
// 协议版本:1.0.0
|
||||
package sdk
|
||||
@@ -1,459 +0,0 @@
|
||||
package a2a
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"synapse/types"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// Synapse P2P SDK (灵隙协议)
|
||||
// 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 灵隙 点对点通信客户端
|
||||
type Client struct {
|
||||
agentEndpoint types.AgentEndpoint
|
||||
authSecret string
|
||||
callbackHandler MessageCallbackHandler
|
||||
tasks map[string]*P2PTaskState
|
||||
tasksMu sync.RWMutex
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// ClientConfig 灵隙 客户端配置
|
||||
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 创建新的 灵隙 客户端
|
||||
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,346 +0,0 @@
|
||||
package a2n
|
||||
|
||||
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 {
|
||||
a2aClient *a2aClient
|
||||
groups map[string]*GroupState
|
||||
groupsMu sync.RWMutex
|
||||
eventHandler GroupEventHandler
|
||||
}
|
||||
|
||||
// a2aClient 是 P2P 客户端的简化接口
|
||||
type a2aClient interface {
|
||||
SendMessage(ctx context.Context, target types.AgentEndpoint, msg *types.UnifiedSynapseMessage) error
|
||||
AgentEndpoint() types.AgentEndpoint
|
||||
}
|
||||
|
||||
// ClientConfig A2N 客户端配置
|
||||
type ClientConfig struct {
|
||||
P2PClient *a2aClient
|
||||
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(a2aClient 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
|
||||
}
|
||||
10
sdk/doc.go
10
sdk/doc.go
@@ -1,10 +0,0 @@
|
||||
// Package sdk 灵隙(Synapse)协议 SDK
|
||||
//
|
||||
// 灵隙协议族(Synapse Protocol Suite)Go 语言 SDK,包含三个子包:
|
||||
//
|
||||
// - sdk/p2a: P2A 协议 - 非 Agent 调用方通过 REST API 调用 Agent 能力
|
||||
// - sdk/a2a: A2A 协议 - Agent 之间的点对点任务协作通信
|
||||
// - sdk/a2n: A2N 协议 - 多 Agent 群组协作(群组管理、任务拆分合并、投票)
|
||||
//
|
||||
// 协议版本:1.0.0
|
||||
package sdk
|
||||
@@ -1,547 +0,0 @@
|
||||
package p2a
|
||||
|
||||
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")
|
||||
}
|
||||
0
test/.gitkeep
Normal file
0
test/.gitkeep
Normal file
Reference in New Issue
Block a user