IA locale Intermédiaire schedule 45 min

settings_suggest Premier agent IA — météo en temps réel

Créer un agent IA qui utilise le tool calling d'Ollama pour appeler de vraies fonctions Python et récupérer la météo de n'importe quelle ville.

psychology C'est quoi un agent IA ? 01

Un agent IA, c'est un LLM qui peut agir — pas seulement générer du texte. Il dispose d'un ensemble d'outils (des fonctions Python) qu'il peut décider d'appeler pour répondre à une question.

Le mécanisme s'appelle tool calling (ou function calling). Voilà comment ça se passe :

L'utilisateur pose une question : "Quel temps fait-il à Lyon ?"

On envoie la question au LLM avec la liste des outils disponibles

Le LLM décide qu'il a besoin de l'outil get_weather et retourne un appel structuré : get_weather(city="Lyon")

Notre code Python exécute la vraie fonction et récupère les données météo

On renvoie le résultat au LLM, qui formule une réponse naturelle

info

Le LLM ne sait pas ce que fait la fonction — il se contente de décider quand l'appeler et avec quels paramètres. C'est notre code Python qui fait l'exécution réelle.

folder_open Préparer le projet 02

On crée un nouveau projet avec ses dépendances. On utilisera Open-Meteo, une API météo gratuite qui ne nécessite aucune clé d'API :

terminalTerminal
mkdir agent-meteo
cd agent-meteo

python -m venv venv              # Windows
python3 -m venv venv             # macOS / Linux

venv\Scripts\activate            # Windows
source venv/bin/activate         # macOS / Linux

# ollama : pour parler au LLM
# requests : pour appeler les APIs météo et géocodage
pip install ollama requests

build Les fonctions outils 03

Crée agent.py. On commence par écrire les fonctions que le LLM pourra appeler. On en fait trois : géocodage (convertir une ville en coordonnées), météo actuelle, et prévisions.

codeagent.py — partie 1 : les outils
import requests

# ── outil 1 : convertir une ville en coordonnées GPS ──────────────────────────
def geocoder_ville(ville: str) -> dict:
    """
    Convertit un nom de ville en latitude/longitude.
    Utilise l'API Open-Meteo Geocoding (gratuite, sans clé).
    Retourne un dict avec lat, lon, et le nom officiel de la ville.
    """
    url = "https://geocoding-api.open-meteo.com/v1/search"
    params = {"name": ville, "count": 1, "language": "fr", "format": "json"}
    
    reponse = requests.get(url, params=params, timeout=10)
    data = reponse.json()
    
    if not data.get("results"):
        return {"erreur": f"Ville '{ville}' introuvable."}
    
    resultat = data["results"][0]
    return {
        "nom": resultat["name"],
        "pays": resultat.get("country", ""),
        "latitude": resultat["latitude"],
        "longitude": resultat["longitude"],
    }


# ── outil 2 : météo actuelle ───────────────────────────────────────────────────
def get_meteo_actuelle(ville: str) -> dict:
    """
    Récupère la météo actuelle pour une ville.
    Retourne température, ressenti, humidité, vent, et description météo.
    """
    # D'abord on géocode la ville
    coords = geocoder_ville(ville)
    if "erreur" in coords:
        return coords
    
    url = "https://api.open-meteo.com/v1/forecast"
    params = {
        "latitude":  coords["latitude"],
        "longitude": coords["longitude"],
        "current": [
            "temperature_2m",
            "apparent_temperature",
            "relative_humidity_2m",
            "wind_speed_10m",
            "weathercode",
        ],
        "timezone": "auto",
    }
    
    reponse = requests.get(url, params=params, timeout=10)
    data = reponse.json()
    current = data["current"]
    
    # Convertir le weathercode en description lisible
    descriptions = {
        0: "Ciel dégagé", 1: "Principalement dégagé",
        2: "Partiellement nuageux", 3: "Couvert",
        45: "Brouillard", 48: "Brouillard givrant",
        51: "Bruine légère", 53: "Bruine modérée", 55: "Bruine dense",
        61: "Pluie légère", 63: "Pluie modérée", 65: "Pluie forte",
        71: "Neige légère", 73: "Neige modérée", 75: "Neige forte",
        80: "Averses légères", 81: "Averses modérées", 82: "Averses violentes",
        95: "Orage", 96: "Orage avec grêle", 99: "Orage avec forte grêle",
    }
    code = current.get("weathercode", 0)
    description = descriptions.get(code, f"Code météo {code}")
    
    return {
        "ville": coords["nom"],
        "pays": coords["pays"],
        "temperature": f"{current['temperature_2m']}°C",
        "ressenti": f"{current['apparent_temperature']}°C",
        "humidite": f"{current['relative_humidity_2m']}%",
        "vent": f"{current['wind_speed_10m']} km/h",
        "description": description,
    }


