Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.
Configurer la LangGraph télémétrie pour les évaluations AgentCore
Cette page explique comment instrumenter un LangGraph agent, comment les intervalles sont identifiés et comment les champs d'évaluation sont extraits. AgentCore Evaluations prend en charge les LangGraph agents créés en Python et TypeScript ; cette page couvre chaque langage séparément, dans le support des agents Python et le support des TypeScript agents. Il se termine par les meilleures pratiques pour structurer un LangGraph agent afin qu'il puisse être évalué de manière fiable.
Rubriques
Support pour les agents Python
Un LangGraph agent Python émet des spans sous le nom de portée opentelemetry.instrumentation.langchain (OpenTelemetry) ou openinference.instrumentation.langchain (OpenInference).
Instrumentez votre agent
Vous pouvez instrumenter un LangGraph agent à l'aide de 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 différents attributs d'étendue. Le service d'évaluation extrait les mêmes valeurs de chacune d'entre elles.
Lorsque votre agent fonctionne 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 utiliser pour 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.
Exemple
- OpenTelemetry
-
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 d'extension des agents d' OpenTelemetry IA générative sur le GitHub site Web, sur lesquelles s'appuie le service d'évaluation.
Ajoutez opentelemetry-instrumentation-langchain à vos dépendances. Le nom du scope émis estopentelemetry.instrumentation.langchain.
requirements.txt:
opentelemetry-instrumentation-langchain>=0.55.0
pyproject.toml:
[project]
dependencies = [
"opentelemetry-instrumentation-langchain>=0.55.0",
]
- OpenInference
-
Ajoutez openinference-instrumentation-langchain à vos dépendances. Le nom du scope émis estopeninference.instrumentation.langchain.
requirements.txt:
openinference-instrumentation-langchain>=0.1.62
pyproject.toml:
[project]
dependencies = [
"openinference-instrumentation-langchain>=0.1.62",
]
L'instrumentation constitue une étape dans 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 l'observabilité.
Comment les travées sont identifiées
L'attribut utilisé pour classer les spans diffère entre les deux bibliothèques d'instruments.
Exemple
- OpenTelemetry
-
La bibliothèque OpenTelemetry d'instrumentation classe les spans à l'aide de traceloop.span.kind cet attribut, et les versions récentes sont également définies. gen_ai.operation.name
| Type d'envergure |
Attribut d'identification |
|
Invoquer un agent
|
traceloop.span.kind= workflow (également gen_ai.operation.name =invoke_agent)
|
|
Outil d'exécution
|
traceloop.span.kind= tool (également gen_ai.operation.name =execute_tool)
|
|
Inférence
|
gen_ai.operation.name = chat
|
- OpenInference
-
La bibliothèque OpenInference d'instrumentation classe les spans à l'aide de cet attribut. openinference.span.kind
| Type d'envergure |
Attribut d'identification |
|
Invoquer un agent
|
openinference.span.kind= CHAIN ou AGENT
|
|
Outil d'exécution
|
openinference.span.kind = TOOL
|
|
Inférence
|
openinference.span.kind = LLM
|
Pour le span de l'agent d'invocation, l'entrée et la sortie ne contiennent pas de liste complète 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'instruments. 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 des messages sous plusieurs formes. Un rôle peut apparaître sous la forme d'une valeur minuscule (human,ai,tool) ou d'un 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.kindouopeninference.span.kind) se trouve sur la plage dans les deux cas. Pour plus d'informations, consultez la section Configuration et diffusion de la télémétrie.
Grâce à la télémétrie fractionnée, le service lit le contenu de l'enregistrement de l'événement en corrélation avec chaque intervalle :
-
Message 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 etbody.output.
-
Appel d'outil : nom de l'outil figurant dans la plage d'outils d'exécution. Les arguments et le résultat de l'outil proviennent de l'enregistrement d'événements de cette plage, dans body.input etbody.output.
Pour plus d'informations, voir Exemples de plages dans la télémétrie fractionnée.
Grâce à la télémétrie unifiée, le même contenu reste sur la plage en tant qu'attributs. Les attributs dépendent de la bibliothèque d'instruments :
-
OpenTelemetry:
-
Invite utilisateur et réponse de l'agent : depuis gen_ai.task.input et vers gen_ai.task.output le span d'invocation de l'agent.
-
Appel d'outil : nom de l'outil provenant degen_ai.tool.name, arguments et résultats provenant de gen_ai.tool.call.arguments etgen_ai.tool.call.result, sur la plage d'outils d'exécution.
-
OpenInference:
-
Invite utilisateur et réponse de l'agent : depuis input.value et vers output.value le span d'invocation de l'agent.
-
Appel d'outil : nom de l'outil provenant detool.name, arguments et résultats provenant de input.value etoutput.value, sur la plage d'outils d'exécution.
Pour plus d'informations, consultez la section Exemples de plages de télémétrie unifiée.
Exemples de plages dans la télémétrie fractionnée
Avec la télémétrie fractionné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 Python déployé sur Amazon AgentCore Bedrock Runtime. Le même agent est affiché dans chaque bibliothèque d'instruments.
Ces exemples ne constituent pas des séries complètes. Ils présentent des données représentatives provenant d'une interaction réelle entre agents, certains champs étant omis et les valeurs longues tronquées pour des raisons de lisibilité.
OpenTelemetry
Exemple
- Invoke agent span
-
L'traceloop.span.kindattribut (workflow) l'identifie comme une étendue d'agent d'invocation ; les versions récentes de la bibliothèque définissent également 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. L'état du LangChain graphe sérialisé content est celui de chaque message. L'entrée enveloppe l'état sous une inputs touche. La sortie l'enveloppe sous une outputs touche, 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 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"
}
]
}
}
}
- Execute tool span
-
L'traceloop.span.kindattribut (tool) l'identifie comme une étendue d'outil 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é en tant que 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
Avec la OpenInference bibliothèque, le type de span est inclus dans l'openinference.span.kindattribut, et les entrées et sorties de l'agent sont sérialisées dans l'enregistrement d'événements corrélé.
Exemple
- Invoke agent span
-
L'openinference.span.kindattribut (CHAINou AGENT lorsque le graphique est compilé avec un nom) l'identifie comme une étendue d'agent d'invocation.
{
"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"
}
]
}
}
}
- Execute tool span
-
L'openinference.span.kindattribut (TOOL) l'identifie comme une étendue d'outil 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é en tant que 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 portées en télémétrie unifiée
Grâce à la télémétrie unifiée, le même contenu reste sur les attributs de la plage et aucun enregistrement d'événement distinct n'est produit. Les exemples suivants proviennent d'un agent de LangGraph planification de voyages Python. Le même agent est affiché dans chaque bibliothèque d'instruments.
Ces exemples ne constituent pas des séries complètes. Ils présentent des données représentatives provenant d'une interaction réelle entre agents, certains champs étant omis et les valeurs longues tronquées pour des raisons de lisibilité.
OpenTelemetry
Exemple
- Invoke agent span
-
L'gen_ai.task.inputattribut contient l'invite de l'utilisateur et l'gen_ai.task.outputattribut 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"
}
}
- Execute tool span
-
L'gen_ai.tool.call.argumentsattribut contient les arguments de l'outil et l'gen_ai.tool.call.resultattribut 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
Exemple
- Invoke agent span
-
L'input.valueattribut contient l'invite de l'utilisateur et l'output.valueattribut 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"
}
}
- Execute tool span
-
L'input.valueattribut contient les arguments de l'outil et l'output.valueattribut 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"
}
}
TypeScript assistance aux agents
Un TypeScript LangGraph agent émet les mêmes types de span qu'un agent Python, de sorte que le service d'évaluation le lit de la même manière. Il existe trois bibliothèques TypeScript d'instruments, chacune ayant son propre nom de domaine et sa propre convention de classification.
Instrumentez votre agent
Ajoutez la bibliothèque d'instrumentation correspondant au chemin que vous souhaitez utiliser pour accéder à vos TypeScript dépendances. Les exemples suivants épinglent une version minimale ; utilisez la dernière version disponible sauf si vous avez une raison de l'épingler.
Exemple
- ADOT (OpenTelemetry)
-
Pour les TypeScript agents sur ADOT, ajoutez le package d'instrumentation automatique AWS Distro Node (@aws/aws-distro-opentelemetry-node-autoinstrumentation) à vos dépendances. Il inclut l' LangChain instrumentation intégrée, qui s'active au démarrage et émet le nom @aws/aws-distro-opentelemetry-instrumentation-langchain du scope.
package.json:
{
"dependencies": {
"@aws/aws-distro-opentelemetry-node-autoinstrumentation": "^0.12.0"
}
}
- Traceloop (OpenTelemetry)
-
Ajoutez l' LangChain instrumentation Traceloop (@traceloop/instrumentation-langchain) à vos dépendances. Le nom de la portée émis est@traceloop/instrumentation-langchain.
package.json:
{
"dependencies": {
"@traceloop/instrumentation-langchain": "^0.27.0"
}
}
- OpenInference
-
Ajoutez @arizeai/openinference-instrumentation-langchain à vos dépendances. Le nom du scope émis est@arizeai/openinference-instrumentation-langchain.
package.json:
{
"dependencies": {
"@arizeai/openinference-instrumentation-langchain": "^4.0.14"
}
}
L'instrumentation constitue une étape dans 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 l'observabilité.
Comment les travées sont identifiées
L'identification des intervalles dépend de la bibliothèque d'instruments :
-
ADOT (OpenTelemetry) : le package d'auto-instrumentation AWS Distro Node (@aws/aws-distro-opentelemetry-node-autoinstrumentation), qui émet le nom du scope@aws/aws-distro-opentelemetry-instrumentation-langchain, sets gen_ai.operation.name (invoke_agent,chat)execute_tool, comme les autres frameworks. ADOT-native
-
Traceloop (OpenTelemetry) : la bibliothèque OpenTelemetry JS de Traceloop (@traceloop/instrumentation-langchain) définit traceloop.span.kind (workflowpour l'étendue de l'agent d'invocation, pour l'étendue de l'outil), task correspondant à la bibliothèque Python. OpenTelemetry Consultez la section Comment les spans sont identifiées dans le cadre de la prise en charge des agents Python.
-
OpenInference: la bibliothèque OpenInference JS (@arizeai/openinference-instrumentation-langchain) définit openinference.span.kind (CHAINouAGENT,LLM)TOOL, de la même manière que la OpenInference bibliothèque Python.
L'extraction sur le terrain dépend de la bibliothèque d'instruments :
-
ADOT (OpenTelemetry) : le span de l'agent d'invocation est un conteneur structurel, et le contenu de la conversation se trouve sur le span d'inférence (chat), dans le format des parties gen_ai.input.messages et les attributs. gen_ai.output.messages
-
Traceloop (OpenTelemetry) : avec la bibliothèque OpenTelemetry JS de Traceloop, la conversation est dans les traceloop.entity.output attributs traceloop.entity.input et, en tant qu'état sérialisé. LangChain Cela correspond à la OpenTelemetry bibliothèque Python ; voir Comment les champs d'évaluation sont extraits dans le cadre du support des agents Python.
-
OpenInference: avec la bibliothèque OpenInference JS, la conversation se trouve dans les output.value attributs input.value et, et les messages d'inférence apparaissent également dans les attributs llm.input_messages.* et llm.output_messages.* indexés. Cela correspond à la OpenInference bibliothèque Python.
Exemples de spans provenant d'un agent TypeScript
Les exemples suivants proviennent d'un agent de TypeScript LangGraph planification de voyages déployé sur Amazon Bedrock AgentCore Runtime avec télémétrie unifiée. Le même agent est présenté dans chacune des trois bibliothèques TypeScript d'instruments.
Ces exemples ne constituent pas des étendues complètes. Ils présentent des données représentatives provenant d'une interaction réelle entre agents, certains champs étant omis et les valeurs longues tronquées pour des raisons de lisibilité.
OpenTelemetry (natif d'ADOT)
Avec la ADOT-native bibliothèque (issue du package d'auto-instrumentation AWS Distro Node@aws/aws-distro-opentelemetry-node-autoinstrumentation, émettant le nom du scope@aws/aws-distro-opentelemetry-instrumentation-langchain), le span de l'agent d'invocation est un conteneur structurel et le contenu de la conversation se trouve sur le span inference (chat), dans le format des parties et les attributs. gen_ai.input.messages gen_ai.output.messages
Exemple
- Invoke agent span
-
L'gen_ai.operation.nameattribut (invoke_agent) l'identifie comme une étendue d'agent d'invocation. Le span contient le nom et le modèle de l'agent, mais aucun contenu de conversation.
{
"traceId": "6a6bd0a1c8d91ed1e70a3906b551618",
"spanId": "ba1833fa7f097041",
"name": "invoke_agent LangGraph",
"kind": "INTERNAL",
"scope": {
"name": "@aws/aws-distro-opentelemetry-instrumentation-langchain",
"version": "0.12.0"
},
"attributes": {
"gen_ai.operation.name": "invoke_agent",
"gen_ai.agent.name": "LangGraph",
"gen_ai.provider.name": "openai",
"gen_ai.request.model": "gpt-4o-mini",
"session.id": "sea-nyc-trip-2-turns"
},
"status": {
"code": "OK"
}
}
- Execute tool span
-
L'gen_ai.operation.nameattribut (execute_tool) l'identifie comme une étendue d'outil d'exécution ; gen_ai.tool.name contient le nom de l'outil. Les gen_ai.tool.call.result attributs gen_ai.tool.call.arguments et contiennent les arguments et le résultat de l'outil.
{
"traceId": "6a6bd0a25c52f3d86a35038f35f5ba30",
"spanId": "5b332f3cd15ace04",
"name": "execute_tool search_flights",
"kind": "INTERNAL",
"scope": {
"name": "@aws/aws-distro-opentelemetry-instrumentation-langchain",
"version": "0.12.0"
},
"attributes": {
"gen_ai.operation.name": "execute_tool",
"gen_ai.tool.name": "search_flights",
"gen_ai.tool.type": "function",
"gen_ai.tool.call.arguments": "{\"origin\": \"SEA\", \"destination\": \"NYC\", \"date\": \"2025-03-15\"}",
"gen_ai.tool.call.result": "{\"origin\": \"SEA\", \"destination\": \"NYC\", \"flights\": [ ... ]}",
"session.id": "sea-nyc-trip-2-turns"
},
"status": {
"code": "OK"
}
}
- Inference span
-
L'gen_ai.operation.nameattribut (chat) l'identifie comme une étendue d'inférence. Les gen_ai.output.messages attributs gen_ai.input.messages et permettent de maintenir la conversation au format parts et de maintenir l'invite gen_ai.system_instructions du système.
{
"traceId": "6a6bd0a1c8d91ed1e70a3906b551618",
"spanId": "7c1f9a2b4d6e8a03",
"name": "chat gpt-4o-mini",
"kind": "INTERNAL",
"scope": {
"name": "@aws/aws-distro-opentelemetry-instrumentation-langchain",
"version": "0.12.0"
},
"attributes": {
"gen_ai.operation.name": "chat",
"gen_ai.provider.name": "openai",
"gen_ai.request.model": "gpt-4o-mini",
"gen_ai.input.messages": "[{\"role\": \"user\", \"parts\": [{\"type\": \"text\", \"content\": \"Hey, how can you help me\"}]}]",
"gen_ai.output.messages": "[{\"role\": \"assistant\", \"parts\": [{\"type\": \"text\", \"content\": \"I can assist you with planning your trip ...\"}]}]",
"gen_ai.system_instructions": "[{\"type\": \"text\", \"content\": \"You are a travel planning assistant ...\"}]",
"session.id": "sea-nyc-trip-2-turns"
},
"status": {
"code": "OK"
}
}
OpenTelemetry (Traceloop)
Avec la bibliothèque OpenTelemetry JS de Traceloop (@traceloop/instrumentation-langchain), le type de span est contenu dans l'traceloop.span.kindattribut (workflowpour l'étendue de l'agent d'appel, task pour l'étendue de l'outil) et gen_ai.operation.name = workflow dans l'étendue de l'agent d'appel. La conversation se trouve dans les traceloop.entity.output attributs traceloop.entity.input et, en tant qu' LangChain état sérialisé.
Exemple
- Invoke agent span
-
L'traceloop.span.kindattribut (workflow) l'identifie comme une étendue d'agent d'invocation. Les traceloop.entity.output attributs traceloop.entity.input et contiennent l' LangChain état sérialisé, à partir duquel l'invite de l'utilisateur (message humain) et la réponse de l'agent (message AI) sont analysées.
{
"traceId": "6a6bd0b1c8d91ed1e70a3906b551618",
"spanId": "ba1833fa7f097041",
"name": "workflow RunnableSequence",
"kind": "INTERNAL",
"scope": {
"name": "@traceloop/instrumentation-langchain",
"version": "0.27.0"
},
"attributes": {
"traceloop.span.kind": "workflow",
"gen_ai.operation.name": "workflow",
"gen_ai.provider.name": "langchain",
"traceloop.workflow.name": "RunnableSequence",
"traceloop.entity.input": "{\"messages\": [{\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain_core\", \"messages\", \"HumanMessage\"], \"kwargs\": {\"content\": \"Hey, how can you help me\"}}]}",
"traceloop.entity.output": "{\"messages\": [{\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain_core\", \"messages\", \"AIMessage\"], \"kwargs\": {\"content\": \"I can assist you with planning your trip ...\"}}]}",
"session.id": "sea-nyc-trip-2-turns"
},
"status": {
"code": "OK"
}
}
- Execute tool span
-
L'traceloop.span.kindattribut (task) l'identifie comme une étendue d'outil d'exécution. Les traceloop.entity.output attributs traceloop.entity.input et contiennent les arguments et le résultat de l'outil.
{
"traceId": "6a6bd0b25c52f3d86a35038f35f5ba30",
"spanId": "5b332f3cd15ace04",
"name": "task search_flights",
"kind": "INTERNAL",
"scope": {
"name": "@traceloop/instrumentation-langchain",
"version": "0.27.0"
},
"attributes": {
"traceloop.span.kind": "task",
"traceloop.entity.name": "search_flights",
"traceloop.entity.input": "{\"origin\": \"SEA\", \"destination\": \"NYC\", \"date\": \"2025-03-15\"}",
"traceloop.entity.output": "{\"output\": {\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain_core\", \"messages\", \"ToolMessage\"], \"kwargs\": {\"status\": \"success\", \"content\": \"{\\\"origin\\\": \\\"SEA\\\", \\\"destination\\\": \\\"NYC\\\", \\\"flights\\\": [ ... ]}\"}}}",
"session.id": "sea-nyc-trip-2-turns"
},
"status": {
"code": "OK"
}
}
OpenInference
Avec la bibliothèque OpenInference JS (@arizeai/openinference-instrumentation-langchain), le type de span est inclus dans l'openinference.span.kindattribut. Le contenu de la conversation se trouve dans les output.value attributs input.value et, et les messages d'inférence apparaissent également dans les attributs llm.input_messages.* et llm.output_messages.* indexés.
Exemple
- Invoke agent span
-
L'openinference.span.kindattribut (CHAIN) l'identifie comme une étendue d'agent d'invocation. Les output.value attributs input.value et contiennent l' LangChain état sérialisé.
{
"traceId": "6a6bd0c1c8d91ed1e70a3906b551618",
"spanId": "0a7990d804132a9b",
"name": "LangGraph",
"kind": "INTERNAL",
"scope": {
"name": "@arizeai/openinference-instrumentation-langchain",
"version": "4.0.14"
},
"attributes": {
"openinference.span.kind": "CHAIN",
"input.value": "{\"messages\": [{\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain_core\", \"messages\", \"HumanMessage\"], \"kwargs\": {\"content\": \"Hey, how can you help me\"}}]}",
"output.value": "{\"messages\": [{\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain_core\", \"messages\", \"AIMessage\"], \"kwargs\": {\"content\": \"I can assist you with planning your trip ...\"}}]}",
"session.id": "sea-nyc-trip-2-turns"
},
"status": {
"code": "OK"
}
}
- Execute tool span
-
L'openinference.span.kindattribut (TOOL) l'identifie comme une étendue d'outil d'exécution ; tool.name contient le nom de l'outil. Les output.value attributs input.value et contiennent les arguments et le résultat de l'outil (sérialisés en tant que a LangChain ToolMessage).
{
"traceId": "6a6bd0c25c52f3d86a35038f35f5ba30",
"spanId": "ab105c12cc40048f",
"name": "search_flights",
"kind": "INTERNAL",
"scope": {
"name": "@arizeai/openinference-instrumentation-langchain",
"version": "4.0.14"
},
"attributes": {
"openinference.span.kind": "TOOL",
"tool.name": "search_flights",
"input.value": "{\"origin\": \"SEA\", \"destination\": \"NYC\", \"date\": \"2025-03-15\"}",
"output.value": "{\"output\": {\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain_core\", \"messages\", \"ToolMessage\"], \"kwargs\": {\"status\": \"success\", \"content\": \"{\\\"origin\\\": \\\"SEA\\\", \\\"destination\\\": \\\"NYC\\\", \\\"flights\\\": [ ... ]}\"}}}",
"session.id": "sea-nyc-trip-2-turns"
},
"status": {
"code": "OK"
}
}
- Inference span
-
L'openinference.span.kindattribut (LLM) l'identifie comme une étendue d'inférence. Les llm.input_messages.* attributs contiennent l'invite du système et l'invite de l'utilisateur, et les llm.output_messages.* attributs contiennent la réponse de l'agent.
{
"traceId": "6a6bd0c1c8d91ed1e70a3906b551618",
"spanId": "1221a062c7f90a8e",
"name": "ChatOpenAI",
"kind": "INTERNAL",
"scope": {
"name": "@arizeai/openinference-instrumentation-langchain",
"version": "4.0.14"
},
"attributes": {
"openinference.span.kind": "LLM",
"llm.model_name": "gpt-4o-mini",
"llm.input_messages.0.message.role": "system",
"llm.input_messages.0.message.content": "You are a travel planning assistant ...",
"llm.input_messages.1.message.role": "user",
"llm.input_messages.1.message.content": "Hey, how can you help me",
"llm.output_messages.0.message.role": "assistant",
"llm.output_messages.0.message.content": "I can assist you with planning your trip ...",
"session.id": "sea-nyc-trip-2-turns"
},
"status": {
"code": "OK"
}
}
Meilleures pratiques pour les LangGraph agents
La façon dont vous créez et invoquez un LangGraph agent a une incidence sur ce qui apparaît dans sa télémétrie et, par conséquent, sur la fiabilité de l'évaluation de l'agent. Les pratiques suivantes permettent de garantir la restauration de l'invite de l'utilisateur, de la réponse de l'agent et de l'activité de l'outil.
1. Choisissez un modèle de construction d'agent
Il existe deux méthodes courantes pour créer un LangGraph agent :
-
Préconstruit create_agent : le moyen le plus rapide de démarrer. Il produit une seule durée d'invocation de l'agent par tour, la conversation passant par LangGraph la boucle d'exécution intégrée. Utilisez-le lorsque vous souhaitez un agent raison-action 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. L'exécution de chaque 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é de la trace.
2. Utiliser les messages dans l'état de votre graphique (recommandé)
Le service d'évaluation reconstitue 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 une extraction des plus fiables. Pour une utilisation personnaliséeStateGraph, conservez la conversation dans un messages champ de votre État :
-
Inclure messages 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 l'invite de l'utilisateur et la réponse directe de l'agent. En cas messages d'absence, le service revient à 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 la remplacer, afin de préserver l'historique complet des conversations.
-
Utilisez des types de LangChain messages canoniques (HumanMessage, AIMessageToolMessage,SystemMessage). L'instrumentation les sérialise correctement et le service reconnaît leurs rôles.
3. Transmettre le message utilisateur dans un format compatible
Lorsque vous appelez un LangGraph agent, vous ajoutez le message utilisateur à l'messagesétat du graphique. LangGraph accepte le message dans trois formats interchangeables, et AgentCore Evaluations les prend tous en charge. Chacune 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 donnent le 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 votre choix.