Accueil / Articles

Déploiement d’un agent LangGraph sur AWS Bedrock AgentCore, étape par étape

Enveloppez un agent LangGraph existant pour AgentCore, structurez le projet à l’aide de la CLI, testez-le localement, déployez-le sur AWS et appelez-le en conservant les sessions et la mémoire intactes.

Éditeur : REACTAPP.TOP8 min de lecture1792 mots

Sources: How to Deploy a LangGraph Agent on AWS Bedrock AgentCore

De nombreux agents LangGraph fonctionnent bien sur un ordinateur portable, mais se bloquent à l’étape de la mise en production. AWS Bedrock AgentCore comble cette lacune grâce à un hébergement serverless qui s’adapte en continu sans nécessiter de redessin. Les sections suivantes expliquent comment intégrer un agent existant dans AgentCore, créer un projet, le lancer localement, le déployer sur AWS et l’appeler une fois qu’il est en ligne.

Qu’est-ce que AgentCore ?

AgentCore est un environnement d’exécution hébergé par AWS où les agents sont déployés, mis à l’échelle et exécutés sans que quiconque ait à gérer des serveurs. Des frameworks tels que LangGraph, Strands, CrewAI et LlamaIndex fonctionnent tous, tout comme les modèles d’OpenAI, Google et Anthropic, ce qui permet de conserver le code de l’agent tel quel. L’isolation des sessions, la mémoire persistante, l’observabilité et la gestion des identités sont intégrées par défaut.

Tout est géré par l’AgentCore CLI. Il s’agit d’un programme Node.js qui génère le squelette du projet, sert l’agent localement et le déploie sur AWS, avec le CDK en arrière-plan.

Prérequis

  • Python 3.12 ou une version ultérieure
  • uv ou pip pour gérer les paquets Python
  • Node.js 20 ou une version ultérieure, car la CLI est distribuée via npm
  • Le AWS CDK installé globalement avec npm install -g aws-cdk
  • Un compte AWS avec des identifiants configurés localement via aws configure
  • Un code LangGraph existant qui génère un StateGraph compilé

Étape 1 : installer la CLI

Un seul outil en ligne de commande gère tout : la création du projet, l’exécution de l’application localement et le déploiement sur AWS.

npm install -g @aws/agentcore

Vérifiez que l’installation a fonctionné :

agentcore --help

Étape 2 : ajouter les dépendances

Deux paquets doivent être ajoutés aux dépendances de l’agent. bedrock-agentcore est le SDK Python qui fournit la classe d’encapsulation BedrockAgentCoreApp, tandis que aws-opentelemetry-distro correspond à la version d’OpenTelemetry pour Python développée par AWS.

# pyproject.toml
[project]
name = "my-agent"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
    "aws-opentelemetry-distro==0.17.0",
 "bedrock-agentcore>=1.6.3",
    "boto3>=1.42.0",
    "langgraph>=1.1.0",
    "langchain-core>=1.2.0",
    # ... your other existing dependencies
]

Installez-le selon le flux de travail habituel pour le projet :

uv sync
# or: pip install -e .

Étape 3 : écrire le point d’entrée dans main.py

AgentCore attend un fichier Python en tant que point d’entrée, contenant une instance de BedrockAgentCoreApp ainsi qu’une fonction décorée avec @app.entrypoint. La logique de LangGraph ne change presque pas ; elle est simplement enveloppée dans un petit point d’entrée de temps de exécution.

# main.py
from langchain_core.messages import HumanMessage
from bedrock_agentcore.runtime import BedrockAgentCoreApp
from graphs.my_agent_graph import build_my_agent  # your existing graph builder

# 1. Instantiate the AgentCore app and logger
app = BedrockAgentCoreApp()
log = app.logger

# 2. Build your graph at module load time (startup)
#    AgentCore initialises the module once, then handles concurrent invocations.
#    Any failure here will prevent a broken agent from going live.
def create_agent():
    log.info("Initialising agent...")
    graph = build_my_agent()   # returns your compiled LangGraph StateGraph
    log.info("Agent ready")
    return graph
try:
    graph = create_agent()
except Exception as e:
    log.error(f"Critical failure during agent initialisation: {e}")
    raise  # fail fast - don't let a broken agent start serving requests

# 3. Async helper that drives the LangGraph streaming loop
async def run_agent(user_input: str, session_id: str = "default-session") -> str:
    responses = []
    config = {"configurable": {"thread_id": session_id}}
    async for chunk in graph.astream(
        {"messages": [HumanMessage(content=user_input)]},
        config=config,
        stream_mode="values",
    ):
        messages = chunk.get("messages", [])
        if messages:
            last = messages[-1]
            if getattr(last, "type", None) == "ai":
                responses.append(last)
    if not responses:
        return "No response"
    content = getattr(responses[-1], "content", "")
    return content if isinstance(content, str) else str(content)

