Łączenie wbudowanych narzędzi i wywoływania funkcji

Gemini umożliwia łączenie narzędzi wbudowanych, takich jak google_search, i wywoływania funkcji (znanych też jako narzędzia niestandardowe) w ramach jednej generacji poprzez zachowanie i udostępnienie historii kontekstu wywołań narzędzi. Kombinacje narzędzi wbudowanych i niestandardowych umożliwiają tworzenie złożonych przepływów pracy, w których np. model może opierać się na danych internetowych w czasie rzeczywistym przed wywołaniem konkretnej logiki biznesowej.

Oto przykład, który umożliwia łączenie narzędzi wbudowanych i niestandardowych za pomocą google_search i funkcji niestandardowej getWeather:

Python

from google import genai
from google.genai import types

client = genai.Client()

getWeather = {
    "name": "getWeather",
    "description": "Gets the weather for a requested city.",
    "parameters": {
        "type": "object",
        "properties": {
            "city": {
                "type": "string",
                "description": "The city and state, e.g. Utqiaġvik, Alaska",
            },
        },
        "required": ["city"],
    },
}

# Turn 1: Initial request with Google Search (built-in) and getWeather (custom) tools enabled
response = client.models.generate_content(
    model="gemini-3-flash-preview",
    contents="What is the northernmost city in the United States? What's the weather like there today?",
    config=types.GenerateContentConfig(
      tools=[
        types.Tool(
          google_search=types.ToolGoogleSearch(),  # Built-in tool
          function_declarations=[getWeather]       # Custom tool
        ),
      ],
      include_server_side_tool_invocations=True
    ),
)

for part in response.candidates[0].content.parts:
    if part.tool_call:
        print(f"Tool call: {part.tool_call.tool_type} (ID: {part.tool_call.id})")
    if part.tool_response:
        print(f"Tool response: {part.tool_response.tool_type} (ID: {part.tool_response.id})")
    if part.function_call:
        print(f"Function call: {part.function_call.name} (ID: {part.function_call.id})")

# Turn 2: Manually build history to circulate both tool and function context
history = [
    types.Content(
        role="user",
        parts=[types.Part(text="What is the northernmost city in the United States? What's the weather like there today?")]
    ),
    # Response from Turn 1 includes tool_call, tool_response, and thought_signatures
    response.candidates[0].content,
    # Return the function_response
    types.Content(
        role="user",
        parts=[types.Part(
            function_response=types.FunctionResponse(
                name="getWeather",
                response={"response": "Very cold. 22 degrees Fahrenheit."},
                id=response.candidates[0].content.parts[2].function_call.id # Match the ID from the function_call
            )
        )]
    )
]

response_2 = client.models.generate_content(
    model="gemini-3-flash-preview",
    contents=history,
    config=types.GenerateContentConfig(
      tools=[
        types.Tool(
          google_search=types.ToolGoogleSearch(),
          function_declarations=[getWeather]
        ),
      ],
      # This flag needs to be enabled for built-in tool context circulation and tool combination
      include_server_side_tool_invocations=True
    ),
)

for part in response_2.candidates[0].content.parts:
    if part.text:
        print(part.text)

JavaScript

import { GoogleGenAI } from '@google/genai';

const client = new GoogleGenAI({});

const getWeather = {
    name: "getWeather",
    description: "Get the weather in a given location",
    parameters: {
        type: "OBJECT",
        properties: {
            location: {
                type: "STRING",
                description: "The city and state, e.g. San Francisco, CA"
            }
        },
        required: ["location"]
    }
};

