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:

MethodRoleReturn Example
Name()Tool nameget_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 logicReturns 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 实战干货.