Function calling et sorties JSON structurées avec Ollama
Le function calling permet à un LLM de décider lui-même d'appeler une fonction de votre code — chercher la météo, interroger une base, envoyer un e-mail — en renvoyant les arguments au bon format. Ollama function calling repose sur deux briques : le paramètre format pour garantir un JSON valide, et le champ tools de l'API pour déclarer les fonctions disponibles. Ce guide montre les deux en Python, quels modèles locaux sont réellement fiables, et comment blinder le tout avec validation de schéma et retry.
#Pourquoi le function calling en local
Un LLM génère du texte, pas des actions. Le function calling comble ce fossé : au lieu de répondre en prose, le modèle renvoie un objet structuré qui dit « appelle la fonction get_meteo avec la ville Paris ». Votre code exécute la fonction, récupère le résultat réel, puis le repasse au modèle qui rédige la réponse finale. C'est le mécanisme de base des agents et des assistants qui interagissent avec le monde extérieur.
En local, l'enjeu est double. D'abord garantir que la sortie soit un JSON parsable à 100 % — un modèle bavard qui ajoute « Voici le JSON : » casse tout votre pipeline. Ensuite s'assurer que le modèle choisisse la bonne fonction avec les bons arguments, ce qui devient délicat avec les petits modèles. Ollama gère les deux via son API, mais avec des garde-fous à connaître.
- Sorties JSON garanties
- Le paramètre format contraint le décodage : le modèle ne peut produire qu'un JSON syntaxiquement valide, voire conforme à un schéma précis.
- Function calling
- Le champ tools déclare des fonctions au format OpenAI ; le modèle renvoie des tool_calls avec les arguments à passer.
- 100 % local
- Tout tourne sur votre machine via le daemon Ollama sur http://localhost:11434, sans clé API ni fuite de données.
#Prérequis et modèles compatibles
Le JSON mode (paramètre format) fonctionne avec n'importe quel modèle. Le function calling via tools, lui, exige un modèle entraîné pour le tool-use — sinon le champ tool_calls reste vide. Tous les modèles ne se valent pas : un 3B « compatible » sur le papier se trompe souvent d'arguments, tandis qu'un 14B+ tient la route sur des schémas simples.
- Ollama installé
- Daemon lancé et joignable sur http://localhost:11434. Vérifiez avec ollama list.
- SDK Python
- pip install ollama pydantic — le client officiel plus Pydantic pour la validation.
- Modèle tool-use fiable
- qwen2.5:14b, llama3.1:8b, mistral-nemo:12b sont de bons points de départ. En Q4_K_M : 8B≈5 Go, 14B≈9 Go de VRAM.
- GPU conseillé
- Une RTX 3060 12GB fait tourner un 8B confortablement ; visez 14B+ (RTX 4070/4080) pour du tool-use fiable.
#Forcer un JSON valide avec le paramètre format
Le cas le plus simple : vous voulez que le modèle réponde toujours par un JSON, jamais par du texte libre. Passez format: 'json' à l'appel chat. Ollama contraint alors le décodage token par token pour produire un objet syntaxiquement valide. Important : gardez une instruction explicite dans le prompt décrivant les champs attendus, sinon le modèle invente une structure.
#JSON structuré par schéma (structured outputs)
Depuis fin 2024, Ollama accepte aussi un schéma JSON complet dans format (pas seulement la chaîne 'json'). Le décodage est alors contraint à respecter le schéma : types, champs requis, énumérations. C'est bien plus robuste que 'json' seul, car le modèle ne peut structurellement pas produire un objet non conforme. Avec Pydantic, on génère le schéma automatiquement.
Ici le décodage est verrouillé sur la forme de Personne, et model_validate_json refait une passe de validation côté Python. Double filet de sécurité : la sortie est garantie parsable ET conforme aux types déclarés. C'est le pattern recommandé pour toute extraction de données en production locale.
#L'API tools pas à pas en Python
Passons au vrai function calling. On déclare les fonctions dans le champ tools au format OpenAI (name, description, parameters en JSON Schema). Le modèle lit ces définitions et, s'il juge utile d'appeler une fonction, renvoie un ou plusieurs tool_calls au lieu d'un message texte. À vous d'exécuter la fonction et de renvoyer le résultat.
- 01Décrire les fonctionsPour chaque fonction, donnez un name clair, une description précise (le modèle s'en sert pour choisir) et un parameters en JSON Schema listant les arguments et lesquels sont required.
- 02Envoyer l'appel avec toolsPassez la liste tools à ollama.chat. Le modèle décide seul s'il appelle une fonction ou répond directement.
- 03Lire les tool_callsInspectez resp['message'].get('tool_calls'). S'il est présent, le modèle veut appeler une fonction avec les arguments fournis.
- 04Exécuter et renvoyerAppelez la vraie fonction Python, puis repassez son résultat au modèle dans un message de role 'tool' pour qu'il rédige la réponse finale.
#La boucle appel → exécution → réponse
Le function calling est un aller-retour. Premier appel : le modèle renvoie un tool_call. Vous exécutez la fonction. Second appel : vous renvoyez le résultat, le modèle rédige la réponse en langage naturel. Voici la boucle complète, réutilisable pour plusieurs fonctions.
#Validation de schéma et patterns de retry
En local, les petits modèles ratent parfois : arguments manquants, mauvais type, fonction inexistante. Ne faites jamais confiance à la sortie brute. Encadrez chaque tool_call par une validation Pydantic, et si elle échoue, relancez avec le message d'erreur en contexte — souvent le modèle se corrige au second essai.
- Valider avant d'exécuter
- Un modèle de Pydantic par fonction attrape les arguments manquants ou mal typés avant qu'ils n'atteignent votre code.
- Retry avec feedback
- Réinjecter le message d'erreur dans le contexte guide le modèle vers la correction. 2-3 essais suffisent presque toujours.
- Liste blanche de fonctions
- Refusez tout nom de fonction hors dispatch. C'est à la fois une sécurité et un garde-fou contre les hallucinations.
- Fallback gracieux
- Après N échecs, répondez au user par un message clair plutôt que de planter — surtout avec un petit modèle.
#Les pièges des petits modèles en tool-use
Le tool-use est cognitivement exigeant : le modèle doit comprendre l'intention, choisir la bonne fonction, mapper les arguments et respecter le format. En dessous de 7B, les résultats sont fragiles. Voici ce qui casse le plus souvent en local et comment y remédier.
- tool_calls vide
- Le modèle répond en texte au lieu d'appeler la fonction. Souvent un modèle non entraîné au tool-use, ou une description de fonction trop floue. Passez à qwen2.5 ou llama3.1, et soignez les descriptions.
- Mauvais arguments
- Le modèle invente ou oublie des champs. Rendez-les required dans le schéma, réduisez le nombre de fonctions exposées à la fois, et validez systématiquement.
- Fonction hallucinée
- Le modèle appelle une fonction qui n'existe pas. Liste blanche obligatoire côté dispatch.
- JSON pollué
- Sans format, un petit modèle ajoute du texte autour du JSON. Utilisez toujours format='json' ou un schéma pour l'extraction pure.
- Trop de fonctions
- Au-delà de 5-6 tools, les petits modèles se perdent. Segmentez par sous-tâche ou faites du routage en deux étapes.
#Pour aller plus loin
Le function calling est la brique de base des agents et des intégrations avancées. Ces guides du site prolongent celui-ci :
- Intégrer Ollama via l'API REST en Python
- L'endpoint OpenAI-compatible sur :11434, le streaming et le JSON mode dans une vraie app FastAPI/Flask.
- Créer un agent IA local avec LangChain et Ollama
- Passer du function calling brut à un agent complet qui enchaîne outils, mémoire et raisonnement.
- MCP et LLM local : connecter des serveurs MCP à Ollama
- Standardiser l'accès aux outils (fichiers, web, bases) via Model Context Protocol au lieu de définir chaque fonction à la main.
Un retour, une erreur, une précision ? Faites-nous signe, ça améliore le guide pour tout le monde.