Function Tool 编写基础:让 Agent 调用自定义逻辑
Function Tool 编写基础:让 Agent 调用自定义逻辑
Tool 是 Agent 真正做事的能力。ADK Go 内置了 Google Search 等工具,但真实业务往往需要自定义逻辑——查数据库、调内部 API、做特定计算。这篇讲如何写一个自定义 Function Tool。
Function Tool 的本质
Function Tool 是一个实现了 tool.Tool 接口的 Go struct。Agent 通过这个接口调用 Tool,Tool 返回结果给 Agent。
type Tool interface {
Name() string
Description() string
InputSchema() string
Call(context.Context, string) (string, error)
}
四个方法:
| 方法 | 作用 | 返回示例 |
|---|---|---|
Name() | 工具名称 | get_weather |
Description() | 描述,Model 据此判断何时调用 | "Get weather for a city" |
InputSchema() | 输入参数 JSON Schema | {"type":"object","properties":{"city":{"type":"string"}}} |
Call() | 实际执行逻辑 | 返回天气字符串 |
最小可用的 Function Tool
场景:查询书籍信息
一个查书的功能,传入书名,返回作者和价格:
package main
import (
"context"
"encoding/json"
"fmt"
)
type bookTool struct{}
func (bookTool) Name() string {
return "get_book_info"
}
func (bookTool) Description() string {
return "Get author and price information for a book by its title"
}
func (bookTool) InputSchema() string {
return `{
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "The title of the book"
}
},
"required": ["title"]
}`
}
func (bookTool) Call(ctx context.Context, input string) (string, error) {
var args struct {
Title string `json:"title"`
}
if err := json.Unmarshal([]byte(input), &args); err != nil {
return "", fmt.Errorf("invalid input: %w", err)
}
// 模拟数据库查询
books := map[string]struct{ Author string; Price float64 }{
"Go 编程": {Author: "张三", Price: 59.00},
"Go 高并发": {Author: "李四", Price: 79.00},
"Go 进阶": {Author: "王五", Price: 99.00},
}
book, ok := books[args.Title]
if !ok {
return fmt.Sprintf("Book '%s' not found", args.Title), nil
}
return fmt.Sprintf("《%s》作者:%s,价格:%.2f元", args.Title, book.Author, book.Price), nil
}
注册到 Agent
myAgent, err := llmagent.New(llmagent.Config{
Name: "book_assistant",
Model: model,
Instruction: "你是书城助手,帮助用户查询书籍信息。",
Tools: []tool.Tool{
bookTool{}, // 注册自定义 Tool
},
})
使用 Tool
用户问:“Go 编程这本书的作者是谁?”
Agent 自动识别需要调用 get_book_info,解析参数,调用 Call() 方法,返回结果。
参数解析:InputSchema 的写法
InputSchema() 返回一个 JSON Schema,描述输入参数的结构。ADK Go 用这个 Schema 做参数验证,也会把它传给 Model,让 Model 知道怎么构造调用参数。
简单参数
{
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称"
}
}
}
多参数
{
"type": "object",
"properties": {
"city": {"type": "string"},
"date": {"type": "string"}
},
"required": ["city"]
}
required 数组声明哪些参数是必填的。
枚举参数
{
"type": "object",
"properties": {
"currency": {
"type": "string",
"enum": ["CNY", "USD", "EUR"]
}
}
}
错误处理
返回错误 vs 返回错误消息
// 方式 1:返回错误(Agent 会看到错误)
func (t myTool) Call(ctx context.Context, input string) (string, error) {
return "", fmt.Errorf("network error: %w", err)
}
// 方式 2:返回错误消息(Agent 会把消息当结果)
func (t myTool) Call(ctx context.Context, input string) (string, error) {
return "API 暂时不可用,请稍后再试", nil
}
建议:可预期的错误(如网络超时)返回错误消息;不可预期的(如编程 Bug)返回 error。
超时处理
给 Tool 加超时,避免慢操作卡住整个 Agent:
func (bookTool) Call(ctx context.Context, input string) (string, error) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
done := make(chan string, 1)
errCh := make(chan error, 1)
go func() {
// 实际查询逻辑
result := doQuery()
done <- result
}()
select {
case result := <-done:
return result, nil
case <-ctx.Done():
return "", fmt.Errorf("query timeout")
}
}
常见问题
Q:Tool 被调用了但 Model 没识别到
A:检查 Description() 是否描述清楚了这个 Tool 什么情况下应该被调用。Model 根据 Description 决定是否调用。
Q:参数解析失败
A:检查 InputSchema() 返回的 JSON Schema 是否和实际传入的参数匹配。Schema 写错会导致 ADK Go 参数验证失败。
Q:Tool 长时间不返回 A:实现超时逻辑,给 Tool 加截止时间,防止慢操作卡住 Agent。
下一步
一个 Tool 写好了,接下来了解如何对 Tool 做性能优化——超时控制、并发处理——让 Agent 更健壮。
← API Key 与 Model 选择 | Function Tool 性能优化 →
想跟着学更多 Go ADK 实战?关注「全栈之巅-梦兽编程」公众号,每周更新 Go / AI 编程实战干货。