async function run() {
    const model = client.getGenerativeModel({
        model: "gemini-3-flash-preview",
    });

    const tools = [
      { googleSearch: {} },
      { functionDeclarations: [getWeather] }
    ];
    // This flag needs to be enabled for built-in tool context circulation and tool combination
    const toolConfig = { includeServerSideToolInvocations: true };

    // Turn 1: Initial request with Google Search (built-in) and getWeather (custom) tools enabled
    const result1 = await model.generateContent({
        contents: [{role: "user", parts: [{text: "What is the northernmost city in the United States? What's the weather like there today?"}]}],
        tools: tools,
        toolConfig: toolConfig,
    });

    const response1 = result1.response;

    for (const part of response1.candidates[0].content.parts) {
        if (part.toolCall) {
            console.log(`Tool call: ${part.toolCall.toolType} (ID: ${part.toolCall.id})`);
        }
        if (part.toolResponse) {
            console.log(`Tool response: ${part.toolResponse.toolType} (ID: ${part.toolResponse.id})`);
        }
        if (part.functionCall) {
            console.log(`Function call: ${part.functionCall.name} (ID: ${part.functionCall.id})`);
        }
    }

    const functionCallId = response1.candidates[0].content.parts.find(p => p.functionCall)?.functionCall?.id;

    // Turn 2: Manually build history to circulate both tool and function context
    const history = [
        {
            role: "user",
            parts:[{text: "What is the northernmost city in the United States? What's the weather like there today?"}]
        },
        // Response from Turn 1 includes tool_call, tool_response, and thought_signatures
        response1.candidates[0].content,
        // Return the function_response
        {
            role: "user",
            parts: [{
                functionResponse: {
                    name: "getWeather",
                    response: {response: "Very cold. 22 degrees Fahrenheit."},
                    id: functionCallId // Match the ID from the function_call
                }
            }]
        }
    ];

    const result2 = await model.generateContent({
        contents: history,
        tools: tools,
        toolConfig: toolConfig,
    });

    for (const part of result2.response.candidates[0].content.parts) {
        if (part.text) {
            console.log(part.text);
        }
    }
}

run();

Go

package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/google/generative-ai-go/genai"
    "google.golang.org/api/option"
)

func main() {
    ctx := context.Background()
    client, err := genai.NewClient(ctx, option.WithAPIKey(os.Getenv("GEMINI_API_KEY")))
    if err != nil {
        log.Exit(err)
    }
    defer client.Close()

    getWeather := &genai.FunctionDeclaration{
        Name:        "getWeather",
        Description: "Get the weather in a given location",
        Parameters: &genai.Schema{
            Type: genai.Object,
            Properties: map[string]*genai.Schema{
                "location": {
                    Type:        genai.String,
                    Description: "The city and state, e.g. San Francisco, CA",
                },
            },
            Required: []string{"location"},
        },
    }

    model := client.GenerativeModel("gemini-3-flash-preview")
    model.Tools = []*genai.Tool{
        {GoogleSearch: &genai.GoogleSearch{}}, // Built-in tool
        {FunctionDeclarations: []*genai.FunctionDeclaration{getWeather}}, // Custom tool
    }
    ist := true
    model.ToolConfig = &genai.ToolConfig{
        IncludeServerSideToolInvocations: &ist, // This flag needs to be enabled for built-in tool context circulation and tool combination
    }

    chat := model.StartChat()

    // Turn 1: Initial request with Google Search (built-in) and getWeather (custom) tools enabled
    prompt := genai.Text("What is the northernmost city in the United States? What's the weather like there today?")
    resp1, err := chat.SendMessage(ctx, prompt)
    if err != nil {
        log.Exitf("SendMessage failed: %v", err)
    }

    if resp1 == nil || len(resp1.Candidates) == 0 || resp1.Candidates[0].Content == nil {
        log.Exit("empty response from model")
    }

    var functionCallID string
    for _, part := range resp1.Candidates[0].Content.Parts {
        switch p := part.(type) {
        case genai.FunctionCall:
            fmt.Printf("Function call: %s (ID: %s)\n", p.Name, p.ID)
            if p.Name == "getWeather" {
                functionCallID = p.ID
            }
        case genai.ToolCallPart:
            fmt.Printf("Tool call: %s (ID: %s)\n", p.ToolType, p.ID)
        case genai.ToolResponsePart:
            fmt.Printf("Tool response: %s (ID: %s)\n", p.ToolType, p.ID)
        }
    }

    if functionCallID == "" {
        log.Exit("no getWeather function call in response")
    }

    // Turn 2: Provide function result back to model.
    // Chat history automatically includes tool_call, tool_response, and thought_signatures from Turn 1.
    fr := genai.FunctionResponse{
        Name: "getWeather",
        ID:   functionCallID,
        Response: map[string]any{
            "response": "Very cold. 22 degrees Fahrenheit.",
        },
    }

    resp2, err := chat.SendMessage(ctx, fr)
    if err != nil {
        log.Exitf("SendMessage for turn 2 failed: %v", err)
    }

    if resp2 == nil || len(resp2.Candidates) == 0 || resp2.Candidates[0].Content == nil {
        log.Exit("empty response from model in turn 2")
    }

    for _, part := range resp2.Candidates[0].Content.Parts {
        if txt, ok := part.(genai.Text); ok {
            fmt.Println(string(txt))
        }
    }
}