# 4. The entrypoint - this is what AgentCore calls on every invocation
@app.entrypoint
async def invoke(payload, context):
    try:
        log.info("Invoke received")
        user_input = payload.get("prompt", "")
        session_id = payload.get("session_id", "default-session")
        if not user_input.strip():
            return {"error": "Prompt cannot be empty"}
        response = await run_agent(user_input, session_id)
        return {"response": response}
    except Exception as e:
        log.error(f"Error: {e}")
        return {"error": str(e)}

# 5. Run the app (only executed when running locally using agentcore dev)
if __name__ == "__main__":
    app.run()

Ce qui est important dans ce fichier

  • BedrockAgentCoreApp() fournit au runtime son noyau : un serveur HTTP, des routes de vérification de santé et des journaux structurés, tout déjà configuré.
  • Le graphe est construit au niveau du module, une seule fois lors de l’importation et non à chaque appel. Ainsi, tout problème de démarrage apparaît immédiatement, et une application défectueuse ne reçoit jamais de trafic.
  • @app.entrypoint indique le gestionnaire des appels entrants. Il reçoit payload, le corps JSON décodé, ainsi que context pour la requête AgentCore. Ce qu’il renvoie doit être un dictionnaire simple qui se serialise en JSON. Une simple chaîne de caractères ou une classe personnalisée ne conviennent pas.
  • session_id entrant devient thread_id de LangGraph, ce qui permet à MemorySaver de conserver une mémoire distincte pour chaque session.

Étape 4 : créer le projet

Ensuite vient la structure du projet. Lancez l’assistant d’initialisation depuis un dossier racine vierge et laissez-le créer le squelette :

agentcore create

Le wizard demande des informations telles que le nom du projet, la langue, la version de Python et le fichier d’entrée. Le fichier d’entrée et la version de Python doivent correspondre à l’application. Par la suite, un fichier agentcore.json apparaît dans le dossier agentcore, indiquant à AgentCore où se trouve le code et comment le lancer.

{
  "name": "MyAgentOnAgentcore",
    "runtimes": [
    {
      "name": "my-agent",
      "build": "CodeZip",
      "entrypoint": "main.py",
      "codeLocation": "app/my-agent/",
      "runtimeVersion": "PYTHON_3_12",
      "networkMode": "PUBLIC",
      "protocol": "HTTP",
	  "envVars": []
	}
  ]
}

Étape 5 : placer le code là où AgentCore le cherche

Déplacez ou copiez le code de l’agent dans le répertoire indiqué par codeLocation dans agentcore.json. Il est facile de se tromper, et une mauvaise localisation rend le déploiement inutilement compliqué. Une erreur fréquente consiste à placer le code directement dans le dossier agentcore lui-même, ce qui peut coûter beaucoup de temps. L’organisation doit ressembler à ceci :

MyAgentOnAgentcore/
├──agentcore
	└── agentcore.json
├──app/
	└──my-agent/          			 ← codeLocation
		├── main.py                  ← entrypoint (matches agentcore.json)
		├── pyproject.toml           ← or requirements.txt
		├── src/
		│   └── my_agent_graph.py    ← your LangGraph graph builder
		│   └── tools.py             ← your tools, utilities, etc.
		└── .env					 ← your configuration variables

main.py doit se trouver au niveau le plus élevé de codeLocation et correspondre exactement au point d’entrée indiqué dans agentcore.json.

Variables d’environnement

AgentCore peut transmettre des variables d’environnement au conteneur de exécution. Localement, un fichier .env constitue la méthode la plus simple. En production, les valeurs peuvent être stockées dans agentcore.json sous la clé envVars, bien qu’un fichier .env fonctionne également. Les secrets tels que les mots de passe et les clés API sont préférablement conservés dans AWS Secrets Manager et chargés au moment de l’exécution ; dans ce cas, le rôle d’exécution d’AgentCore doit disposer des permissions nécessaires pour les lire. Notez que envVars est un tableau d’objets JSON, chacun contenant les champs name et value.

Étape 6 : validation

Avant d’exécuter quoi que ce soit, laissez la CLI vérifier la configuration :

agentcore validate

Les erreurs simples, comme l’absence d’un fichier d’entrée ou un chemin incorrect dans la configuration, sont détectées ici même.

Étape 7 : exécuter et tester localement

Démarrez l’environnement d’exécution local avec :

agentcore dev

Il lance un serveur HTTP local qui imite fidèlement l’environnement de production. Lorsque tout est correctement configuré, l’affichage indique que l’application est prête et écoute sur un port local.

Appel du serveur local

Une invocation peut se faire via la ligne de commande :

agentcore invoke "Summarise last month sales"

ou via curl :

curl -X POST http://localhost:8080/invocations \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Summarise last month sales", "session_id": "test-001"}'

Une bonne réponse est un JSON contenant la réponse de l’agent :

{"response": "Last month, total sales were $1.2M across 3 regions..."}

Étape 8 : déployer sur AWS

Lorsque les tests locaux sont satisfaisants, déployez sur AgentCore Runtime :

agentcore deploy

