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

Date: 2023-06-25 · Author: Robert Scales · https://guidetoartificialintelligence.com/fr/apprendre/assistant-meteo-appel-de-fonctions/

**Answer:** 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](https://open-meteo.com/en/docs), 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.

```python
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

```python
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) :

```json
{"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

```python
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

```python
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

- How to call functions with chat models: https://github.com/openai/openai-cookbook/blob/cb0da816f9cf1d2dcceb2389f702b709e9398ba4/examples/How_to_call_functions_with_chat_models.ipynb
- Weather Forecast API (Docs): https://open-meteo.com/en/docs
- Function calling and other API updates: https://openai.com/blog/function-calling-and-other-api-updates