REST

# Turn 1: Initial request with Google Search (built-in) and getWeather (custom) tools enabled
curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
  "contents": [{
    "role": "user",
    "parts": [{
      "text": "What is the northernmost city in the United States? What'\''s the weather like there today?"
    }]
  }],
  "tools": [{
    "googleSearch": {}
  }, {
    "functionDeclarations": [{
      "name": "getWeather",
      "description": "Get the weather in a given location",
      "parameters": {
          "type": "OBJECT",
          "properties": {
              "location": {
                  "type": "STRING",
                  "description": "The city and state, e.g. San Francisco, CA"
              }
          },
          "required": ["location"]
      }
    }]
  }],
  "toolConfig": {
    "includeServerSideToolInvocations": true
  }
}'

# Turn 2: Manually build history to circulate both tool and function context
# The following request assumes you have captured candidates[0].content from Turn 1 response,
# and extracted function_call.id for getWeather.
# Replace FUNCTION_CALL_ID and insert candidate content from turn 1.
curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
  "contents": [
    {
      "role": "user",
      "parts": [{"text": "What is the northernmost city in the United States? What'\''s the weather like there today?"}]
    },
    YOUR_CANDIDATE_CONTENT_FROM_TURN_1_RESPONSE,
    {
      "role": "user",
      "parts": [{
        "functionResponse": {
          "name": "getWeather",
          "id": "FUNCTION_CALL_ID",
          "response": {"response": "Very cold. 22 degrees Fahrenheit."}
        }
      }]
    }
  ],
  "tools": [{
    "googleSearch": {}
  }, {
    "functionDeclarations": [{
      "name": "getWeather",
      "description": "Get the weather in a given location",
      "parameters": {
          "type": "OBJECT",
          "properties": {
              "location": {
                  "type": "STRING",
                  "description": "The city and state, e.g. San Francisco, CA"
              }
          },
          "required": ["location"]
      }
    }]
  }],
  "toolConfig": {
    "includeServerSideToolInvocations": true
  }
}'

Jak to działa

Modele Gemini 3 używają cyrkulacji kontekstu narzędzia, aby umożliwić łączenie narzędzi wbudowanych i niestandardowych. Cyrkulacja kontekstu narzędzia umożliwia zachowanie i udostępnienie kontekstu narzędzi wbudowanych oraz udostępnienie go narzędziom niestandardowym w ramach tego samego wywołania.

Włączanie łączenia narzędzi

  • Aby włączyć cyrkulację kontekstu narzędzia, musisz ustawić flagę include_server_side_tool_invocations na true.
  • Aby wywołać zachowanie kombinacji, dołącz function_declarations wraz z narzędziami wbudowanymi, których chcesz użyć.
    • Jeśli nie uwzględnisz function_declarations, cyrkulacja kontekstu narzędzia nadal będzie działać na uwzględnionych narzędziach wbudowanych, o ile flaga jest ustawiona.

