Déployer un agent IA en production avec AgentCore
[!NOTE] TL;DR : pour déployer un agent IA en production, j'ai comparé AgentCore Runtime (microVM de session), Lambda et les fonctions durables, avec Pydantic AI v2.64 et Strands 1.62 côté code. Verdict chiffré, architecture et code complet ci-dessous.
Quatre Lambdas et une layer
Au commencement était le requirements.txt. Il était déjà en conflit avec la limite des 250 Mo.
Ma production documentaire tourne sur quatre fonctions Lambda, de 512 Mo à 2048 Mo, avec un timeout de 900 s chacune. Toute la stack IA, dont pydantic-ai-slim 1.107.0, passe par une layer partagée. Le déploiement de ces fonctions en VPC prend entre 6 et 9 minutes. Ça se voit sur la facture.
Quand j'ai voulu y greffer un agent conversationnel avec état de session, le mur est arrivé vite. Quinze minutes de timeout, 6 Mo de payload synchrone, aucune affinité de session entre deux appels. L'agent repartait de zéro à chaque invocation.
La question
Ma question principale était la suivante : quel runtime choisir pour déployer un agent IA en production, entre AgentCore Runtime, Lambda et un microVM, et à quel coût réel par session ?
Le déploiement standard qui coince
On imagine spontanément que Lambda suffit, puisque l'agent tient dans une fonction Python. Les chiffres disent l'inverse.
Les cold starts représentent moins de 1 % des invocations, pour une durée allant de moins de 100 ms à plus d'une seconde selon la documentation AWS. Mesurés sur une fonction Python embarquant FastAPI, httpx et pydantic, ils atteignent 2.276 s en moyenne sans SnapStart, contre 0.808 s avec SnapStart, sur 1008 points de mesure. Le détail des imports pèse lourd : environ 60 ms pour pydantic, 295 ms pour boto3, mesurés le 27 septembre 2026.
Une fonction Lambda, c'est une chambre d'hôtel. À chaque nouvelle chambre, tu paies l'init complet, même si l'occupant n'y reste que cinq minutes. SnapStart, étendu aux images conteneur le 2 septembre 2026, c'est la photo de la chambre déjà faite : on restaure la photo au lieu de refaire le lit, en dessous de la seconde. La photo se paie quand même, avec un minimum de 3 heures de stockage facturées par version publiée. SnapStart change la donne.
Mais le vrai nœud n'est pas la latence, c'est l'état. L'état, voilà le vrai nœud. Lambda ne conserve rien entre deux invocations : il faut rebrancher soi-même S3, DynamoDB ou un store vectoriel pour chaque session. Bien que Lambda reste imbattable pour un appel court déclenché par un événement, un agent qui boucle sur lui-même ne rentre pas dans ce moule. Pour le débat agent contre pipeline déterministe, j'avais déjà publié une grille de décision et une mesure du coût de la preuve.
L'architecture cible
J'ai retenu une cible hybride : un Lambda routeur pour l'authentification et le contrôle des entrées, qui oriente ensuite vers AgentCore Runtime pour la session agent elle-même.
flowchart LR
U["Utilisateur ou frontend"] --> APIG["API Gateway"]
APIG --> RL["Lambda routeur : auth et validation"]
RL -->|"InvokeAgentRuntime avec runtimeSessionId"| RT["AgentCore Runtime : microVM de session"]
subgraph SESSION["Session isolée"]
AG["Agent Pydantic AI ou Strands"]
AG --> BR["Amazon Bedrock : appel du modèle"]
AG --> GW["AgentCore Gateway : outils MCP"]
AG --> CI["Code Interpreter : bac à sable"]
end
RT --> SESSION
GW -->|"OAuth via AgentCore Identity"| EXT["APIs externes"]
RT --> OBS["CloudWatch : spans, logs, métriques"]
RT --> MEM["AgentCore Memory : STM et LTM"]
Chaque runtimeSessionId obtient sa microVM dédiée, un peu comme une serre attribuée à un locataire. Personne d'autre n'y entre, et quand le locataire part, on arrache la serre et on stérilise le sol. AgentCore fait exactement cela : à la fin de la session, la microVM est terminée et sa mémoire est sanitizée, ce qui efface toute trace de la conversation. Par défaut, une session meurt après 900 s d'inactivité et vit au maximum 28800 s, soit 8 h.
Côté framework, les deux options tiennent la route. Pydantic AI v2.64.0, publiée le 30 octobre 2026 d'après la page des releases, apporte les toolsets, la capacité MCP en mode natif ou local, le streaming et l'exécution durable sur neuf moteurs dont AWS Lambda. Strands Agents, en v1.62.0 au 21 octobre 2026 avec une cadence hebdomadaire (changelog strandsagents.com), apporte les sessions snapshot et les orchestrations multi-agents Graph et Swarm avec le protocole A2A. Les deux tournent nativement sur AgentCore Runtime, qui accepte de toute façon n'importe quel framework.
L'agent et son runtime
Voici d'abord l'agent Pydantic AI : sortie typée, dépendances injectées, budgets de retries séparés pour les tools et pour la sortie.
import httpx
from pydantic import BaseModel, Field
from pydantic_ai import Agent, RunContext
from pydantic_ai.models.bedrock import BedrockConverseModel
class Deps(BaseModel):
model_config = {"arbitrary_types_allowed": True}
user_id: str = Field(..., description="Identifiant utilisateur propagé par l'authentification.")
gateway_url: str = Field(..., description="URL du AgentCore Gateway servant les outils MCP.")
class BriefOutput(BaseModel):
summary: str = Field(..., description="Résumé structuré, trois phrases maximum.")
sources: list[str] = Field(..., min_length=1, description="Identifiants des sources utilisées.")
confidence: float = Field(..., ge=0.0, le=1.0)
agent: Agent[Deps, BriefOutput] = Agent(
BedrockConverseModel("eu.anthropic.claude-sonnet-4-5-20250929-v1:0"),
deps_type=Deps,
output_type=BriefOutput,
retries={"tools": 3, "output": 2},
instructions="Tu produis un brief sourcé. Toute affirmation doit s'appuyer sur une source.",
)
@agent.tool
async def search_docs(ctx: RunContext[Deps], query: str) -> str:
"""Recherche plein texte dans la base documentaire via le Gateway AgentCore."""
async with httpx.AsyncClient(timeout=10.0) as client:
resp = await client.post(f"{ctx.deps.gateway_url}/tools/search_docs", json={"query": query})
resp.raise_for_status()
return resp.text
L'entrée AgentCore n'est qu'un habillage HTTP autour de cet agent. Le contrat impose POST /invocations et GET /ping sur le port 8080, en arm64 uniquement.
import os
from agent_def import Deps, agent
from bedrock_agentcore.runtime import BedrockAgentCoreApp
app = BedrockAgentCoreApp()
@app.entrypoint
async def invoke(payload: dict, context) -> dict:
prompt = payload.get("prompt", "")
if not isinstance(prompt, str) or not prompt.strip():
return {"error": "Le champ prompt doit être une chaîne non vide."}
deps = Deps(user_id=str(payload.get("user_id", "anonymous")), gateway_url=os.environ["GATEWAY_URL"])
result = await agent.run(prompt, deps=deps)
return {"output": result.output.model_dump(), "session_id": context.session_id}
Avec Strands, le déploiement est encore plus court : la CLI agentcore (template agent-python-strands) génère le projet, et agentcore deploy pousse le runtime. Côté état, SnapshotSessionManager ou AgentCoreMemorySessionManager remplacent la persistance manuelle. Le reste est du câblage.
Pour un déploiement en direct code, on package les dépendances pour arm64, seule architecture supportée :
uv pip install --python-platform aarch64-manylinux2014 --python-version 3.13 --target=deployment_package --only-binary=:all: -r pyproject.toml
cd deployment_package && zip -r ../deployment_package.zip .
cd .. && zip deployment_package.zip main.py agent_def.py
Puis on crée le runtime, avec son cycle de vie et le MMDSv2 devenu obligatoire le 30 juin 2026 :
import boto3
client = boto3.client("bedrock-agentcore-control", region_name="eu-west-1")
resp = client.create_agent_runtime(
agentRuntimeName="doc-brief-agent",
agentRuntimeArtifact={"codeConfiguration": {"code": {"s3": {"bucket": "bedrock-agentcore-code-123456789012-eu-west-1", "prefix": "doc-brief-agent/deployment_package.zip"}}, "runtime": "PYTHON_3_13", "entryPoint": ["main.py"]}},
networkConfiguration={"networkMode": "PUBLIC"},
roleArn="arn:aws:iam::123456789012:role/AgentCoreRuntimeRole",
platformVersion="V2",
metadataConfiguration={"requireMMDSv2": True},
lifecycleConfiguration={"idleRuntimeSessionTimeout": 900, "maxLifetime": 28800},
)
print(resp["agentRuntimeArn"])
Deux détails de la plateforme V2 changent la façon d'écrire le code (et c'est exactement là que tout le monde se trompe). Premier détail : les clients boto3 se créent dans le handler via boto3.Session(), jamais au niveau du module, sinon ils capturent les credentials d'avant le snapshot. Second détail : la clé aws:AgentCoreRuntimeSourceSid permet de limiter les accès IAM à la session en cours. Pas de magie.
Les chiffres
Sur AgentCore Runtime, la grille tarifaire publiée par AWS facture le microVM v1 à 0.0895 USD par vCPU-heure et 0.00945 USD par Go-heure, avec un minimum d'une seconde et de 128 Mo de mémoire. En v2, le CPU passe à 0.1276 USD par vCPU-heure et la mémoire à 0.0169 USD par Go-heure, mais seul le CPU actif est facturé, et la mémoire idle est récupérée après 120 s. Le Gateway facture 0.005 USD pour 1000 invocations.
Les quotas publiés donnent : requête synchrone de 15 minutes maximum, payload de 100 Mo, streaming de 60 minutes, jobs asynchrones de 8 h. Par session, le plafond est de 2 vCPU et 8 Go. Le compte monte à 5000 sessions actives en us-east-1 et 25 créations de session par seconde. Le fonctionnement des sessions et microVM est documenté ici.
Côté Lambda, la page tarifaire annonce 0.20 USD par million de requêtes, plus 0.0000166667 USD par GB-s en x86, l'Arm revenant 20 % moins cher. À 1769 Mo de mémoire, une fonction dispose d'un vCPU entier.
À partir de ces tarifs, voici le coût d'une exécution agent de 300 s à 2 vCPU et 4 Go. C'est un calcul fait main depuis les prix publiés (mesuré sur ma prod, ce serait mieux, et c'est justement la limite de cet article) :
{
"type": "bar",
"data": {
"labels": ["AgentCore Runtime v1, microVM 2 vCPU/4 Go", "AgentCore Runtime v2, microVM 2 vCPU/4 Go", "Lambda 2048 Mo, x86", "Lambda 2048 Mo, Arm"],
"datasets": [{ "label": "Coût estimé par exécution de 300 s, en USD", "data": [0.0181, 0.0269, 0.0100, 0.0080], "backgroundColor": ["#3b82f6", "#8b5cf6", "#22c55e", "#16a34a"] }]
},
"options": {
"responsive": true,
"plugins": { "title": { "display": true, "text": "Coût estimé d'une exécution agent de 300 s, calculé depuis les tarifs publiés, région us-east-1" } },
"scales": { "y": { "title": { "display": true, "text": "USD par exécution" } } }
}
}
Le point qui change tout : sur AgentCore, le temps d'attente vers le modèle ne facture pas de CPU, tandis que Lambda facture chaque milliseconde, init comprise. Les agents passent couramment 30 à 70 % de leur temps en attente I/O, et l'écart de facture suit.
La grille de comparaison
| Critère | AgentCore Runtime, microVM | Lambda, fonction événement | Lambda durable ou MicroVM |
|---|---|---|---|
| Isolation | microVM dédiée par session, mémoire sanitizée à la fin | processus isolé, mais aucun état entre invocations | microVM Firecracker, avec suspend et resume |
| Démarrage | restore de snapshot en V2, constant quelle que soit l'image | cold start de moins de 100 ms à plus d'1 s | sub-seconde avec SnapStart |
| Coût | 0.0895 USD/vCPU-h et 0.00945 USD/Go-h en v1, CPU actif seul facturé | 0.20 USD par million de requêtes, plus GB-s, init facturé | idem Lambda, avec 14 jours de rétention d'historique par défaut |
| Durée maximale | 8 h par session, 15 min en synchrone | 900 s par invocation, jusqu'à 1 an en durable asynchrone | 8 h pour les Lambda MicroVMs |
| Charge opérationnelle | gérée par AWS, avec Gateway, Identity, Memory et Observability intégrés | à ta charge : état, sessions, streaming, retries | à ta charge : packaging arm64, cycle de vie, sécurité du code exécuté |
| Adaptation | native pour Pydantic AI et Strands, MCP et A2A | adapté aux appels courts et déterministes | adapté à l'exécution de code généré, bac à sable |
Les fonctions durables Lambda, lancées à re:Invent 2025, ajoutent le checkpoint et le replay : un ExecutionTimeout jusqu'à un an, 86400 s par défaut, et une exécution synchrone toujours plafonnée à 15 minutes. Pydantic AI Harness embarque d'ailleurs AWSLambdaDurability, qui checkpoint les appels modèle et les tool calls dans des steps de fonction durable. Pour l'exécution de code généré par l'agent, le Code Interpreter d'AgentCore fournit un bac à sable de 10 Go de disque, et les Lambda MicroVMs, bâtis sur la même virtualisation Firecracker que les 15 trillions d'invocations mensuelles de Lambda, offrent suspend et resume jusqu'à 8 h.
Limites et ce qui reste à tester
Mes coûts sont calculés depuis les grilles tarifaires, pas mesurés en production. Le test prévu ensuite est un run de charge à 25 créations de session par seconde, la limite par défaut, pour observer le vrai comportement de scaling.
Je n'ai pas testé le compute Instances, qui monte à 14 jours de session, ni le baseline engagé v2 annoncé pour octobre 2026 à 0.0997 USD par vCPU-heure. La validation du prompt en entrée reste ma responsabilité : AgentCore ne vérifie pas qu'un champ prompt est une chaîne, et un bloc toolUse transmis tel quel serait exécuté sans passer par le modèle. J'ai perdu une demi-journée sur des traces tronquées avant de comprendre que mes spans dépassaient la limite UDP des segments, réponses LLM comprises.
Enfin, pydantic-ai publie une release toutes les une à deux semaines, v2.49.0 le 23 septembre, v2.64.0 le 30 octobre 2026. En production, épingle tes versions.
Le principe qui survit
Plus généralement, le choix du runtime pour un agent se joue sur une seule variable : la durée de vie de l'état entre deux appels du modèle. Dès qu'un agent attend, reprend ou conserve du contexte, la facture et la latence suivent le runtime, pas le framework. Lambda cartographie l'instant, AgentCore cartographie la session, et aucun des deux ne pardonne d'être choisi à l'envers.
Bref : si ton agent dort entre deux appels LLM, mets-le dans une session, pas dans une fonction. Voilà, merci à tous !
Si ce choix de runtime se pose sur ton trafic réel, regarde ce que nous déployons en production ou contacte l'équipe.