Aller au contenu principal

Définir des Fonctions en Python

De la théorie au code

Vous connaissez maintenant le processus en 5 étapes et la structure JSON Schema. Il est temps d’écrire du vrai code Python. Dans cette leçon, vous allez créer des fonctions, leurs spécifications et préparer le tout pour l’API Mistral.

Installer le SDK Mistral

Commencez par installer le package officiel :

pip install mistralai

Configurez votre clé API. La bonne pratique est d’utiliser une variable d’environnement :

export MISTRAL_API_KEY="votre-clé-api"
import os
from mistralai import Mistral

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

Créer les fonctions Python

Prenons un exemple concret : un système de suivi de paiements avec un DataFrame Pandas. C’est l’exemple classique de la documentation Mistral, et il illustre parfaitement le concept.

import pandas as pd
import json

# Données de paiements
data = {
    "transaction_id": ["T1001", "T1002", "T1003", "T1004", "T1005"],
    "customer_id": ["C001", "C002", "C003", "C002", "C001"],
    "payment_amount": [125.50, 89.99, 350.00, 45.00, 210.75],
    "payment_date": ["2026-03-15", "2026-03-16", "2026-03-17", "2026-03-17", "2026-03-18"],
    "payment_status": ["payé", "payé", "en_attente", "payé", "échoué"]
}
df = pd.DataFrame(data)

Maintenant, définissez les fonctions qui accèdent à ces données :

def retrieve_payment_status(df: pd.DataFrame, transaction_id: str) -> str:
    """Récupère le statut d'un paiement par son identifiant."""
    if transaction_id in df["transaction_id"].values:
        status = df[df["transaction_id"] == transaction_id]["payment_status"].values[0]
        return json.dumps({"status": status})
    return json.dumps({"error": f"Transaction {transaction_id} introuvable"})


def retrieve_payment_date(df: pd.DataFrame, transaction_id: str) -> str:
    """Récupère la date d'un paiement par son identifiant."""
    if transaction_id in df["transaction_id"].values:
        date = df[df["transaction_id"] == transaction_id]["payment_date"].values[0]
        return json.dumps({"date": date})
    return json.dumps({"error": f"Transaction {transaction_id} introuvable"})

Deux points importants :

  • Les fonctions retournent toujours une chaîne JSON — c’est ce que le modèle attend comme résultat de tool
  • Elles gèrent le cas d’erreur (transaction introuvable) — vous verrez plus tard pourquoi c’est essentiel

Écrire les spécifications JSON

Chaque fonction a besoin de sa spécification pour que le modèle la comprenne :

tools = [
    {
        "type": "function",
        "function": {
            "name": "retrieve_payment_status",
            "description": "Récupère le statut d'un paiement (payé, en_attente, échoué) "
                           "à partir de l'identifiant de transaction.",
            "parameters": {
                "type": "object",
                "properties": {
                    "transaction_id": {
                        "type": "string",
                        "description": "Identifiant unique de la transaction (ex: T1001)"
                    }
                },
                "required": ["transaction_id"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "retrieve_payment_date",
            "description": "Récupère la date à laquelle un paiement a été effectué "
                           "à partir de l'identifiant de transaction.",
            "parameters": {
                "type": "object",
                "properties": {
                    "transaction_id": {
                        "type": "string",
                        "description": "Identifiant unique de la transaction (ex: T1001)"
                    }
                },
                "required": ["transaction_id"]
            }
        }
    }
]

Organiser les fonctions dans un dictionnaire

Pour pouvoir appeler dynamiquement la bonne fonction à partir de son nom (retourné par le modèle), créez un dictionnaire de dispatch :

from functools import partial

# On fixe le DataFrame dans chaque fonction
available_functions = {
    "retrieve_payment_status": partial(retrieve_payment_status, df),
    "retrieve_payment_date": partial(retrieve_payment_date, df),
}

functools.partial permet de pré-remplir le paramètre df pour que l’appel dynamique n’ait plus qu’à passer le transaction_id. Ainsi, quand le modèle dit « appelle retrieve_payment_status avec transaction_id=T1001 », votre code fait simplement :

function_name = "retrieve_payment_status"
function_params = {"transaction_id": "T1001"}

result = available_functions[function_name](**function_params)
# → '{"status": "payé"}'

L’option tool_choice

Quand vous envoyez des tools à l’API, vous pouvez contrôler comment le modèle les utilise via le paramètre tool_choice :

# Le modèle décide s'il utilise un tool ou répond directement (par défaut)
response = client.chat.complete(
    model="mistral-large-latest",
    messages=messages,
    tools=tools,
    tool_choice="auto"
)

# Force le modèle à utiliser un tool
response = client.chat.complete(
    model="mistral-large-latest",
    messages=messages,
    tools=tools,
    tool_choice="any"
)

# Empêche le modèle d'utiliser les tools
response = client.chat.complete(
    model="mistral-large-latest",
    messages=messages,
    tools=tools,
    tool_choice="none"
)

Le mode "auto" est le plus courant en production. Le modèle répond directement aux questions générales et utilise les fonctions uniquement quand c’est pertinent.

Points clés à retenir

  • Les fonctions doivent retourner des chaînes JSON — jamais des objets Python bruts
  • Utilisez un dictionnaire de dispatch pour mapper les noms de fonctions à leurs implémentations
  • functools.partial est idéal pour pré-remplir des paramètres comme les connexions DB ou DataFrames
  • tool_choice contrôle si le modèle peut/doit utiliser les fonctions (auto, any, none)
  • Gérez toujours les cas d’erreur dans vos fonctions (paramètre invalide, donnée introuvable)