Części zwracane przez interfejs API

W ramach jednej odpowiedzi interfejs API zwraca części toolCall i toolResponse dla wywołania narzędzia wbudowanego. W przypadku wywołania funkcji (narzędzia niestandardowego) interfejs API zwraca część wywołania functionCall, do której użytkownik w następnej kolejce dostarcza część functionResponse.

  • toolCall i toolResponse: interfejs API zwraca te części, aby zachować kontekst narzędzi uruchamianych po stronie serwera oraz wynik ich wykonania na potrzeby następnej kolejki.
  • functionCall i functionResponse: interfejs API wysyła wywołanie funkcji do użytkownika, aby go wypełnił, a użytkownik odsyła wynik w odpowiedzi funkcji (te części są standardowe dla wszystkich wywołań funkcji w interfejsie Gemini API, a nie tylko dla funkcji łączenia narzędzi).
  • (Narzędzie do wykonywania kodu tylko) executableCode i codeExecutionResult: Gdy używasz narzędzia do wykonywania kodu, zamiast functionCall i functionResponse, interfejs API zwraca executableCode (kod wygenerowany przez model, który ma zostać wykonany) i codeExecutionResult (wynik wykonania kodu).

Aby zachować kontekst i umożliwić łączenie narzędzi , musisz w każdej kolejce zwracać do modelu wszystkie części, w tym wszystkie pola, które zawierają.

Krytyczne pola w zwracanych częściach

Niektóre części zwracane przez interfejs API będą zawierać pola id, tool_type, i thought_signature. Te pola są niezbędne do zachowania kontekstu narzędzia (a tym samym do łączenia narzędzi). W kolejnych żądaniach musisz zwracać wszystkie części tak, jak są one podane w odpowiedzi.

  • id: unikalny identyfikator, który mapuje wywołanie na jego odpowiedź. id jest ustawiany we wszystkich odpowiedziach na wywołanie funkcji, niezależnie od cyrkulacji kontekstu narzędzia. W odpowiedzi funkcji musisz podać to samo id, które interfejs API podaje w wywołaniu funkcji. Narzędzia wbudowane automatycznie udostępniają id między wywołaniem narzędzia a odpowiedzią narzędzia.
    • Znajduje się we wszystkich częściach związanych z narzędziami: toolCall, toolResponse, functionCall, functionResponse, executableCode, codeExecutionResult.
  • tool_type: identyfikuje konkretne używane narzędzie; literał narzędzia wbudowanego lub (np. URL_CONTEXT) lub nazwa funkcji (np. getWeather).
    • Znajduje się w częściach toolCall i toolResponse.
  • thought_signature: rzeczywisty zaszyfrowany kontekst osadzony w każdej części zwracanej przez interfejs API. Nie można odtworzyć kontekstu bez sygnatur myśli. Jeśli w każdej kolejce nie zwrócisz sygnatur myśli dla wszystkich części, model zwróci błąd.
    • Znajduje się we wszystkich częściach.

Dane specyficzne dla narzędzia

Niektóre narzędzia wbudowane zwracają argumenty danych widoczne dla użytkownika, które są specyficzne dla typu narzędzia.

Narzędzie Argumenty wywołania narzędzia widoczne dla użytkownika (jeśli występują) Odpowiedź narzędzia widoczna dla użytkownika (jeśli występuje)
GOOGLE_SEARCH queries search_suggestions
GOOGLE_MAPS queries places
google_maps_widget_context_token
URL_CONTEXT urls
Adresy URL do przeglądania
urls_metadata
retrieved_url: przeglądane adresy URL
url_retrieval_status: stan przeglądania
FILE_SEARCH Brak Brak

Przykładowa struktura żądania łączenia narzędzi

