Use this file to discover all available pages before exploring further.
An agent is a flow that gives a model access to tools and lets it decide when and how to call them. The model can call multiple tools across multiple turns until it has enough information to produce a final answer.Genkit handles the tool call loop automatically: when the model returns a tool request, Genkit executes the tool, feeds the result back to the model, and continues until the model stops calling tools.
The following example shows a simple research agent with two tools: a web search tool and a calculator.
import { genkit, z } from 'genkit';import { googleAI } from '@genkit-ai/google-genai';const ai = genkit({ plugins: [googleAI()], model: 'googleai/gemini-2.0-flash' });// Tool 1: web search (simulated)const searchWeb = ai.defineTool( { name: 'searchWeb', description: 'Search the web for current information on a topic.', inputSchema: z.object({ query: z.string().describe('The search query') }), outputSchema: z.string(), }, async ({ query }) => { // In production, call a real search API return `Search results for "${query}": [result 1, result 2, result 3]`; });// Tool 2: calculatorconst calculate = ai.defineTool( { name: 'calculate', description: 'Evaluate a mathematical expression.', inputSchema: z.object({ expression: z.string().describe('Math expression to evaluate') }), outputSchema: z.number(), }, async ({ expression }) => { // In production, use a safe math evaluation library return eval(expression) as number; });// Define the agent as a flowconst researchAgent = ai.defineFlow( { name: 'researchAgent', inputSchema: z.string(), outputSchema: z.string(), }, async (question) => { const response = await ai.generate({ system: 'You are a helpful research assistant. Use the tools available to answer questions accurately.', prompt: question, tools: [searchWeb, calculate], maxTurns: 5, // allow up to 5 tool-call rounds }); return response.text; });// Run the agentconst answer = await researchAgent('What is 15% of the population of France?');console.log(answer);
The description is the primary signal the model uses to decide whether to call a tool. Be specific and include details about when it should and should not be used.
Tool descriptions are as important as your prompt. A clear, accurate description dramatically improves how reliably the model calls the right tool at the right time.
The maxTurns option caps the number of tool-calling rounds. The default is 5. If the model has not finished within maxTurns iterations, generate() throws a GenkitError.
const response = await ai.generate({ prompt: 'Research the history of the Eiffel Tower and give me 5 key facts.', tools: [searchWeb], maxTurns: 10, // allow more turns for complex research tasks});
After a generate() call completes, you can inspect which tools were called using response.toolRequests. This is useful for logging, debugging, and building audit trails:
const response = await ai.generate({ prompt: 'What is the weather in Tokyo and London?', tools: [getWeather],});// response.toolRequests contains ALL tool requests made during the runfor (const toolRequest of response.toolRequests) { console.log(`Tool called: ${toolRequest.toolRequest.name}`); console.log(`Input: ${JSON.stringify(toolRequest.toolRequest.input)}`);}console.log('Final answer:', response.text);
response.toolRequests only contains tool requests from the final model message. To see all tool calls across all turns, inspect response.messages.
By default, the model can choose whether to call tools. Set toolChoice: 'required' to force the model to call at least one tool, or toolChoice: 'none' to disable tool calls even if tools are listed:
// Force the model to always call a toolconst response = await ai.generate({ prompt: 'I need to check the user account for alice@example.com.', tools: [lookupUser], toolChoice: 'required', // model MUST call a tool});// Disable tool calls (useful for read-only analysis turn)const analysis = await ai.generate({ prompt: 'Summarize the data we have collected so far.', messages: previousMessages, tools: [lookupUser], toolChoice: 'none', // model must NOT call tools});
Valid values:
'auto' (default) — model decides whether to call tools.
Set returnToolRequests: true to stop the automatic tool call loop and handle tool execution yourself. This gives you full control over how tools are invoked:
const response = await ai.generate({ prompt: 'Delete all records older than 30 days.', tools: [deleteRecords], returnToolRequests: true, // stop before executing tools});if (response.toolRequests.length > 0) { const toolRequest = response.toolRequests[0]; console.log('Model wants to call:', toolRequest.toolRequest.name); console.log('With input:', toolRequest.toolRequest.input); // Add a human-approval step here before proceeding const approved = await askHumanForApproval(toolRequest); if (!approved) { throw new Error('Operation not approved.'); }}
Genkit supports a formal interrupt mechanism for workflows that require human approval or input before a tool runs. Use ai.defineInterrupt() to mark a tool as interruptible:
const approvePayment = ai.defineInterrupt({ name: 'approvePayment', description: 'Request approval to process a payment.', inputSchema: z.object({ amount: z.number(), recipient: z.string(), }),});const response = await ai.generate({ prompt: 'Process a $500 payment to vendor@example.com.', tools: [approvePayment],});// If the model called an interrupt tool, response.interrupts is non-emptyif (response.interrupts.length > 0) { const interrupt = response.interrupts[0]; // Persist response.messages — you need them to resume later const savedMessages = response.messages; // ... wait for human approval ... // Resume by providing the tool response const resumedResponse = await ai.generate({ messages: savedMessages, resume: approvePayment.respond(interrupt, { approved: true }), }); console.log(resumedResponse.text);}
package mainimport ( "context" "fmt" "log" "github.com/firebase/genkit/go/ai" "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/plugins/googlegenai")type WeatherInput struct { City string `json:"city"`}func main() { ctx := context.Background() g := genkit.Init(ctx, genkit.WithPlugins(&googlegenai.GoogleAI{}), genkit.WithDefaultModel("googleai/gemini-2.0-flash"), ) weatherTool := genkit.DefineTool(g, "getWeather", "Get the current weather for a city.", func(ctx *ai.ToolContext, input WeatherInput) (string, error) { // In production, call a real weather API return fmt.Sprintf("Weather in %s: 22°C, partly cloudy", input.City), nil }, ) resp, err := genkit.Generate(ctx, g, ai.WithPrompt("What is the weather like in Tokyo and Paris today?"), ai.WithTools(weatherTool), ai.WithMaxTurns(5), ) if err != nil { log.Fatal(err) } fmt.Println(resp.Text())}
Tools
Learn how tools are defined and resolved.
Flows
Wrap agents in observable, deployable flows.
Structured output
Return typed results from agent runs.
Sessions
Persist agent conversation history across requests.