Accueil / Articles / Notes pratiques : Le meilleur framework d’agents en 2026 : LangGraph vs OpenAI Agents

Notes pratiques : Le meilleur framework d’agents en 2026 : LangGraph vs OpenAI Agents

Guide pratique pas à pas : Le meilleur framework d’agents en 2026 – LangGraph contre OpenAI Agents : contrats, vérifications et emplacements de code prêts à l’emploi pour les équipes qui mettent en œuvre ce modèle.

2343 mots

Ce guide reconstitue le parcours allant des matières premières à un système fonctionnel pour : Le meilleur framework d’agents en 2026 : LangGraph vs OpenAI Agents SDK vs Claude Agent SDK. L’accent est mis sur des étapes opérationnelles, des vérifications explicites, ainsi que du code que vous pouvez intégrer directement dans un dépôt sans devoir deviner l’intention derrière les actions. Pour une vue d’ensemble, définissez les entrées, le responsable de chaque étape et les critères d’achèvement avant de modifier du code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu, sans avoir à deviner l’état caché. Enregistrez les temps d’exécution ainsi que le coût en tokens ou en requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts permet d’éviter des factures inattendues lorsque le parcours passe de la démonstration à des environnements partagés.

Les trois primitives

Lorsque vous travaillez sur The Three Primitives, notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les flags fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système. Créez un point de contrôle après les étapes coûteuses. Le mécanisme de reprise ne doit pas facturer à nouveau la même appel au LLM lorsque l’opérateur réessaie un nœud ultérieur.

Une croyance à abandonner en 2025

Lorsque vous travaillez sur une croyance à abandonner pour l’année 2025, notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Documentez ensemble le parcours normal et le parcours de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non livrés font partie du produit, et non d’améliorations apportées ultérieurement. Créez un point de contrôle après chaque étape coûteuse. Le système de reprise ne doit pas facturer à nouveau la même appel d’LLM lorsque l’opérateur tente à nouveau un nœud ultérieur.

# Install: pip install "openai-agents[litellm]"
# Env: export GEMINI_API_KEY=...
import os
from agents import Agent, Runner, function_tool
from agents.extensions.models.litellm_model import LitellmModel

@function_tool
def current_time_utc() -> str:
    """Return the current UTC time as an ISO-8601 string."""
    from datetime import datetime, timezone
    return datetime.now(timezone.utc).isoformat(timespec="seconds")

# OpenAI Agents SDK using Gemini via LiteLLM. No OpenAI key required.
gemini_model = LitellmModel(
    model="gemini/gemini-2.5-pro",
    api_key=os.environ["GEMINI_API_KEY"],
)

agent = Agent(
    name="time-agent",
    instructions="Answer time questions using the current_time_utc tool.",
    model=gemini_model,
    tools=[current_time_utc],
)

result = Runner.run_sync(agent, "What is the current UTC time?")
print(result.final_output)
# -> "The current UTC time is 2026-07-06T14:32:11+00:00."

Présentation de Meridian

Lorsque vous travaillez sur Introducing Meridian, notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de garantir l’honnêteté des modifications ultérieures du code. Préférez des unités petites et testables plutôt que des scripts volumineux. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité et non un processus complexe et embrouillé. Faites des points de contrôle après les étapes coûteuses. Le système de reprise ne doit pas facturer à nouveau la même appel du LLM lorsque l’opérateur réessaie un nœud ultérieur. Lorsque vous travaillez sur Introducing Meridian, notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de garantir l’honnêteté des modifications ultérieures du code. Enregistrez les temps d’exécution ainsi que le coût en tokens ou en requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite des factures inattendues lorsque le processus passe de l’environnement de démonstration à des environnements partagés.

Charge de travail 1 : Voix et streaming en temps réel

Charge de travail 1 : La transmission vocale et en temps réel fonctionne le mieux lorsqu’elle est considérée comme une entité mesurable. Capturez un transcript exemplaire, un cas d’échec et la note de réversion avant d’élargir le périmètre. Conservez les configurations en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système. Maintenez un état du graphe simple et typé. Les blocs imbriqués masquent l’identité du nœud qui a écrit tel champ, ce qui empêche la reprise après interruption.

