适配器契约预览
以下 Agent Adapter SDK 示例在公开 Go SDK 包稳定前仅作说明之用。它们描述 Edge Server 内的 Agent Runtime adapter 契约,不代表当前已有稳定的 import path。
Runtime adapter 把 Claude Code、Codex、OpenCode 或自定义 agent runtime 这类执行引擎接入 AgentHub。
还不是稳定公开 SDK
把本页视为 adapter contract preview。它适合理解 Edge Server 集成边界、事件归一化、取消和审批行为;它还不是第三方包使用指南、marketplace 提交指南或兼容性承诺。
概览
每个 runtime adapter 应提供三类能力:
- 能力元数据:帮助 AgentHub 选择合适的 profile/runtime 组合。
- 生命周期控制:让 Edge 可以 start、cancel、timeout 和 inspect 一次 run。
- 结构化事件:让 Desktop/Web/Hub 渲染 progress、tool call、diff、artifact、approval 和 completion state。
Adapter 不是用户可见的 Agent Profile。Profile 是产品配置,adapter 是执行机器。
稳定性边界
| 区域 | 当前稳定性 | 说明 |
|---|---|---|
| Runtime 目的 | 方向稳定 | Adapter 把真实 coding runtime 归一化成 AgentHub run 和 event |
| Event 分类 | 契约已定 | Progress、tool call、tool result、diff、artifact、approval、completion、failure 是预期类别 |
| Import path | 未稳定 | example.com/agenthub/adapter 只是说明性占位 |
| 公开包 | 未稳定 | 暂时不能把第三方接入写成通用可用 |
| 提交流程 | 未定义 | 第三方 review、签名、兼容测试和文档要求仍需要产品化 |
| 安全策略 | 必须具备 | Adapter 必须尊重 workspace allowlist、cancellation、redaction 和 approval policy |
本页升级为稳定 SDK 参考前,需要公开 package path、版本策略、conformance tests、迁移说明和第三方 review 流程。
复制示例前先确认
下面的代码写得像 SDK 指南,是为了让契约更容易读;它还不是可以直接复制上线的生产集成材料。
| 问题 | 当前答案 |
|---|---|
| 现在能 import 这个 path 吗? | 不能。example.com/agenthub/adapter 是占位符。 |
| 现在能发布第三方 adapter 吗? | 不能写成稳定生态提交。review 和 packaging 流程尚未定义。 |
| Event 名称稳定了吗? | API 与事件 中的 run-scoped 词汇是公开方向;schema 仍需要版本化 conformance test。 |
| Provider key 放哪里? | 只能放本地/服务端 secret storage。不要把 API key 放进前端代码、文档截图或 issue 中复制的 adapter 示例。 |
| 稳定 SDK 还缺什么? | 公开 module path、语义化版本、conformance test、迁移策略、安全审查和提交流程清单。 |
如果你在 AgentHub 仓库内部实现,请使用 Edge Server 拥有的接口和测试。如果你在设计外部 adapter,在公开 package 存在前,把本页当成产品契约上下文,而不是稳定 SDK。
AgentAdapter 接口
// AgentAdapter defines the contract every runtime adapter must implement.
type AgentAdapter interface {
// Name returns the unique identifier for this agent type.
Name() string
// Capabilities lists what this agent can do.
Capabilities() []Capability
// Execute runs a task and streams progress events.
Execute(ctx context.Context, task Task, events chan<- AgentEvent) (*AgentResult, error)
// Validate checks whether the configuration is valid.
Validate(config AgentConfig) error
}
实现适配器
步骤 1:定义 Agent
创建一个结构体保存 Agent 配置和运行状态:
package myadapter
import "example.com/agenthub/adapter" // illustrative placeholder
type MyAgent struct {
config adapter.AgentConfig
client *http.Client
}
func New(config adapter.AgentConfig) *MyAgent {
return &MyAgent{
config: config,
client: &http.Client{Timeout: 30 * time.Minute},
}
}
func (a *MyAgent) Name() string {
return "my-agent"
}
步骤 2:声明能力
能力声明帮助编排器选择正确的 Agent:
func (a *MyAgent) Capabilities() []adapter.Capability {
return []adapter.Capability{
{
Name: "code_review",
Description: "Reviews code for bugs, style, and security issues.",
Priority: 8,
},
{
Name: "refactoring",
Description: "Suggests and applies code refactorings.",
Priority: 6,
},
}
}
步骤 3:实现 Execute
Execute 是适配器核心方法。它接收任务、流式上报进度,并返回最终结果:
func (a *MyAgent) Execute(
ctx context.Context,
task adapter.Task,
events chan<- adapter.AgentEvent,
) (*adapter.AgentResult, error) {
defer close(events)
events <- adapter.AgentEvent{
Type: adapter.EventProgress,
Message: "Analyzing task...",
}
prompt := buildPrompt(task)
events <- adapter.AgentEvent{
Type: adapter.EventProgress,
Message: "Generating response...",
}
response, err := a.callLLM(ctx, prompt)
if err != nil {
return nil, fmt.Errorf("llm call failed: %w", err)
}
return &adapter.AgentResult{
Content: response.Text,
ToolCalls: response.ToolCalls,
TokensUsed: response.Usage.TotalTokens,
CompletedAt: time.Now(),
}, nil
}
步骤 4:校验配置
func (a *MyAgent) Validate(config adapter.AgentConfig) error {
if config.APIKey == "" {
return fmt.Errorf("api_key is required")
}
if config.Model == "" {
return fmt.Errorf("model is required")
}
return nil
}
步骤 5:注册适配器
在适配器的 init() 函数中注册:
package myadapter
import "example.com/agenthub/adapter" // illustrative placeholder
func init() {
adapter.Register("my-agent", func(config adapter.AgentConfig) (adapter.AgentAdapter, error) {
if err := (&MyAgent{}).Validate(config); err != nil {
return nil, err
}
return New(config), nil
})
}
然后在主程序中引入适配器:
package main
import (
_ "github.com/your-org/agenthub-myadapter"
"github.com/tokendancelab/agenthub/cmd/server"
)
func main() {
server.Run()
}
事件类型
| 事件类型 | 说明 |
|---|---|
EventProgress | 中间状态更新,会显示在聊天线程中。 |
EventToolCall | Agent 发起了工具调用,例如读取文件或运行命令。 |
EventError | 发生非致命错误,Agent 仍可继续。 |
EventComplete | Agent 完成当前任务。 |
最佳实践
- 持续上报进度:用户需要看到任务仍在推进。
- 尊重取消:始终响应
ctx.Done(),平台会设置硬超时。 - 工具调用幂等:让工具可以安全重试。
- 结构化输出:能返回 JSON 时优先返回结构化结果,便于在聊天卡片中渲染。
- 最小配置:只要求必要字段,例如 API Key 和模型名,其余使用合理默认值。
示例:最小 HTTP 适配器
下面是一个封装 HTTP LLM API 的完整适配器:
package httpadapter
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"example.com/agenthub/adapter" // illustrative placeholder
)
type HTTPAgent struct {
config adapter.AgentConfig
endpoint string
}
func New(config adapter.AgentConfig) *HTTPAgent {
endpoint := config.Extra["endpoint"]
if endpoint == "" {
endpoint = "https://api.example.com/v1/chat"
}
return &HTTPAgent{
config: config,
endpoint: endpoint,
}
}
func (a *HTTPAgent) Name() string { return "http-agent" }
func (a *HTTPAgent) Capabilities() []adapter.Capability {
return []adapter.Capability{{
Name: "general", Description: "General-purpose assistant.",
}}
}
func (a *HTTPAgent) Execute(
ctx context.Context,
task adapter.Task,
events chan<- adapter.AgentEvent,
) (*adapter.AgentResult, error) {
defer close(events)
events <- adapter.AgentEvent{
Type: adapter.EventProgress, Message: "Calling LLM...",
}
body := map[string]interface{}{
"model": a.config.Model,
"messages": []map[string]string{{"role": "user", "content": task.Prompt}},
}
buf := new(bytes.Buffer)
json.NewEncoder(buf).Encode(body)
req, _ := http.NewRequestWithContext(ctx, "POST", a.endpoint, buf)
req.Header.Set("Authorization", "Bearer "+a.config.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
var result struct {
Choices []struct {
Message struct {
Content string `json:"content"`
} `json:"message"`
} `json:"choices"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("decode failed: %w", err)
}
return &adapter.AgentResult{
Content: result.Choices[0].Message.Content,
CompletedAt: time.Now(),
}, nil
}
func (a *HTTPAgent) Validate(config adapter.AgentConfig) error {
if config.APIKey == "" {
return fmt.Errorf("api_key is required")
}
return nil
}
下一步
- 阅读 系统架构,理解适配器在整体链路中的位置。
- 阅读 API 与事件,保持事件形状一致。
- 按 快速上手 验证本地 runtime 链路。
- 在公开 adapter SDK 包和贡献流程稳定后,再通过 Pull Request 将适配器提案提交到 TokenDanceLab/AgentHub。