# ── outil 3 : prévisions sur 3 jours ──────────────────────────────────────────
def get_previsions(ville: str, jours: int = 3) -> dict:
    """
    Récupère les prévisions météo pour les N prochains jours (max 7).
    Retourne min/max de température et description pour chaque jour.
    """
    jours = min(max(jours, 1), 7)  # entre 1 et 7
    coords = geocoder_ville(ville)
    if "erreur" in coords:
        return coords
    
    url = "https://api.open-meteo.com/v1/forecast"
    params = {
        "latitude":  coords["latitude"],
        "longitude": coords["longitude"],
        "daily": ["temperature_2m_max", "temperature_2m_min", "weathercode"],
        "timezone": "auto",
        "forecast_days": jours,
    }
    
    reponse = requests.get(url, params=params, timeout=10)
    data = reponse.json()
    daily = data["daily"]
    
    previsions = []
    for i in range(jours):
        previsions.append({
            "date": daily["time"][i],
            "max": f"{daily['temperature_2m_max'][i]}°C",
            "min": f"{daily['temperature_2m_min'][i]}°C",
        })
    
    return {"ville": coords["nom"], "previsions": previsions}

schema Décrire les outils au LLM 04

Pour qu'Ollama sache quels outils sont disponibles et comment les utiliser, on doit lui fournir une description structurée de chaque fonction. C'est le "contrat" entre notre code et le LLM :

