
Les deux guides précédents ont écrit la boucle d'un agent à la main — instructif, mais on ne la réécrit pas à chaque projet. PydanticAI fournit cette boucle, et lui ajoute ce qui sépare un script d'un composant de production : une sortie typée garantie — un modèle Pydantic validé, jamais du texte à parser —, l'injection de dépendances, et des retries automatiques quand le modèle se trompe. Vous construirez un agent release-notes qui résume les notes de version d'un dépôt GitHub en une structure typée, sur un modèle servi par Ollama. Au passage, vous verrez les trois modes de sortie structurée de PydanticAI et lequel choisir selon le modèle. Public visé : développeur ayant lu les deux guides sur les agents et le function calling.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Pourquoi une sortie typée change tout par rapport au texte libre.
- L'anatomie d'un agent PydanticAI : modèle, dépendances, sortie, outils.
- Les trois modes de sortie structurée — Tool, Native, Prompted — et lequel choisir.
- Injecter du contexte avec les dépendances (
deps). - Gérer les erreurs d'outil avec
ModelRetry.
Prérequis
Section intitulée « Prérequis »Ce guide prolonge Comprendre les agents IA et Function calling. Il vous faut :
- Une instance Ollama avec le modèle
qwen2.5. - Les bases de Pydantic et un accès internet pour l'API GitHub.
- Python 3.10+.
Du texte libre à la sortie typée
Section intitulée « Du texte libre à la sortie typée »Les agents des guides précédents renvoyaient une chaîne de caractères. Pour un affichage, c'est suffisant. Pour un composant qui s'insère dans une chaîne de traitement — alimenter une base, déclencher une action, être testé — c'est ingérable : il faut parser ce texte, espérer qu'il a la bonne forme, gérer le cas où il ne l'a pas.
PydanticAI renverse le problème. On déclare la sortie attendue comme un modèle Pydantic, et l'agent garantit de renvoyer une instance validée de ce modèle — ou d'échouer franchement. Plus de parsing, plus d'espoir : le type est une certitude. C'est le même principe que le function calling du guide précédent — un schéma, une validation — appliqué cette fois à la réponse finale de l'agent.
Anatomie d'un agent PydanticAI
Section intitulée « Anatomie d'un agent PydanticAI »Un agent PydanticAI se déclare en un objet Agent, paramétré par quatre éléments. Le modèle d'abord. PydanticAI fournit une classe dédiée à Ollama auto-hébergé — OllamaModel, branchée sur un OllamaProvider.
from pydantic_ai import Agentfrom pydantic_ai.models.ollama import OllamaModelfrom pydantic_ai.providers.ollama import OllamaProvider
MODELE = OllamaModel( "qwen2.5", provider=OllamaProvider(base_url="http://localhost:11434/v1"))Les trois autres éléments sont le type des dépendances (deps_type), le type de sortie (output_type) et les outils. Les deux types sont déclarés à la création de l'agent ; les outils, par décoration. On les détaille un par un.
La sortie typée : un modèle Pydantic garanti
Section intitulée « La sortie typée : un modèle Pydantic garanti »La sortie de l'agent release-notes est une structure, pas une phrase :
from pydantic import BaseModel, Field
class SyntheseRelease(BaseModel): """Synthèse structurée des dernières releases d'un dépôt."""
derniere_version: str = Field(description="Tag de la release la plus récente") nombre_de_releases: int = Field(description="Nombre de releases examinées") resume: str = Field(description="Résumé factuel des changements, 2 à 3 phrases")Ce modèle devient le output_type de l'agent. Dès lors, agent.run_sync(...) renvoie un résultat dont le .output est une instance de SyntheseRelease — ou une exception UnexpectedModelBehavior si le modèle n'y est pas parvenu, même après ses retries. Il n'y a pas d'entre-deux : un appel qui réussit a produit une sortie valide et typée.
Les trois modes de sortie structurée
Section intitulée « Les trois modes de sortie structurée »Comment PydanticAI obtient-il cette structure du modèle ? Il existe trois modes, et les confondre coûte des heures de débogage.
Le Tool Output est le mode par défaut. PydanticAI décrit le schéma de sortie comme un outil que le modèle doit appeler pour livrer son résultat. C'est l'approche la plus compatible — « supportée par pratiquement tous les modèles » — et celle que la documentation recommande pour les modèles locaux.
Le Native Output s'appuie sur la fonction de structured output native du modèle, qui le contraint à respecter le schéma. Très fiable, mais tous les modèles ne le supportent pas — à éviter par défaut sur un modèle local.
Le Prompted Output injecte le schéma JSON dans les instructions et parse la réponse texte. Compatible avec tout modèle, mais « souvent l'approche la moins fiable » : rien ne contraint le modèle.
| Mode | Support | Fiabilité | Pour un modèle local |
|---|---|---|---|
| Tool Output (défaut) | Tous les modèles | Très haute | Recommandé |
| Native Output | Modèles sélectionnés | Très haute | Rarement supporté |
| Prompted Output | Tous les modèles | Modérée | Solution de repli |
L'agent de ce guide utilise le Tool Output — il suffit de passer la classe Pydantic à output_type, sans rien envelopper.
Injecter des dépendances
Section intitulée « Injecter des dépendances »Un agent a besoin de ressources — un client HTTP, une connexion, une configuration. Les coder en variables globales rend l'agent impossible à tester et à réutiliser. PydanticAI propose l'injection de dépendances : un objet, déclaré par deps_type, passé à chaque exécution et transmis aux outils.
from dataclasses import dataclassimport httpx
@dataclassclass Deps: """Dépendances de l'agent — ici, le client HTTP partagé.""" client: httpx.ClientL'agent se déclare alors avec ses quatre paramètres, et le budget de retries — retries=3 couvre les outils comme la sortie :
agent = Agent( MODELE, deps_type=Deps, output_type=SyntheseRelease, retries=3, system_prompt=( "Tu résumes les notes de version d'un dépôt GitHub. Appelle l'outil " "lister_releases, puis produis une synthèse structurée et factuelle." ),)Outils et retries
Section intitulée « Outils et retries »Un outil PydanticAI qui a besoin des dépendances se décore avec @agent.tool et reçoit un RunContext typé — par lequel il accède à ctx.deps.
from pydantic_ai import ModelRetry, RunContext
@agent.tooldef lister_releases(ctx: RunContext[Deps], depot: str) -> list[dict]: """Liste les dernières releases d'un dépôt GitHub.
depot : identifiant au format owner/repo, par exemple astral-sh/uv. """ if "/" not in depot: raise ModelRetry("Le dépôt doit être au format owner/repo.") return recuperer_releases(ctx.deps.client, depot)Deux mécanismes se rencontrent ici. Le RunContext[Deps] donne à l'outil le client HTTP injecté — ctx.deps.client — sans aucun global. Et le ModelRetry : si l'outil lève cette exception, PydanticAI ne plante pas — il renvoie le message d'erreur au modèle, qui corrige son appel et réessaie, dans la limite du budget retries. Un argument mal formé devient ainsi une boucle de correction automatique, pas un échec.
Faire tourner l'agent
Section intitulée « Faire tourner l'agent »L'agent s'exécute avec run_sync, en lui passant l'instance des dépendances :
def resumer(depot: str) -> SyntheseRelease: with httpx.Client(timeout=15) as client: resultat = agent.run_sync( f"Résume les dernières releases du dépôt {depot}.", deps=Deps(client=client), ) return resultat.outputSur le dépôt astral-sh/uv, la sortie est une structure, pas un paragraphe :
Type de sortie : SyntheseReleaseDernière version : 0.11.16Releases examinées : 5Résumé : Les dernières mises à jour du dépôt astral-sh/uv ontapporté plusieurs corrections de bugs et améliorations fonctionnelles...La suite de tests du lab tient en trois contrôles. La logique métier : la fonction qui interroge l'API GitHub renvoie des releases exploitables — un test d'intégration, déterministe sur un dépôt public stable. Le retry : un dépôt mal formé lève bien ModelRetry — un test unitaire, sans modèle ni réseau. Et la sortie typée : l'agent complet renvoie une instance de SyntheseRelease — ce que PydanticAI garantit dès lors que run_sync n'a pas levé d'exception.
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
UnexpectedModelBehavior: Exceeded maximum output retries | Le modèle ne produit pas une sortie valide | Augmenter retries ; si besoin, passer en Prompted Output |
Agent(output_retries=...) déprécié | Ancienne API | Utiliser retries={'output': N} ou retries=N |
| L'agent ne voit pas les dépendances | deps non passé à run_sync, ou deps_type absent | Déclarer deps_type et passer deps= à chaque run |
| Le modèle boucle sur le même outil | Modèle faible en function calling | Choisir un modèle solide (qwen2.5) ; relire sa doc |
ModelRetry non prise en compte | Levée hors d'un outil ou d'un validateur | ModelRetry ne vaut que dans un outil ou une fonction de sortie |
À retenir
Section intitulée « À retenir »- PydanticAI fournit la boucle d'un agent ; on ne la réécrit pas à chaque projet.
- La sortie typée est garantie :
run_syncréussi renvoie une instance Pydantic validée, jamais du texte à parser. - Trois modes de sortie : Tool Output (défaut, recommandé pour les modèles locaux), Native Output (réservé aux modèles compatibles), Prompted Output (repli).
- Les dépendances s'injectent via
deps_typeetRunContext— pas de variables globales. ModelRetrytransforme une erreur d'outil en boucle de correction automatique.- Le mode de sortie et le modèle se choisissent ensemble : un modèle faible échoue même en Tool Output.
Prochaines étapes
Section intitulée « Prochaines étapes »Pour aller plus loin
Section intitulée « Pour aller plus loin »- Documentation PydanticAI — la référence du framework, dont les modes de sortie et les modèles.
- PydanticAI et Ollama — la configuration officielle pour un Ollama auto-hébergé.