+200 XP

Function calling et structured outputs

Donner à Gemini une fonction qu'il peut appeler le transforme d'un générateur de texte en quelque chose qui peut lire votre agenda, interroger votre API de pricing ou requêter votre base de données, et récupérer du JSON propre signifie que votre code peut réellement exploiter le résultat sans bricolage à coups de regex. Ces deux capacités sont la colonne vertébrale de toute intégration Gemini sérieuse, et elles fonctionnent très différemment sous le capot. Voyons les deux en profondeur.

Le function calling est une négociation, pas une exécution

Le changement de perspective le plus important : Gemini n'exécute jamais votre fonction. Il décide seulement *qu'une* fonction doit être appelée et *avec quels arguments*. C'est vous qui exécutez le code. Vous renvoyez le résultat. Gemini continue.

Cet aller-retour, c'est la boucle de tool calling :

  1. Vous envoyez un prompt plus une liste de *déclarations* de fonctions (nom, description, paramètres).
  2. Gemini répond soit par du texte normal, soit par une part functionCall contenant la fonction choisie et les arguments.
  3. Votre code exécute réellement cette fonction.
  4. Vous renvoyez le résultat sous forme de functionResponse.
  5. Gemini utilise le résultat pour produire sa réponse finale (ou appeler une autre fonction).

Le modèle fait de la reconnaissance d'intention structurée. Votre travail consiste à câbler l'exécution réelle et à continuer de réinjecter l'état dans la conversation.

Déclarer une fonction

Une déclaration de fonction est un schéma. Gemini lit les champs description pour décider quand et comment l'appeler, donc traitez-les comme du prompt engineering, pas comme de la documentation. Des descriptions vagues produisent des appels erronés.

python
from google import genai
from google.genai import types

client = genai.Client()

get_weather = types.FunctionDeclaration(
    name="get_weather",
    description="Get the current temperature for a city. Use only when the user asks about weather.",
    parameters={
        "type": "object",
        "properties": {
            "city": {"type": "string", "description": "City name, e.g. 'Lisbon'"},
            "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
        },
        "required": ["city"],
    },
)

weather_tool = types.Tool(function_declarations=[get_weather])

Ceci utilise le Google Gen AI SDK unifié, la bibliothèque Python actuellement recommandée. Le même SDK cible à la fois l'API Gemini (via les clés AI Studio) et Vertex AI en changeant la configuration du client, vous n'écrivez donc la boucle qu'une fois.

Exécuter la boucle

Voici l'aller-retour complet avec un modèle Flash. Notez que le *résultat de la fonction* revient dans la conversation comme une nouvelle content part, pas comme du texte brut.

python
def get_weather_impl(city, unit="celsius"):
    return {"city": city, "temp": 19, "unit": unit}  # l'appel API réel se place ici

config = types.GenerateContentConfig(tools=[weather_tool])
contents = ["What's the weather in Lisbon right now?"]

resp = client.models.generate_content(
    model="gemini-2.5-flash", contents=contents, config=config
)

part = resp.candidates[0].content.parts[0]
if part.function_call:
    call = part.function_call
    result = get_weather_impl(**call.args)

    contents.append(resp.candidates[0].content)  # l'appel du modèle
    contents.append(types.Content(role="user", parts=[
        types.Part.from_function_response(name=call.name, response=result)
    ]))

    final = client.models.generate_content(
        model="gemini-2.5-flash", contents=contents, config=config
    )
    print(final.text)  # "It's 19°C in Lisbon right now."

Les lignes clés sont les deux appels contents.append. Vous rejouez l'appel de fonction du modèle dans l'historique, puis vous ajoutez le résultat. Sautez le premier append et Gemini perd la trace de ce qu'il a demandé.

Laisser Gemini appeler plusieurs fonctions

