
Pydantic AI : Le Guide de Production (Au-delà du Hello World)
Les sorties brutes des LLM cassent les applications. Vous demandez du JSON, vous obtenez du Markdown. Vous demandez un nombre entre 1 et 10, vous obtenez "Bien sûr ! Voici un nombre : sept." Si vous avez construit quelque chose de réel avec des APIs LLM, vous avez écrit du code d'analyse défensif qui vous fait remettre en question vos choix de carrière. Pydantic AI règle ça -- c'est le framework d'agents type-safe construit par la même équipe derrière Pydantic et FastAPI. Pensez-y comme "FastAPI pour les agents IA" : vous définissez ce que vous voulez avec des annotations de type Python, et le framework gère la validation, les nouvelles tentatives et les appels d'outils.
Ce guide Pydantic AI est destiné aux développeurs qui ont déjà effectué leur premier appel LLM et veulent des patterns de production : des sorties structurées qui ne se cassent pas, l'injection de dépendances pour des agents testables, et des outils du monde réel au-delà des APIs météo. À la fin, vous aurez des agents fonctionnels avec des outils, DI, streaming et tests.
<!-- IMAGE: Architecture d'agent Pydantic AI -- L'agent reçoit un prompt, appelle des outils via RunContext, valide la sortie via le modèle Pydantic -->Pydantic AI en un Coup d'Œil
| Attribut | Détails |
|---|---|
| Ce que c'est | Framework d'agents IA type-safe pour Python |
| Construit par | L'équipe Pydantic (Samuel Colvin et al.) |
| Philosophie | "FastAPI pour les agents IA" -- les annotations de type pilotent tout |
| Licence | MIT (open-source) |
| Version actuelle | v1.74.0 (mars 2026) |
| Version Python | 3.9+ |
| Modèles supportés | OpenAI, Anthropic, Google Gemini, Groq, Mistral, Ollama, et plus |
| Fonctionnalités clés | Sorties structurées, appels d'outils, injection de dépendances, streaming, TestModel |
| Étoiles GitHub | 16 000+ |
| Prêt pour la production | Oui -- v1.0 sortie en septembre 2025 |
| Observabilité | Intégration native Logfire (basée sur OpenTelemetry) |
| Courbe d'apprentissage | Faible si vous connaissez Pydantic/FastAPI ; modérée sinon |
Les fonctionnalités remarquables sont les sorties structurées (validées avec des modèles Pydantic), l'injection de dépendances (comme Depends de FastAPI), et TestModel (LLM simulé pour les tests sans appels API). Si vous venez de LangChain et vous demandez "y a-t-il quelque chose de plus propre ?", c'est probablement ça.
Installation et Premier Agent
# Installer avec le support OpenAI (remplacer openai par anthropic, google, etc.)
pip install "pydantic-ai[openai]"
# Définir votre clé API
export OPENAI_API_KEY="sk-..."Votre premier agent en 5 lignes :
from pydantic_ai import Agent
agent = Agent("openai:gpt-4o", system_prompt="You are a helpful assistant.")
result = agent.run_sync("What's the capital of France?")
print(result.output) # "Paris"C'est tout. Agent encapsule le modèle, run_sync envoie un prompt et retourne un résultat. Le result.output est une simple chaîne ici, mais ça va changer.
Sorties Structurées -- Pourquoi Pydantic AI Existe
C'est la fonctionnalité centrale. Au lieu d'obtenir une chaîne du LLM et espérer qu'elle est du JSON valide, vous définissez un modèle Pydantic et l'agent retourne un objet Python validé.
Avant : Sortie LLM Brute
# L'ancienne façon -- espérer le meilleur
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Review the movie Inception. Return JSON with title, rating (1-10), summary."}]
)
# response.choices[0].message.content est une chaîne
# Peut-être du JSON. Peut-être avec des balises Markdown. Peut-être que rating est "eight".
# Vous vous débrouillez seul.Après : Structuré avec Pydantic AI
from pydantic import BaseModel
from pydantic_ai import Agent
class MovieReview(BaseModel):
title: str
rating: int # Garanti d'être un int, pas "eight"
summary: str
recommended: bool
agent = Agent("openai:gpt-4o", result_type=MovieReview)
result = agent.run_sync("Review the movie Inception")
review = result.output # C'est une instance MovieReview, pas une chaîne
print(f"{review.title}: {review.rating}/10")
print(f"Recommandé : {review.recommended}")
print(review.summary)La différence est comme le jour et la nuit. result.output est un vrai objet MovieReview. Si le LLM retourne rating: "eight" au lieu de rating: 8, la validation de Pydantic le détecte. Pour un regard plus approfondi sur le fonctionnement de cela avec différents fournisseurs, consultez notre guide sur les sorties structurées avec les fournisseurs LLM.
Ce qui Se Passe Quand la Validation Échoue
Voici la partie qu'aucun autre tutoriel ne montre : que se passe-t-il quand le LLM fait une erreur ?
from pydantic import BaseModel, Field
from pydantic_ai import Agent
class StrictReview(BaseModel):
title: str
rating: int = Field(ge=1, le=10) # Doit être 1-10
pros: list[str] = Field(min_length=2) # Au moins 2 avantages
agent = Agent("openai:gpt-4o", result_type=StrictReview)
# Si le LLM retourne rating=15 ou seulement 1 avantage :
# 1. La validation Pydantic échoue
# 2. Le message d'erreur est renvoyé AU LLM
# 3. Le LLM réessaie avec la sortie corrigée
# 4. Cela se répète jusqu'à la limite de nouvelles tentatives
result = agent.run_sync("Review the movie Inception")Cette boucle de réessai avec feedback est la fonctionnalité tueuse de Pydantic AI. Le LLM apprend de ses propres erreurs de validation. Vous n'écrivez pas de logique de réessai -- le framework s'en charge.
Verdict : Les sorties structurées sont la meilleure raison unique d'utiliser Pydantic AI plutôt que des appels API bruts. Si vous analysez manuellement du JSON LLM, arrêtez.
Outils et Appels de Fonctions
Les outils permettent à votre agent d'appeler des fonctions Python pour obtenir de vraies données. Au lieu que le LLM hallucine des faits, il peut interroger votre base de données, rechercher dans vos docs, ou appeler une API.
Enregistrer un Outil
from pydantic_ai import Agent
agent = Agent("openai:gpt-4o")
@agent.tool
async def search_docs(query: str) -> str:
"""Search the documentation for relevant articles."""
# Votre vraie logique de recherche ici
results = await doc_search_engine.search(query, limit=5)
return "\n".join(r.title + ": " + r.snippet for r in results)Le décorateur @agent.tool enregistre la fonction. Pydantic AI lit les annotations de type et le docstring de la fonction pour indiquer au LLM ce que fait l'outil, quels arguments il prend, et ce qu'il retourne. Pas d'écriture manuelle de schéma -- vos annotations de type SONT le schéma. Pour un contexte sur comment les appels de fonctions LLM fonctionnent sous le capot, nous avons un guide dédié.
RunContext : Passer des Données aux Outils
C'est là que Pydantic AI diverge des autres frameworks. RunContext vous permet de passer des données d'exécution (connexions base de données, infos utilisateur, clients API) à vos outils sans état global.
from dataclasses import dataclass
from pydantic_ai import Agent, RunContext
@dataclass
class SupportDeps:
customer_id: str
db_connection: DatabaseConnection
agent = Agent("openai:gpt-4o", deps_type=SupportDeps)
@agent.tool
async def get_order_history(ctx: RunContext[SupportDeps], limit: int = 5) -> str:
"""Fetch recent orders for the current customer."""
orders = await ctx.deps.db_connection.query(
"SELECT * FROM orders WHERE customer_id = $1 ORDER BY date DESC LIMIT $2",
ctx.deps.customer_id, limit
)
return format_orders(orders)Le ctx.deps donne à l'outil accès à ce que vous avez passé à l'exécution. L'outil n'importe pas une connexion base de données globale -- il en reçoit une. C'est l'injection de dépendances, et c'est ce qui rend vos agents testables.
Un Exemple d'Outil du Monde Réel
@agent.tool
async def run_sql_query(ctx: RunContext[SupportDeps], sql: str) -> str:
"""Run a read-only SQL query against the analytics database.
Only SELECT queries are allowed."""
if not sql.strip().upper().startswith("SELECT"):
return "Error: only SELECT queries are allowed"
results = await ctx.deps.db_connection.fetch(sql)
return json.dumps(results, default=str)Verdict : Les appels d'outils dans Pydantic AI sont plus propres que dans tout autre framework grâce aux annotations de type qui font le gros du travail. Vous écrivez des fonctions Python normales avec des annotations de type. Le framework figure out le reste.
Injection de Dépendances -- La Fonctionnalité que LangChain Aurait Voulu Avoir
Si vous avez utilisé Depends de FastAPI, vous comprenez déjà le système DI de Pydantic AI. Sinon, voici la version courte : au lieu que votre agent aille chercher ce dont il a besoin (connexions base de données globales, clients API, config), vous lui donnez tout à l'exécution.
Définir des Dépendances
from dataclasses import dataclass
from pydantic_ai import Agent
@dataclass
class AppDeps:
db: AsyncDatabasePool
search_client: SearchAPIClient
current_user: User
agent = Agent(
"openai:gpt-4o",
deps_type=AppDeps,
system_prompt="You are a customer support agent."
)Utiliser des Dépendances dans les Outils
@agent.tool
async def lookup_account(ctx: RunContext[AppDeps]) -> str:
"""Look up the current user's account details."""
account = await ctx.deps.db.fetchrow(
"SELECT * FROM accounts WHERE user_id = $1",
ctx.deps.current_user.id
)
return json.dumps(account, default=str)
# Exécuter avec de vraies dépendances
result = await agent.run(
"What's my account status?",
deps=AppDeps(db=real_db, search_client=real_search, current_user=user)
)Pourquoi DI Rend Vos Agents Testables
C'est la vraie récompense. Dans LangChain, vous passeriez le contexte via des kwargs de chaîne ou des closures -- il n'y a pas de pattern standard. Dans Pydantic AI, remplacer les vraies dépendances par des doublures de test est trivial :
# Dans votre fichier de test
from pydantic_ai import Agent
from your_app import agent, AppDeps
async def test_account_lookup():
mock_deps = AppDeps(
db=MockDatabase({"user_123": {"status": "active", "plan": "pro"}}),
search_client=MockSearch(),
current_user=User(id="user_123")
)
result = await agent.run("What's my account status?", deps=mock_deps)
assert "active" in result.output
assert "pro" in result.outputPas de monkey-patching. Pas de mocking d'imports globaux. Vous passez juste des deps différents.
Verdict : L'injection de dépendances est pourquoi les développeurs Python expérimentés préfèrent Pydantic AI. C'est l'influence FastAPI qui se montre.
Fournisseurs de Modèles -- OpenAI, Anthropic, Gemini, Ollama
Pydantic AI est agnostique au modèle. Changer de fournisseur est un changement d'une ligne :
# OpenAI
agent = Agent("openai:gpt-4o")
# Anthropic
agent = Agent("anthropic:claude-sonnet-4-20250514")
# Google Gemini
agent = Agent("google-gla:gemini-2.0-flash")
# Ollama local
agent = Agent("ollama:llama3.1")Tout le reste -- outils, sorties structurées, DI -- reste identique. Votre logique métier ne change pas quand vous changez de modèle.
| Fournisseur | Modèles | Tier gratuit | Complexité de configuration |
|---|---|---|---|
| OpenAI | GPT-4o, GPT-4o mini, o1 | Crédit $5 (nouveaux comptes) | Faible -- clé API seulement |
| Anthropic | Claude Sonnet, Haiku, Opus | Pas de tier gratuit | Faible -- clé API seulement |
| Google Gemini | Gemini 2.0 Flash, Pro | Tier gratuit généreux | Moyen -- configuration projet |
| Groq | Llama, Mixtral | Tier gratuit disponible | Faible -- clé API seulement |
| Ollama (local) | Llama, Mistral, Phi, etc. | Complètement gratuit | Moyen -- installer Ollama |
Verdict : La conception agnostique au modèle signifie que vous n'êtes jamais enfermé chez un fournisseur. Commencez avec OpenAI pour la commodité, benchmarkez avec Anthropic, et utilisez Ollama pour le développement local.
Réponses en Streaming
Pour les interfaces de chat et les applications en temps réel, le streaming est essentiel. Pydantic AI le supporte tout en maintenant la sécurité des types :
from pydantic_ai import Agent
from pydantic import BaseModel
class AnalysisResult(BaseModel):
summary: str
sentiment: str
confidence: float
agent = Agent("openai:gpt-4o", result_type=AnalysisResult)
async def stream_analysis(text: str):
async with agent.run_stream(f"Analyze this text: {text}") as stream:
async for partial in stream.stream_structured():
# partial est un AnalysisResult partiellement validé
print(f"Streaming: {partial}")
# Le résultat final est entièrement validé
result = await stream.get_output()
print(f"Final: {result.summary} ({result.confidence:.0%} confiant)")Cela fonctionne très bien avec StreamingResponse de FastAPI -- même écosystème, mêmes patterns. La documentation des agents Pydantic AI couvre les options de streaming avancées, y compris le streaming de texte uniquement avec stream_text().
Pydantic AI vs LangGraph vs OpenAI Agents SDK
Vous êtes ici, donc vous posez probablement la question : "devrais-je utiliser Pydantic AI ou LangGraph ?" Réponse honnête : ils résolvent des problèmes différents, et vous pourriez utiliser les deux.
Tableau de Comparaison des Fonctionnalités
| Fonctionnalité | Pydantic AI | LangGraph | OpenAI Agents SDK |
|---|---|---|---|
| Sécurité des types | Complète (modèles Pydantic) | Partielle (TypedDict) | Minimale |
| Injection de dépendances | Intégrée (style FastAPI) | Aucune | Aucune |
| Sorties structurées | Native avec réessai | Via des parseurs de sortie | Via le mode JSON |
| Appels d'outils | Décorateur @agent.tool | Décorateur @tool | Définitions de fonctions |
| Multi-agents | Transferts basiques | Avancé (machines à états) | Transferts + guardrails |
| Streaming | Streaming typé | Événements de streaming | Streaming |
| Support de modèles | 10+ fournisseurs | Principalement modèles LangChain | OpenAI uniquement |
| Tests | TestModel intégré | Pas de tests intégrés | Pas de tests intégrés |
| Courbe d'apprentissage | Faible (si vous connaissez Pydantic) | Élevée (concepts de graphe) | Faible (API simple) |
| Taille de la communauté | Croissante (16K étoiles) | Grande (écosystème LangChain) | Croissante (soutien OpenAI) |
| Idéal pour | Agents propres et testables | Workflows d'état complexes | Projets OpenAI uniquement |
Quand Utiliser Chacun
Choisissez Pydantic AI quand vous voulez du code d'agent propre et type-safe. C'est idéal pour les tâches à agent unique avec des outils (bots de support client, extraction de données, agents de revue de code) et les situations où la testabilité importe. Si votre équipe utilise déjà FastAPI et Pydantic, la courbe d'apprentissage est presque plate.
Choisissez LangGraph quand vous avez besoin de workflows multi-étapes complexes avec branchement conditionnel, approbation humaine dans la boucle, et gestion d'état sophistiquée. LangGraph excelle dans l'orchestration de plusieurs étapes, pas dans la qualité d'un agent individuel. Pour un regard approfondi, consultez notre comparaison complète LangGraph vs CrewAI vs OpenAI Agents SDK.
Choisissez OpenAI Agents SDK quand vous êtes à 100% sur OpenAI, voulez la configuration la plus simple possible, et n'avez pas besoin de support multi-fournisseur ou de DI.
Le Pattern de Combinaison
Voici ce que les équipes expérimentées font en réalité : utiliser Pydantic AI pour les agents individuels (code propre, testable, sorties typées) et LangGraph pour l'orchestration entre agents (routage, machines à états, logique conditionnelle). Ils ne se concurrencent pas -- ils sont des couches complémentaires.
# Agent Pydantic AI -- propre, testable, type-safe
support_agent = Agent("openai:gpt-4o", result_type=SupportResponse, deps_type=SupportDeps)
# Graphe LangGraph -- orchestre quand appeler quel agent
graph = StateGraph(SupportState)
graph.add_node("classify", classify_intent)
graph.add_node("support", lambda state: support_agent.run_sync(state["query"]))
graph.add_node("escalate", escalate_to_human)Verdict : Choisissez Pydantic AI pour du code d'agent propre et testable. Choisissez LangGraph pour des workflows multi-étapes complexes. Ils ne s'excluent pas mutuellement.
Tester Vos Agents avec TestModel
C'est la section qui sépare un guide débutant d'un guide de production. Chaque vrai codebase a besoin de tests, et tester des agents est notoirement difficile -- les appels LLM sont lents, coûteux et non-déterministes. Pydantic AI livre une solution : TestModel.
from pydantic_ai import Agent
from pydantic_ai.models.test import TestModel
from pydantic import BaseModel
class SupportResponse(BaseModel):
answer: str
confidence: float
escalate: bool
agent = Agent("openai:gpt-4o", result_type=SupportResponse)
# Dans les tests : remplacer le vrai modèle par TestModel
def test_support_agent():
with agent.override(model=TestModel()):
result = agent.run_sync("I need help with billing")
# TestModel retourne des données structurées valides correspondant à votre result_type
assert isinstance(result.output, SupportResponse)
assert isinstance(result.output.confidence, float)
assert isinstance(result.output.escalate, bool)TestModel génère des données valides correspondant à votre result_type sans faire d'appels API. Zéro coût, déterministe, rapide. La documentation de test Pydantic AI couvre les patterns avancés comme FunctionModel pour des réponses personnalisées et capture_run_messages pour inspecter les appels d'outils.
Tester les Outils et DI Ensemble
def test_order_lookup_tool():
# Dépendances simulées
mock_deps = SupportDeps(
customer_id="test-123",
db_connection=MockDB(orders=[{"id": "ord-1", "status": "shipped"}])
)
with agent.override(model=TestModel()):
result = agent.run_sync(
"Where is my order?",
deps=mock_deps
)
assert isinstance(result.output, SupportResponse)Pas d'appels API. Pas de tests fragiles. Pas de coût. Exécutez ça en CI/CD à côté du reste de votre suite de tests.
C'est le lacune de contenu n°1 sur tout le SERP. Aucun autre guide Pydantic AI ne couvre les tests. Si vous construisez des agents pour la production, c'est ce dont vous avez besoin.
Observabilité -- Intégration Logfire en 5 Minutes
Les agents en production ont besoin d'observabilité IA. Vous voulez voir chaque appel LLM, invocation d'outil, latence, nombre de tokens, et coût. Pydantic AI s'intègre nativement avec Logfire, la plateforme d'observabilité de l'équipe Pydantic (basée sur OpenTelemetry).
import logfire
from pydantic_ai import Agent
logfire.configure() # Utilise la variable d'env LOGFIRE_TOKEN
logfire.instrument_pydantic_ai()
agent = Agent("openai:gpt-4o", result_type=MovieReview)
# Chaque exécution est maintenant tracée automatiquement
result = agent.run_sync("Review Inception")Trois lignes. Vous obtenez des traces complètes montrant : le prompt envoyé, la réponse du modèle, les appels d'outils (le cas échéant), les passes/échecs de validation, les nouvelles tentatives, la latence, et le coût estimé. Si Logfire n'est pas votre truc, Langfuse est une alternative open-source solide avec support d'ingénierie de contexte pour tracer l'évolution de vos prompts.
FAQ
Qu'est-ce que Pydantic AI et en quoi est-il différent de LangChain ?
Pydantic AI est un framework d'agents type-safe où les annotations de type Python pilotent la validation, les schémas d'outils et l'injection de dépendances. LangChain est un framework plus grand axé sur le chaînage des appels LLM. La différence clé : Pydantic AI valide les sorties au niveau du framework et fournit une injection de dépendances intégrée pour la testabilité -- LangChain ne fait ni l'un ni l'autre par défaut.
Comment construire un agent IA type-safe avec Pydantic AI ?
Définissez un BaseModel Pydantic pour votre sortie, passez-le comme result_type à Agent, et appelez run_sync() ou run(). L'agent retourne une instance validée de votre modèle, pas une chaîne brute. Consultez la section Sorties Structurées pour des exemples complets.
Devrais-je utiliser Pydantic AI ou LangGraph pour des agents en production ?
Utilisez Pydantic AI pour les agents individuels où la sécurité des types, la testabilité et la propreté du code importent. Utilisez LangGraph pour orchestrer des workflows multi-étapes complexes avec routage conditionnel. De nombreuses équipes utilisent les deux -- des agents Pydantic AI à l'intérieur d'une couche d'orchestration LangGraph.
Comment Pydantic AI gère-t-il les appels d'outils et l'injection de dépendances ?
Décorez une fonction avec @agent.tool et Pydantic AI lit ses annotations de type pour générer le schéma d'outil. Pour DI, définissez deps_type sur l'Agent et acceptez RunContext[VosDeps] dans les outils. Les dépendances d'exécution (connexions DB, clients API) circulent sans état global.
Comment ajouter le streaming à un agent Pydantic AI ?
Utilisez agent.run_stream() à la place de agent.run(). Il retourne un gestionnaire de contexte asynchrone qui produit des résultats partiels via stream_structured() ou stream_text(). Le résultat final est toujours entièrement validé contre votre result_type.
Pydantic AI est-il prêt pour la production en 2026 ?
Oui. La version 1.0 a été livrée en septembre 2025 avec un engagement de stabilité API. Il est soutenu par l'équipe Pydantic (la bibliothèque Python la plus téléchargée pour la validation de données) et est actuellement à v1.74.0 avec des mises à jour régulières.
Puis-je utiliser Pydantic AI avec Ollama et des modèles locaux ?
Oui. Utilisez Agent("ollama:llama3.1") et assurez-vous qu'Ollama tourne localement. Installez l'extra du fournisseur ollama : pip install "pydantic-ai[ollama]". Les sorties structurées et les outils fonctionnent de la même façon qu'avec les fournisseurs cloud.
Comment tester les agents Pydantic AI ?
Utilisez TestModel -- un modèle simulé qui génère des données structurées valides correspondant à votre result_type sans appels API. Enveloppez votre test dans agent.override(model=TestModel()) et exécutez des assertions sur la sortie. Consultez la section Tests pour des exemples pytest complets.
Pydantic AI fonctionne-t-il avec FastAPI ?
Parfaitement. Ils partagent la même philosophie d'injection de dépendances et sont construits par la même équipe. Vous pouvez utiliser des agents Pydantic AI dans des endpoints FastAPI, partager des types de dépendances entre eux, et streamer des réponses d'agents via StreamingResponse.
Quelle est la différence entre Pydantic AI et l'OpenAI Agents SDK ?
Pydantic AI est agnostique au modèle (fonctionne avec OpenAI, Anthropic, Gemini, Ollama, etc.), a l'injection de dépendances, TestModel pour les tests, et la validation Pydantic. L'OpenAI Agents SDK est plus simple mais limité aux modèles OpenAI et manque de DI et de tests intégrés. Choisissez Pydantic AI pour la flexibilité ; choisissez OpenAI Agents SDK pour la configuration OpenAI uniquement la plus simple possible.
Principaux Points à Retenir et Prochaines Étapes
| Concept | Insight Clé | Prochaine Étape |
|---|---|---|
| Sorties Structurées | Votre result_type est validé et réessayé automatiquement | Définir des modèles Pydantic pour toutes les sorties d'agents |
| Outils | Les annotations de type SONT le schéma -- pas de définitions manuelles | Construire des outils avec @agent.tool et RunContext |
| Injection de Dépendances | Passer les deps d'exécution explicitement pour la testabilité | Définir une dataclass deps_type pour chaque agent |
| Tests | TestModel élimine les coûts API en CI/CD | Ajouter agent.override(model=TestModel()) à votre suite de tests |
| Fournisseurs de Modèles | Changement de modèle en une ligne, pas de changements de code | Commencer avec OpenAI, benchmarker les alternatives plus tard |
| Observabilité | Configuration Logfire en 3 lignes pour des traces complètes | Ajouter logfire.instrument_pydantic_ai() en production |
Commencez avec un petit agent qui a des sorties structurées. Ajoutez un outil. Ajoutez des dépendances. Écrivez un test avec TestModel. C'est le chemin de production -- et vous avez maintenant tout ce dont vous avez besoin pour l'emprunter.
La documentation officielle de Pydantic AI et le dépôt GitHub sont excellents pour aller plus loin. Le framework évolue vite, alors mettez en favori le changelog.