# Install: pip install openai-agents
# Env: export OPENAI_API_KEY=...
import asyncio
from agents import function_tool
from agents.realtime import RealtimeAgent, RealtimeRunner

@function_tool
def lookup_billing_balance(account_id: str) -> str:
    """Return the current outstanding balance for an account."""
    # In production, this hits the billing service. Here it is a stub.
    return "42.17 USD outstanding as of 2026-07-06."

voice_agent = RealtimeAgent(
    name="meridian-billing-voice",
    instructions=(
        "You are Meridian's billing voice assistant. Answer politely, briefly. "
        "Confirm the account_id before disclosing any balance."
    ),
    tools=[lookup_billing_balance],
)

async def main():
    runner = RealtimeRunner(
        starting_agent=voice_agent,
        config={"model_settings": {"model_name": "gpt-realtime-2.1"}},
    )

    # session handles the audio stream and tool calls
    session = await runner.run()

    async with session:
        # Wire the audio input source here via sounddevice or pyaudio
        async for event in session:
            if event.type == "history_updated":
                # The item contains the finalized transcript once the turn ends
                print(f"History updated with item: {event.item}")
            elif event.type == "error":
                print(f"Error: {event.error}")
                break

asyncio.run(main())

Charge de travail 2 : Orchestration multi-agents durable avec HITL

Charge de travail 2 : L’orchestration multi-agents durable avec HITL fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un enregistrement idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Documentez ensemble le parcours réussi et le parcours de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non livrés font partie du produit, et non d’une mise en forme ultérieure. Maintenez l’état du graphe plat et typé. Les blocs imbriqués masquent le fait que tel nœud a écrit telle champ et perturbent la reprise après interruption.

from typing import TypedDict, Literal
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
from langgraph.types import interrupt, Command
from langchain_google_genai import ChatGoogleGenerativeAI

# LangGraph is provider-agnostic. Here it uses Gemini.
llm = ChatGoogleGenerativeAI(model="gemini-2.5-pro", temperature=0)

class RefundState(TypedDict):
    order_id: str
    amount_usd: float
    customer_reason: str
    fraud_risk: Literal["low", "medium", "high"] | None
    finance_decision: Literal["approved", "denied"] | None
    human_review_needed: bool

def fraud_check(state: RefundState) -> RefundState:
    """Run the LLM-backed fraud check against the customer's stated reason."""
    prompt = (
        f"Assess fraud risk for refund of ${state['amount_usd']:.2f}. "
        f"Customer reason: {state['customer_reason']!r}. "
        "Respond with one word: low, medium, or high."
    )
    verdict = llm.invoke(prompt).content.strip().lower()
    if verdict not in {"low", "medium", "high"}:
        verdict = "high"  # fail-closed on ambiguous LLM output
    return {**state, "fraud_risk": verdict}

def finance_approval(state: RefundState) -> RefundState:
    """Above $500 or medium risk, pause for a human. Otherwise auto-approve."""
    needs_human = state["amount_usd"] > 500 or state["fraud_risk"] in {"medium", "high"}
    if needs_human:
        # Pause the graph. On resume, interrupt returns the human's decision.
        human_decision = interrupt({
            "order_id": state["order_id"],
            "amount_usd": state["amount_usd"],
            "fraud_risk": state["fraud_risk"],
            "prompt": "Approve (yes/no)?",
        })
        return {**state, "human_review_needed": True, "finance_decision": human_decision}
    return {**state, "human_review_needed": False, "finance_decision": "approved"}

# Build the graph
graph = StateGraph(RefundState)
graph.add_node("fraud_check", fraud_check)
graph.add_node("finance_approval", finance_approval)
graph.add_edge(START, "fraud_check")
graph.add_conditional_edges(
    "fraud_check",
    lambda s: "finance_approval" if s["fraud_risk"] != "high" else END,
)
graph.add_edge("finance_approval", END)

