Agent SDK
AI Agent 运行时框架 — 通过可插拔的 Skill(技能)、SubAgent(子代理)与 MCP 工具协议实现领域能力的动态扩展
目录
特性
- 零外部依赖 — 仅使用 Go 标准库,无第三方依赖
- SubAgent 子代理 — 声明式配置子代理,LLM 自动编排多 Agent 协作
- Plan 编排 — LLM 生成多步执行计划,支持顺序和 DAG 并行两种模式
- Skill 插件化 — 技能以目录包形式组织,支持热发现与动态加载
- 多种文件系统 — 内置
MemFS、ZipFS,支持任意 fs.FS 实现
- MCP 协议 — 支持 stdio/SSE 两种传输方式,连接外部工具服务器
- 内置工具集 — 提供 Bash、HTTP、文件读取、Web 搜索等基础工具
- 脚本工具 — 自动将技能包内的
.py/.js/.sh 脚本注册为工具
- 对话摘要 — LLM 驱动的多级对话压缩,自动控制上下文长度
安装
go get github.com/opentoys/agentsdk
要求 Go 1.23 或更高版本。
快速开始
基本用法
package main
import (
"context"
"fmt"
"os"
"github.com/opentoys/agentsdk"
"github.com/opentoys/agentsdk/modules/aichat"
"github.com/opentoys/agentsdk/modules/log"
"github.com/opentoys/agentsdk/tool"
"github.com/opentoys/agentsdk/types"
)
func main() {
chatClient := aichat.New(
aichat.WithKey(os.Getenv("OPENAI_API_KEY")),
aichat.WithBase(os.Getenv("OPENAI_API_BASE")),
aichat.WithModel(os.Getenv("OPENAI_API_MODEL")),
)
skillsFS := os.DirFS("/path/to/skills")
agent := agentsdk.New(types.Config{
SkillsFS: skillsFS,
Debug: &log.DefaultLog{},
ChatClient: chatClient,
Tools: []types.Tool{
tool.DefineBashTool(),
tool.DefineHTTPTool(),
},
})
resp, err := agent.Run(context.Background(), "帮我分析这个数据")
if err != nil {
panic(err)
}
fmt.Println(resp)
}
使用 SubAgent
agent := agentsdk.New(types.Config{
ChatClient: chatClient,
SubAgents: []types.SubAgentConfig{
{Name: "researcher", Description: "搜索和研究信息的子代理"},
{Name: "writer", Description: "撰写内容的子代理"},
},
Tools: []types.Tool{tool.DefineBashTool()},
})
// LLM 自动选择并调用子代理
agent.Run(ctx, "帮我调研 AI 趋势并写报告")
使用 Plan 编排
agent := agentsdk.New(types.Config{
ChatClient: chatClient,
SubAgents: []types.SubAgentConfig{
{Name: "researcher", Description: "搜索和研究信息"},
{Name: "writer", Description: "撰写内容"},
{Name: "reviewer", Description: "审查和改进内容"},
},
Tools: []types.Tool{tool.DefineHTTPTool()},
})
// LLM 生成计划 → 按步骤自动执行
agent.RunWithPlan(ctx, "调研 AI 趋势,写一份报告,并审查质量")
使用 VFS 内存文件系统
mem := vfs.NewMem()
mem.WriteFile("my-skill/SKILL.md", []byte(skillContent))
mem.WriteFile("my-skill/scripts/run.py", []byte(scriptContent))
agent := agentsdk.New(types.Config{
SkillsFS: mem,
ChatClient: chatClient,
})
使用 MCP 外部工具
config, _ := mcp.LoadConfig("mcp_config.json")
mcpClient := mcp.NewClient(ctx, config)
defer mcpClient.Close()
agent := agentsdk.New(types.Config{
ChatClient: chatClient,
McpSessions: mcpClient,
SkillsFS: os.DirFS("./skills"),
})
核心架构
设计理念
Agent 不硬编码任何领域知识,而是通过四层工具体系 + SubAgent + 可插拔模块获取能力:
┌──────────────┐
│ Agent │ ◄── 入口 / 协调者
│ Run/Plan │
└──────┬───────┘
│
┌──────────┬───────────┼───────────┬──────────┐
▼ ▼ ▼ ▼ ▼
┌──────────┐ ┌────────┐ ┌────────┐ ┌─────────┐ ┌──────────┐
│ SubAgent │ │ skill/ │ │ tool/ │ │ MCP │ │ Custom │
│ 子代理 │ │ parser │ │ 内置 │ │ 远程 │ │ Tool │
│ (声明式) │ │ +gen │ │ │ │ │ │ (NewTool)│
└──────────┘ └────────┘ └────────┘ └─────────┘ └──────────┘
│ │ │ │ │
Agent实例 技能包 Bash/ MCP服务器 自定义
(独立LLM) (脚本) HTTP/Read (SSE/stdio) Exec函数
四层工具体系
| 层级 |
来源 |
说明 |
示例 |
| SubAgent |
Config.SubAgents |
声明式子代理配置,每个子代理拥有独立的 Agent 实例和消息历史 |
researcher, writer, dealer |
| BaseTools |
Config.Tools |
通用基础能力,也可通过 NewTool() 自定义 |
bash, http, read, search |
| Script Tools |
技能包 scripts/ 目录 |
技能专属的 .py/.ts/.js/.sh 脚本,自动或手动定义为工具 |
run_query.py, run_deploy.sh |
| MCP Tools |
外部 MCP 服务器 |
通过 Model Context Protocol 连接的远程服务工具 |
组件查询, 数据库操作 |
执行流程
用户输入 userPrompt
│
▼
┌─ Run() ──────────────────────────────────────────────┐
│ loadSkill(SkillsFS) → ParseSkillPackages │
│ ├─ 0 个技能: runWithSkill(nil) │
│ ├─ 1 个技能: runWithSkill(skill) │
│ └─ N 个技能: selectSkill() → LLM 选择 → runWithSkill │
└────────────────────────────────────────────────────────┘
│
▼
┌─ runWithSkill() ─────────────────────────────────────┐
│ 合并工具: SubAgent tools + Base tools + Script + MCP │
│ │
│ 工具调用循环 (最多 20 轮): │
│ ├─ LLM 决定调用哪个工具 │
│ ├─ SubAgent → tool.Exec() → 子 Agent.Run() │
│ ├─ 含 "__" → McpSessions.CallTool (MCP) │
│ ├─ 在 Tools 中 → tool.Exec() (内置/自定义) │
│ ├─ 在 scriptMap 中 → bash() (脚本工具) │
│ └─ 无 tool_calls → 返回最终文本 │
└────────────────────────────────────────────────────────┘
SubAgent 子代理
SubAgent 是框架内置的多 Agent 协作机制。通过声明式配置,将多个子 Agent 注册为父 Agent 可调用的 Tool,由 LLM 自动编排调用。
最常用的模式:每个子代理是一个拥有独立消息历史和 SystemPrompt 的 Agent,被包装为 Tool 注册到父 Agent。LLM 在工具调用循环中自动决定何时调用哪个子代理。
agent := agentsdk.New(types.Config{
ChatClient: chatClient,
SubAgents: []types.SubAgentConfig{
{
Name: "dealer",
Description: "荷官,负责发牌和判定胜负",
SystemPrompt: "你是德州扑克荷官...",
},
{
Name: "xiaoming",
Description: "松凶型玩家",
SystemPrompt: "你是玩家小明,松凶型风格...",
},
},
})
// 一行启动,LLM 自动编排子代理间的交互
agent.Run(ctx, "开始一局德州扑克")
工作原理:
父 Agent 收到 "开始一局德州扑克"
│
├─ LLM: "调用 dealer 发牌"
│ └─ dealer 子代理执行 → 返回发牌结果
├─ LLM: "调用 xiaoming 做决策"
│ └─ xiaoming 子代理执行 → 返回决策
├─ LLM: "调用 xiaohong 做决策"
│ └─ xiaohong 子代理执行 → 返回决策
└─ ... LLM 持续编排直到游戏结束
Plan 编排模式
通过 RunWithPlan() 实现。LLM 先生成多步骤执行计划(顺序或 DAG),然后框架按计划依次调用子代理,支持步骤间传递中间结果。
agent := agentsdk.New(types.Config{
ChatClient: chatClient,
SubAgents: []types.SubAgentConfig{
{Name: "researcher", Description: "搜索和研究信息"},
{Name: "writer", Description: "撰写内容"},
{Name: "reviewer", Description: "审查内容"},
},
Tools: []types.Tool{tool.DefineHTTPTool()},
})
// LLM 自动生成计划并按步骤执行
agent.RunWithPlan(ctx, "调研 AI 趋势并写报告")
LLM 生成的计划示例:
{
"setps": [
{"name": "researcher", "input": "调研 2024 年 AI 领域重大突破"},
{"name": "writer", "input": "基于 {{result:researcher}} 撰写一份报告"},
{"name": "reviewer", "input": "审查 {{result:writer}} 的质量和准确性", "after": ["writer"]}
]
}
- 步骤间通过
{{result:步骤名}} 引用前序步骤的输出
after 字段定义依赖关系,无依赖的步骤可并行执行
- 无子代理时自动退化为普通
Run()
SubAgentConfig 配置
| 字段 |
类型 |
必填 |
说明 |
Name |
string |
是 |
子代理名称,同时作为注册到父 Agent 的 Tool 名称 |
Description |
string |
是 |
能力描述,LLM 依据此描述决定何时调用该子代理 |
SystemPrompt |
string |
否 |
系统提示词,定义子代理的角色和行为准则 |
SkillsFS |
fs.FS |
否 |
子代理专属技能文件系统,nil 时复用父 Agent 的 |
Tools |
[]Tool |
否 |
子代理专属工具集,nil 时复用父 Agent 的 |
McpSessions |
ClientSessioner |
否 |
子代理专属 MCP 会话,nil 时复用父 Agent 的 |
Parameters |
map[string]any |
否 |
自定义 Tool 参数 Schema,nil 时使用默认的 input 字段 |
PlanStep 结构
| 字段 |
类型 |
说明 |
Name |
string |
步骤名称,对应子代理名或工具名 |
Input |
string |
传递给该步骤的自然语言输入 |
After |
[]string |
前置依赖步骤名称列表(DAG 模式) |
API 参考
Agent
Config 结构体
| 字段 |
类型 |
必填 |
说明 |
ChatClient |
types.OpenAIChatClient |
是 |
LLM 聊天客户端 |
SkillsFS |
fs.FS |
否 |
技能文件系统 |
Debug |
types.Logger |
否 |
日志接口实现 |
McpSessions |
types.ClientSessioner |
否 |
MCP 会话管理器 |
History |
[]types.ChatCompletionMessage |
否 |
初始消息历史 |
Tools |
[]Tool |
否 |
自定义工具集合 |
SubAgents |
[]SubAgentConfig |
否 |
声明式子代理配置列表 |
核心方法
| 方法 |
签名 |
说明 |
New |
New(cfg types.Config) *Agent |
创建并初始化 Agent(含子代理注册) |
Run |
(a *Agent) Run(ctx, prompt string) (string, error) |
主入口:技能选择 + 工具调用循环,LLM 自动编排子代理 |
RunWithPlan |
(a *Agent) RunWithPlan(ctx, prompt string) (string, error) |
Plan 模式:LLM 生成计划 → DAG/顺序执行子代理 |
CallTool |
(a *Agent) CallTool(ctx, name, args string) (string, error) |
通过名称调用已注册的工具(含子代理) |
注册函数
| 函数 |
签名 |
说明 |
NewTool |
NewTool(cfg types.ToolConfig) Tool |
创建自定义 Tool(快捷构造) |
WarpSkill |
WarpSkill(sk *SkillPackage, a *Agent) Tool |
将 SkillPackage 包装为 Tool |
RegisterExec |
RegisterExec(ext, exec string) |
注册脚本执行器(如 .py → python3) |
RegisterBash |
RegisterBash(f func(context.Context, string) (string, error)) |
覆盖默认的 Bash 执行函数 |
用于 NewTool() 快捷创建自定义工具:
t := agentsdk.NewTool(types.ToolConfig{
Name: "my_tool",
Description: "工具描述",
Exec: func(ctx context.Context, in string) (string, error) {
return "result", nil
},
})
| 字段 |
类型 |
必填 |
说明 |
Name |
string |
是 |
工具名称 |
Description |
string |
是 |
工具描述 |
Parameters |
map[string]any |
否 |
自定义参数 Schema |
Exec |
func(ctx, in string) (out string, e error) |
是 |
执行函数 |
默认脚本执行器映射:
| 扩展名 |
执行命令 |
.py |
python3 |
.js |
node |
.tengo |
tengo |
Skill 系统
Skill Package 结构
每个技能是一个包含 SKILL.md(或 skill.md)的目录:
my-skill/
├── SKILL.md # 技能元数据 + 指令
├── scripts/ # 可选:脚本工具
│ ├── run.py
│ └── deploy.sh
└── resources/ # 可选:资源文件
SKILL.md 格式
支持 Claude Code 和 OpenAI 双格式解析。使用 YAML frontmatter 定义元数据:
---
name: my-skill
description: 描述技能的功能
allowedTools:
- bash
- http
---
# 技能指令
这里是技能的详细指令内容...
Skill 元数据字段
| 字段 |
类型 |
说明 |
Name |
string |
技能名称(唯一标识) |
Description |
string |
技能描述 |
AllowedTools |
[]string |
允许使用的工具列表 |
Model |
string |
推荐使用的模型 |
Author |
string |
作者 |
Version |
string |
版本号 |
License |
string |
许可证 |
Tools |
[]ToolDefinition |
自定义工具定义 |
type Tool struct {
Prompt string // 可选:附加提示信息(不序列化到 API)
Type string // 工具类型,默认 "function"
Function *FunctionDefinition // 函数定义(名称、描述、参数 Schema)
Exec func(ctx context.Context, in string) (string, error) // 执行函数
}
内置工具
| 工具名 |
创建函数 |
功能说明 |
安全特性 |
bash |
DefineBashTool() |
Shell 命令执行 |
危险命令拦截 + 2 分钟超时 |
http_request |
DefineHTTPTool() |
HTTP 请求(curl兼容) |
JSON 自动美化输出 |
read_local |
DefineReadLocal(fsys fs.FS) |
文件/目录读取 |
基于 fs.FS,支持任意文件系统 |
tavily_search |
DefineTavilySearch() |
Tavily AI 搜索 |
默认上限 20 条 |
Bash 工具
支持环境变量 $WORKDIR 作为命令的工作根目录。
危险命令拦截列表:rm -rf /、rm -rf /*、> /dev/sd、> /dev/null、mkfs、dd if=
result, err := tool.Bash("ls -la") // 默认 2 分钟超时
result, err = tool.BashWithTimeout("sleep 5", 10*time.Second) // 自定义超时
VFS 虚拟文件系统
技能加载通过 fs.FS 接口抽象,内置多种虚拟文件系统实现。
MemFS — 内存文件系统
适用于动态构建技能内容或测试场景。
mem := vfs.NewMem() // 创建内存 FS
mem.WriteFile("calc/SKILL.md", data) // 写入文件(自动创建目录)
mem.Remove("calc/old.md") // 删除文件
mem.Export(zipWriter) // 导出为 ZIP
mem.Merge("prefix/", otherFS) // 合并其他 FS
ZipFS — ZIP 文件系统
zipFS := vfs.NewZip()
r, _ := vfs.ZipReadFile("skills.zip") // 本地文件
r, _ := vfs.ZipReadURL("https://...") // 远程 URL
zipFS.Add("my-skill", r)
工具函数
| 函数 |
签名 |
说明 |
ZipReadFile |
ZipReadFile(name string) (*zip.Reader, error) |
读取本地 ZIP 文件 |
ZipReadURL |
ZipReadURL(url string) (*zip.Reader, error) |
从 URL 读取 ZIP |
ZipCreate |
ZipCreate(content map[string]string) *zip.Reader |
从内存创建 ZIP |
CreateZip |
CreateZip(w io.Writer, files map[string][]byte) error |
写入 ZIP 到 Writer |
ParseZip |
ParseZip(data []byte) (map[string][]byte, error) |
解析 ZIP 到内存 Map |
Memory 系统
LLM 驱动的多级对话摘要系统,用于控制上下文长度。
三级摘要策略
| 策略 |
触发条件 |
行为 |
| Leaf |
单个对话片段 |
增量摘要 |
| Condensed D1 |
多个 leaf 合并 |
压缩为单节点 |
| Condensed D2 |
多 session 合并 |
更高层级压缩 |
自动升级策略
normal policy → 检查 token 是否超预算 (150%)
↓ 超出
aggressive policy → 再检查
↓ 还超出
deterministicFallback → 截断到目标长度
summarizer := &memory.LLMSummarizer{
Generate: func(ctx context.Context, prompt string) (string, error) {
return llmClient.Generate(prompt)
},
}
summary, err := memory.BuildSummarize(ctx, summarizer, messages, memory.SummarizeOptions{
TargetTokens: 4000,
IsCondenced: true,
Depth: 1,
})
模块系统
aichat — OpenAI Chat 客户端
路径:modules/aichat/
基于 HTTP 直连 OpenAI 兼容 API 的轻量级客户端,采用 函数选项模式 配置。
client := aichat.New(
aichat.WithKey("sk-xxx"),
aichat.WithBase("https://api.openai.com/v1"),
aichat.WithModel("gpt-4o"),
)
| 选项 |
说明 |
默认值 |
WithKey |
API Key |
无(必填) |
WithBase |
Base URL |
https://api.deepseek.com/v1 |
WithModel |
默认模型 |
deepseek-v4-flash |
mcp — MCP 协议客户端
路径:modules/mcp/
支持 stdio / SSE 两种传输方式,内建连接重试与指数退避机制。
config, _ := mcp.LoadConfig("servers.json")
client := mcp.NewClient(ctx, config)
defer client.Close()
// 工具名格式: serverName__toolName(自动添加前缀避免冲突)
tools, _ := client.ListTools(ctx)
result, _ := client.CallTool(ctx, "serverName__toolName", map[string]any{"arg": "value"})
log — 日志模块
路径:modules/log/
实现 types.Logger 接口,用于 Agent 调试输出。
agent := agentsdk.New(types.Config{
Debug: &log.DefaultLog{},
})
dag — DAG 执行引擎
路径:modules/dag/
有向无环图(DAG)执行引擎,用于 RunWithPlan 的计划编排。
g := dag.New()
g.AddNode("step1", "输入描述1")
g.AddNode("step2", "输入描述2")
g.AddEdge("step1", "step2") // step2 依赖 step1
g.Run(ctx, func(ctx context.Context, name, prompt string) error {
// 执行节点逻辑
dag.SetResultKV(ctx, name, result) // 存储结果
return nil
})
results := dag.GetResult(ctx) // map[string]string
也支持从 JSON 构建:
g := dag.NewJson([]byte(`{"setps": [
{"name": "research", "input": "调研AI趋势"},
{"name": "write", "input": "写报告", "after": ["research"]}
]}`))
设计模式
| 模式 |
应用位置 |
说明 |
| Strategy |
OpenAIChatClient, Summarizer 接口 |
可替换 LLM 后端和摘要引擎 |
| Plugin |
Skill Package + SubAgent |
技能/子代理作为插件热发现和注册 |
| Pipeline |
发现 → 选择 → 执行 |
Agent 的核心流水线 |
| Adapter |
MCP Client, aichat |
将外部格式适配为统一的 Tool 接口 |
| Option |
aichat.New |
函数选项模式,灵活配置客户端 |
| Delegate |
SubAgent (Agent-as-Tool) |
父 Agent 委托子 Agent 处理子任务 |
| Orchestrate |
RunWithPlan (DAG) |
LLM 生成计划,框架按 DAG 编排 |
参考项目
- goskills — Reference skills implementation
- anna — Reference memory implementation
- go-mcp — Downgrading MCP to Go 1.23
License
MIT