Combiner les outils intégrés et l'appel de fonction

Gemini permet de combiner des outils intégrés, tels que google_search, et l'appel de fonction (également appelé outils personnalisés) dans une même génération en conservant et en exposant l'historique du contexte des appels d'outils. Les combinaisons d'outils intégrées et personnalisées permettent de créer des workflows complexes et agentiques où, par exemple, le modèle peut s'ancrer dans des données Web en temps réel avant d'appeler votre logique métier spécifique.

Voici un exemple qui permet des combinaisons d'outils intégrés et personnalisés avec google_search et une fonction personnalisée 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
  }
}'

Fonctionnement

Les modèles Gemini 3 utilisent la circulation du contexte des outils pour permettre les combinaisons d'outils intégrés et personnalisés. La circulation du contexte des outils permet de préserver et d'exposer le contexte des outils intégrés, et de le partager avec les outils personnalisés lors du même appel, tour après tour.

Activer la combinaison d'outils

  • Vous devez définir l'option include_server_side_tool_invocations sur true pour activer la circulation du contexte de l'outil.
  • Incluez function_declarations, ainsi que les outils intégrés que vous souhaitez utiliser, pour déclencher le comportement combiné.
    • Si vous n'incluez pas function_declarations, la circulation du contexte d'outil agira toujours sur les outils intégrés inclus, à condition que l'indicateur soit défini.

L'API renvoie des pièces

Dans une même réponse, l'API renvoie les parties toolCall et toolResponse pour l'appel d'outil intégré. Pour l'appel de fonction (outil personnalisé), l'API renvoie la partie d'appel functionCall, à laquelle l'utilisateur fournit la partie functionResponse au tour suivant.

  • toolCall et toolResponse : l'API renvoie ces parties pour préserver le contexte des outils exécutés côté serveur et le résultat de leur exécution pour le prochain tour.
  • functionCall et functionResponse : l'API envoie l'appel de fonction à l'utilisateur pour qu'il le remplisse, et l'utilisateur renvoie le résultat dans la réponse de la fonction (ces parties sont standard pour tous les appels de fonction dans l'API Gemini, et ne sont pas propres à la fonctionnalité de combinaison d'outils).
  • (Outil d'exécution de code uniquement) executableCode et codeExecutionResult : Lorsque vous utilisez l'outil d'exécution de code, au lieu de functionCall et functionResponse, l'API renvoie executableCode (le code généré par le modèle qui est censé être exécuté) et codeExecutionResult (le résultat du code exécutable).

Vous devez renvoyer toutes les parties, y compris tous les champs qu'elles contiennent, au modèle à chaque tour pour maintenir le contexte et permettre les combinaisons d'outils.

Champs critiques dans les pièces renvoyées

Certaines parties renvoyées par l'API incluent les champs id, tool_type et thought_signature. Ces champs sont essentiels pour maintenir le contexte de l'outil (et donc pour les combinaisons d'outils). Vous devez renvoyer toutes les parties telles qu'elles sont indiquées dans la réponse dans vos requêtes ultérieures.

  • id : identifiant unique qui mappe un appel à sa réponse. id est défini sur toutes les réponses aux appels de fonction, quelle que soit la circulation du contexte de l'outil. Vous devez fournir le même id dans la réponse de la fonction que celui fourni par l'API dans l'appel de la fonction. Les outils intégrés partagent automatiquement le id entre l'appel d'outil et la réponse de l'outil.
    • Présent dans toutes les parties liées aux outils : toolCall, toolResponse, functionCall, functionResponse, executableCode, codeExecutionResult
  • tool_type : identifie l'outil spécifique utilisé, le nom de l'outil littéral intégré (par exemple, URL_CONTEXT) ou le nom de la fonction (par exemple, getWeather).
    • Trouvé dans les parties toolCall et toolResponse.
  • thought_signature : contexte chiffré réel intégré à chaque partie renvoyée par l'API. Le contexte ne peut pas être reconstitué sans les signatures de pensée. Si vous ne renvoyez pas les signatures de pensée pour toutes les parties à chaque tour, le modèle générera une erreur.
    • Trouvé dans toutes les parties.

Données spécifiques à l'outil

Certains outils intégrés renvoient des arguments de données visibles par l'utilisateur, spécifiques au type d'outil.

Outil Arguments d'appel d'outil visibles par l'utilisateur (le cas échéant) Réponse de l'outil visible par l'utilisateur (le cas échéant)
GOOGLE_SEARCH queries search_suggestions
GOOGLE_MAPS queries places
google_maps_widget_context_token
URL_CONTEXT urls
 URL à parcourir
urls_metadata
retrieved_url : URL parcourues
url_retrieval_status : état de l'exploration
FILE_SEARCH Aucun Aucun

Exemple de structure de demande de combinaison d'outils

La structure de requête suivante montre la structure de requête de la requête : "Quelle est la ville la plus au nord des États-Unis ? Quel temps fait-il là-bas aujourd'hui ?" Il combine trois outils : les outils Gemini intégrés google_search et code_execution, ainsi qu'une fonction personnalisée 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
  }
}

Jetons et tarifs

Notez que les parties toolCall et toolResponse des requêtes sont comptabilisées dans prompt_token_count. Étant donné que ces étapes intermédiaires de l'outil sont désormais visibles et vous sont renvoyées, elles font partie de l'historique des conversations. Cela ne s'applique qu'aux requêtes, et non aux réponses.

L'outil de recherche Google fait exception à cette règle. La recherche Google applique déjà son propre modèle de tarification au niveau des requêtes. Les jetons ne sont donc pas facturés deux fois (consultez la page Tarifs).

Pour en savoir plus, consultez la page Jetons.

Limites

  • Par défaut, le mode VALIDATED (le mode AUTO n'est pas pris en charge) est activé lorsque l'indicateur include_server_side_tool_invocations est activé.
  • Les outils intégrés tels que google_search s'appuient sur des informations de localisation et d'heure actuelle. Par conséquent, si votre system_instruction ou votre function_declaration.description comporte des informations de localisation et d'heure incohérentes, la fonctionnalité de combinaison d'outils risque de ne pas fonctionner correctement.

Outils compatibles

La circulation standard du contexte d'outil s'applique aux outils côté serveur (intégrés). L'exécution de code est également un outil côté serveur, mais elle dispose de sa propre solution intégrée pour la circulation du contexte. L'utilisation de l'ordinateur et l'appel de fonctions sont des outils côté client qui disposent également de solutions intégrées pour la circulation du contexte.

Outil Côté exécution Compatibilité avec la circulation du contexte
La recherche Google Côté serveur Compatible
Google Maps Côté serveur Compatible
Contexte de l'URL Côté serveur Compatible
Recherche de fichiers Côté serveur Compatible
Exécution de code Côté serveur Pris en charge (intégré, utilise les pièces executableCode et codeExecutionResult)
Utilisation de l'ordinateur Côté client Pris en charge (intégré, utilise les pièces functionCall et functionResponse)
Fonctions personnalisées Côté client Pris en charge (intégré, utilise les pièces functionCall et functionResponse)

Étape suivante