

# LangGraph
<a name="supported-frameworks-langgraph"></a>

Cette page explique comment instrumenter un [LangGraph](https://langchain-ai.github.io/langgraph/)agent, comment les intervalles sont identifiés et comment les champs d'évaluation sont extraits. Il se termine par [les meilleures pratiques](#langgraph-best-practices) pour structurer un LangGraph agent afin qu'il puisse être évalué de manière fiable.

 **Rubriques** 
+  [Instrumez votre agent](#langgraph-instrument) 
+  [Comment les travées sont identifiées](#langgraph-span-identification) 
+  [Comment les champs d'évaluation sont extraits](#langgraph-extraction) 
  +  [À partir des enregistrements d'événements](#langgraph-extraction-event-records) 
  +  [À partir des attributs span](#langgraph-extraction-attributes) 
+  [Exemples de périodes avec des enregistrements d'événements](#langgraph-examples-with) 
+  [Exemples de périodes sans enregistrement d'événements](#langgraph-examples-without) 
+  [Bonnes pratiques pour les LangGraph agents](#langgraph-best-practices) 

## Instrumez votre agent
<a name="langgraph-instrument"></a>

Vous pouvez instrumenter un LangGraph agent avec l'une des deux bibliothèques d'instrumentation suivantes : **OpenTelemetry**(`opentelemetry-instrumentation-langchain`) ou **OpenInference**(`openinference-instrumentation-langchain`). Amazon Bedrock AgentCore Evaluations prend en charge les deux bibliothèques. Les bibliothèques émettent des noms de portée différents et utilisent des attributs d'étendue différents. Le service d'évaluation extrait les mêmes valeurs de chacun d'entre eux.

Lorsque votre agent s'exécute avec le AWS Distro for OpenTelemetry (ADOT), par exemple sur Amazon Bedrock AgentCore Runtime, vous n'avez pas besoin d'ajouter de code d'instrumentation explicite. Il suffit d'ajouter la bibliothèque d'instrumentation aux dépendances de votre projet. ADOT le découvre au démarrage et l'active automatiquement.

Ajoutez la bibliothèque d'instrumentation correspondant au chemin que vous souhaitez accéder à vos dépendances. Les exemples suivants épinglent une version minimale ; utilisez la dernière version disponible sauf si vous avez une raison de l'épingler.

**Example**  
REMARQUE : Utilisez la version `0.55.0` ou une version ultérieure. La version 0.55.0 a ajouté la prise en charge des nouvelles [conventions de durée des agents d' OpenTelemetry IA générative](https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/gen-ai-agent-spans.md) sur lesquelles repose le service d'évaluation.  
Ajoutez `opentelemetry-instrumentation-langchain` à vos dépendances. Le nom de la portée émis est`opentelemetry.instrumentation.langchain`.  
 `requirements.txt`:  

```
opentelemetry-instrumentation-langchain>=0.55.0
```
 `pyproject.toml`:  

```
[project]
dependencies = [
    "opentelemetry-instrumentation-langchain>=0.55.0",
]
```
Ajoutez `openinference-instrumentation-langchain` à vos dépendances. Le nom de la portée émis est`openinference.instrumentation.langchain`.  
 `requirements.txt`:  

```
openinference-instrumentation-langchain>=0.1.62
```
 `pyproject.toml`:  

```
[project]
dependencies = [
    "openinference-instrumentation-langchain>=0.1.62",
]
```

**Note**  
L'instrumentation est l'une des étapes de la mise en place de l'observabilité. Pour exporter la télémétrie à des fins d'évaluation, effectuez la configuration complète dans [Configurer](supported-frameworks.md#supported-frameworks-setup) l'observabilité.

## Comment les travées sont identifiées
<a name="langgraph-span-identification"></a>

L'attribut utilisé pour classer les intervalles diffère entre les deux bibliothèques d'instrumentation.

**Example**  
La bibliothèque OpenTelemetry d'instrumentation classe les travées à l'aide de l'`traceloop.span.kind`attribut, et les versions récentes sont également définies. `gen_ai.operation.name`  


| Type de travée | Attribut d'identification | 
| --- | --- | 
| Invoquer l'agent |  `traceloop.span.kind`= `workflow` (également `gen_ai.operation.name` =`invoke_agent`) | 
| Exécuter l'outil |  `traceloop.span.kind`= `tool` (également `gen_ai.operation.name` =`execute_tool`) | 
| Inférence |  `gen_ai.operation.name` = `chat`  | 
La bibliothèque OpenInference d'instrumentation classe les travées à l'aide de l'`openinference.span.kind`attribut.  


| Type de travée | Attribut d'identification | 
| --- | --- | 
| Invoquer l'agent |  `openinference.span.kind`= `CHAIN` ou `AGENT`  | 
| Exécuter l'outil |  `openinference.span.kind` = `TOOL`  | 
| Inférence |  `openinference.span.kind` = `LLM`  | 

## Comment les champs d'évaluation sont extraits
<a name="langgraph-extraction"></a>

En ce qui concerne la durée d'appel de l'agent, l'entrée et la sortie ne contiennent pas de liste propre par message. Le contenu est plutôt l'**état du LangChain graphe sérialisé** : une chaîne JSON qui enveloppe l'état complet. La forme exacte de cet état sérialisé diffère entre les deux bibliothèques d'instrumentation. Dans les deux cas, le service l'analyse pour trouver l'invite de l'utilisateur (le message humain) et la réponse de l'agent (le message AI).

LangGraph sérialise également les rôles de message sous plusieurs formes. Un rôle peut apparaître sous forme de valeur minuscule (`human`,`ai`,`tool`) ou de nom de classe de LangChain message (`HumanMessage`,`AIMessage`,`ToolMessage`). Le service reconnaît les deux formulaires.

L'emplacement de ce contenu dépend de la manière dont la télémétrie a été collectée. L'attribut d'identification (`traceloop.span.kind`ou`openinference.span.kind`) se trouve sur la plage dans les deux cas. Pour plus d'informations, voir [Spans, enregistrements d'événements et signaux de télémétrie](supported-frameworks-telemetry.md).

### À partir des enregistrements d'événements
<a name="langgraph-extraction-event-records"></a>

Lorsque la télémétrie est divisée, le service lit le contenu de l'enregistrement d'événements corrélé à chaque intervalle :
+  Demande de **l'utilisateur** et **réponse de l'agent** : à partir de l'enregistrement des événements de l'intervalle d'appel de l'agent, dans `body.input` et`body.output`.
+  **Appel à l'outil** : nom de l'outil issu de la plage d'outils d'exécution. Les arguments et le résultat de l'outil proviennent de l'enregistrement des événements de cette plage, dans `body.input` et`body.output`.

Pour des exemples, voir [Exemples de périodes avec enregistrements d'événements](#langgraph-examples-with).

### À partir des attributs span
<a name="langgraph-extraction-attributes"></a>

Lorsque la télémétrie n'est pas divisée, le même contenu reste sur la plage que les attributs. Les attributs dépendent de la bibliothèque d'instrumentation :
+  **OpenTelemetry**:
  +  Demande de **l'utilisateur** et **réponse de l'agent** : depuis `gen_ai.task.input` et `gen_ai.task.output` pendant la durée d'appel de l'agent.
  +  **Appel** à l'outil : nom de l'outil à partir de`gen_ai.tool.name`, arguments et résultats de `gen_ai.tool.call.arguments` et`gen_ai.tool.call.result`, sur la durée d'exécution de l'outil.
+  **OpenInference**:
  +  Demande de **l'utilisateur** et **réponse de l'agent** : depuis `input.value` et `output.value` pendant la durée d'appel de l'agent.
  +  **Appel** à l'outil : nom de l'outil à partir de`tool.name`, arguments et résultats de `input.value` et`output.value`, sur la durée d'exécution de l'outil.

Pour des exemples, voir [Exemples de périodes sans enregistrement d'événements](#langgraph-examples-without).

## Exemples de périodes avec des enregistrements d'événements
<a name="langgraph-examples-with"></a>

Lorsque la télémétrie est divisée, la plage contient les attributs d'identification et le contenu est enregistré dans un enregistrement d'événements corrélé. Les exemples suivants proviennent d'un agent de LangGraph planification de voyages déployé sur Amazon AgentCore Bedrock Runtime. Le même agent est affiché sous chaque bibliothèque d'instruments.

**Note**  
Ces exemples ne sont pas des étendues complètes. Ils présentent des données représentatives d'une interaction réelle avec un agent, certains champs étant omis et les valeurs longues tronquées pour des raisons de lisibilité.

### OpenTelemetry
<a name="langgraph-examples-otel"></a>

**Example**  
L'`traceloop.span.kind`attribut (`workflow`) l'identifie comme une durée d'appel de l'agent ; les versions récentes de la bibliothèque ont également pour valeur `gen_ai.operation.name` =`invoke_agent`.  

```
{
  "traceId": "6a01eef11066751d68f90def0da1f80a",
  "spanId": "ba1833fa7f097041",
  "parentSpanId": "836a5ccf9a2186cc",
  "name": "travel_agent.workflow",
  "kind": "INTERNAL",
  "scope": {
    "name": "opentelemetry.instrumentation.langchain",
    "version": "0.60.0"
  },
  "startTimeUnixNano": 1778511607308521744,
  "endTimeUnixNano": 1778511610930280395,
  "durationNano": 3621758651,
  "attributes": {
    "traceloop.span.kind": "workflow",
    "gen_ai.operation.name": "invoke_agent",
    "gen_ai.agent.name": "travel_agent",
    "gen_ai.provider.name": "langgraph",
    "traceloop.workflow.name": "travel_agent",
    "session.id": "sea-nyc-trip-2-turns-adot_v17_opentelemetry_0_60_0"
  },
  "status": {
    "code": "OK"
  }
}
```
L'enregistrement d'événements corrélé contient la conversation. Chaque message correspond à l'état du LangChain graphe sérialisé. `content` L'entrée place l'état sous une `inputs` clé. La sortie l'enveloppe sous une `outputs` clé, chaque message étant un objet LangChain constructeur. L'invite de l'utilisateur est le message humain et la réponse de l'agent est le message AI contenu dans cet état sérialisé.  

```
{
  "spanId": "ba1833fa7f097041",
  "traceId": "6a01eef11066751d68f90def0da1f80a",
  "scope": {
    "name": "opentelemetry.instrumentation.langchain"
  },
  "body": {
    "input": {
      "messages": [
        {
          "content": "{\"inputs\": {\"messages\": [{\"role\": \"user\", \"content\": \"Hey, how can you help me\"}]}, \"tags\": [], \"metadata\": {\"ls_integration\": \"langchain_create_agent\", \"lc_agent_name\": \"travel_agent\", \"thread_id\": \"sea-nyc-trip-2-turns-adot_v17_opentelemetry_0_60_0\"}, \"kwargs\": {\"name\": \"travel_agent\"}}",
          "role": "user"
        }
      ]
    },
    "output": {
      "messages": [
        {
          "content": "{\"outputs\": {\"messages\": [{\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"schema\", \"messages\", \"HumanMessage\"], \"kwargs\": {\"content\": \"Hey, how can you help me\", \"type\": \"human\", \"id\": \"12345678-1234-1234-1234-123456789012\"}}, {\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"schema\", \"messages\", \"AIMessage\"], \"kwargs\": {\"content\": \"Hello! I'm your travel planning assistant ...\", \"type\": \"ai\"}}]}, \"kwargs\": {\"tags\": []}}",
          "role": "assistant"
        }
      ]
    }
  }
}
```
L'`traceloop.span.kind`attribut (`tool`) l'identifie comme une plage d'outils d'exécution ; `gen_ai.tool.name` contient le nom de l'outil et `gen_ai.operation.name` =`execute_tool`.  

```
{
  "traceId": "6a01eefa5c52f3d86a35038f35f5ba30",
  "spanId": "5b332f3cd15ace04",
  "parentSpanId": "922a21edc04eba29",
  "name": "execute_tool search_flights",
  "kind": "INTERNAL",
  "scope": {
    "name": "opentelemetry.instrumentation.langchain",
    "version": "0.60.0"
  },
  "startTimeUnixNano": 1778511614892698232,
  "endTimeUnixNano": 1778511614893399618,
  "durationNano": 701386,
  "attributes": {
    "traceloop.span.kind": "tool",
    "gen_ai.operation.name": "execute_tool",
    "gen_ai.tool.name": "search_flights",
    "gen_ai.tool.type": "function",
    "gen_ai.tool.description": "Search for available flights between cities.",
    "gen_ai.provider.name": "langgraph",
    "traceloop.workflow.name": "travel_agent",
    "session.id": "sea-nyc-trip-2-turns-adot_v17_opentelemetry_0_60_0"
  },
  "status": {
    "code": "OK"
  }
}
```
L'enregistrement d'événement corrélé contient l'entrée (arguments) et la sortie (résultat, sérialisé sous forme de a LangChain `ToolMessage`) de l'outil.  

```
{
  "spanId": "5b332f3cd15ace04",
  "traceId": "6a01eefa5c52f3d86a35038f35f5ba30",
  "scope": {
    "name": "opentelemetry.instrumentation.langchain"
  },
  "body": {
    "input": {
      "messages": [
        { "content": "{\"origin\": \"SEA\", \"destination\": \"NYC\", \"date\": \"2025-03-15\"}" }
      ]
    },
    "output": {
      "messages": [
        {
          "role": "tool",
          "name": "search_flights",
          "content": "{\"origin\": \"SEA\", \"destination\": \"NYC\", \"flights\": [ ... ]}"
        }
      ]
    }
  }
}
```

### OpenInference
<a name="langgraph-examples-openinference"></a>

Avec la OpenInference bibliothèque, le type d'intervalle est inclus dans l'`openinference.span.kind`attribut, et les entrées et sorties de l'agent sont sérialisées dans l'enregistrement d'événements corrélé.

**Example**  
L'`openinference.span.kind`attribut (`CHAIN`ou `AGENT` lorsque le graphe est compilé avec un nom) l'identifie comme un span d'agent d'appel.  

```
{
  "traceId": "6a387ee61078243c1cc455ed45c6c313",
  "spanId": "0a7990d804132a9b",
  "parentSpanId": "29ae22014173881c",
  "name": "LangGraph",
  "kind": "INTERNAL",
  "scope": {
    "name": "openinference.instrumentation.langchain",
    "version": "0.1.66"
  },
  "startTimeUnixNano": 1782087405949310976,
  "endTimeUnixNano": 1782087408945828864,
  "durationNano": 2996517888,
  "attributes": {
    "openinference.span.kind": "CHAIN",
    "input.mime_type": "application/json",
    "output.mime_type": "application/json",
    "llm.input_messages.0.message.role": "user",
    "session.id": "sea-nyc-trip-2-turns-oi-0-1-66"
  },
  "status": {
    "code": "OK"
  }
}
```
L'enregistrement d'événements corrélé contient la conversation. L'invite de l'utilisateur est le message de rôle humain et la réponse de l'agent est le AI-role message contenu dans les messages sérialisés.  

```
{
  "spanId": "0a7990d804132a9b",
  "traceId": "6a387ee61078243c1cc455ed45c6c313",
  "scope": {
    "name": "openinference.instrumentation.langchain"
  },
  "body": {
    "input": {
      "messages": [
        {
          "role": "user",
          "content": "{\"messages\": [{\"role\": \"user\", \"content\": \"Hey, how can you help me\"}]}"
        }
      ]
    },
    "output": {
      "messages": [
        {
          "content": "{\"messages\": [{\"type\": \"human\", \"data\": {\"content\": \"Hey, how can you help me\", ...}}, {\"type\": \"ai\", \"data\": {\"content\": \"Hello! I'm your travel planning assistant ...\", ...}}]}",
          "role": "assistant"
        }
      ]
    }
  }
}
```
L'`openinference.span.kind`attribut (`TOOL`) l'identifie comme une plage d'outils d'exécution ; `tool.name` contient le nom de l'outil.  

```
{
  "traceId": "6a387ef07b8f4f3732fab45d3c0b51ff",
  "spanId": "ab105c12cc40048f",
  "parentSpanId": "9b2d4e72760690b4",
  "name": "search_flights",
  "kind": "INTERNAL",
  "scope": {
    "name": "openinference.instrumentation.langchain",
    "version": "0.1.66"
  },
  "startTimeUnixNano": 1782087411724620032,
  "endTimeUnixNano": 1782087411725306880,
  "durationNano": 686848,
  "attributes": {
    "openinference.span.kind": "TOOL",
    "tool.name": "search_flights",
    "tool.description": "Search for available flights between cities.",
    "input.mime_type": "application/json",
    "output.mime_type": "application/json",
    "session.id": "sea-nyc-trip-2-turns-oi-0-1-66"
  },
  "status": {
    "code": "OK"
  }
}
```
L'enregistrement d'événement corrélé contient l'entrée (arguments) et la sortie (résultat, sérialisé sous forme de a LangChain `ToolMessage`) de l'outil.  

```
{
  "spanId": "ab105c12cc40048f",
  "traceId": "6a387ef07b8f4f3732fab45d3c0b51ff",
  "scope": {
    "name": "openinference.instrumentation.langchain"
  },
  "body": {
    "input": {
      "messages": [
        {
          "role": "user",
          "content": "{\"origin\": \"SEA\", \"destination\": \"NYC\", \"date\": \"2025-03-15\"}"
        }
      ]
    },
    "output": {
      "messages": [
        {
          "content": "{\"type\": \"tool\", \"data\": {\"content\": \"{\\\"origin\\\": \\\"SEA\\\", \\\"destination\\\": \\\"NYC\\\", \\\"flights\\\": [ ... ]}\", \"type\": \"tool\", \"name\": \"search_flights\", \"tool_call_id\": \"toolu_bdrk_01LzXXJCfpfuS7Bpf7e1qLMg\", \"status\": \"success\"}}",
          "role": "assistant"
        }
      ]
    }
  }
}
```

## Exemples de périodes sans enregistrement d'événements
<a name="langgraph-examples-without"></a>

Lorsque la télémétrie n'est pas divisée, le même contenu reste dans les attributs span et aucun enregistrement d'événement distinct n'est produit. Les exemples suivants proviennent d'un agent de LangGraph planification de voyages. Le même agent est affiché sous chaque bibliothèque d'instruments.

**Note**  
Ces exemples ne sont pas des étendues complètes. Ils présentent des données représentatives d'une interaction réelle avec un agent, certains champs étant omis et les valeurs longues tronquées pour des raisons de lisibilité.

### OpenTelemetry
<a name="langgraph-examples-without-otel"></a>

**Example**  
L'`gen_ai.task.input`attribut contient l'invite de l'utilisateur et l'`gen_ai.task.output`attribut contient l'état sérialisé avec la réponse de l'agent. Les deux sont l'état du LangChain graphe sérialisé.  

```
{
  "traceId": "6a4de7b85e61747e6b568a1f4768e89d",
  "spanId": "31ea3d5882dac680",
  "name": "LangGraph.workflow",
  "kind": "INTERNAL",
  "scope": {
    "name": "opentelemetry.instrumentation.langchain",
    "version": "0.62.1"
  },
  "attributes": {
    "traceloop.span.kind": "workflow",
    "gen_ai.operation.name": "invoke_agent",
    "gen_ai.agent.name": "LangGraph",
    "gen_ai.task.input": "{\"inputs\": {\"messages\": [{\"role\": \"user\", \"content\": \"Hey, how can you help me\"}]}, \"tags\": [], \"metadata\": { ... }, \"kwargs\": {\"name\": \"LangGraph\"}}",
    "gen_ai.task.output": "{\"outputs\": {\"messages\": [{\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"schema\", \"messages\", \"HumanMessage\"], \"kwargs\": {\"content\": \"Hey, how can you help me\", \"type\": \"human\"}}, {\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"schema\", \"messages\", \"AIMessage\"], \"kwargs\": {\"content\": \"Hello! I'm your travel planning assistant ...\", \"type\": \"ai\"}}]}, \"kwargs\": {\"tags\": []}}",
    "session.id": "sea-nyc-trip-2-turns-unified"
  },
  "status": {
    "code": "OK"
  }
}
```
L'`gen_ai.tool.call.arguments`attribut contient les arguments de l'outil et l'`gen_ai.tool.call.result`attribut contient le résultat de l'outil, sérialisé sous la forme d'un LangChain `ToolMessage`.  

```
{
  "traceId": "6a4de7c376913db82e6f0f336a16731d",
  "spanId": "b64c37adefae74f0",
  "name": "execute_tool search_flights",
  "kind": "INTERNAL",
  "scope": {
    "name": "opentelemetry.instrumentation.langchain",
    "version": "0.62.1"
  },
  "attributes": {
    "traceloop.span.kind": "tool",
    "gen_ai.operation.name": "execute_tool",
    "gen_ai.tool.name": "search_flights",
    "gen_ai.tool.description": "Search for available flights between cities.",
    "gen_ai.tool.call.arguments": "{\"input_str\": \"{'origin': 'SEA', 'destination': 'NYC', 'date': '2025-03-15'}\", \"inputs\": {\"origin\": \"SEA\", \"destination\": \"NYC\", \"date\": \"2025-03-15\"}, \"metadata\": { ... }}",
    "gen_ai.tool.call.result": "{\"output\": {\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"schema\", \"messages\", \"ToolMessage\"], \"kwargs\": {\"content\": \"{\\\"origin\\\": \\\"SEA\\\", \\\"destination\\\": \\\"NYC\\\", \\\"flights\\\": [ ... ]}\", \"type\": \"tool\", \"name\": \"search_flights\", \"status\": \"success\"}}}",
    "session.id": "sea-nyc-trip-2-turns-unified"
  },
  "status": {
    "code": "OK"
  }
}
```

### OpenInference
<a name="langgraph-examples-without-openinference"></a>

**Example**  
L'`input.value`attribut contient l'invite de l'utilisateur et l'`output.value`attribut contient l'état sérialisé avec la réponse de l'agent.  

```
{
  "traceId": "6a387ee61078243c1cc455ed45c6c313",
  "spanId": "b8c0b67876b78b91",
  "name": "LangGraph",
  "kind": "INTERNAL",
  "scope": {
    "name": "openinference.instrumentation.langchain",
    "version": "0.1.66"
  },
  "attributes": {
    "openinference.span.kind": "CHAIN",
    "input.value": "{\"messages\": [{\"role\": \"user\", \"content\": \"Hey, how can you help me\"}]}",
    "output.value": "{\"messages\": [{\"type\": \"human\", \"data\": {\"content\": \"Hey, how can you help me\"}}, {\"type\": \"ai\", \"data\": {\"content\": \"Hello! I'm your travel planning assistant ...\"}}]}",
    "session.id": "sea-nyc-trip-2-turns-oi-0-1-66"
  },
  "status": {
    "code": "OK"
  }
}
```
L'`input.value`attribut contient les arguments de l'outil et l'`output.value`attribut contient le résultat de l'outil, sérialisé sous la forme d'un LangChain `ToolMessage`.  

```
{
  "traceId": "6a387ef07b8f4f3732fab45d3c0b51ff",
  "spanId": "58752612d9b22ae1",
  "name": "search_flights",
  "kind": "INTERNAL",
  "scope": {
    "name": "openinference.instrumentation.langchain",
    "version": "0.1.66"
  },
  "attributes": {
    "openinference.span.kind": "TOOL",
    "tool.name": "search_flights",
    "tool.description": "Search for available flights between cities.",
    "input.value": "{\"origin\": \"SEA\", \"destination\": \"NYC\", \"date\": \"2025-03-15\"}",
    "output.value": "{\"type\": \"tool\", \"data\": {\"content\": \"{\\\"origin\\\": \\\"SEA\\\", \\\"destination\\\": \\\"NYC\\\", \\\"flights\\\": [ ... ]}\", \"name\": \"search_flights\"}}",
    "session.id": "sea-nyc-trip-2-turns-oi-0-1-66"
  },
  "status": {
    "code": "OK"
  }
}
```

## Bonnes pratiques pour les LangGraph agents
<a name="langgraph-best-practices"></a>

La façon dont vous créez et invoquez un LangGraph agent influe sur ce qui apparaît dans sa télémétrie, et donc sur la fiabilité de l'évaluation de l'agent. Les pratiques suivantes permettent de garantir que l'invite de l'utilisateur, la réponse de l'agent et l'activité de l'outil sont récupérables.

### 1. Choisissez un modèle de construction de l'agent
<a name="langgraph-bp-construction"></a>

Il existe deux méthodes courantes pour créer un LangGraph agent :
+  **Préconfiguré `create_agent`** : le moyen le plus rapide de démarrer. Il produit un seul intervalle d'appel par tour, la conversation passant par LangGraph la boucle d'exécution intégrée. Utilisez-le lorsque vous souhaitez un agent raisonné standard sans flux de contrôle personnalisé.

  ```
  from langchain.agents import create_agent
  
  agent = create_agent(model=model, tools=[search_flights, book_flight])
  ```
+  **Personnalisé `StateGraph`** : vous donne un contrôle total sur les nœuds, les arêtes et le routage conditionnel. Chaque exécution de nœud devient sa propre étendue, de sorte que les traces sont plus granulaires. Utilisez-le lorsque vous avez besoin d'une orchestration personnalisée.

  ```
  from langgraph.graph import StateGraph, START, END
  from typing_extensions import TypedDict
  
  class State(TypedDict):
      messages: list
  
  graph = StateGraph(State)
  graph.add_node("generate_response", generate_response)
  graph.add_node("tools", run_tools)
  graph.add_edge(START, "generate_response")
  agent = graph.compile()
  ```

Les deux modèles sont évalués de la même manière ; la différence réside dans la granularité du tracé.

### 2. Utiliser `des messages` dans l'état de votre graphe (recommandé)
<a name="langgraph-bp-messages"></a>

Le service d'évaluation reconstruit la conversation à partir des messages d'entrée et de sortie de l'agent. L'utilisation d'un `messages` champ n'est pas obligatoire, mais elle permet l'extraction la plus fiable possible. Pour une personnalisation`StateGraph`, conservez la conversation dans un `messages` champ de votre État :
+  **`messages`À inclure dans votre État (recommandé).** Vous pouvez ajouter d'autres champs personnalisés (tels que `user_id` des métadonnées). Lorsqu'elle `messages` est présente, l'extraction standard trouve directement l'invite de l'utilisateur et la réponse de l'agent. En cas `messages` d'absence, le service se contente de reconstruire la conversation à partir de périodes d'inférence individuelles, ce qui est moins fiable.
+  **Ajoutez, ne remplacez pas.** Suivez la LangGraph convention qui consiste à ajouter de nouveaux messages à la liste plutôt que de les remplacer, afin de préserver l'historique complet des conversations.
+  **Utilisez des types de LangChain messages canoniques** (`HumanMessage`,`AIMessage`,`ToolMessage`,`SystemMessage`). L'instrumentation les sérialise correctement et le service reconnaît leurs rôles.

### 3. Transmettre le message de l'utilisateur dans un format compatible
<a name="langgraph-bp-invocation"></a>

Lorsque vous appelez un LangGraph agent, vous ajoutez le message utilisateur à l'`messages`état du graphe. LangGraph accepte le message dans trois formats interchangeables, et AgentCore Evaluations les prend tous en charge. Chacun produit des intervalles et des enregistrements d'événements que le service peut lire.
+  **Tuple** : une `(role, content)` paire :

  ```
  agent.invoke({"messages": [("user", user_message)]}, config=config)
  ```
+  **LangChain objet de message** : a `HumanMessage` (ou autre classe de message) :

  ```
  from langchain_core.messages import HumanMessage
  
  agent.invoke({"messages": [HumanMessage(content=user_message)]}, config=config)
  ```
+  **Dictionnaire** : un `{"role", "content"}` dictionnaire :

  ```
  agent.invoke({"messages": [{"role": "user", "content": user_message}]}, config=config)
  ```

Les trois formats aboutissent au même `messages` état, de sorte que l'invite de l'utilisateur et la réponse de l'agent sont extraites de la même manière, quel que soit le format choisi.