View a markdown version of this page

LangGraph - Amazon Bedrock AgentCore

LangGraph

En esta página se explica cómo instrumentar a un LangGraphagente, cómo se identifican los intervalos y cómo se extraen los campos de evaluación. Termina con las mejores prácticas para estructurar un LangGraph agente de modo que pueda evaluarse de forma fiable.

Temas

Instrumente a su agente

Puede instrumentar un LangGraph agente con cualquiera de las dos bibliotecas de instrumentación: OpenTelemetry(opentelemetry-instrumentation-langchain) o OpenInference(openinference-instrumentation-langchain). Amazon Bedrock AgentCore Evaluations es compatible con ambas bibliotecas. Las bibliotecas emiten distintos nombres de ámbito y utilizan distintos atributos de ámbito. El servicio de evaluación extrae los mismos valores de cada uno.

Cuando su agente utiliza la AWS Distro for OpenTelemetry (ADOT), como en Amazon Bedrock AgentCore Runtime, no necesita añadir código de instrumentación explícito. Basta con añadir la biblioteca de instrumentación a las dependencias de su proyecto. ADOT la descubre al inicio y la activa automáticamente.

Añada la biblioteca de instrumentación de la ruta que desee a sus dependencias. Los siguientes ejemplos fijan una versión mínima; usa la última versión disponible a menos que tengas un motivo para marcarla.

ejemplo
OpenTelemetry

NOTA: Utilice la versión 0.55.0 o posterior. La versión 0.55.0 agregó compatibilidad con las nuevas convenciones OpenTelemetry generativas de intervalo de agentes de IA en las que se basa el servicio de evaluación.

Añada opentelemetry-instrumentation-langchain a sus dependencias. El nombre del ámbito emitido es. opentelemetry.instrumentation.langchain

requirements.txt:

opentelemetry-instrumentation-langchain>=0.55.0

pyproject.toml:

[project] dependencies = [ "opentelemetry-instrumentation-langchain>=0.55.0", ]
OpenInference

Añada openinference-instrumentation-langchain a sus dependencias. El nombre del ámbito emitido esopeninference.instrumentation.langchain.

requirements.txt:

openinference-instrumentation-langchain>=0.1.62

pyproject.toml:

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

Cómo se identifican los intervalos

El atributo utilizado para clasificar los intervalos difiere entre las dos bibliotecas de instrumentación.

ejemplo
OpenTelemetry

La biblioteca de OpenTelemetry instrumentación clasifica los tramos mediante el traceloop.span.kind atributo y también los establece en las versiones recientes. gen_ai.operation.name

Tipo de tramo Atributo identificativo

Invoca al agente

traceloop.span.kind= workflow (también gen_ai.operation.name =invoke_agent)

Ejecute la herramienta

traceloop.span.kind= tool (también gen_ai.operation.name =execute_tool)

Inferencia

gen_ai.operation.name = chat

OpenInference

La biblioteca de OpenInference instrumentación clasifica los tramos mediante el atributo. openinference.span.kind

Tipo de tramo Atributo identificativo

Invoca al agente

openinference.span.kind= CHAIN o AGENT

Ejecute la herramienta

openinference.span.kind = TOOL

Inferencia

openinference.span.kind = LLM

Cómo se extraen los campos de evaluación

Para el intervalo de agentes de invocación, la entrada y la salida no contienen una lista limpia por mensaje. En cambio, el contenido es el estado del LangChain gráfico serializado: una cadena JSON que contiene el estado completo. La forma exacta de este estado serializado difiere entre las dos bibliotecas de instrumentación. En ambos casos, el servicio lo analiza para encontrar el mensaje del usuario (el mensaje humano) y la respuesta del agente (el mensaje de la IA).

LangGraph también serializa las funciones de los mensajes en más de una forma. Un rol puede aparecer como un valor en minúscula (human,ai,tool) o como un nombre de clase de LangChain mensaje (HumanMessage,,AIMessage). ToolMessage El servicio reconoce ambos formularios.

La ubicación de este contenido depende de cómo se recopiló la telemetría. El atributo de identificación (traceloop.span.kindoopeninference.span.kind) está en el intervalo en ambos casos. Para obtener más información, consulte Intervalos, registros de eventos y señales de telemetría.

