灵隙(synapse) v1.0.0 版本

This commit is contained in:
lychang
2026-06-28 03:24:26 +08:00
parent 88b606b9ca
commit 812f68d5aa
48 changed files with 5566 additions and 1499 deletions

21
CHANGELOG.md Normal file
View 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
View File

View 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
View 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 | 初始版本(混合抽象层与实现层) |

View File

@@ -3,7 +3,7 @@
> 版本:1.0.0
> 状态:规范
> 层级:子协议层
> 基于:[灵隙原始协议](01-raw-protocol.md)
> 基于:[灵隙原始协议](01_raw-protocol.md)
> 前缀:`p2p:`
---

View File

@@ -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:`
---

View File

@@ -3,7 +3,7 @@
> 版本:1.0.0
> 状态:规范
> 层级:子协议层
> 基于:[灵隙原始协议](01-raw-protocol.md)
> 基于:[灵隙原始协议](01_raw-protocol.md)
> 前缀:`caller:`(内部转换)
---

View File

@@ -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 {

View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

View 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
View 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)

View File

@@ -8,7 +8,7 @@ import (
"sync"
"time"
"synapse/types"
"synapse/pkg/types"
)
// =============================================================================

View File

@@ -8,7 +8,7 @@ import (
"sync"
"time"
"synapse/types"
"synapse/pkg/types"
)
// =============================================================================

View File

@@ -6,7 +6,7 @@ import (
"testing"
"time"
"synapse/types"
"synapse/pkg/types"
)
// =============================================================================

View File

@@ -13,7 +13,7 @@ import (
"strings"
"time"
"synapse/types"
"synapse/pkg/types"
)
// =============================================================================

View File

@@ -18,7 +18,7 @@ import (
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"
"synapse/types"
"synapse/pkg/types"
)
// =============================================================================

View File

@@ -5,7 +5,7 @@ import (
"encoding/json"
"net/http"
"synapse/types"
"synapse/pkg/types"
)
// =============================================================================

View File

@@ -14,7 +14,7 @@ import (
"sync"
"time"
"synapse/types"
"synapse/pkg/types"
)
// =============================================================================

View File

@@ -14,7 +14,7 @@ import (
"sync"
"time"
"synapse/types"
"synapse/pkg/types"
)
// =============================================================================

View File

@@ -15,7 +15,7 @@ import (
"sync"
"time"
"synapse/types"
"synapse/pkg/types"
)
// =============================================================================

View File

@@ -13,7 +13,7 @@ import (
"sync"
"time"
"synapse/types"
"synapse/pkg/types"
)
// =============================================================================

View File

@@ -15,7 +15,7 @@ import (
"sync"
"time"
"synapse/types"
"synapse/pkg/types"
)
// =============================================================================

View File

@@ -17,7 +17,7 @@ import (
"sync"
"time"
"synapse/types"
"synapse/pkg/types"
)
// =============================================================================

View File

@@ -8,7 +8,7 @@ import (
"sync"
"time"
"synapse/types"
"synapse/pkg/types"
)
// =============================================================================

View File

@@ -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"
)
// =============================================================================

View File

@@ -12,7 +12,7 @@ import (
"sync"
"time"
"synapse/types"
"synapse/pkg/types"
)
// =============================================================================

View File

@@ -15,7 +15,7 @@ import (
"sync"
"time"
"synapse/types"
"synapse/pkg/types"
)
// =============================================================================

1250
pkg/sdk/client.go Normal file

File diff suppressed because it is too large Load Diff

21
pkg/sdk/doc.go Normal file
View 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

View File

@@ -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(),
}
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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
View File