# Checkpointer. For production, swap MemorySaver for PostgresSaver.
compiled = graph.compile(checkpointer=MemorySaver())

# Run it. Interrupt fires on the $850 refund and the graph pauses.
config = {"configurable": {"thread_id": "order-4291"}}
result = compiled.invoke(
    {
        "order_id": "4291",
        "amount_usd": 850.00,
        "customer_reason": "arrived damaged, no photo",
        "fraud_risk": None,
        "finance_decision": None,
        "human_review_needed": False,
    },
    config=config,
)

# Later, a human reviewer says yes. Resume with Command.
final = compiled.invoke(Command(resume="approved"), config=config)

Charge de travail 3 : Axée sur le codage et centrée sur les fichiers et la shell

Charge de travail 3 : Les tâches liées à la programmation et axées sur les fichiers et le shell fonctionnent le mieux lorsqu’elles sont considérées comme une surface mesurable. Capturez un enregistrement idéal, un cas d’échec et une note de réversion avant d’élargir le périmètre. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité plutôt qu’un processus embrouillé. Maintenez l’état des graphes simple et typé. Les blocs imbriqués cachent le fait que tel nœud a écrit tel champ et perturbent la reprise après interruption. Charge de travail 3 : Les tâches liées à la programmation et axées sur les fichiers et le shell fonctionnent le mieux lorsqu’elles sont considérées comme une surface mesurable. Capturez un enregistrement idéal, un cas d’échec et une note de réversion avant d’élargir le périmètre. Enregistrez les temps d’exécution ainsi que le coût des tokens ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite des factures inattendues lorsque le processus passe d’une démonstration à des environnements partagés.

# Install: pip install claude-agent-sdk
# Env: export ANTHROPIC_API_KEY=...
import anyio
from claude_agent_sdk import (
    ClaudeSDKClient,
    ClaudeAgentOptions,
    AgentDefinition,
    HookMatcher,
)

# PreToolUse hook: block Bash calls that look like rm -rf
async def block_dangerous_bash(input_data, tool_use_id, context):
    if input_data.get("tool_name") == "Bash":
        cmd = input_data.get("tool_input", {}).get("command", "")
        if "rm -rf" in cmd or "rm  -rf" in cmd:
            return {
                "hookSpecificOutput": {
                    "hookEventName": "PreToolUse",
                    "permissionDecision": "deny",
                    "permissionDecisionReason": "rm -rf blocked by policy",
                }
            }
    return {}

# Subagent: runs in isolated context to lint one file
lint_agent = AgentDefinition(
    description="Run linters on a single file and return a concise report.",
    prompt=(
        "You are the lint subagent. Given a file path, run the project's linter "
        "on it and return a one-paragraph summary of failures. Do not fix anything."
    ),
    tools=["Bash", "Read"], # Note: tools is deprecated in favor of skills in recent SDKs
)

options = ClaudeAgentOptions(
    system_prompt=(
        "You are Meridian's code migration agent. Walk the target directory, "
        "apply the migration, run tests, and open a PR. Prefer small commits."
    ),
    allowed_tools=["Bash", "Read", "Write", "Edit", "Glob", "Grep"],
    hooks={"PreToolUse": [HookMatcher(hooks=[block_dangerous_bash])]},
    agents={"lint": lint_agent},
    # resume="mig-run-2026-07-06-01",  # uncomment to resume a prior session
)

async def main():
    async with ClaudeSDKClient(options=options) as client:
        await client.query(
            "Migrate services/payments/ from Java 17 to Java 21. "
            "For every file you touch, delegate to the `lint` subagent afterward. "
            "Do NOT commit or open PRs yet. Stop after changes are on disk."
        )
        async for message in client.receive_response():
            print(message)

anyio.run(main)

Charge de travail 4 : Orchestration d’outils à forte utilisation de MCP

Pour la charge de travail 4 : Orchestration d’outils à forte utilisation de MCP, il convient de définir les entrées, le responsable de l’étape et les critères de fin avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. La configuration doit être conservée en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du schéma. Authentifiez-vous au niveau du gateway et réautorisez-vous au niveau du plan de données. Un token porteur seul ne constitue pas une frontière entre les tenants.

