Pydantic et définition des outils avec le SDK xAI
Sur cette page
Leçon corrigée le 3 novembre 2026 d’après la documentation Function calling de xAI et le SDK
xai-sdk1.21.0 : le SDK ne propose ni décorateur@client.tool()ni exécution automatique des fonctions ; les outils se construisent avectool()(à partir d’un schéma Pydantic, par exemple) et les résultats se renvoient avectool_result. Source : Function calling.
Simplifier la définition des outils
Écrire des schémas JSON à la main est verbeux et sujet aux erreurs. Deux approches permettent de générer les définitions d’outils à partir de votre code Python : les modèles Pydantic, que le SDK xAI transforme en outils avec sa fonction tool(), et un petit décorateur maison qui lit la signature de vos fonctions.
Approche Pydantic
Pydantic est une bibliothèque Python qui permet de définir des modèles de données avec validation automatique. L’API Grok peut utiliser ces modèles pour générer les schémas JSON des outils.
from pydantic import BaseModel, Field
from typing import Optional
class WeatherParams(BaseModel):
city: str = Field(description="Nom de la ville")
unit: Optional[str] = Field(
default="celsius",
description="Unité de température",
enum=["celsius", "fahrenheit"]
)
Ce modèle Pydantic génère automatiquement le schéma JSON équivalent à ce que vous auriez écrit manuellement. Les avantages :
- Type checking : Python vérifie les types à l’exécution
- Validation : Pydantic valide les données automatiquement
- Documentation : les
Field(description=...)servent de documentation - Réutilisabilité : le même modèle sert pour la validation et la documentation API
Modèles complexes
Pydantic gère les structures imbriquées, les listes et les types avancés :
from typing import List, Optional
from pydantic import BaseModel, Field
class Address(BaseModel):
street: str = Field(description="Numéro et nom de rue")
city: str = Field(description="Ville")
zip_code: str = Field(description="Code postal")
country: str = Field(default="FR", description="Code pays ISO")
class CreateOrderParams(BaseModel):
product_id: str = Field(description="Identifiant du produit")
quantity: int = Field(description="Quantité commandée", ge=1, le=100)
shipping_address: Address = Field(description="Adresse de livraison")
notes: Optional[str] = Field(
default=None,
description="Instructions spéciales de livraison"
)
tags: List[str] = Field(
default=[],
description="Tags pour le suivi de commande"
)
Les contraintes ge (greater or equal) et le (less or equal) sont traduites en contraintes JSON Schema que le modèle respecte.
La fonction tool() du SDK xAI
Le SDK xAI ne prend pas une classe Pydantic telle quelle : il attend une définition d’outil, que construit sa fonction tool() à partir d’un nom, d’une description et d’un schéma JSON. Pydantic fournit justement ce schéma, avec model_json_schema() :
from xai_sdk.chat import tool
outils = [
tool(
name="get_weather",
description="Obtenir la météo actuelle d'une ville",
parameters=WeatherParams.model_json_schema(),
),
]
Utilisation avec le SDK
L’outil se déclare à la création de la conversation. Le SDK n’exécute pas vos fonctions : quand Grok demande un appel, votre code l’exécute, ajoute le résultat avec tool_result, puis relance la génération.
import json
from xai_sdk import Client
from xai_sdk.chat import tool_result, user
client = Client(api_key="votre-cle-api")
chat = client.chat.create(model="grok-4.5", tools=outils)
chat.append(user("Météo à Bordeaux ?"))
response = chat.sample()
if response.tool_calls:
chat.append(response)
for appel in response.tool_calls:
args = WeatherParams.model_validate_json(appel.function.arguments)
resultat = {"temp": 22, "unit": args.unit, "city": args.city} # votre implémentation
chat.append(tool_result(json.dumps(resultat)))
response = chat.sample()
print(response.content)
model_validate_json vérifie au passage que les arguments produits par le modèle respectent le schéma : un argument manquant ou d’un mauvais type lève une erreur avant que votre fonction ne soit appelée.
Un décorateur maison pour aller plus vite
Le SDK ne fournit pas de décorateur @tool. Rien n’empêche d’en écrire un, en quelques lignes, qui lit la signature et la docstring d’une fonction :
import inspect
from pydantic import create_model
from xai_sdk.chat import tool
def outil(fonction):
"""Construit la définition xAI d'une fonction à partir de sa signature et de sa docstring."""
champs = {
nom: (p.annotation, ... if p.default is inspect.Parameter.empty else p.default)
for nom, p in inspect.signature(fonction).parameters.items()
}
schema = create_model(fonction.__name__, **champs).model_json_schema()
description = (inspect.getdoc(fonction) or fonction.__name__).split("\n\n")[0]
return tool(name=fonction.__name__, description=description, parameters=schema)
def get_weather(city: str, unit: str = "celsius") -> dict:
"""Obtenir la météo actuelle d'une ville (ex. Paris, Lyon)."""
return {"temp": 22, "unit": unit, "city": city}
outils = [outil(get_weather)]
Le nom de la fonction devient celui de l’outil, le premier paragraphe de la docstring sa description, et les paramètres sans valeur par défaut deviennent obligatoires. Les descriptions de chaque paramètre, en revanche, ne sont pas extraites : pour des paramètres ambigus, revenez à un modèle Pydantic avec Field(description=...).
Comparaison des trois approches
| Approche | Avantages | Limites |
|---|---|---|
| Schéma JSON écrit à la main | Contrôle total, aucune dépendance | Verbeux, erreurs de frappe non détectées |
Modèle Pydantic + tool() | Validation des arguments, descriptions par champ | Une classe par outil |
| Décorateur maison | Le plus concis | Pas de description par paramètre, code à maintenir |
Bonnes pratiques pour les descriptions
Quelle que soit l’approche, la description est ce que lit le modèle pour décider d’appeler l’outil. Dites quand l’utiliser, ce qu’il renvoie, et le format des paramètres :
class SearchProductsParams(BaseModel):
query: str = Field(description="Termes de recherche : nom du produit ou mots-clés")
category: str = Field(default="all", description="Catégorie : electronics, clothing, food ou all")
max_results: int = Field(default=10, ge=1, le=50, description="Nombre maximum de résultats")
recherche = tool(
name="search_products",
description=(
"Rechercher des produits dans le catalogue e-commerce. À utiliser quand "
"l'utilisateur cherche un produit par nom, description ou catégorie. "
"Renvoie une liste de produits avec prix, disponibilité et lien."
),
parameters=SearchProductsParams.model_json_schema(),
)
Points clés à retenir
- Pydantic génère les schémas JSON à partir de modèles Python, avec validation des arguments
- Le SDK xAI construit les outils avec
tool(name=..., description=..., parameters=...) - Le SDK n’exécute pas vos fonctions : votre code le fait, puis renvoie le résultat avec
tool_result - Le SDK n’a pas de décorateur
@tool; un décorateur maison est possible, au prix des descriptions par paramètre - Soignez les descriptions : ce sont elles que le modèle lit pour choisir un outil