codeagent.py — partie 2 : schémas des outils
# Ce tableau décrit nos outils dans le format qu'Ollama comprend.
# Pour chaque outil : son nom, sa description, et ses paramètres.
OUTILS = [
    {
        "type": "function",
        "function": {
            "name": "get_meteo_actuelle",
            "description": (
                "Retourne la météo actuelle (température, vent, humidité, description) "
                "pour une ville donnée. Appelle cet outil quand l'utilisateur demande "
                "la météo actuelle ou le temps qu'il fait."
            ),
            "parameters": {
                "type": "object",
                "properties": {
                    "ville": {
                        "type": "string",
                        "description": "Le nom de la ville, ex: 'Lyon', 'Paris', 'Tokyo'",
                    }
                },
                "required": ["ville"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_previsions",
            "description": (
                "Retourne les prévisions météo pour les prochains jours pour une ville. "
                "Appelle cet outil quand l'utilisateur demande les prévisions, "
                "la météo de demain, ou la météo de la semaine."
            ),
            "parameters": {
                "type": "object",
                "properties": {
                    "ville": {
                        "type": "string",
                        "description": "Le nom de la ville",
                    },
                    "jours": {
                        "type": "integer",
                        "description": "Nombre de jours de prévisions (1 à 7, défaut: 3)",
                    },
                },
                "required": ["ville"],
            },
        },
    },
]

play_arrow La boucle agent complète 05

Maintenant on assemble tout. La boucle agent suit toujours le même pattern : envoyer au LLM → vérifier si le LLM veut appeler un outil → appeler l'outil → renvoyer le résultat au LLM → afficher la réponse finale.

codeagent.py — partie 3 : boucle agent
import json
import ollama

# Mapping nom_outil → vraie fonction Python
FONCTIONS_DISPONIBLES = {
    "get_meteo_actuelle": get_meteo_actuelle,
    "get_previsions":     get_previsions,
}


def executer_agent(question: str) -> str:
    """
    Envoie une question à l'agent, gère le tool calling,
    et retourne la réponse finale en texte.
    """
    historique = [
        {
            "role": "system",
            "content": (
                "Tu es un assistant météo en français. "
                "Utilise les outils disponibles pour répondre aux questions météo. "
                "Formule toujours ta réponse de manière naturelle et agréable."
            ),
        },
        {"role": "user", "content": question},
    ]

    # ── Premier appel : le LLM décide s'il a besoin d'un outil ──
    reponse = ollama.chat(
        model="gemma4:e2b",
        messages=historique,
        tools=OUTILS,       # on lui passe les outils disponibles
    )

    message = reponse.message

    # ── Le LLM veut appeler un (ou plusieurs) outil(s) ──
    if message.tool_calls:
        # Ajouter le message du LLM (avec ses demandes d'outils) à l'historique
        historique.append({"role": "assistant", "content": message.content or "", "tool_calls": [
            {"function": {"name": tc.function.name, "arguments": tc.function.arguments}}
            for tc in message.tool_calls
        ]})

        # Exécuter chaque outil demandé
        for tool_call in message.tool_calls:
            nom_outil = tool_call.function.name
            arguments = tool_call.function.arguments

            print(f"  → Appel outil : {nom_outil}({arguments})")

            if nom_outil in FONCTIONS_DISPONIBLES:
                # Appel de la vraie fonction Python
                resultat = FONCTIONS_DISPONIBLES[nom_outil](**arguments)
            else:
                resultat = {"erreur": f"Outil '{nom_outil}' inconnu."}

            # Ajouter le résultat de l'outil dans l'historique
            historique.append({
                "role": "tool",
                "content": json.dumps(resultat, ensure_ascii=False),
            })

        # ── Deuxième appel : le LLM formule la réponse finale ──
        reponse_finale = ollama.chat(
            model="gemma4:e2b",
            messages=historique,
        )
        return reponse_finale.message.content

    else:
        # Le LLM n'a pas besoin d'outil — réponse directe
        return message.content


# ── Point d'entrée ─────────────────────────────────────────────────────────────
print("=== Agent Météo IA (Ollama + Open-Meteo) ===")
print("Exemples : 'Quel temps fait-il à Lyon ?', 'Prévisions pour Tokyo sur 5 jours'")
print("Tape 'quitter' pour terminer.\n")

while True:
    question = input("Toi : ").strip()
    
    if question.lower() in ["quitter", "exit", "q"]:
        print("À bientôt !")
        break
    
    if not question:
        continue
    
    try:
        print()
        reponse = executer_agent(question)
        print(f"Agent : {reponse}\n")
    except Exception as e:
        print(f"[Erreur] {e}\n")
lightbulb

Le **arguments dans FONCTIONS_DISPONIBLES[nom_outil](**arguments) est la façon Python de passer un dictionnaire comme arguments nommés. Si arguments = {"ville": "Lyon", "jours": 3}, alors get_previsions(**arguments) équivaut à get_previsions(ville="Lyon", jours=3).

science Tester l'agent 06

terminalTerminal
python agent.py

Essaie ces questions :

  • "Quel temps fait-il à Lyon ?" → appelle get_meteo_actuelle
  • "Prévisions météo pour Tokyo la semaine prochaine" → appelle get_previsions
  • "Bonjour !" → réponse directe sans outil
  • "Il fait beau à Grenoble ?" → le LLM doit comprendre qu'il faut appeler un outil
info

Tu viens de construire un vrai agent IA. Le principe que tu as appris ici — décrire des outils, laisser le LLM décider quand les appeler, exécuter les fonctions, renvoyer les résultats — est exactement le même que celui utilisé dans des systèmes comme ChatGPT Plugins ou Claude avec ses outils. Bienvenue dans l'ère des agents.