La matrice de décision

Pour la matrice de décision, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Documentez ensemble le parcours idéal et le parcours de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non traités font partie du produit, et non d’une mise en forme ultérieure. Imposez une approbation humaine pour les étapes qui entraînent des dépenses ou modifient des données de production. La configuration en temps de compilation ne garantit pas l’exhaustivité du fonctionnement commercial.

Au sujet de CrewAI

Pour On CrewAI, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier du code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité plutôt qu’un processus embrouillé. Mettez en place une approbation humaine pour les actions qui entraînent des dépenses ou modifient des données de production. Une connexion en temps de compilation ne garantit pas la complétude du processus métier. Pour On CrewAI, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier du code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Enregistrez les temps d’exécution ainsi que le coût en tokens ou en requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite les factures inattendues lorsque le processus passe de l’environnement de démonstration à des environnements partagés.

Le véritable choix

Lorsque vous travaillez sur The Real Choice, notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système. Créez un point de contrôle après les étapes coûteuses. Le système de reprise ne doit pas facturer à nouveau la même appel d’LLM lorsque l’opérateur réessaie un nœud ultérieur.

Liste de contrôle opérationnelle

Cette liste de contrôle est la plus efficace lorsqu’elle est considérée comme une surface mesurable. Capturez un enregistrement exemplaire, un cas d’échec et une note de réversion avant d’élargir le périmètre. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Nommez les artefacts, définites des vérifications de succès et refusez toute exécution partielle silencieuse.

Gardez l’état du graphe plat et typé. Les blocs imbriqués cachent le fait que tel nœud a écrit telle champ et perturbent la reprise après interruption.

Ajoutez un test de base qui met à l’épreuve le chemin critique dans les processus d’intégration continue en utilisant des fixtures, et non des API payantes en temps réel, chaque fois que le budget le permet.

Enregistrez les temps d’exécution ainsi que le coût des tokens ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite les factures inattendues lorsque le chemin passe de la démo aux environnements partagés.

Gardez l’état du graphe plat et typé. Les blocs imbriqués cachent le fait que tel nœud a écrit telle champ et perturbent la reprise après interruption.

Au préalable de promouvoir l’ensemble technique, figez les versions, conservez une transcription exemplaire pour le chemin critique, et vérifiez les étapes de rollback. Les environnements partagés nécessitent des limites de débit, des contrôles d’attribution et un responsable clair pour la rotation des secrets. Préférez une fiabilité solide à de brillantes démonstrations ponctuelles.

Note de lot pour 2c64e0b378d9 : éviter d’inclure les clés du fournisseur dans le répertoire, fixer une limite pour les tokens par session, et stocker les transcriptions à côté des fichiers de test afin que les remplacements ultérieurs de modèles restent comparables.

La note de renforcement 0 fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez une transcription exemplaire, un cas d’échec et la note de réversion avant d’élargir le périmètre. Préférez des unités petites et testables plutôt que des scripts complexes. Lorsqu’une étape échoue, l’échec doit pointer vers une seule responsabilité plutôt que vers un processus embrouillé.

Détail de renforcement 0/821 : mesurez le temps d’exécution, la classe de l’erreur et la consommation de tokens pour cette note, puis décidez si vous souhaitez conserver le changement en vous basant sur un ensemble de questions prédéfini plutôt que sur des observations subjectives.

Pour la note de renforcement 1, définissez les entrées, le responsable de l’étape et les critères de fin avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Enregistrez les temps d’exécution ainsi que le coût des jetons ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite les factures inattendues lorsque le parcours passe de l’environnement de démonstration à des environnements partagés.

Détail de renforcement 1/821 : mesurez le temps d’exécution réel, la catégorie de l’erreur et la consommation de jetons pour cette note, puis décidez s’il convient de conserver la modification en vous basant sur un ensemble de questions prédéfini plutôt que sur des observations subjectives.