Poniższa struktura żądania pokazuje strukturę żądania prompta: „Jakie jest najbardziej wysunięte na północ miasto w Stanach Zjednoczonych? Jaka jest tam dzisiaj pogoda?”. Łączy 3 narzędzia: wbudowane narzędzia Gemini google_search i code_execution oraz funkcję niestandardową get_weather.

{
  "model": "models/gemini-3-flash-preview",
  "contents": [{
    "parts": [{
      "text": "What is the northernmost city in the United States? What's the weather like there today?"
    }],
    "role": "user"
  }, {
    "parts": [{
      "thoughtSignature": "...",
      "toolCall": {
        "toolType": "GOOGLE_SEARCH_WEB",
        "args": {
          "queries": ["northernmost city in the United States"]
        },
        "id": "a7b3k9p2"
      }
    }, {
      "thoughtSignature": "...",
      "toolResponse": {
        "toolType": "GOOGLE_SEARCH_WEB",
        "response": {
          "search_suggestions": "..."
        },
        "id": "a7b3k9p2"
      }
    }, {
      "functionCall": {
        "name": "getWeather",
        "args": {
          "city": "Utqiaġvik, Alaska"
        },
        "id": "m4q8z1v6"
      },
      "thoughtSignature": "..."
    }],
    "role": "model"
  }, {
    "parts": [{
      "functionResponse": {
        "name": "getWeather",
        "response": {
          "response": "Very cold. 22 degrees Fahrenheit."
        },
        "id": "m4q8z1v6"
      }
    }],
    "role": "user"
  }],
  "tools": [{
    "functionDeclarations": [{
      "name": "getWeather"
    }]
  }, {
    "googleSearch": {
    }
  }, {
    "codeExecution": {
    }
  }],
  "toolConfig": {
    "includeServerSideToolInvocations": true
  }
}

Tokeny i ceny

Pamiętaj, że części toolCall i toolResponse w żądaniach są wliczane do prompt_token_count. Ponieważ te pośrednie kroki narzędzia są teraz widoczne i zwracane, stanowią one część historii rozmowy. Dotyczy to tylko przypadku żądań, a nie odpowiedzi.

Wyjątkiem od tej reguły jest narzędzie Wyszukiwarka Google. Wyszukiwarka Google stosuje już własny model cenowy na poziomie zapytania, więc tokeny nie są naliczane podwójnie (patrz strona Ceny).

Więcej informacji znajdziesz na stronie Tokeny.

Ograniczenia

  • Gdy włączona jest flaga include_server_side_tool_invocations, domyślnie używany jest tryb VALIDATED (AUTO nie jest obsługiwany).
  • Narzędzia wbudowane, takie jak google_search, korzystają z informacji o lokalizacji i aktualnej godzinie. Jeśli więc system_instruction lub function_declaration.description zawierają sprzeczne informacje o lokalizacji i czasie, funkcja łączenia narzędzi może nie działać prawidłowo.

Obsługiwane narzędzia

Standardowa cyrkulacja kontekstu narzędzia dotyczy narzędzi po stronie serwera (wbudowanych). Wykonywanie kodu to też narzędzie po stronie serwera, ale ma własne wbudowane rozwiązanie do cyrkulacji kontekstu. Korzystanie z komputera i wywoływanie funkcji to narzędzia po stronie klienta, które też mają wbudowane rozwiązania do cyrkulacji kontekstu.

Narzędzie Strona wykonania Obsługa cyrkulacji kontekstu
Wyszukiwarka Google Po stronie serwera Obsługiwane
Mapy Google Po stronie serwera Obsługiwane
Kontekst adresu URL Po stronie serwera Obsługiwane
Wyszukiwanie plików Po stronie serwera Obsługiwane
Wykonywanie kodu Po stronie serwera Obsługiwane (wbudowane, używa części executableCode i codeExecutionResult)
Korzystanie z komputera Po stronie klienta Obsługiwane (wbudowane, używa części functionCall i functionResponse)
Funkcje niestandardowe Po stronie klienta Obsługiwane (wbudowane, używa części functionCall i functionResponse)

Co dalej?