Créer avec l’IA

Comment créer un assistant météo avec l’appel de fonctions?

· Par Robert Scales · 4 min de lecture

Réponse courte

Vous décrivez une fonction météo au modèle en JSON Schema, vous laissez gpt-3.5-turbo-0613 répondre avec les arguments qu’il veut utiliser, vous exécutez la vraie recherche météo dans votre propre code, puis vous renvoyez le résultat pour que le modèle rédige la réponse. Le modèle ne va jamais chercher la météo lui-même; il décide seulement quand la demander et avec quelles valeurs.

Depuis le 13 juin, l’API Chat Completions d’OpenAI accepte une liste de fonctions : de courtes descriptions d’outils que votre programme peut exécuter. Quand une question exige l’un de ces outils, le modèle s’arrête et répond par une demande structurée plutôt que par du texte : le nom de la fonction et ses arguments en JSON, un format de données en texte brut composé de champs nommés. Votre code exécute l’outil et lui remet le résultat. Un assistant météo est le premier projet classique de l’appel de fonctions (function calling), parce que le modèle ne peut pas connaître par lui-même la météo du jour, et c’est une bonne façon de voir chaque étape de la boucle.

Ce tutoriel utilise Python, la série 0.27 de la bibliothèque openai, le nouveau modèle gpt-3.5-turbo-0613 et Open-Meteo, un service météo gratuit pour un usage non commercial sous les 10 000 appels par jour, qui ne demande aucune clé d’API.

Étape 1 : décrire la fonction

La description est écrite en JSON Schema, une façon standard d’indiquer quels champs contient un objet et de quel type est chacun.

functions = [{
    "name": "get_current_weather",
    "description": "Obtient la météo actuelle d’un lieu. Utilise-la chaque fois que l’utilisateur demande la météo en ce moment.",
    "parameters": {
        "type": "object",
        "properties": {
            "latitude": {"type": "number", "description": "Latitude du lieu, en degrés décimaux"},
            "longitude": {"type": "number", "description": "Longitude du lieu, en degrés décimaux"},
            "place_name": {"type": "string", "description": "Ville et pays, p. ex. Montréal, Canada"},
            "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
        },
        "required": ["latitude", "longitude", "place_name", "unit"]
    }
}]

Open-Meteo fonctionne avec des coordonnées; la fonction demande donc la latitude et la longitude. Pour les villes connues, le modèle peut généralement les fournir; une application en production ajouterait une véritable étape de recherche du lieu.

Étape 2 : laisser le modèle décider

import json, openai, requests

messages = [
    {"role": "system", "content": "Tu es un assistant météo. Si le lieu n’est pas clair, demande de quelle ville parle l’utilisateur."},
    {"role": "user", "content": "Ai-je besoin d’un manteau à Montréal en ce moment?"}
]
response = openai.ChatCompletion.create(
    model="gpt-3.5-turbo-0613",
    messages=messages,
    functions=functions,
    function_call="auto",
)
message = response["choices"][0]["message"]

Avec function_call réglé à "auto", le modèle choisit si un outil est nécessaire. Ici, il devrait l’être. Le finish_reason de la réponse devient "function_call" et le message ressemble à peu près à ceci (sortie donnée à titre d’illustration) :

{"role": "assistant", "content": null,
 "function_call": {"name": "get_current_weather",
  "arguments": "{\"latitude\": 45.5, \"longitude\": -73.57, \"place_name\": \"Montreal, Canada\", \"unit\": \"celsius\"}"}}

Remarquez que arguments est une chaîne de caractères. C’est le modèle qui l’a écrite; traitez-la donc comme une saisie d’utilisateur : analysez-la et vérifiez-la avant de l’utiliser.

Étape 3 : exécuter la vraie recherche

def get_current_weather(latitude, longitude, place_name, unit):
    r = requests.get("https://api.open-meteo.com/v1/forecast", params={
        "latitude": latitude, "longitude": longitude,
        "current_weather": "true", "temperature_unit": unit,
    }, timeout=10)
    r.raise_for_status()
    now = r.json()["current_weather"]
    return {"place": place_name, "temperature": now["temperature"], "unit": unit,
            "windspeed_kmh": now["windspeed"], "weathercode": now["weathercode"]}

