Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/firebase/genkit/llms.txt

Use this file to discover all available pages before exploring further.

Tool Calling

Tool calling (also known as function calling) allows AI models to interact with external systems, access real-time data, and perform actions. Models can decide when to use tools and how to call them based on the conversation context.

Defining Tools

Create tools that models can use:
import { genkit, z } from 'genkit';
import { googleAI } from '@genkit-ai/google-genai';

const ai = genkit({ plugins: [googleAI()] });

const menuTool = ai.defineTool(
  {
    name: 'todaysMenu',
    description: "Use this tool to retrieve all the items on today's menu",
    inputSchema: z.object({}),
    outputSchema: z.object({
      menuData: z.array(z.object({
        title: z.string(),
        price: z.string(),
        description: z.string(),
      })),
    }),
  },
  async () => {
    return { menuData: getMenuFromDatabase() };
  }
);

Using Tools

Pass tools to the model and let it decide when to use them:
response, _ := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.5-flash"),
    ai.WithPrompt("What's the weather like in San Francisco?"),
    ai.WithTools(weatherTool),
)
fmt.Println(response.Text())

Tool Choice Strategies

Control when and how the model uses tools:
// Auto: Model decides whether to use tools
resp, _ := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.5-flash"),
    ai.WithPrompt("What's the weather in Tokyo?"),
    ai.WithTools(weatherTool),
    ai.WithToolChoice(ai.ToolChoiceAuto),
)

// Required: Model must use a tool
resp, _ := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.5-flash"),
    ai.WithPrompt("Get the weather data"),
    ai.WithTools(weatherTool),
    ai.WithToolChoice(ai.ToolChoiceRequired),
)

Structured Tool Output

Define complex return types for tools:
type WeatherData struct {
    Location  string  `json:"location"`
    TempC     float64 `json:"temp_c"`
    TempF     float64 `json:"temp_f"`
    Condition string  `json:"condition"`
}

weatherTool := genkit.DefineTool(g, "weather", 
    "Get current weather for a location",
    func(ctx *ai.ToolContext, input WeatherInput) (WeatherData, error) {
        // Fetch weather data
        return simulateWeather(input.Location), nil
    },
)

Tool Interrupts (Human-in-the-Loop)

Pause execution for human approval before taking sensitive actions:
type TransferInput struct {
    ToAccount string  `json:"toAccount" jsonschema:"description=destination account ID"`
    Amount    float64 `json:"amount" jsonschema:"description=amount in dollars (e.g. 50.00 for $50)"`
}

type TransferInterrupt struct {
    Reason    string  `json:"reason"` // "insufficient_balance" or "confirm_large"
    ToAccount string  `json:"toAccount"`
    Amount    float64 `json:"amount"`
    Balance   float64 `json:"balance,omitempty"`
}

transferTool := genkit.DefineTool(g, "transferMoney",
    "Transfers money to another account. Use this when the user wants to send money.",
    func(ctx *ai.ToolContext, input TransferInput) (TransferOutput, error) {
        // Check if this is a large transfer that needs confirmation
        if !ctx.IsResumed() && input.Amount > 100 {
            return TransferOutput{}, ai.InterruptWith(ctx, TransferInterrupt{
                "confirm_large", input.ToAccount, input.Amount, accountBalance,
            })
        }

        // Process the transfer
        accountBalance -= input.Amount
        return TransferOutput{"completed", "Transfer successful", accountBalance}, nil
    })

Handling Interrupts

Handle tool interrupts and resume execution:
resp, err := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.5-flash"),
    ai.WithPrompt("Transfer $5000 to account ABC123"),
    ai.WithTools(transferTool),
)

if resp.FinishReason == ai.FinishReasonInterrupted {
    for _, interrupt := range resp.Interrupts() {
        meta, _ := ai.InterruptAs[TransferInterrupt](interrupt)

        // Get user confirmation
        fmt.Printf("Confirm transfer of $%.2f? (yes/no): ", meta.Amount)
        if getUserConfirmation() {
            // Resume with approval
            part, _ := transferTool.RestartWith(interrupt)
            resp, _ = genkit.Generate(ctx, g,
                ai.WithMessages(resp.History()...),
                ai.WithTools(transferTool),
                ai.WithToolRestarts(part),
            )
        } else {
            // Cancel the transfer
            part, _ := transferTool.RespondWith(interrupt,
                TransferOutput{"cancelled", "Transfer cancelled by user.", accountBalance})
            resp, _ = genkit.Generate(ctx, g,
                ai.WithMessages(resp.History()...),
                ai.WithTools(transferTool),
                ai.WithToolResponses(part),
            )
        }
    }
}

