Function Tool Basics: Letting Agent Call Custom Logic
Table of Contents
Function Tool Basics: Letting Agent Call Custom Logic
Tools are Agent’s real capability to get things done. ADK Go has built-in Google Search and other tools, but real business often needs custom logic—querying databases, calling internal APIs, doing specific calculations. This article covers how to write a custom Function Tool.
Function Tool Essence
A Function Tool is a Go struct implementing the tool.Tool interface. Agent calls Tools through this interface, Tool returns results to Agent.
type Tool interface {
Name() string
Description() string
InputSchema() string
Call(context.Context, string) (string, error)
}
Four methods:
| Method | Role | Return Example |
|---|---|---|
Name() | Tool name | get_weather |
Description() | Description, Model uses this to decide when to call | "Get weather for a city" |
InputSchema() | Input parameter JSON Schema | {"type":"object","properties":{"city":{"type":"string"}}} |
Call() | Actual execution logic | Returns weather string |
Minimal Usable Function Tool
Scenario: Query Book Information
A book query feature, input book title, returns author and price:
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)
}
// Simulate database query
books := map[string]struct{ Author string; Price float64 }{
"Go Programming": {Author: "Zhang San", Price: 59.00},
"Go Concurrency": {Author: "Li Si", Price: 79.00},
"Go Advanced": {Author: "Wang Wu", Price: 99.00},
}
book, ok := books[args.Title]
if !ok {
return fmt.Sprintf("Book '%s' not found", args.Title), nil
}
return fmt.Sprintf("『%s』Author: %s, Price: %.2f", args.Title, book.Author, book.Price), nil
}
Register to Agent
myAgent, err := llmagent.New(llmagent.Config{
Name: "book_assistant",
Model: model,
Instruction: "你是书城助手,帮助用户查询书籍信息。",
Tools: []tool.Tool{
bookTool{}, // Register custom Tool
},
})
Using the Tool
User asks: “Who is the author of the Go Programming book?”
Agent automatically recognizes it needs to call get_book_info, parses parameters, calls Call() method, returns result.
Parameter Parsing: Writing InputSchema
InputSchema() returns a JSON Schema describing input parameter structure. ADK Go uses this Schema for parameter validation, also passes it to Model so Model knows how to construct call parameters.
Simple Parameter
{
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name"
}
}
}
Multiple Parameters
{
"type": "object",
"properties": {
"city": {"type": "string"},
"date": {"type": "string"}
},
"required": ["city"]
}
required array declares which parameters are mandatory.
Enum Parameters
{
"type": "object",
"properties": {
"currency": {
"type": "string",
"enum": ["CNY", "USD", "EUR"]
}
}
}
Error Handling
Return Error vs Return Error Message
// Method 1: Return error (Agent sees the error)
func (t myTool) Call(ctx context.Context, input string) (string, error) {
return "", fmt.Errorf("network error: %w", err)
}
// Method 2: Return error message (Agent treats message as result)
func (t myTool) Call(ctx context.Context, input string) (string, error) {
return "API temporarily unavailable, please try again later", nil
}
Suggestion: Return error messages for expected errors (e.g., network timeout); return error for unexpected ones (e.g., programming bugs).
Timeout Handling
Add timeout to Tool, avoid slow operations blocking entire 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() {
// Actual query logic
result := doQuery()
done <- result
}()
select {
case result := <-done:
return result, nil
case <-ctx.Done():
return "", fmt.Errorf("query timeout")
}
}
Common Issues
Q: Tool called but Model didn’t recognize it
A: Check if Description() clearly describes when this Tool should be called. Model decides whether to call based on Description.
Q: Parameter parsing failed
A: Check if InputSchema() returned JSON Schema matches actual input parameters. Wrong Schema causes ADK Go parameter validation failure.
Q: Tool doesn’t return for a long time A: Implement timeout logic, add deadline to Tool, prevent slow operations from blocking Agent.
Next Steps
Tool written. Next, learn about Tool performance optimization—timeout control, concurrency handling—so Agent is more robust.
← API Key & Model Selection | Function Tool Performance →
Follow “Mengshou Programming” on WeChat for more Go ADK hands-on tutorials, weekly updates on Go / AI programming 实战干货.
