aster 提供了强大的工具系统,让 Agent 能够与外部世界交互。本章节通过实际示例展示三种工具使用方式。
graph TB
Agent[Agent] --> ToolRegistry[Tool Registry]
ToolRegistry --> Builtin[内置工具]
ToolRegistry --> MCP[MCP 工具]
ToolRegistry --> Custom[自定义工具]
Builtin --> FS[文件系统<br/>Read, Write]
Builtin --> Bash[命令执行<br/>Bash]
Builtin --> HTTP[网络请求<br/>HttpRequest]
Builtin --> Search[搜索<br/>WebSearch]
MCP --> MCPServer1[MCP Server 1]
MCP --> MCPServer2[MCP Server 2]
MCP --> MCPServerN[MCP Server N]
Custom --> Business[业务工具]
Custom --> Integration[第三方集成]
Custom --> Skills[Agent Skills]
style Agent fill:#10b981
style ToolRegistry fill:#3b82f6
style Builtin fill:#f59e0b
style MCP fill:#8b5cf6
style Custom fill:#ec4899
包含工具:
Read / Write - 文件读写Bash - 命令执行HttpRequest - HTTP 请求WebSearch - 网络搜索特性:
能力:
sequenceDiagram
participant User
participant Agent
participant LLM
participant Registry
participant Tool
participant Sandbox
User->>Agent: Send("读取 config.json")
Agent->>LLM: 发送消息 + 可用工具列表
LLM->>Agent: 返回工具调用决策
Agent->>Registry: GetTool("Read")
Registry->>Agent: 返回 Tool 实例
Agent->>Tool: Execute(path="config.json")
Tool->>Sandbox: 在沙箱中读取文件
Sandbox->>Tool: 返回文件内容
Tool->>Agent: 返回执行结果
Agent->>LLM: 发送工具结果
LLM->>Agent: 生成最终响应
Agent->>User: 返回响应
1. Tool Registry (工具注册表)
type Registry interface {
// 注册工具
Register(name string, factory ToolFactory) error
// 获取工具
GetTool(name string, config map[string]interface{}) (Tool, error)
// 列出所有工具
List() []string
}
2. Tool 接口
type Tool interface {
// 工具名称
Name() string
// 工具描述(供 LLM 理解)
Description() string
// 输入参数 Schema(JSON Schema)
InputSchema() map[string]interface{}
// 执行工具逻辑
Execute(ctx context.Context, input map[string]interface{}, tc *ToolContext) (interface{}, error)
}
3. Tool Context (工具上下文)
type ToolContext struct {
Sandbox sandbox.Sandbox // 沙箱实例
AgentID string // Agent ID
ConversationID string // 会话 ID
Metadata map[string]interface{} // 元数据
}
每个工具只做一件事:
// ✅ 好的设计
func (t *ReadTool) Execute(...) { /* 只负责读文件 */ }
func (t *WriteTool) Execute(...) { /* 只负责写文件 */ }
// ❌ 不好的设计
func (t *FsTool) Execute(...) {
// 既读又写,职责不清
}
提供清晰的输入 Schema:
func (t *HttpRequestTool) InputSchema() map[string]interface{} {
return map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"url": map[string]interface{}{
"type": "string",
"description": "Request URL",
},
"method": map[string]interface{}{
"type": "string",
"enum": []string{"GET", "POST", "PUT", "DELETE"},
"description": "HTTP method",
},
},
"required": []string{"url"},
}
}
所有文件和命令操作都在沙箱中执行:
func (t *BashTool) Execute(ctx context.Context, input map[string]interface{}, tc *ToolContext) (interface{}, error) {
cmd := input["cmd"].(string)
// 通过沙箱执行,而不是直接执行
result, err := tc.Sandbox.Run(ctx, cmd)
return result, err
}
提供清晰的错误信息:
func (t *ReadTool) Execute(...) (interface{}, error) {
if !fileExists(path) {
return nil, fmt.Errorf("file not found: %s", path)
}
if !hasPermission(path) {
return nil, fmt.Errorf("permission denied: %s", path)
}
// ...
}
// 注册所有内置工具
toolRegistry := tools.NewRegistry()
builtin.RegisterAll(toolRegistry)
// 在 Agent 模板中声明可用工具
templateRegistry.Register(&types.AgentTemplateDefinition{
ID: "assistant",
Tools: []interface{}{"Read", "Write", "Bash"},
})
// Agent 自动拥有这些工具能力
agent, _ := agent.Create(ctx, config, deps)
agent.Chat(ctx, "请读取 config.json 文件")
// 创建 MCP Manager
mcpManager := mcp.NewMCPManager(toolRegistry)
// 添加 MCP Server
mcpManager.AddServer(&mcp.MCPServerConfig{
ServerID: "my-tools",
Endpoint: "http://localhost:8080/mcp",
})
// 连接并自动注册工具
mcpManager.ConnectAll(ctx)
// Agent 可以使用 MCP 工具: my-tools:calculator
// 实现 Tool 接口
type WeatherTool struct{}
func (t *WeatherTool) Name() string { return "weather" }
func (t *WeatherTool) Description() string {
return "Get current weather for a city"
}
func (t *WeatherTool) InputSchema() map[string]interface{} {
return map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"city": map[string]interface{}{"type": "string"},
},
"required": []string{"city"},
}
}
func (t *WeatherTool) Execute(ctx context.Context, input map[string]interface{}, tc *ToolContext) (interface{}, error) {
city := input["city"].(string)
// 调用天气 API...
return map[string]interface{}{"temp": 25, "condition": "sunny"}, nil
}
// 注册工具
toolRegistry.Register("weather", func(config map[string]interface{}) (tools.Tool, error) {
return &WeatherTool{}, nil
})
建议按以下顺序学习:
对于昂贵的工具调用可以实现缓存:
type CachedWeatherTool struct {
cache map[string]interface{}
ttl time.Duration
}
func (t *CachedWeatherTool) Execute(ctx context.Context, input map[string]interface{}, tc *ToolContext) (interface{}, error) {
city := input["city"].(string)
// 检查缓存
if result, ok := t.cache[city]; ok {
return result, nil
}
// 调用 API
result := callWeatherAPI(city)
// 缓存结果
t.cache[city] = result
return result, nil
}
多个独立工具可以并发执行:
// aster 内部自动处理工具并发
// 无需手动管理
详见 安全最佳实践。