Modifying Tool Input on Resume

Adjust tool parameters when resuming after an interrupt:
if meta.Reason == "insufficient_balance" {
    fmt.Printf("You requested $%.2f but only have $%.2f\n", meta.Amount, meta.Balance)
    
    // Restart with adjusted amount
    part, err := transferTool.RestartWith(interrupt,
        ai.WithNewInput(TransferInput{meta.ToAccount, meta.Balance}))
    
    resp, _ = genkit.Generate(ctx, g,
        ai.WithMessages(resp.History()...),
        ai.WithTools(transferTool),
        ai.WithToolRestarts(part),
    )
}

Multi-Step Tool Workflows

Models can use multiple tools in sequence to accomplish complex tasks:
// Define multiple tools
searchTool := genkit.DefineTool(g, "search", "Search the web", searchFunc)
calculatorTool := genkit.DefineTool(g, "calculator", "Perform calculations", calcFunc)
weatherTool := genkit.DefineTool(g, "weather", "Get weather data", weatherFunc)

// Model can use them in sequence
resp, _ := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.5-flash"),
    ai.WithPrompt("What's the temperature difference between Tokyo and Paris?"),
    ai.WithTools(searchTool, calculatorTool, weatherTool),
)
// Model will:
// 1. Call weatherTool for Tokyo
// 2. Call weatherTool for Paris
// 3. Call calculatorTool to find the difference
// 4. Return the answer

Best Practices

Write Clear Tool Descriptions

Help the model understand when and how to use tools:
Go
genkit.DefineTool(g, "searchProducts",
    "Search for products in the catalog. Use this when the user asks about product availability, features, or pricing.",
    searchFunc,
)

Use Structured Input and Output

Define clear schemas for tool parameters:
Go
type SearchInput struct {
    Query    string   `json:"query" jsonschema:"description=The search query"`
    Category string   `json:"category,omitempty" jsonschema:"enum=electronics,enum=clothing,enum=books"`
    MaxPrice float64  `json:"maxPrice,omitempty" jsonschema:"description=Maximum price filter"`
}

Handle Errors Gracefully

Return meaningful error messages:
Go
func(ctx *ai.ToolContext, input SearchInput) (SearchResult, error) {
    results, err := searchAPI(input.Query)
    if err != nil {
        return SearchResult{}, fmt.Errorf("search failed: %w", err)
    }
    return results, nil
}

Use Tool Interrupts for Sensitive Actions

Always require human approval for:
  • Financial transactions
  • Data deletion
  • Sending emails or messages
  • Making purchases
  • Modifying important settings

Complete Example

Here’s a complete payment agent with tool interrupts:
paymentAgent := genkit.DefineFlow(g, "paymentAgent", 
    func(ctx context.Context, request string) (string, error) {
        resp, err := genkit.Generate(ctx, g,
            ai.WithModelName("googleai/gemini-2.5-flash"),
            ai.WithSystem("You are a helpful payment assistant."),
            ai.WithPrompt(request),
            ai.WithTools(transferMoney),
        )
        if err != nil {
            return "", err
        }

        // Handle interrupts
        for resp.FinishReason == ai.FinishReasonInterrupted {
            var restarts, responses []*ai.Part

            for _, interrupt := range resp.Interrupts() {
                meta, _ := ai.InterruptAs[TransferInterrupt](interrupt)

                if meta.Reason == "confirm_large" {
                    if getUserConfirmation() {
                        part, _ := transferMoney.RestartWith(interrupt)
                        restarts = append(restarts, part)
                    } else {
                        part, _ := transferMoney.RespondWith(interrupt,
                            TransferOutput{"cancelled", "Cancelled by user", balance})
                        responses = append(responses, part)
                    }
                }
            }

            resp, err = genkit.Generate(ctx, g,
                ai.WithMessages(resp.History()...),
                ai.WithTools(transferMoney),
                ai.WithToolRestarts(restarts...),
                ai.WithToolResponses(responses...),
            )
        }

        return resp.Text(), nil
    })

Next Steps

Build docs developers (and LLMs) love