Dans l’ombre, l’interface en ligne de commande empaquette le code, crée un bucket S3 pour le déploiement direct du code, génère un rôle d’exécution IAM et déploie AgentCore Runtime à l’aide de CDK. Le premier déploiement prend quelques minutes, tandis que les suivants sont généralement plus rapides.

Lorsque le déploiement est terminé, vérifiez l’état du runtime. Les résultats affichent l’ARN du runtime ainsi que l’URL HTTP permettant d’appeler l’agent déployé.

agentcore status

Étape 9 : invoquer l’agent déployé

La CLI est le moyen le plus simple pour tester la version déployée :

agentcore invoke --prompt "Who are the top 5 customers by revenue?" --session-id "session-id-with-length-greater-than-or-equal-33"

Appeler via HTTP nécessite de signer la requête avec AWS SigV4 :

curl -X POST "https://bedrock-agentcore.us-east-1.amazonaws.com/runtimes/arn-of-MyAgentOnAgentcore-xxxx/invocations" \
  --user "$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY" \
  --aws-sigv4 "aws:amz:us-east-1:bedrock-agentcore" \
  -H "Content-Type: application/json" \
  -H "x-amzn-bedrock-agentcore-runtime-session-id: session-id-with-length-greater-than-or-equal-33" \
  -d '{"prompt": "Who are the top 5 customers by revenue?"}'

Utilisez l’URL HTTP fournie par agentcore status.

Pour les appels provenant d’autre code Python, le SDK est généralement l’option la plus appropriée. Stockez l’ARN de runtime dans une variable d’environnement, transmettez le prompt, et envoyez à nouveau l’ID de session précédent à chaque appel ultérieur.

# invoke_agent.py
import json
import boto3
import os
from dotenv import load_dotenv

load_dotenv()

client = boto3.client("bedrock-agentcore", region_name="us-east-1")
runtime_arn = os.getenv("AGENTCORE_RUNTIME_ARN")

def invoke(prompt: str, session_id: str) -> str:
    payload = json.dumps({"prompt": prompt, "session_id": session_id})
    response = client.invoke_agent_runtime(
        agentRuntimeArn=runtime_arn,
        payload=payload,
        contentType="application/json"
    )
    body = json.loads(response["response"].read().decode())
    return body["response"]

if __name__ == "__main__":
    session_id = ""
    print("Ready. Type 'exit' to quit.")

    while True:
        prompt = input("Your question: ").strip()
        if prompt == "exit":
            break

        if not prompt:
            continue

        params = {
            "agentRuntimeArn": runtime_arn,
            "payload": json.dumps({"prompt": prompt}),
            "contentType": "application/json"
        }
        if session_id:
            params["runtimeSessionId"] = session_id

        try:
            response = client.invoke_agent_runtime(**params)
            body = json.loads(response["response"].read().decode())
            session_id = response.get("runtimeSessionId", session_id)
            print(f"Agent: {body['response']}\n")
        except Exception as e:
            print(f"Error: {e}")

Définissez AGENTCORE_RUNTIME_ARN dans le fichier .env :

AGENTCORE_RUNTIME_ARN=arn:aws:bedrock-agentcore:us-east-1:123456789012:agent-runtime/MyAgentOnAgentcore-xxxx

Préservation de l’état de session entre appels

Regardez runtimeSessionId dans l’exemple boto3. AgentCore le renvoie avec chaque réponse. Le renvoyer dans la demande suivante permet d’orienter les demandes ultérieures vers le même contexte de session, et combiné avec MemorySaver de LangGraph, l’agent conserve toute la mémoire de la conversation malgré des appels HTTP distincts.

Problèmes courants

  • Les échecs de démarrage sont intentionnels. Lorsque create_agent() génère une erreur au moment de l’importation, AgentCore ne lance pas l’environnement de exécution. Ce comportement empêche un agent mal configuré, par exemple celui qui manque de crédentielles ou dont l’URL de base de données est incorrecte, de fonctionner en silence.
  • Les permissions doivent être ajoutées. Le rôle d’exécution AgentCore nécessite un accès explicite à tous les services AWS utilisés par l’agent, y compris Secrets Manager et S3. Ajoutez ces permissions après le premier déploiement.
  • Les versions doivent correspondre. La version Python déclarée dans pyproject.toml doit coïncider avec la valeur de configuration du runtime dans agentcore.json.
  • Renvoyez des dictionnaires simples. Si le point d’entrée renvoie une chaîne de caractères ou un objet personnalisé, le cadre de runtime le rejette.

Qu’est-ce qui change réellement

Pour passer d’un agent local LangGraph à AgentCore, il suffit de quelques modifications : installer deux paquets supplémentaires, envelopper le graphe dans BedrockAgentCoreApp, créer une structure de projet, tester localement, puis déployer et l’appeler. Le graphe, les outils, les appels au modèle ainsi que la configuration de la mémoire restent pour l’essentiel inchangés. AgentCore gère la partie infrastructure, tandis que LangGraph conserve la logique de l’agent.