Donnez-lui plus d'une déclaration et Gemini choisit la bonne, ou en demande plusieurs. Le parallel function calling, c'est quand une seule réponse contient plusieurs parts functionCall (par exemple « compare la météo à Lisbonne et Madrid » produit deux appels d'un coup). Bouclez sur resp.candidates[0].content.parts, exécutez chacun, et ajoutez toutes les réponses ensemble avant le tour suivant.

Vous pouvez aussi contraindre le comportement avec tool_config. Régler le mode de function calling sur ANY force Gemini à appeler une fonction plutôt que de répondre en prose, ce qui est utile quand un appel d'outil est le seul résultat acceptable :

python
config = types.GenerateContentConfig(
    tools=[weather_tool],
    tool_config=types.ToolConfig(
        function_calling_config=types.FunctionCallingConfig(mode="ANY")
    ),
)

Les modes sont AUTO (par défaut, le modèle décide), ANY (doit appeler quelque chose) et NONE (n'appelle jamais). Utilisez `ANY` avec une liste `allowed_function_names` pour cantonner le modèle à un sous-ensemble précis.

Structured outputs : quand vous voulez des données, pas un outil

Le function calling sert à *agir*. Le structured output sert à *extraire*. Si vous voulez simplement que Gemini renvoie du JSON propre et parsable, ne le simulez pas avec un outil. Utilisez la fonctionnalité de response schema, qui contraint le décodage du modèle pour que la sortie corresponde à coup sûr à votre schéma.

C'est du constrained decoding : Gemini est restreint au moment de la génération à ne produire que des tokens qui maintiennent la sortie valide vis-à-vis de votre schéma. Vous n'espérez pas du JSON. Vous l'obtenez.

python
from pydantic import BaseModel

class Invoice(BaseModel):
    vendor: str
    total: float
    currency: str
    line_items: list[str]

resp = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="Extract the invoice details from this email: ...",
    config=types.GenerateContentConfig(
        response_mime_type="application/json",
        response_schema=Invoice,
    ),
)

invoice = Invoice.model_validate_json(resp.text)
print(invoice.total, invoice.currency)

Passer un modèle Pydantic comme response_schema est la voie idiomatique : le SDK le convertit en schéma, Gemini le respecte, et vous validez directement en objets typés. Pas de prompt qui supplie « ne renvoie que du JSON ». Pas de blocs markdown parasites à nettoyer.

Quand le schéma est dynamique

Si votre structure est décidée à l'exécution, passez un dict de schéma brut plutôt qu'une classe Pydantic. Utilisez propertyOrdering pour figer l'ordre des champs, car l'ordre peut affecter la qualité sur des extractions complexes :

python
schema = {
    "type": "object",
    "properties": {
        "sentiment": {"type": "string", "enum": ["positive", "neutral", "negative"]},
        "topics": {"type": "array", "items": {"type": "string"}},
    },
    "required": ["sentiment", "topics"],
    "propertyOrdering": ["sentiment", "topics"],
}

Pour la classification en particulier, il existe aussi response_mime_type="text/x.enum" avec un schéma enum, qui force la réponse à être exactement l'un de vos labels et rien d'autre. C'est la manière la plus propre de faire de la classification mono-label.

Choisir entre les deux

Ils se recoupent, soyez donc délibéré :

  • Structured output quand vous maîtrisez toute l'interaction et voulez juste des données parsées en retour d'un seul appel. Plus rapide, plus simple, forme garantie.
  • Function calling quand Gemini doit aller chercher *à l'extérieur* de lui-même : données live, vos APIs, effets de bord, raisonnement multi-étapes où chaque étape dépend de résultats réels.

Un pattern courant combine les deux : un appel de fonction récupère des données réelles, puis une étape de génération finale les renvoie via un response schema pour que votre code en aval reste typé.

Ne réinventez pas ce que la plateforme fournit

Certaines « fonctions » existent déjà comme outils intégrés. Le grounding avec Google Search, le code execution et l'URL context sont des outils natifs que vous activez dans la config plutôt que d'implémenter vous-même. Si vous avez besoin de faits récents, activez le Search grounding au lieu d'écrire votre propre fonction de recherche :

python
config = types.GenerateContentConfig(
    tools=[types.Tool(google_search=types.GoogleSearch())]
)

Vous ne pouvez pas librement mélanger certains outils intégrés avec vos propres déclarations de fonctions dans un même appel, et les règles diffèrent entre l'API Gemini et Vertex AI : vérifiez l'outil dont vous avez besoin avant de combiner.

Function Calling with the Gemini API

Watch on YouTube

Vérification des acquis

1. Quelle est la distinction conceptuelle la plus importante sur la façon dont Gemini gère le function calling ?

2. Pourquoi la leçon dit-elle que les champs `description` de la fonction doivent être traités comme du prompt engineering plutôt que comme de la documentation ?

3. Dans la boucle de tool calling, qu'est-ce que votre code renvoie à Gemini après avoir réellement exécuté la fonction choisie ?

CHOIX MULTIPLES

4. Sélectionnez TOUTES les affirmations correctes sur la boucle de tool calling et les déclarations de fonctions.

Sélectionnez toutes les réponses correctes.

CHOIX MULTIPLES

5. Sélectionnez TOUTES les affirmations correctes sur les bénéfices et la conception du function calling et des structured outputs décrits dans la leçon.

Sélectionnez toutes les réponses correctes.

Prototypez dans AI Studio, puis passez à l'échelle correctement

Avant d'écrire la moindre boucle, construisez la déclaration de fonction visuellement dans Google AI Studio. Vous pouvez définir des outils, voir Gemini émettre des parts functionCall, et copier du code fonctionnel dans votre langage. C'est le moyen le plus rapide d'ajuster ces fameuses chaînes description.

Quand vous passez en production, la décision porte sur *l'endroit* où tourne la boucle :

  • API Gemini (ai.google.dev) pour des backends d'applications et des services rapides. Le code SDK ci-dessus suffit.
  • Vertex AI quand vous avez besoin de contrôles entreprise : IAM, VPC, résidence des données et déploiement managé. Même SDK, init du client différente.
  • Agent Development Kit (ADK) quand un modèle avec une poignée d'outils devient un véritable agent doté de planification, de mémoire et de nombreux outils. ADK vous fournit la boucle, l'état et l'orchestration d'outils sous forme de framework, ce qui vous évite d'écrire à la main la tuyauterie d'ajout de réponses. Voir la documentation ADK pour l'approche agent-first.

Pour de l'automatisation Workspace interne, vous n'avez pas toujours besoin de tout cela. Apps Script peut appeler Gemini et agir directement sur Docs, Sheets ou Gmail, ce qui est souvent le chemin le plus court vers un outil fonctionnel dans Google Workspace.

Détails durement acquis qui vous feront gagner des heures

Les descriptions sont des prompts. La précision de Gemini pour choisir et remplir les fonctions suit presque entièrement la qualité de vos champs description et de la documentation des paramètres. Explicitez *quand* utiliser une fonction et *quand ne pas* l'utiliser.

Validez chaque argument. Gemini remplit les arguments à partir d'un schéma, mais il peut quand même halluciner une valeur plausible mais fausse, surtout pour des IDs ou des enums dont il se souvient à moitié. Traitez les arguments de fonction comme une entrée utilisateur non fiable. Validez avant d'exécuter quoi que ce soit ayant des effets de bord.

Utilisez `required` et `enum` de façon agressive. Chaque contrainte que vous placez dans le schéma est une contrainte que le modèle doit satisfaire. Les enums éliminent des classes entières de valeurs parasites. Les champs requis empêchent les appels à moitié remplis.

Choisissez le tier par tâche. Flash gère la grande majorité du routage d'outils et de l'extraction avec une latence et un coût faibles. Passez à Pro quand la décision sur *quel* outil appeler demande un vrai raisonnement sur un long contexte, ou quand l'extraction dépend d'une logique subtile multi-documents.

Renvoyez toujours les résultats de fonction. Le bug le plus fréquent est d'oublier d'ajouter l'appel de fonction du modèle avant la réponse. Sans cela, l'historique de conversation est incohérent et la réponse finale dérive.

Points clés à retenir

  • Vous exécutez, Gemini décide. La boucle de tool calling, c'est : déclarer les fonctions, recevoir un functionCall, l'exécuter vous-même, ajouter à la fois l'appel et le functionResponse, puis générer la réponse finale.
  • Utilisez `response_schema` avec un modèle Pydantic pour obtenir du JSON valide garanti via le constrained decoding. Arrêtez de demander du JSON par prompt et de nettoyer des blocs markdown.
  • Agir vs extraire : le function calling va chercher hors du modèle des données live et des effets de bord ; le structured output parse des données à partir d'un seul appel. Combinez-les quand vous récupérez des données réelles, puis les renvoyez typées.
  • Investissez dans les champs `description`, les enums et `required`. La précision des outils tient surtout à la qualité du schéma, et chaque contrainte en est une que le modèle doit satisfaire.
  • Prototypez dans AI Studio, puis passez à l'API Gemini, Vertex AI ou ADK selon que vous avez besoin d'échelle, de contrôles entreprise ou d'une orchestration d'agent complète, et validez toujours les arguments de fonction avant d'agir dessus.