Retour au blog

API & SDKGuide

L'appel de fonctions en Python avec une API compatible OpenAI

Envoyez des outils, lisez tool_calls et renvoyez le résultat avec le rôle tool : une boucle d'appel de fonctions en deux étapes en Python avec MiniMax-M2.7.

Par CheapTokens TeamPublié le Mis à jour le 6 min de lecture

Schéma d'une boucle d'appel d'outils en deux étapes : envoyer les outils, lire tool_calls, renvoyer le résultat comme message d'outil
Sur cette page
  1. Comment fonctionne l'appel de fonctions
  2. Configurer le client
  3. Déclarer une fonction et envoyer la première requête
  4. Ce que vous devriez voir
  5. Plusieurs appels et boucles plus longues
  6. Pourquoi MiniMax-M2.7 convient aux boucles d'outils
  7. Si cela ne fonctionne pas
  8. Étapes suivantes

L'appel de fonctions (appel d'outils) permet à un modèle de demander à votre code d'exécuter une fonction. Vous envoyez une liste tools avec la requête, le modèle répond avec tool_calls au lieu de texte, vous exécutez la fonction vous-même, et vous renvoyez son résultat dans un message avec role: tool. Le modèle rédige ensuite la réponse finale. Ce guide montre toute la boucle en Python contre le point de terminaison compatible OpenAI https://api.cheaptokens.dev/v1/chat/completions avec MiniMax-M2.7 (id MiniMaxAI/MiniMax-M2.7, contexte de 180K, jusqu'à 8 192 jetons de sortie par réponse).

L'appel d'outils est indiqué comme pris en charge pour tous les modèles du catalogue (CheapTokens docs, vérifié le 22 octobre 2026). Vous avez besoin d'une clé API ; si vous n'avez pas encore fait de requête, commencez par votre première requête API.

Comment fonctionne l'appel de fonctions#

Le modèle n'exécute jamais rien. Il produit seulement une requête structurée : « appeler get_weather avec {\"city\": \"Paris\"} ». Votre programme fait le vrai travail et rend compte. Cela forme une boucle de deux requêtes :

  1. Requête 1. Vous envoyez le message utilisateur plus la liste tools. Le modèle répond soit en texte, soit avec un ou plusieurs tool_calls.
  2. Votre code. Vous analysez chaque appel, exécutez la fonction correspondante et collectez le résultat.
  3. Requête 2. Vous renvoyez toute la conversation : les messages d'origine, le message assistant avec tool_calls, et un message role: tool par appel. Le modèle utilise les résultats pour rédiger la réponse finale.

Chaque requête renvoie tout l'historique, donc les boucles d'outils consomment plus de jetons d'entrée qu'une question unique. Si c'est nouveau pour vous, lisez qu'est-ce qu'un jeton.

Configurer le client#

Installez le SDK OpenAI (pip install openai) et placez votre clé dans une variable d'environnement plutôt que dans le code source :

bash
export CHEAPTOKENS_API_KEY="your-key-here"

L'URL de base est https://api.cheaptokens.dev/v1. Prenez l'id du modèle dans la documentation CheapTokens : différents fournisseurs nomment différemment le même modèle. Ce guide utilise MiniMaxAI/MiniMax-M2.7.

Déclarer une fonction et envoyer la première requête#

Une fonction est décrite dans le tableau tools. Chaque entrée a type: function et un objet function avec un name, un description et parameters écrit en JSON Schema. La description est ce que le modèle lit pour décider quand appeler la fonction, alors rédigez-la comme une phrase courte et concrète.

Voici un script complet. La fonction météo est un stub avec des données fictives, donc l'exemple ne nécessite aucun service supplémentaire :

python
import json
import os

from openai import OpenAI

client = OpenAI(
    base_url='https://api.cheaptokens.dev/v1',
    api_key=os.environ['CHEAPTOKENS_API_KEY'],
)
MODEL = 'MiniMaxAI/MiniMax-M2.7'


def get_weather(city: str) -> dict:
    # Stub with fake data: replace with a real lookup
    fake = {'Paris': 14, 'Tokyo': 19}
    return {'city': city, 'temp_c': fake.get(city, 10)}


FUNCTIONS = {'get_weather': get_weather}

tools = [
    {
        'type': 'function',
        'function': {
            'name': 'get_weather',
            'description': 'Get the current temperature in Celsius for a city.',
            'parameters': {
                'type': 'object',
                'properties': {
                    'city': {'type': 'string', 'description': 'City name, for example Paris'},
                },
                'required': ['city'],
            },
        },
    }
]

messages = [{'role': 'user', 'content': 'What is the weather in Paris right now?'}]

# Step 1: the model decides whether to call a function
first = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
message = first.choices[0].message

if not message.tool_calls:
    print(message.content)
    raise SystemExit

# Step 2: run every requested call and send the results back
messages.append(message)
for call in message.tool_calls:
    args = json.loads(call.function.arguments)
    result = FUNCTIONS[call.function.name](**args)
    messages.append({
        'role': 'tool',
        'tool_call_id': call.id,
        'content': json.dumps(result),
    })

final = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
print(final.choices[0].message.content)

Trois détails décident si la boucle fonctionne :

  • call.function.arguments est une chaîne JSON, pas un dictionnaire. Analysez-la avec json.loads.
  • Chaque message role: tool doit porter le tool_call_id de l'appel auquel il répond, qui est call.id.
  • Le content d'un message d'outil est une chaîne. Utilisez json.dumps pour les résultats structurés.

Ce que vous devriez voir#

Si vous affichez les champs après la première requête, ils ressemblent à ceci (l'id d'appel diffère à chaque exécution) :

text
finish_reason: tool_calls
call.id: call_abc123
call.function.name: get_weather
call.function.arguments: {"city": "Paris"}

Le script affiche ensuite la réponse finale, une phrase simple du type « Il fait actuellement 14 °C à Paris. » La formulation change d'une exécution à l'autre ; le nombre vient de votre fonction.

Plusieurs appels et boucles plus longues#

Un modèle peut renvoyer plus d'un appel dans une seule réponse, par exemple quand vous posez une question sur la météo dans deux villes. C'est pourquoi l'exemple boucle sur message.tool_calls et répond à chaque appel par son propre id. Le guide d'appel de fonctions d'OpenAI décrit le même comportement : avec le tool_choice par défaut de auto, le modèle peut appeler zéro, une ou plusieurs fonctions (docs OpenAI, vérifié le 22 octobre 2026 ; la page n'a pas de date de dernière mise à jour et décrit la forme plus récente de l'API Responses, tandis que ce guide utilise la forme Chat Completions du point de terminaison /v1/chat/completions).

Les agents répètent les deux étapes jusqu'à ce que le modèle cesse de demander des outils. Enveloppez les requêtes dans une boucle et fixez toujours une limite d'étapes :

python
for _ in range(5):  # safety limit against endless loops
    response = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
    message = response.choices[0].message
    if not message.tool_calls:
        print(message.content)
        break
    messages.append(message)
    for call in message.tool_calls:
        args = json.loads(call.function.arguments)
        result = FUNCTIONS[call.function.name](**args)
        messages.append({'role': 'tool', 'tool_call_id': call.id, 'content': json.dumps(result)})

Si vous voulez que la réponse apparaisse jeton par jeton, combinez cela avec le streaming en Python.

Pourquoi MiniMax-M2.7 convient aux boucles d'outils#

Une boucle d'outils envoie de nombreuses requêtes avec un historique croissant. Pour ce schéma, CheapTokens propose MiniMax-M2.7 : contexte de 180K, jusqu'à 8 192 jetons de sortie par réponse, un seul prix à toute heure de la journée sans heures de pointe, et un seul solde pour tous les modèles. Entrée et sortie comptées ensemble, c'est moins cher que le prix de référence d'OpenRouter (comparaison de prix CheapTokens, vérifiée le 31 octobre 2026).

Prix actuels par 1M de jetons : entrée $0.05, sortie $0.15. À titre d'exemple de charge de travail (pas une mesure), l'aller-retour de deux requêtes ci-dessus avec environ 2 700 jetons d'entrée et 350 de sortie au total coûte $0.00019. La liste complète des prix est sur la page de tarification.

Pour essayer un autre modèle, changez uniquement l'id ; la liste des ids est sur la page des modèles.

Si cela ne fonctionne pas#

  • message.tool_calls est None. Le modèle a répondu en texte. Rendez la fonction description plus spécifique, et assurez-vous que la question nécessite réellement la fonction.
  • json.JSONDecodeError sur arguments. Le modèle a produit un JSON invalide. Enveloppez json.loads dans try/except et renvoyez le texte de l'erreur comme résultat d'outil, pour que le modèle puisse réessayer avec des arguments corrigés.
  • La deuxième requête échoue ou le modèle ignore le résultat. Vérifiez que chaque appel a exactement un message role: tool avec un tool_call_id correspondant, et que le message assistant avec tool_calls figure dans l'historique avant ceux-ci.
  • KeyError dans FUNCTIONS[...]. Le modèle a nommé une fonction que vous n'avez pas enregistrée. Renvoyez une courte chaîne d'erreur comme résultat d'outil au lieu de planter.
  • La réponse est tronquée. Vérifiez finish_reason : la valeur length signifie que la limite de jetons de sortie a été atteinte. Voir pourquoi une réponse IA s'arrête en milieu de phrase.
  • Erreurs 401 ou d'authentification. Vérifiez que la clé est dans CHEAPTOKENS_API_KEY et que l'URL de base se termine par /v1.

Étapes suivantes#

Remplacez le stub par une vraie fonction : une requête de base de données, un appel de recherche ou une calculatrice. Ajoutez d'autres entrées à tools et à FUNCTIONS, et gardez chaque résultat de fonction court, car il revient dans l'historique à chaque requête suivante. Pour obtenir une clé et faire le premier appel, ouvrez la documentation CheapTokens.