De los registros de eventos

Cuando se divide la telemetría, el servicio lee el contenido del registro de eventos correlacionado con cada intervalo:

  • Mensaje del usuario y respuesta del agente: del registro de eventos del intervalo de agentes invocado, en y. body.input body.output

  • Llamada de herramienta: el nombre de la herramienta que aparece en el intervalo de herramientas de ejecución. Los argumentos y el resultado de la herramienta provienen del registro de eventos de ese intervalo, en body.input ybody.output.

Para ver ejemplos, consulte Ejemplos de intervalos con registros de eventos.

De los atributos de span

Cuando la telemetría no está dividida, el mismo contenido permanece en el tramo que los atributos. Los atributos dependen de la biblioteca de instrumentación:

  • OpenTelemetry:

    • Mensaje del usuario y respuesta del agente: desde gen_ai.task.input y gen_ai.task.output en el intervalo de invocación del agente.

    • Llamada a la herramienta: el nombre de la herramienta desdegen_ai.tool.name, y los argumentos y el resultado de gen_ai.tool.call.arguments ygen_ai.tool.call.result, en el intervalo de la herramienta de ejecución.

  • OpenInference:

    • Mensaje del usuario y respuesta del agente: desde input.value y output.value en el intervalo del agente de invocación.

    • Llamada a la herramienta: el nombre de la herramienta desdetool.name, y los argumentos y el resultado de input.value youtput.value, en el intervalo de la herramienta de ejecución.

Para ver ejemplos, consulte Ejemplos de intervalos sin registros de eventos.

Un ejemplo de tramos con registros de eventos

Cuando se divide la telemetría, el intervalo contiene los atributos de identificación y el contenido reside en un registro de eventos correlacionado. Los siguientes ejemplos provienen de un agente de LangGraph planificación de viajes desplegado en Amazon Bedrock AgentCore Runtime. En cada biblioteca de instrumentación se muestra el mismo agente.

nota

Estos ejemplos no son períodos completos. Muestran datos representativos de una interacción real entre agentes, omitiendo algunos campos y truncando los valores largos para facilitar la lectura.

OpenTelemetry

ejemplo
Invoke agent span

El traceloop.span.kind atributo (workflow) lo identifica como un intervalo de agentes de invocación; las versiones recientes de la biblioteca también establecen el valor =. 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" } }

El registro de eventos correlacionados contiene la conversación. El de cada mensaje content es el estado del LangChain gráfico serializado. La entrada envuelve el estado bajo una inputs clave. La salida lo envuelve bajo una outputs clave, con cada mensaje como un objeto LangChain constructor. El mensaje del usuario es el mensaje humano y la respuesta del agente es el mensaje de la IA dentro de ese estado serializado.

{ "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

El traceloop.span.kind atributo (tool) lo identifica como un intervalo de herramientas de ejecución; gen_ai.tool.name contiene el nombre de la herramienta y 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" } }

El registro de eventos correlacionados contiene la entrada (argumentos) y la salida (resultado, serializado como a) de la herramienta. LangChain ToolMessage

{ "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

En la OpenInference biblioteca, el tipo de intervalo se incluye en el openinference.span.kind atributo y la entrada y la salida del agente se serializan en el registro de eventos correlacionados.

ejemplo
Invoke agent span

El openinference.span.kind atributo (CHAINo AGENT cuando el gráfico se compila con un nombre) lo identifica como un intervalo de agentes de invocación.

{ "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" } }

El registro de eventos correlacionados contiene la conversación. El mensaje del usuario es el mensaje de rol humano y la respuesta del agente es el AI-role mensaje de los mensajes serializados.

{ "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

El openinference.span.kind atributo (TOOL) lo identifica como un intervalo de herramientas de ejecución; tool.name contiene el nombre de la herramienta.

{ "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" } }

El registro de eventos correlacionados contiene la entrada (argumentos) y la salida (resultado, serializado como a) de la herramienta. LangChain ToolMessage

{ "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" } ] } } }

El ejemplo abarca espacios sin registros de eventos

Cuando la telemetría no está dividida, el mismo contenido permanece en los atributos del tramo y no se genera ningún registro de eventos independiente. Los siguientes ejemplos son de un agente de planificación de LangGraph viajes. En cada biblioteca de instrumentación se muestra el mismo agente.

