Claude Agent SDK contre OpenAI Agents SDK contre Google ADK : un agent, trois versions
Claude Agent SDK contre OpenAI Agents SDK contre Google ADK : un agent d’étiquetage nutritionnel développé trois fois, avec ce que gère chaque kit et ce que vous devez encore écrire vous-même.
Tous les grands fournisseurs d’IA proposent désormais un SDK d’agent, et les arguments présentés sont presque identiques : donnez-nous vos outils et une instruction, et nous exécuterons le processus complet — appels aux outils, sortie structurée, sessions et suivi inclus. Si la publicité est si similaire, qu’est-ce qui distingue vraiment ces kits, et sur lequel devriez-vous vous baser ?
La façon de le découvrir a été de créer le même agent trois fois : avec le Claude Agent SDK d’Anthropic, avec le OpenAI Agents SDK et avec le Agent Development Kit (ADK) de Google. Cet agent n’est pas un jouet : c’est un outil d’analyse d’étiquettes nutritionnelles qui lit une photo, utilise quelques outils pour analyser l’information, fournit un verdict et s’améliore à mesure qu’il voit davantage d’étiquettes. Avec un seul agent et trois SDK différents, toute différence dans les résultats provient du SDK et non du problème lui-même.
En bref : les trois approches convergent presque entièrement sur l’étape la plus difficile, à savoir le cycle de raisonnement, et divergent pour tout ce qui l’entoure. Elles diffèrent quant à la manière dont un outil est déclaré, comment une image est intégrée, et si des résultats textuels sont affichés. Ce qui est le plus révélateur, c’est qu’elles diffèrent également sur ce qu’aucune d’elles ne fait à votre place. À la fin, vous saurez quel ensemble convient à quel travail et combien de travail vous restera à effectuer une fois que vous en aurez choisi un.
Un autre élément commun relie les trois versions. L’agent a été conçu pour apprendre de son expérience. Il se souvient des produits qu’il a déjà examinés ainsi que des noms d’additifs dont il a réussi à déterminer la résolution, et il enregistre ces informations sur disque, de sorte que chaque exécution ultérieure s’appuie moins sur le modèle, se termine plus rapidement et se comporte de manière plus déterministe. Les poids du modèle ne changent jamais. C’est l’agent qui accumule les compétences. Aucun SDK ne vous offre cela directement, ce qui en fait un outil utile pour comprendre où s’arrête l’outil fourni et où commence votre propre travail d’ingénierie.
First, what counts as an agent
Un modèle de langage simple n’est pas un agent. Pensez-y comme à une fonction qui prend du texte et des images et qui renvoie du texte. Rien n’est conservé entre les appels : il ne peut pas exécuter de code ni récupérer une page, et il ne peut même pas s’appeler lui-même à nouveau. Donnez-lui une étiquette nutritionnelle et il vous indiquera comment il procéderait pour l’analyser, mais cette analyse ne se produit jamais.
Un agent, quant à lui, est un modèle qui fonctionne en boucle avec des outils. Le schéma reste constant : vous donnez au modèle un objectif ainsi qu’une liste d’outils qu’il peut utiliser. Il répond soit par un résultat final, soit en demandant d’utiliser un outil, par exemple « chercher E211 ». S’il demande l’utilisation d’un outil, quelque chose l’exécute et renvoie le résultat, puis le modèle reprend son travail. Le cycle se répète jusqu’à ce qu’une réponse finale apparaisse.
Celui-ci est l’idée fondamentale : le modèle prend une décision, un outil s’exécute, le résultat revient, le modèle décide à nouveau. Notre outil d’enquête effectue de nombreuses itérations, une fois pour la recherche du produit, une fois pour chaque additif, puis pour la vérification « Healthier Choice », l’évaluation de la qualité et une synthèse finale. À chaque étape, le modèle choisit sa prochaine action en se basant sur ce qu’il vient d’apprendre. C’est ce qui confère à cet système un caractère autonome, et c’est aussi la raison pour laquelle une seule appel API placé dans une boucle for ne suffit pas.
Remarquez ce que le modèle émet réellement : seulement « Je voudrais appeler check_sfa_additive avec ces arguments ». Quelque chose d’autre doit transformer ce souhait en une véritable appel de fonction et faire en sorte que tout fonctionne. Ce quelque chose, c’est le harness, qui contient plus de mécanismes moins spectaculaires que la plupart des gens ne l’imaginent. Il se divise en trois couches. Une couche de développement est là où l’on construit l’agent. Une couche en temps de exécution est là où on l’utilise en production. Une plaine de contrôle est là où l’on gère toute une flotte. C’est la couche de développement qui fait tourner le cycle, et c’est là que se trouvent ces SDK.
Commencez par la couche de développement, où un agent unique est créé. Elle se compose du cycle lui-même (appel du modèle, détection des demandes d’appel d’outil, répétition jusqu’à achèvement, sortie en toute sécurité en cas de limite de tours ou de budget) ainsi que de la personnalité, c’est-à-dire le prompt système qui indique au modèle qui il est et comment agir. Autour de ces éléments se trouvent les schémas d’outil, qui décrivent chaque outil au modèle dans le format attendu, ainsi que l’exécution des outils, qui lance réellement votre fonction, un serveur MCP ou un outil intégré comme la lecture de fichiers, et qui capture la sortie. Un client MCP permet à l’agent d’accéder à des serveurs de tiers. La gestion du contexte conserve le transcript en cours, gère les images et les supprime lorsque sa longueur devient excessive. La connexion aux fournisseurs couvre l’authentification, les points de terminaison et les formats de demande pour un fournisseur donné. En plus de cela, il y a les sorties structurées, qui imposent et vérifient que la réponse finale respecte une forme prédéfinie, ainsi que les workflows multi-agents qui enchaînent des agents de manière séquentielle, en parallèle ou en cycles.
Les SDK rendent tout cela simple. En passant de l’exécution d’un script à celle d’un service, la couche Runtime apparaît, accompagnée d’une liste encore plus longue. L’agent a besoin d’une exécution hébergée, c’est-à-dire d’un point de terminaison offrant une isolation des sessions que d’autres systèmes peuvent appeler. Il nécessite également une passerelle d’outils, ainsi que des mécanismes de tentative répétée, de délai d’expiration et de gestion des identifiants, afin que les appels aux outils restent fiables en cas de problème avec une API intermédiaire, et une gestion mémoire, de sorte que les sessions et l’état à long terme soient stockés dans un espace de stockage persistant après la fin du processus et pouvant être partagés entre les instances. Il a besoin d’une identité de charge de travail à présenter lors des appels aux outils, afin que ces appels ne soient pas effectués à l’aide d’une clé personnelle, ainsi que d’outils d’observabilité, avec des traces et des segments montrant quels outils ont été utilisés, avec quoi et pourquoi. Ajoutez un environnement de test pour le code propre de l’agent ou les outils du navigateur, des boucles résilientes qui reprennent à partir de la dernière étape terminée en cas de panne, ainsi qu’une capacité d’extension grâce à la concurrence et à l’auto-échelle derrière l’endpoint.
Sous celui-ci se trouve le plan de contrôle, qui gère plusieurs agents en même temps. Un registre permet d’publier et de découvrir les agents en temps de exécution, au lieu de les relier directement dans le code. Chaque agent a besoin d’une identité en tant qu’entité propre et identifiable dans le répertoire, ainsi que d’une définition stricte des permissions qui doit être appliquée de manière ferme et non simplement supposée. Par la suite, on a besoin d’un journal d’audit et de conformité montrant ce que la flotte a fait, d’une gouvernance des données déterminant ce que la mémoire partagée peut stocker et divulguer, ainsi que de contrôles de coûts et de quotas avec des budgets et des limites de vitesse. Les agents peuvent communiquer entre eux via un protocole ouvert comme A2A. Comme les résultats des outils reviennent directement dans le modèle, des mécanismes de protection et de défense contre les injections sont essentiels, et l’évaluation permet de savoir si les réponses sont correctes, et non seulement si l’exécution s’est terminée. Un registre dédié aux outils et aux serveurs MCP gère l’inventaire, la gestion des versions, la découverte, le regroupement ainsi que l’authentification par serveur. La gestion rapide des versions vous permet de fixer, de comparer et de revenir en arrière sur les prompts afin que les exécutions restent reproductibles. En bref, chacun de ces kits constitue un harnais prêt à l’emploi. Ceux de Claude, d’OpenAI et de Google fournissent le mécanisme de base, les composants logiciels, le traitement du contexte ainsi que les connexions aux fournisseurs, vous laissant ainsi vous concentrer sur les parties essentielles : vos outils, vos prompts et la forme de vos résultats. ADK est le nom atypique parmi eux, bien qu’il remplisse la même fonction ; par conséquent, les « SDKs » mentionnés ci-dessous englobent de manière générale les trois solutions.
Les opinions divergent quant aux éléments du harnais à fournir, au degré d’opinionné de chacun d’eux, ainsi qu’au point exact où le harnais s’arrête et où votre code doit reprendre le contrôle. La manière la plus claire de préciser cela consiste à créer un agent dans chacun d’eux. Une fois les trois agents en place, on peut définir l’ensemble du harnais en indiquant ce que chaque kit fournit et ce qu’il laisse à votre charge.
L’agent à tester : un enquêteur d’étiquettes nutritionnelles
Un test fiable doit soumettre ces kits à la tâche pour laquelle ils ont été conçus : un cycle de raisonnement impliquant plusieurs outils, où chaque étape suivante dépend des résultats de l’étape précédente. Une simple appel à un modèle encapsulé dans un framework ne compte pas. C’est l’enquêteur qui effectue réellement le travail. On lui montre une photo d’une étiquette nutritionnelle, et il effectue cinq tâches différentes.
- Il lit l’étiquette à l’aide de sa propre vision artificielle, en extrayant le nom du produit, la liste complète des ingrédients et additifs, la teneur en sucre et en graisses saturées par 100 ml, ainsi que l’indication de savoir si le produit est une boisson. La photo constitue la seule source fiable, sans base de données de produits à laquelle se référer.
- Pour chaque additif détecté, que l’étiquette indique
E471ou « Lécithine de soja », il consulte la liste des additifs autorisés par la SFA de Singapour et indique son statut ainsi que tout avertissement destiné aux consommateurs. - Il interroge l’ensemble de données HPB sur data.gov.sg pour vérifier si le produit porte le symbole Healthier Choice.
- Pour les boissons, il détermine le grade Nutri-Grade A/B/C/D en fonction de la teneur en sucre et en graisses saturées.
- Il assemble tous ces éléments en un jugement hiérarchisé, soit VERTE, AMBRE ou ROUGE selon les constatations, en citant à chaque fois l’outil ou l’étiquette qui a fourni ces informations, ainsi que le raisonnement du modèle en premier lieu.
Il ne commence pas non plus à zéro à chaque fois. Il se souvient de ce qu’il a déjà vu et apprend au fur et à mesure, de sorte que tant un produit examiné hier qu’un nom d’additif dont il a eu du mal à déchiffrer le nom précédemment sont disponibles la prochaine fois. Nous en parlerons plus en détail plus tard.
Ainsi, le banc d’essai offre un cycle d’essai de longueur variable, une source de données réelle de Singapour, une entrée d’images, un résultat que l’on peut vérifier facilement dans les allées d’un supermarché, ainsi qu’un agent qui s’améliore un peu à chaque exécution.
Assurer une comparaison équitable
Tout ce qui n’était pas le SDK devait rester identique. Les trois démos appellent un proxy LiteLLM, un passage compatible OpenAI qui dessert également une extrémité /v1/messages d’Anthropic pour le SDK Claude. Un seul fichier .env racine contient la clé, l’URL de base et le nom du modèle pour chacun d’eux, les modèles étant Claude Sonnet 4.6, GPT-5.4 et Gemini 3.5 Flash. La manière dont chaque SDK est orienté vers ce proxy révèle beaucoup d’informations.
Chaque démonstration existe dans son propre projet uv, ce qui vous permet de l’exécuter sans installer les dépendances des autres. Le tableau des additifs SFA a été créé une seule fois à partir du PDF « Permitted-Additives » publié par l’agence alimentaire de Singapour, générant ainsi sfa_additives.json (environ 540 entrées, dont environ 270 portent des numéros E), puis copié tel quel dans chaque projet. Les images de test étaient également identiques : deux produits réels de Singapour, un sachet Milo 3in1 et une boîte de lait Marigold HL.
Cela ne laisse que ce qui relève véritablement de la décision du SDK : la manière dont un outil est défini, comment une image est intégrée, comment le modèle est configuré, s’il existe une sortie structurée et comment on peut voir ce qui s’est passé.
Un avertissement avant de présenter le code. Les extraits ci-dessous contiennent une fonction recall_product ainsi qu’un argument e_number_hint. Ils permettent le fonctionnement de la mémoire et de l’amélioration automatique abordées plus loin, donc ignorez-les pour l’instant.
Le Claude Agent SDK d’Anthropic
Le Claude Agent SDK est directement intégré à Claude Code, l’agent que de nombreuses personnes utilisent déjà dans une terminal, et cette décision unique influence tout le reste. Vous ne créez pas de boucles à partir de primitives ; vous utilisez plutôt un agent complet et éprouvé, auquel vous fournissez vos outils et vos instructions. Des outils intégrés (Read, Write, Edit, Bash, WebFetch) sont déjà disponibles, le modèle de permissions et la gestion du contexte sont déjà résolus, et vos outils personnalisés s’attachent via un serveur MCP qui fonctionne à l’intérieur de votre propre processus. Tout s’exécute de manière asynchrone grâce à anyio.
Ce que vous achetez, c’est un véritable agent capable d’utiliser un ordinateur, prêt à l’emploi. Tout ce qui nécessite de lire des fichiers, d’exécuter des commandes de shell, d’éditer du code source ou de naviguer est fourni dès le premier jour sous une forme adaptée à un environnement professionnel, car il s’agit du même mécanisme qui alimente Claude Code. MCP est considéré comme un élément de premier plan, ce qui signifie qu’une outil personnalisé que vous écrivez fonctionnera sans modification dans Claude Code, dans Claude Desktop ainsi que dans votre propre application. Parmi ces trois solutions, celle-ci repose sur l’environnement d’exécution d’agent le plus mature.
Le coût apparaît dans les outils, où vous devez entrer manuellement le nom, la description et le schéma :
@tool(
"check_sfa_additive",
"Check whether a food additive is permitted by the Singapore Food Agency, "
"using the full SFA permitted-additives list parsed from the official PDF. "
"Accepts an E-number ('E211') OR a name ('Sodium Benzoate', 'Soy Lecithin'). "
"If you pass a name and also know its E/INS number, set e_number_hint — an "
"unrecognised name that the hint resolves is REMEMBERED for next time.",
{"additive": str, "e_number_hint": str},
)
async def check_sfa_additive(args: dict) -> dict:
raw = args["additive"].strip()
hint = args.get("e_number_hint", "").strip()
entry = _resolve_additive(raw)
if not entry and hint:
entry = _resolve_additive(hint)
if entry: # learn: this label name -> this number, persisted to disk
num = entry["e_number"] or entry.get("ins") or ""
_MEMORY["learned_aliases"][_norm(raw)] = re.sub(r"\(.*\)", "", num).lower()
_save_memory(_MEMORY)
return _ok(_format_entry(entry, raw))
Ce dictionnaire {arg: type} exige plus de saisie que les deux autres kits, mais il ne laisse aucune place à l’ambiguïté. Les outils sont ensuite mis à disposition via un serveur MCP en mémoire, et le modèle les référence avec un préfixe mcp__<server>__<tool> que vous devez retenir lors de la saisie de allowed_tools:
server = create_sdk_mcp_server(
name="sg-nutrition-tools",
version="1.0.0",
tools=[recall_product, check_sfa_additive, check_hcs, calculate_nutri_grade],
)
options = ClaudeAgentOptions(
system_prompt=SYSTEM_PROMPT,
mcp_servers={"sg": server},
allowed_tools=[
"mcp__sg__recall_product",
"mcp__sg__check_sfa_additive",
"mcp__sg__check_hcs",
"mcp__sg__calculate_nutri_grade",
],
model=CLAUDE_MODEL,
permission_mode="bypassPermissions",
max_turns=20,
# Point the bundled CLI at the LiteLLM proxy's Anthropic endpoint.
env={
**os.environ,
"ANTHROPIC_BASE_URL": BASE_URL,
"ANTHROPIC_AUTH_TOKEN": API_KEY,
},
)
Les images sont ajoutées de la même manière manuelle. Il n’existe pas d’argument image=. query() accepte un itérateur asynchrone de dictionnaires de messages, ce qui vous permet de créer vous-même les blocs de contenu natifs de Claude, un bloc de texte accompagné d’un bloc d’image :
async def message_stream():
yield {
"type": "user",
"message": {"role": "user", "content": [
{"type": "text", "text": "Investigate this nutrition label."},
{"type": "image", "source": {"type": "base64", "media_type": mime, "data": b64}},
]},
}
async with ClaudeSDKClient(options=options) as client:
await client.query(message_stream())
async for message in client.receive_response():
... # stream ToolUseBlock / TextBlock as they arrive
Le bord tranchant correspond aux connexions du modèle. Le SDK fait fonctionner la CLI Claude Code fournie, qui utilise le format de messages d’Anthropic ; vous pouvez donc la rediriger vers le proxy via des variables d’environnement (ANTHROPIC_BASE_URL et ANTHROPIC_AUTH_TOKEN). Aucun objet client n’a besoin d’être transmis. L’avantage est un cycle entièrement visible : affichez chaque ToolUseBlock dès son arrivée, ce qui vous permet de suivre en direct le déroulement de chaque cycle d’ajout. Voici un exemple d’exécution sur le sachet Milo :
sausheong claude-demo % uv run python main.py ../milo.jpeg
Investigating image: ../milo.jpeg
────────────────────────────────────────────────────────────
I'll start by reading the label carefully, then run all checks in parallel.
**Reading from the label:**
- **Product:** Nestlé MILO 3in1
- **Net weight:** 27g powder sachet (add hot water to prepare — this is a **powder, not a ready-to-drink beverage**)
- **Ingredients:** Sugar, PROTOMAL® Malt Extract (Contains Barley), Skimmed Milk (Cow's Milk), Cocoa, Palm Oil, Minerals (Dicalcium Phosphate, Disodium Phosphate, Ferric Pyrophosphate), Resistant Dextrin, Flavourings, Vitamins (C, B3, D, B6, B2, B12)
- **Identifiable additives:** Dicalcium Phosphate (E341), Disodium Phosphate (E339), Ferric Pyrophosphate; Flavourings (generic — cannot identify specific ones)
Kicking off recall + HCS check + all additive checks simultaneously:
→ tool: recall_product({'product_name': 'Nestlé MILO 3in1'})
→ tool: check_hcs({'product_name': 'MILO 3in1'})
→ tool: check_sfa_additive({'additive': 'Dicalcium Phosphate', 'e_number_hint': 'E341'})
→ tool: check_sfa_additive({'additive': 'Disodium Phosphate', 'e_number_hint': 'E339'})
→ tool: check_sfa_additive({'additive': 'Ferric Pyrophosphate', 'e_number_hint': 'E450'})
All checks returned. Note: this is a **powder**, not a ready-to-drink beverage, so Nutri-Grade (which requires per-100ml liquid values) does **not apply** — I will skip `calculate_nutri_grade`.
---
## Reasoning
**Step 1 — Product recall.** This product has been seen before. The prior verdict is consistent with what I read now — I'll treat this fresh investigation as the authoritative one and use the prior as a cross-check.
**Step 2 — Label reading.**
- MILO 3in1 is a **27g powder sachet** — you add hot water to make a drink. It is classified as a "Tonic Food Drink" on the label. Because it is sold as a dry powder (not a pre-packaged ready-to-drink liquid), Nutri-Grade per-100ml calculation does not apply.
- Nutritional values per serving (27g): Energy 110 kcal, Total Fat ~2.4g, Saturated Fat ~1.2g, Total Sugars ~13.8g (of which Added Sugars ~10g), Sodium ~38mg.
- **Sugar is the #1 ingredient** — it appears first in the ingredient list, before the malt extract.
**Step 3 — Additives.**
- **Dicalcium Phosphate (E341):** SFA-permitted ✅ — used as a mineral/calcium source.
- **Disodium Phosphate (E339):** SFA-permitted ✅ — used as an acidity regulator/mineral.
- **Ferric Pyrophosphate:** SFA-permitted ✅ — used as an iron supplement/mineral source.
- **Resistant Dextrin:** A dietary fibre ingredient (prebiotic fibre), not a regulated additive — no E number, no SFA check needed.
- **Flavourings:** Generic label — cannot identify specific flavouring compounds; SFA check not possible.
- **Vitamins (C, B3, D, B6, B2, B12):** Added as nutritional fortification, not food additives in the regulatory sense. Riboflavin (B2) carries E101 when used as a colourant, but here it's explicitly declared as a vitamin supplement — standard and expected.
**Step 4 — Healthier Choice Symbol.** HCS certified ✅ (MILO 3in1 Activ-Go appears multiple times in the HPB dataset — `check_hcs`).
**Step 5 — Sugar content reality check.** Each 27g sachet contains ~13.8g total sugars (~10g added sugars). That's more than **half the sachet's weight is sugar**, and exceeds the WHO daily added-sugar guideline of 25g in less than one cup. This is label-read, not a tool finding.
---
## Verdict
### 🟢 GREEN — No Concern
- **All three declared additives are SFA-permitted** (Dicalcium Phosphate E341, Disodium Phosphate E339, Ferric Pyrophosphate) — `check_sfa_additive` ×3.
- **Carries the Healthier Choice Symbol** — HPB has certified MILO 3in1 Activ-Go — `check_hcs`.
- **Vitamins and minerals fortified** — B2, B3, B6, B12, C, D, plus calcium and iron — label.
- **Contains barley malt extract** — a whole-grain source of energy — label.
- **Resistant Dextrin** (prebiotic dietary fibre) is a functional ingredient, not a concern — label.
### 🟡 AMBER — Worth Knowing
- **Palm Oil present** — a saturated fat source. Saturated fat is 1.2g per sachet (~4.4g per 100g of powder). Not alarming at one serving, but worth knowing if you have multiple cups a day — label.
- **"Flavourings" are not specified** — cannot determine whether natural or artificial. No SFA check was possible — label.
- **Nutri-Grade does not apply here** — this is sold as a powder. If you reconstitute it and compare the drink to ready-to-drink alternatives, it would likely land in Grade C or D territory based on sugar content, but no regulatory grade is required on the packet.
### 🔴 RED — Genuinely Concerning
- **Sugar is the #1 ingredient** — it appears first in the list, meaning it is the dominant ingredient by weight. Each sachet delivers **~13.8g total sugars / ~10g added sugars** — label. That's 40% of the WHO's recommended 25g/day added-sugar limit in a single cup. If you're drinking 2–3 sachets a day (as Nestlé's own label suggests for daily energy), you are consuming **20–30g of added sugar from MILO alone**, likely exceeding the daily limit.
- **Marketing framing vs. nutritional reality:** The packet prominently says "Nourishing Energy" and shows vitamins, but the sugar load is the dominant nutritional feature. The fortification (vitamins, minerals) does not offset this — label.
**Bottom line for a Singapore shopper:** MILO 3in1 is safe from an additives and regulatory standpoint — everything checks out with SFA and HPB. But it is a **sugar-heavy product** dressed up in health language. Treat it as an occasional treat rather than a daily nutritious breakfast drink, especially for children.
Où ça pose problème. Il n’existe pas de sortie structurée native, ce qui signifie que vous obtenez du texte que vous devez soit parser vous-même, soit demander explicitement sa forme, ce qui représente la principale différence quotidienne par rapport au SDK d’OpenAI. Les schémas sont manuels, il faut donc écrire davantage et maintenir la synchronisation avec chaque fonction. La connexion aux modèles s’effectue via l’environnement, et le proxy ciblé doit proposer une interface /v1/messages au format Anthropic, car une interface compatible OpenAI seule ne suffit pas. Considérez la liste allowed_tools comme une approbation préalable, et non comme un environnement isolé : cela signifie « sauter la demande de confirmation pour ces outils », et non « l’agent ne peut voir que ces outils » ; ceux qui la perçoivent comme une liste blanche seront surpris. Oubliez le préfixe mcp__sg__ : l’outil restera alors non approuvé, sans aucun message d’erreur. L’empreinte mémoire est également plus importante, car vous lancez le moteur Claude Code en arrière-plan et vous ne réalisez pas une simple appel API.
Choisissez-le lorsque l’agent doit manipuler une machine. Cela inclut la lecture et l’écriture de fichiers, l’exécution de commandes shell, l’édition d’une base de code ou la navigation, toute tâche pour laquelle les outils intégrés de Claude Code vous épargnent la nécessité de créer un environnement isolé. C’est aussi le choix naturel si vous utilisez déjà MCP, ou si vous développez quelque chose qui doit ressembler à Claude Code. La boucle de streaming est facile à suivre, ce qui est pratique pour les agents qui fonctionnent sur de longues périodes.
Préférez-le lorsque vous avez besoin d’une boucle d’appel de fonctions légère et structurée qui renvoie un objet organisé, car c’est là que le SDK d’OpenAI excelle. Préférez un autre choix si vous ne pouvez pas fournir d’endpoint au format Anthropic ou si la taille des dépendances est importante. Une tâche simple comme « appeler trois outils et renvoyer un objet Pydantic » nécessite bien moins de ressources.
SDK OpenAI Agents
Le SDK d’OpenAI est un wrapper léger, conçu de manière intentionnelle pour respecter les principes du Python, et il s’adapte pleinement à ce langage. Les indications de type se transforment en schémas d’outil, les docstrings en descriptions d’outil, et les modèles Pydantic en contrats de sortie. Aucune règle ne prescrit un organisation particulière du projet : il n’y a ni environnement d’exécution séparé ni nécessité d’utiliser MCP. Vous écrivez des fonctions et des modèles, créez un Agent et le passez à Runner.run. L’objectif est d’assurer la distance la plus courte possible entre une fonction Python et un agent fonctionnel.
Son meilleur atout est le voyage aller-retour structuré. En spécifiant output_type=YourModel, ce qui est renvoyé est un objet Python validé et non un bloc de données que l’on espère être du JSON ; le SDK écrit la requête, analyse la réponse, la valide et retourne vers le modèle si sa structure n’est pas correcte. Les schémas fournis gratuitement grâce aux indications de type, ainsi qu’un tableau de bord de suivi gratuit sur le propre backend d’OpenAI, font de cette solution le moyen le plus rapide d’obtenir un agent structuré et observable. Lorsque la tâche consiste à « prendre cette entrée et me fournir un résultat structuré », aucun autre outil n’est aussi direct.
Les outils n’ont absolument pas besoin de créer de schémas :
@function_tool
async def check_sfa_additive(additive: str, e_number_hint: str = "") -> str:
"""Check whether a food additive is permitted by the Singapore Food Agency.
Looks the additive up in the full SFA permitted-additives list (parsed from
the official SFA PDF). Accepts either an E-number or a plain-English name as
printed on a Singapore label.
Args:
additive: An E-number ('E211', 'e211', 'en:e211') OR an additive name
('Sodium Benzoate', 'Soy Lecithin', 'MSG').
e_number_hint: Optional. If you pass a name and also know its E/INS
number, supply it here. If the name isn't recognised but the number
is, the mapping is REMEMBERED so the name resolves directly next time. """
...
Le décorateur lit les indications de type ainsi que la section Args de la documentation et construit le schéma JSON, de sorte qu’il n’y a rien à gérer manuellement.
C’est dans le résultat que cela se révèle vraiment utile. Le verdict est déclaré sous forme de modèle Pydantic :
class NutritionVerdict(BaseModel):
reasoning: str # declared first: the model works out loud before committing
product_name: str
summary: str
green: list[Finding]
amber: list[Finding]
red: list[Finding]
recommendation: str
Avec output_type=NutritionVerdict, l’exécution renvoie un objet validé, sans étape de parsing et sans risque d’erreur. Placer reasoning en premier est un astuce utile à adopter : la sortie structurée sert également de espace de travail provisoire, permettant au modèle de réfléchir à voix haute avant de formuler des conclusions, tout en conservant ensemble l’objet organisé et les raisonnements associés.
La connexion au modèle et l’entrée d’image sont les plus simples des trois :
client = AsyncOpenAI(base_url=BASE_URL, api_key=API_KEY)
model = OpenAIChatCompletionsModel(model=OPENAI_MODEL, openai_client=client)
agent = Agent(name="SG Nutrition Investigator", model=model,
instructions=INSTRUCTIONS, output_type=NutritionVerdict,
tools=[recall_product, check_sfa_additive, check_hcs, calculate_nutri_grade])
user_input = [{"role": "user", "content": [
{"type": "input_text", "text": "Investigate this nutrition label."},
{"type": "input_image", "image_url": _data_url(image_path)}, # base64 data URL
]}]
result = await Runner.run(agent, input=user_input)
verdict: NutritionVerdict = result.final_output
C’est un client AsyncOpenAI destiné au proxy, enveloppé dans OpenAIChatCompletionsModel, avec un élément input_image contenant la photo. Rien de compliqué.
Points à surveiller. Par défaut, les appels aux outils s’exécutent en parallèle, ce qui permet au modèle de lancer plusieurs appels check_sfa_additive en même temps. Les nôtres étant asynchrones et ne présentant aucun effet secondaire, c’est un avantage. Un outil qui modifie l’état partagé, maintient une connexion non réentrante ou dépend de l’ordre d’exécution se comportera mal, et les échecs apparaîtront de manière intermittente en fonction du timing, ce qui est le pire scénario possible. Il faut soit rendre les outils compatibles avec la concurrence, soit limiter le fonctionnement du modèle.
Le suivi présente ses propres difficultés. Le tableau de bord est excellent, mais les traces sont envoyées vers l’endpoint d’ingestion propre à OpenAI, et derrière un proxy de tiers, la clé émise par ce dernier ne peut pas authentifier les requêtes. Ainsi, en exécutant set_tracing_disabled(True), votre meilleur outil d’observabilité disparaît en une seule ligne. Loin de api.openai.com, il vous faut recourir à OpenTelemetry ou à des systèmes de journalisation développés en interne. D’autres problèmes surviennent également : derrière un proxy, vous avez besoin de OpenAIChatCompletionsModel, car le chemin par défaut de l’API Responses vise les endpoints d’OpenAI, et un choix incorrect entraîne des erreurs déroutantes. Le schéma output_type doit respecter des règles strictes, de sorte que les modèles présentant un enchaînement profond ou de nombreuses unions peuvent provoquer des tentatives de validation répétées ; Finding et NutritionVerdict restent donc simples pour cette raison. Aucun outil de utilisation informatique n’est fourni avec le boîtier, il n’y a donc ni système de fichiers, ni shell, ni navigation, sauf si vous les ajoutez.
Voici le résultat pour le sachet Milo :
sausheong openai-demo % uv run python main.py ../milo.jpeg
Investigating image: ../milo.jpeg
────────────────────────────────────────────────────────────
Product: Nestlé MILO 3 in 1 Activ-Go
Reasoning: I read the sachet as **Nestlé MILO 3 in 1 Activ-Go**. The front says “goodness of malt, milk & cocoa” and the back says “MILO Ionic Food Drink,” so this is a **powdered drink mix**, not a ready-to-drink beverage. Because it is not sold as a beverage with per-100ml nutrition for the prepared drink as consumed, I did **not** calculate Nutri-Grade; that tool is only for beverages.
I first checked whether this product had been investigated before. A prior record existed, and it broadly matched what I can read here: a sweetened powdered malt-cocoa-milk drink sachet with permitted phosphate additives, relatively high in sugar for a single sachet, and not found in the HCS database. I used that only as background and then re-read this label fresh.
From the nutrition panel, I can read:
- Serving size: **1 sachet (27g)**
- Servings per package: **1**
- Per 100g / per serving shown side by side
- **Total sugars: 50.1g per 100g; 13.8g per serving**
- **Saturated fat: 0.5g per 100g; 1.2g per serving**
This is a powder, so the relevant direct reading is per 100g, not per 100ml.
I can read the ingredient list as:
**Sugar, Protomalt™ Malt Extract (contains barley), skimmed milk/cow’s milk, cocoa, palm oil, minerals (dicalcium phosphate, disodium phosphate), fibre (pyrophosphate), resistant dextrin, flavourings, vitamins (C, B3, D, B6, B2, B12).**
For additives, I checked each identifiable additive individually where possible:
- **Dicalcium phosphate**: checked and found **permitted by SFA**. That lands in GREEN.
- **Disodium phosphate**: checked and found **permitted by SFA**. That also lands in GREEN.
- The label also contains the broader terms **“phosphates”** and **“pyrophosphate”/“fibre (pyrophosphate)”** in wording around ingredients, but the image does not clearly identify a specific additive code for the pyrophosphate entry, and the generic umbrella term “phosphates” is not reliable enough on its own. I therefore do not treat the generic term as confirmed permitted just from the broad word alone. I only rely on the specifically named phosphates I could check.
- **Flavourings** is too generic to identify a specific additive, so I could not check it individually.
- The vitamins and minerals are nutrients rather than red-flag food additives in the SFA additive sense.
I also checked for the **Healthier Choice Symbol** using the product name. It was **not found** in the HCS database.
Main nutritional takeaways from the label:
- Sugar is the **first ingredient**, which usually means it is the largest ingredient by weight.
- The product contains **50.1g sugar per 100g** powder, and **13.8g sugar per 27g sachet**. That is quite a lot for one small sachet drink.
- Saturated fat is **1.2g per sachet**, which is not extreme, but the bigger concern here is sugar.
So the overall picture is: the identifiable phosphate additives appear permitted, but nutritionally this is still a **sweetened convenience drink mix** rather than a lower-sugar everyday option.
Summary: This is a powdered MILO drink sachet, not a ready-to-drink beverage. The clearly identifiable phosphate additives on the label are permitted by SFA, but sugar is the first ingredient and the product contains 50.1g sugar per 100g, or 13.8g per 27g sachet. It was not found in the Healthier Choice Symbol database.
🟢 Dicalcium phosphate is permitted by SFA. [check_sfa_additive]
🟢 Disodium phosphate is permitted by SFA. [check_sfa_additive]
🟢 This is a powdered drink mix rather than a ready-to-drink beverage, so Nutri-Grade was not applicable here. [label]
🟡 Generic terms such as “flavourings” and the unclear “fibre (pyrophosphate)” wording are not specific enough to confirm exact additive identities from the image alone. [label]
🟡 The product was not found in the Healthier Choice Symbol database. [check_hcs]
🟡 Saturated fat is 1.2g per 27g sachet (0.5g per 100g listed on the label panel image). [label]
🔴 Sugar is the first ingredient on the label. [label]
🔴 Total sugars are 50.1g per 100g and 13.8g per 27g sachet, which is high for a small single-serve drink mix. [label]
Recommendation: Fine as an occasional sachet drink if convenience matters, but not the best everyday choice if you are trying to reduce sugar. If you drink MILO often, consider using plain MILO powder with your own milk and less sugar, or choose a lower-sugar version.
Choisissez-le lorsque la tâche est structurée en étapes claires, comme la classification, l’extraction, l’évaluation ou l’émission de verdicts, partout où un objet structuré est préférable au texte libre et que vous souhaitez utiliser un minimum de code Python. Les équipes qui apprécient les indications de type l’apprécieront, et grâce au backend d’OpenAI, le suivi gratuit rend le débogage d’un agent à plusieurs étapes agréable.
Évitez-le lorsque vos outils ne peuvent pas être rendus sûrs en termes de concurrence et que vous préférez ne pas gérer le mode parallèle par défaut, lorsque vous avez besoin d’observabilité mais que vous êtes derrière un gateway non OpenAI (le suivi disparaît), ou lorsque l’agent doit utiliser directement des outils de système de fichiers et de shell, ce qui relève du domaine de Claude. Pour plusieurs agents coopérant selon un flux de contrôle explicite, ADK convient mieux, et c’est le troisième kit.
Google’s Agent Development Kit (ADK)
Parmi ces trois, ADK est véritablement un framework avec des spécifications bien définies, contrairement aux autres qui restent plus légers. Il intègre des agents, des sessions, un Runner, un SessionService ainsi que des workflows multi-agents explicites grâce à SequentialAgent, ParallelAgent et des agents en boucle. Une structure de projet prédéfinie permet à ses outils de localiser vos agents, et adk web ouvre une interface de chat locale équipée d’un inspecteur d’événements. Les outils se présentent sous forme de fonctions Python simples documentées selon le style des docstrings de Google, sans décorateurs ni dictionnaires de schémas, et l’encapsulation LiteLlm en fait l’option la moins dépendante d’un fournisseur donné.
Le concept repose sur une architecture pour les systèmes et non sur des scripts. Lorsque « l’agent » désigne en réalité plusieurs agents, tels qu’un planificateur qui transfère des tâches à des travailleurs, un pipeline séquentiel ou une distribution parallèle, ADK les propose comme composants de première classe pouvant être assemblés, vous épargnant ainsi la nécessité de créer manuellement des flux de contrôle. Les sessions sont intégrées par défaut, l’interface de débogage est la meilleure du marché, et la couche de modèle indépendante des fournisseurs vous empêche de dépendre d’un seul éditeur. Il est conçu pour évoluer vers une application complexe, à état, multi-agents.
Les définitions des outils sont ici les plus simples :
async def check_sfa_additive(additive: str, e_number_hint: str = "") -> dict:
"""Check whether a food additive is permitted under Singapore Food Agency rules.
Looks the additive up in the full SFA permitted-additives list (parsed from
the official SFA PDF). Accepts either an E-number or a plain-English name as
printed on a Singapore label.
Args:
additive: An E-number ('E211', 'e211', 'en:e211') OR an additive name
('Sodium Benzoate', 'Soy Lecithin', 'MSG').
e_number_hint: Optional E/INS number for when you pass a name. If the
name isn't recognised but the number is, the mapping is REMEMBERED
so the name resolves directly next time.
Returns:
dict with the input, resolved e_number/name, permitted status, schedule
and any consumer note. found=False if it is not in the SFA list.
"""
...
Le schéma est construit à partir de ces docstrings de style Google Args et Returns, ce qui signifie que leur qualité a une réelle importance. Une docstring vague nuit directement au choix des outils par le modèle, donc la docstring constitue en réalité le contrat.
Pour que adk web trouve votre root_agent, le projet doit disposer d’une structure de paquets spécifique :
adk-demo/
sg_nutrition_investigator/
__init__.py # from . import agent
agent.py # tools + root_agent
main.py # standalone Runner + InMemorySessionService
root_agent = LlmAgent(
name="sg_nutrition_investigator",
model=LiteLlm(model="openai/" + GEMINI_MODEL, api_base=BASE_URL, api_key=API_KEY),
instruction=INSTRUCTION,
tools=[FunctionTool(recall_product), FunctionTool(check_sfa_additive),
FunctionTool(check_hcs), FunctionTool(calculate_nutri_grade)],
)
# image input — the cleanest of the three:
message = types.Content(role="user", parts=[
types.Part(text="Investigate this nutrition label."),
types.Part.from_bytes(data=Path(image_path).read_bytes(), mime_type=mime),
])
Le modèle est acheminé via l’encapsuleur LiteLlm d’ADK, où un préfixe openai/ fait en sorte que litellm utilise le protocole compatible OpenAI avec le proxy. Transmettre des images à l’aide de types.Part.from_bytes est la méthode la plus élégante des trois, et adk web offre l’expérience de développement intégrée la plus agréable dans cette comparaison.
Points à considérer. La plupart concernent le poids. En dehors de adk web, il faut configurer un Runner, un SessionService ainsi que des identifiants explicites d’application, d’utilisateur et de session avant l’envoi du premier message, ce qui représente beaucoup de formalités par rapport aux quelques lignes nécessaires ailleurs. La structure globale joue également un rôle essentiel. La ligne from . import agent dans __init__.py permet à adk web de découvrir root_agent ; toute déviation de cette structure perturbe cette découverte. litellm génère des messages d’alerte pour des backends non utilisés (avertissements inoffensifs concernant Bedrock et SageMaker du type « no module named botocore »), tandis qu’ADK ajoute un avertissement de schéma [EXPERIMENTAL] en plus, ce qui conduit à la suppression automatique des deux à l’importation. Une sortie structurée est également plus complexe à gérer, car le response_schema de Gemini est plus sensible derrière un proxy que le output_type d’OpenAI, c’est pourquoi cette démonstration utilise un jugement basé sur un texte fourni en entrée. À juste titre, cela dépend autant du proxy que de l’ADK. Un dernier point : sur la voie LiteLlm combinée à Gemini-flash, l’agent a décrit moins ses réflexions que les autres avec la même entrée, ce qui montre que ce comportement dépend du modèle et de sa configuration, et non seulement du SDK.
La sortie pour le sachet Milo :
sausheong adk-demo % uv run python main.py ../milo.jpeg
Investigating image: ../milo.jpeg
────────────────────────────────────────────────────────────
→ tool: recall_product({'product_name': 'Nestle MILO 3 in 1'})
→ tool: check_sfa_additive({'e_number_hint': 'E341', 'additive': 'Dicalcium Phosphate'})
→ tool: check_sfa_additive({'additive': 'Disodium Phosphate', 'e_number_hint': 'E339'})
→ tool: check_sfa_additive({'additive': 'Ferric Pyrophosphate'})
→ tool: check_hcs({'product_name': 'Nestle MILO 3 in 1'})
→ tool: calculate_nutri_grade({'saturated_fat_per_100ml': 0.5, 'sugar_per_100ml': 6.1})
## Reasoning
We investigated the label of **Nestle MILO 3 in 1 (with Activ-Go)**. Here is our step-by-step analysis:
1. **Product Categorisation & Reconstitution**:
* This is a dry beverage powder sold in single-serving 27g sachets.
* The nutritional information panel provides two columns: "Per Serving 27g" (the dry sachet) and "Per 100ml*" (as reconstituted by adding 200ml of hot water).
* For the **Singapore Nutri-Grade** system, the grading is calculated based on the beverage *as reconstituted* according to the directions.
2. **Sugar & Saturated Fat Analysis (Per 100ml prepared)**:
* **Total Sugars**: **6.1g per 100ml** (of which 4.8g is Added Sugar).
* **Saturated Fat**: **0.5g per 100ml**.
* Feeding these values into the Nutri-Grade calculator yields **Nutri-Grade C** (due to the sugar content being > 5g/100ml). Grade C beverages are classified as "less healthy" and must display the Nutri-Grade label at retail.
3. **Ingredients & Additives Analysis**:
* **Sugar is the #1 Ingredient**: Refined sugar is listed first on the ingredients list, meaning it is the largest ingredient by weight in this packet—even ahead of malt extract and milk powder.
* **Additives**:
* **Dicalcium Phosphate** (E341(ii)) and **Disodium Phosphate** (E339(ii)) are SFA-approved food additives used as stabilizers and emulsifiers.
* **Ferric Pyrophosphate** is an SFA-approved mineral nutrient used to fortify the drink with Iron.
* **Healthier Choice Symbol (HCS)**: The packet displays the red HCS logo. This is because it is formulated to be "25% lower in sugar" *compared to regular malted beverages*. However, in absolute terms, it still carries enough sugar to land in Nutri-Grade C.
---
## Verdict
### 🟢 Green Findings
* **Fortified with Vitamins & Minerals** `[label]`: Contains B-vitamins (B2, B3, B6, B12), Vitamin C, Vitamin D, Calcium, and Iron (via *Ferric Pyrophosphate* `[check_sfa_additive]`).
* **Contains Soluble Fibre** `[label]`: Features *Resistant Dextrin*, a prebiotic soluble dietary fibre (1.1g per serving) that supports gut health.
* **SFA-Approved Additives** `[check_sfa_additive]`: Phosphates used for stabilizing and emulsifying (*Dicalcium Phosphate* and *Disodium Phosphate*) are fully permitted.
* **Healthier Choice Certified** `[check_hcs]`: Carries Singapore's HCS logo for having at least 25% less sugar than standard malted chocolate drinks.
### 🟡 Amber Findings
* **Nutri-Grade C** `[calculate_nutri_grade]`: When prepared as instructed, it gets a **Grade C** because of its **6.1g of sugar per 100ml**. Grade C drinks are in the "less healthy" bracket.
* **Contains Palm Oil** `[label]`: Palm oil is added to give a rich mouthfeel, contributing **1.2g of saturated fat per serving** (0.5g/100ml).
### 🔴 Red Findings
* **Sugar is the Main Ingredient** `[label]`: Sugar is listed as the very first ingredient on the label, which means there is more plain sugar by weight than malt extract (*PROTOMALT*) or milk powder.
* **High Sugar Load per Cup** `[label]`: A single small 27g sachet contains **13.8g of total sugar** (equivalent to about **3 teaspoons of sugar**), of which **10.8g** (approx. 2.5 teaspoons) is added refined sugar. Drinking multiple cups a day can quickly max out your recommended daily limit for added sugars.
Choisissez ADK lorsque vous construisez un système : plusieurs agents coopérant entre eux, des flux de travail séquentiels, parallèles ou en boucle explicites, un véritable état de session ainsi que la possibilité d’échanger des modèles entre différents fournisseurs. L’inspecteur adk web se paie lui-même pendant le développement, et l’architecture compense son coût initial à mesure que le projet se développe.
Évitez-le lorsque il n’y a qu’un seul agent simple. Le code de base pour le lanceur et la session, ainsi que le schéma imposé, représentent des coûts supplémentaires que vous ne récupérerez jamais. Pour obtenir le résultat le plus rapide en tapant du texte, utilisez le SDK d’OpenAI ; pour un agent utilisant un ordinateur avec des outils intégrés, optez pour celui de Claude. ADK justifie sa complexité à grande échelle, mais en deçà, c’est plus un framework que ce dont vous avez besoin.
Exécution des démos
Avec les trois agents créés, voici comment les exécuter. Chacun est un projet uv, et chacun lit le fichier .env racine qui contient la clé du proxy, l’URL de base ainsi que les noms des trois modèles :
cd openai-demo # or claude-demo, or adk-demo
uv run python main.py ../milo.jpeg
uv run python main.py ../hl.jpeg
Pour régénérer le tableau SFA à partir du PDF, exécutez uv run --with pdfplumber python build_additives.py. Fournissez votre propre photo d’étiquette en indiquant son chemin. Les deux étiquettes fournies sont des produits réels de Singapour et obtiennent les notes attendues : le lait reçoit la note Nutri-Grade B, tandis que Milo, en poudre, obtient une note C une fois préparé.
Aller au-delà d’une seule tentative
Une démonstration qui se limite à une seule tentative masque les aspects intéressants. Trois ajouts, identiques en forme dans tous les SDK, rendent les agents plus réalistes, et chacun en dit long sur le SDK sous-jacent.
Données réelles. La vérification des additifs n’est pas un dictionnaire jouet à sept entrées. Le fichier build_additives.py analyse le PDF officiel de la SFA, dont les trois tableaux utilisent trois layouts de colonnes différents (ce qui constitue en soi une aventure), afin d’obtenir environ 540 additifs. Lors du chargement, chaque démonstration crée un index de noms normalisé de sorte que l’appellation « monosodium glutamate » corresponde à l’appellation officielle du PDF, « Monosodium L-glutamate ». Un index des numéros E/INS, basé sur le numéro de base, permet à « 500 » de correspondre encore à « 500(ii) ». Une courte liste gère les termes argotiques réels absents du PDF, tels que « MSG » et « vitamine C ». Tout ce qui n’apparaît vraiment pas dans la liste s’affiche simplement comme « pas dans la liste », et jamais comme un faux « autorisé ».
Mémoire et apprentissage. Ces deux aspects sont mieux compris ensemble :
- Expliquer. Chaque agent présente son raisonnement, sous forme d’un champ
reasoningtapé dans le cas d’OpenAI, ainsi que d’une section## Reasoningen tête pour Claude et ADK. - Rappeler. Un outil
recall_products’exécute en premier et affiche tout jugement antérieur concernant le même produit ; chaque exécution enregistre son propre résultat. Lorsque l’on scanne une étiquette deux fois, la deuxième passe compare son résultat avec la première conclusion. - Apprendre. Si une étiquette affiche un nom que le tableau ne reconnaît pas et que le modèle fournit le nombre correct via
e_number_hint, la correspondance nom-numéro est enregistrée dans un petit fichieragent_memory.json. Lors de la prochaine exécution, ce nom est reconnu sans avoir à interroger le modèle.
C’est là l’apprentissage au niveau du système. Les poids ne changent jamais, pourtant l’agent affine ses propres données et outils grâce à l’expérience. Il s’agit d’un type d’apprentissage pratique et réalisable, contrairement au réglage fin, que aucun de ces outils ne met en œuvre en temps de exécution. Chacun inclut une couche de session native que l’on utilise pour la mémoire de production (SQLiteSession d’OpenAI, le stockage de sessions de Claude, SessionService d’ADK), et le fichier JSON permet simplement de rendre la démonstration portable et facile à examiner.
Plus il lit d’étiquettes, mieux il se perfectionne. Une photo dont les noms d’additifs sont convertis en nombres par le modèle apprend à cette recherche un alias nouveau, et chaque produit analysé devient quelque chose que la prochaine exécution peut retrouver. Lorsque le « phosphate disodique » apparaît pour la première fois, l’exécution s’appuie sur le modèle pour le traduire en E339 ; par la suite, le tableau déterministe connaît déjà cette correspondance, ce qui rend le processus plus rapide, moins coûteux et reproductible sans appel au modèle. Au fil du temps, avec une série de produits, agent_memory.json se transforme en un dictionnaire d’alias ainsi qu’en un historique des décisions compilé par l’agent lui-même. En orientant une flotte vers un seul fichier de mémoire partagée, un nom appris par un agent devient connu de tous. Le modèle reste inchangé, tandis que l’agent évolue.
Dernier détail tiré des tests réels : lorsqu’on leur demandait de convertir un numéro E accompagné d’un nom, les trois modèles ont tous fonctionné de manière fiable, transformant « Phosphate disodique » en E339 sans aucune aide. C’est le mécanisme de boucle qui fait son travail, les connaissances générales des modèles compensant ce que la recherche déterministe rate, et le système mémorisant le résultat.
Qui fournit quoi
Avec les trois agents créés, testés et étendus, il est possible de décrire l’ensemble du système en indiquant ce que chaque SDK fournit réellement par rapport à ce qui devait être développé manuellement. Imaginez une grille où le vert signifie que c’est le SDK qui le fournit, l’ambre signifie que c’est partiel et le rouge signifie que c’est à développer soi-même. Les composants se répartissent dans ces mêmes trois niveaux : le développement pour créer l’agent, le temps de exécution pour le faire fonctionner, et le plan de contrôle pour gérer toute la flotte. Les SDKs se trouvent dans le premier niveau.
En regardant la couche de développement, presque tout est en vert ; les outils concordent presque entièrement sur la manière de créer un agent. Si l’on examine les deux couches en dessous, le schéma s’inverse : quelques cellules vertes ou ambrées aux extrémités (les workflows et l’évaluation d’ADK, le suivi d’OpenAI) se détachent sur un océan rouge partagé par les trois. C’est ce contraste qui constitue la véritable découverte. Ils s’accordent sur la création de l’agent, mais c’est à vous de gérer son exécution et sa gestion.
Les lacunes dans les SDK
La couche de développement est ce que les SDK rendent simple. La couche de temps de exécution et la couche de contrôle y sont pour la plupart absentes, et en réalité ce ne sont pas là les fonctionnalités que les SDK sont censés fournir.
Commencez par les problèmes qui sont apparus uniquement lors de la compilation de démonstration. L’un d’eux est le tool gateway, que la couche de runtime distingue d’une simple invocation d’outil. Le framework appelle votre outil de manière fiable, mais ne fait rien pour en assurer la fiabilité. L’API HCS de data.gov.sg, par exemple, répond parfois avec un code 503 accompagné d’un corps HTML, et une utilisation naïve de response.json() provoquait l’échec complet de l’exécution (une leçon apprise à grands frais). Un outil d’aide _safe_json, des délais d’attente de dix secondes ainsi que des solutions de secours du type « pas dans la liste » ont résolu le problème, permettant ainsi à un fournisseur instable de devenir simplement un élément à prendre en compte sans plus faire échouer l’agent. Les tentatives de réessai, les mécanismes de retard et la gestion des erreurs au sein des outils relèvent entièrement de vous, tâches que le gateway est censé assumer.
Un deuxième aspect est la mémoire gérée. Ces trois approches comprennent toutes une couche de session, qui représente l’état de la conversation sur un nœud, mais qui n’est pas identique à une application qui stocke des données accessibles par l’ensemble du système. Le fichier agent_memory.json généré ici (les alias appris, l’historique de recall_product, les mises à jour qui améliorent l’agent avec chaque étiquette) constitue une logique de domaine appliquée en surcouche, et l’idée selon laquelle « plus on l’utilise, mieux il fonctionne » n’est pas quelque chose fourni par un framework. Il en va de même pour l’utilisation de données réelles : le parsing du PDF SFA en plus de 540 éléments structurés, la création de l’index normalisé ainsi que des mécanismes de recherche par numéro de base ne relèvent pas de la responsabilité d’un SDK, et l’alimentation d’un agent en données réelles est un processus à configurer soi-même, et non un mécanisme à activer simplement.
La sécurité en matière de concurrence pour les outils est pire que le simple fait qu’ils ne soient pas gérés, car c’est le framework lui-même qui crée le risque. Par défaut, le SDK d’OpenAI exécute en parallèle des appels à des outils indépendants, et il revient à vous de rendre ces outils sûrs dans ce contexte. Un autre aspect courant : l’affichage du résultat et son enregistrement quelque part relèvent de code d’application ordinaire auquel aucun SDK ne touche.
En ce qui concerne la couche de temps de exécution, l’agent a d’abord besoin d’une exécution hébergée, c’est-à-dire d’un moyen d’être invoqué depuis autre chose qu’une session de terminal, permettant de gérer de nombreuses exécutions simultanées et d’assurer une scalabilité en arrière-plan. Il nécessite également une identité de charge de travail, c’est-à-dire des identifiants présentés aux outils afin que les appels soient effectués au nom d’une charge de travail spécifique et non à l’aide d’une clé API cachée dans une variable d’environnement. Enfin, il a besoin de boucles durables et reprendables, de sorte qu’en cas de panne à mi-parcours, l’exécution puisse se poursuivre au lieu de devoir être relancée complètement. Aucun de ces éléments n’existe dans un script sur un ordinateur portable, et aucun n’est ce que le SDK prétend fournir. Le temps de exécution doit également être capable de scaler les agents à mesure que la charge augmente.
Le plan de contrôle est le domaine où ces trois éléments ne servent pratiquement à rien. L’évaluation est le premier cas : l’affirmation « il s’est exécuté sans erreur » ne signifie pas pour autant que « c’était correct », et c’est en grande partie à vous de mesurer correctement la qualité de l’agent. ADK constitue une exception partielle grâce à son cadre d’évaluation, tandis que les deux autres laissent cette question ouverte.
Ensuite, il y a les garde-fous et la défense contre l’injection de commandes. Chaque résultat obtenu avec un outil représente une entrée non fiable renvoyée au modèle, et une photo d’étiquette ou une réponse API pourrait contenir une instruction du type « ignorez vos règles ». La couverture n’est pas uniforme et rien n’est automatique, donc le modèle de menace relève de votre responsabilité.
Vient ensuite l’identité de l’agent avec le limitation des permissions. Ces outils vous permettent de définir une personnalité pour l’agent, mais ils ne précisent pas son statut en tant que principal du répertoire ni ce à quoi il a accès, ce qui vous oblige à le lier à un propriétaire réel et à limiter son accès.
Se connecter à un serveur MCP est une autre histoire, car les trois solutions proposent un client pour cela. Cependant, la création d’un registre des outils et de MCP, qui catalogue et versionne les outils et gère un ensemble de serveurs MCP avec des fonctionnalités de découverte, de mise en commun, de gestion des identifiants par serveur et de vérification de leur état, reste à votre charge.
Le même problème apparaît à un niveau de portée supérieur dans le registre des agents, où les agents sont publiés afin que d’autres puissent les découvrir et les appeler en temps de exécution, et non grâce à des connexions codées à l’avance. ADK constitue une exception partielle, car il prend en charge les communications A2A et les cartes d’agent, tandis que les deux autres solutions laissent cette tâche à l’utilisateur.
Enfin, il y a la gouvernance des coûts et des quotas, un journal d’audit prouvant ce que la flotte a fait, la gouvernance du stockage de données partagé ainsi qu’un versionnement rapide. L’instruction du système n’est qu’une chaîne de caractères que vous possédez et le modèle est stochastique ; par conséquent, fixer, comparer et revenir en arrière sur les instructions relève d’une discipline que vous devez vous imposer.
Tout cela n’est pas une critique. S’arrêter à la boucle d’agent et aux composants associés constitue un point logique pour tracer la limite. Cela signifie cependant que le choix d’un SDK résout peut-être la moitié des exigences d’un agent en production.
Alors, quel SDK devriez-vous utiliser ?
Pour en revenir aux kits eux-mêmes, voici une description de chacun en une phrase. Choisissez le Claude Agent SDK lorsque l’agent doit gérer un ordinateur, manipuler des fichiers, utiliser le shell, du code et naviguer sur Internet ; il s’agit du runtime le plus mature, dont le fonctionnement repose sur des schémas manuels, sans sortie de type défini et sans connexion via des variables d’environnement. Choisissez le OpenAI Agents SDK pour des entrées et sorties structurées avec un usage minimal du Python ; il offre le temps de traitement le plus rapide pour les échanges de type défini, à condition d’accepter la configuration par défaut utilisant des outils en parallèle ainsi que la perte de suivi des actions derrière un proxy. Choisissez Google ADK lorsque plusieurs agents doivent coopérer au sein de workflows explicites soutenus par des sessions réelles ; son interface de développement est la plus robuste et ses choix de modèles les plus variés, bien qu’un agent isolé simple le trouve lourd à utiliser.
En réalité, il s’agit de différents outils et non de trois versions d’une même balance. Le même agent peut être implémenté dans tous, car le noyau central, à savoir le mécanisme de raisonnement parmi les outils, est commun à tous. Ce sont les éléments supplémentaires qui entourent ce noyau qui les différencient, et ce sont eux qui devraient guider votre choix.
Ce que la création trois fois a révélé
La création de l’agent à trois reprises a permis de tirer une conclusion évidente : le mécanisme de gestion de l’agent est un problème déjà résolu et standardisé, et les trois kits le mettent en œuvre de manière efficace. Les véritables différences résident dans les éléments supplémentaires, tels que les schémas écrits à la main ou déduits automatiquement, les sorties en texte libre ou en texte formaté, ainsi que l’utilisation d’un environnement d’exécution intégré, d’un client léger ou d’un framework complet. Il s’agit là de questions d’adaptation et non de qualité. Adaptez le SDK à la tâche, que ce soit Claude pour l’utilisation d’un ordinateur, OpenAI pour des résultats textés ou ADK pour les systèmes multi-agents ; chacun de ces trois outils vous sera utile.
La leçon essentielle concerne les limites, et les trois couches indiquent où elles se situent. Les kits gèrent la couche de développement et vous la fournissent en une après-midi. La couche de temps de exécution et la couche de contrôle, qui permettent d’exploiter réellement un agent et de gérer une flotte, vous appartiennent, et c’est là que se concentre l’essentiel des efforts d’ingénierie. L’outil d’enquête n’est devenu utile qu’après l’ajout d’éléments que aucun SDK ne fournit : le tableau SFA analysé pour servir de référence, les mécanismes de gestion des pannes que gérerait normalement une passerelle d’outils, ainsi que la mémoire qui améliore ses performances à chaque étiquetage. Choisissez donc un SDK en fonction de sa compatibilité avec la couche de développement, puis prévoyez des ressources pour les deux couches suivantes.
Une histoire plus vaste se cache juste derrière celle-ci, et elle se trouve dans ces couches inférieures. Étant donné que la création d’agents uniques est désormais monnaie courante, la concurrence s’est déplacée vers celui qui gère ces agents et qui en assure la supervision. De nouveaux produits émergent dans ce domaine : des environnements d’exécution tels que AWS Bedrock AgentCore et le Gemini Enterprise Agent Runtime de Google, capables d’héberger tout ce que produisent ces trois solutions, ainsi que des systèmes de gestion comme Agent 365 de Microsoft et Google Enterprise, qui enregistrent, identifient et contrôlent l’ensemble des agents. Ils vendent, presque mot pour mot, les éléments constitutifs de ce réseau. Le problème, c’est que chaque fournisseur de cloud souhaite disposer des trois couches, ce qui fait que le SDK que vous choisissez aujourd’hui vous oriente vers l’environnement d’exécution et les outils de gestion d’un fournisseur en particulier demain.
Lectures complémentaires
- Construire des agents auto-corrigés avec LangGraph : cinq conseils pratiques — Créez un agent de recherche LangGraph qui réessaie en cas d’échec, avec gestion de l’état, arêtes conditionnelles, sauvegardes intermédiaires, approbation humaine, ainsi que des explications sur les petits nœuds, accompagnées de code fonctionnel.
- 10 frameworks d’agents IA à connaître en 2026 : LangGraph, CrewAI et d’autres — Dix frameworks d’agents classés selon la manière dont ils coordonnent les agents, avec leurs avantages et inconvénients, leurs meilleures utilisations, ainsi qu’un guide pour choisir celui qui convient à votre équipe.