if message.get("function_call"):
    args = json.loads(message["function_call"]["arguments"])
    result = get_current_weather(**args)

Le bloc current_weather d’Open-Meteo contient l’heure, la température, la vitesse du vent, la direction du vent et un code météo. La vitesse du vent est donnée en km/h, sauf indication contraire. Les codes suivent un tableau publié : 0 veut dire ciel dégagé, 1 à 3 vont de généralement dégagé à couvert, 61 à 65 désignent la pluie, de faible à forte, et 95 un orage.

Étape 4 : renvoyer le résultat pour obtenir la réponse

messages.append(message)
messages.append({"role": "function", "name": "get_current_weather",
                 "content": json.dumps(result)})
final = openai.ChatCompletion.create(model="gpt-3.5-turbo-0613", messages=messages)
print(final["choices"][0]["message"]["content"])

Le nouveau rôle function indique au modèle que ce contenu est la sortie d’un outil, et non quelque chose que l’utilisateur a tapé. Une réponse typique pourrait ressembler à ceci (à titre d’illustration) : « Il fait 14 °C à Montréal, avec un vent léger et un ciel couvert; un manteau léger est donc une bonne idée. »

À essayer : exécutez les quatre étapes, puis posez trois questions différentes : une sur une ville célèbre, une sur une petite ville qui porte le même nom qu’une plus grande, et une qui n’a rien à voir avec la météo. Notez quand le modèle appelle la fonction, quand il vous demande de préciser et quand il répond tout simplement.

Comment décrire une fonction pour que le modèle l’appelle correctement?

Rédigez la description pour un lecteur qui n’a jamais vu votre code, parce que c’est exactement ce qu’est le modèle. Dites ce que fait la fonction et quand l’utiliser. Donnez à chaque paramètre une description avec un exemple, comme le fait le livre de recettes (cookbook) d’OpenAI avec « The city and state, e.g. San Francisco, CA ». Utilisez enum quand seules quelques valeurs sont permises, comme les deux unités de température, pour que le modèle ne puisse pas en inventer une troisième.

Ne rendez obligatoires que les champs dont vous avez vraiment besoin, et gardez des noms simples : latitude vaut mieux que lat_dd. Si le modèle continue de deviner, ajoutez un message système comme celui du livre de recettes, qui lui dit de ne pas supposer de valeurs et de poser une question quand une demande est ambiguë. Vous pouvez aussi forcer un appel avec function_call={"name": "get_current_weather"}, ou bloquer les appels avec "none".

Enfin, protégez les cas limites. OpenAI précise que ses modèles ont été entraînés à produire du JSON conforme à la signature, mais votre code devrait quand même valider les types et les plages de valeurs, et gérer une erreur 400 d’Open-Meteo, qui renvoie une courte raison en JSON quand un paramètre est erroné. Pour tout ce qui a des effets dans le monde réel, comme envoyer un courriel ou faire un achat, OpenAI conseille de demander à l’utilisateur de confirmer avant que votre code agisse.

Sources

  1. How to call functions with chat models — OpenAI Cookbook (GitHub), 16 juin 2023
  2. Weather Forecast API (Docs) — Open-Meteo, 14 juin 2023
  3. Function calling and other API updates — OpenAI, 13 juin 2023

Recevez gratuitement le Guide de l’intelligence artificielle

Abonnez-vous à l’infolettre hebdomadaire et recevez le guide gratuit : ce qui a changé en IA cette semaine, une leçon, un prompt à essayer. Désabonnement en un clic.

Ce que nous faisons de votre adresse

Retour en haut

Affichage

Vos choix sont enregistrés dans ce navigateur seulement. Aucune donnée n’est transmise.

Taille du texte
100 %
Couleurs
Lecture
Autres réglages
Déclaration d’accessibilité