nota

Estos ejemplos no son períodos completos. Muestran datos representativos de una interacción real entre agentes, omitiendo algunos campos y truncando los valores largos para facilitar la lectura.

OpenTelemetry

ejemplo
Invoke agent span

El gen_ai.task.input atributo contiene la solicitud del usuario y el gen_ai.task.output atributo contiene el estado serializado con la respuesta del agente. Ambos son el estado del LangChain gráfico serializado.

{ "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

El gen_ai.tool.call.arguments atributo contiene los argumentos de la herramienta y el gen_ai.tool.call.result atributo contiene el resultado de la herramienta, serializado como a. 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

ejemplo
Invoke agent span

El input.value atributo contiene la solicitud del usuario y el output.value atributo contiene el estado serializado con la respuesta del agente.

{ "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

El input.value atributo contiene los argumentos de la herramienta y el output.value atributo contiene el resultado de la herramienta, serializado como a. 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" } }

Mejores prácticas para los agentes LangGraph

La forma en que se crea e invoca un LangGraph agente afecta a lo que aparece en su telemetría y, por lo tanto, a la fiabilidad con la que se puede evaluar el agente. Las siguientes prácticas ayudan a garantizar que se puedan recuperar el mensaje del usuario, la respuesta del agente y la actividad de la herramienta.

1. Elija un patrón de construcción de agentes

Existen dos formas comunes de crear un LangGraph agente:

  • Prediseñado create_agent: la forma más rápida de empezar. Produce un único intervalo de agentes de invocación por turno, y la conversación pasa por el ciclo LangGraph de ejecución integrado. Úselo cuando desee un agente estándar de razón-acción sin un flujo de control personalizado.

    from langchain.agents import create_agent agent = create_agent(model=model, tools=[search_flights, book_flight])
  • Personalizado StateGraph: le brinda un control total sobre los nodos, los bordes y el enrutamiento condicional. La ejecución de cada nodo se convierte en su propio intervalo, por lo que las trazas son más granulares. Úselo cuando necesite una orquestación personalizada.

    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()

Ambos patrones se evalúan de la misma manera; la diferencia es la granularidad de la traza.

2. Usa mensajes en el estado del gráfico (recomendado)

El servicio de evaluación reconstruye la conversación a partir de los mensajes de entrada y salida del agente. El uso de un messages campo no es obligatorio, pero permite una extracción más fiable. Como costumbreStateGraph, mantén la conversación en un messages campo de tu estado:

  • messagesInclúyelo en tu estado (recomendado). Puedes añadir otros campos personalizados (como user_id metadatos). Cuando messages está presente, la extracción estándar busca directamente el mensaje del usuario y la respuesta del agente. Si no messages está presente, el servicio recurre a reconstruir la conversación a partir de intervalos de inferencia individuales, lo que resulta menos fiable.

  • Añada, no sustituya. Sigue la LangGraph convención de añadir nuevos mensajes a la lista en lugar de sobrescribirlos, de forma que se conserve todo el historial de conversaciones.

  • Usa tipos de LangChain mensajes canónicos (HumanMessage,,,AIMessage). ToolMessage SystemMessage La instrumentación los serializa correctamente y el servicio reconoce sus funciones.

3. Transmita el mensaje del usuario en un formato compatible

Al invocar a un LangGraph agente, se añade el mensaje del usuario al messages estado del gráfico. LangGraph acepta el mensaje en tres formatos intercambiables y AgentCore Evaluations los admite todos. Cada uno produce intervalos y registros de eventos que el servicio puede leer.

  • Tupla: un (role, content) par:

    agent.invoke({"messages": [("user", user_message)]}, config=config)
  • LangChain objeto de mensaje: a HumanMessage (u otra clase de mensaje):

    from langchain_core.messages import HumanMessage agent.invoke({"messages": [HumanMessage(content=user_message)]}, config=config)
  • Diccionario: un {"role", "content"} diccionario:

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

Los tres formatos tienen el mismo messages estado, por lo que el mensaje del usuario y la respuesta del agente se extraen de forma idéntica, independientemente del que se elija.