View a markdown version of this page

LangGraph - Amazon Bedrock AgentCore

LangGraph

Questa pagina spiega come strumentare un LangGraphagente, come vengono identificati gli intervalli e come vengono estratti i campi di valutazione. Si chiude con le migliori pratiche per strutturare un LangGraph agente in modo che possa essere valutato in modo affidabile.

Argomenti

Strumenta il tuo agente

Puoi strumentare un LangGraph agente con una delle due librerie di strumentazione: OpenTelemetry(opentelemetry-instrumentation-langchain) o OpenInference(openinference-instrumentation-langchain). Amazon Bedrock AgentCore Evaluations supporta entrambe le librerie. Le librerie emettono nomi di ambito diversi e utilizzano attributi span diversi. Il servizio di valutazione estrae gli stessi valori da ciascuna di esse.

Quando il tuo agente utilizza AWS Distro for OpenTelemetry (ADOT), ad esempio su Amazon Bedrock AgentCore Runtime, non è necessario aggiungere codice di strumentazione esplicito. È sufficiente aggiungere la libreria di strumentazione alle dipendenze del progetto. ADOT la rileva all'avvio e la attiva automaticamente.

Aggiungi la libreria di strumentazione per il percorso che desideri alle tue dipendenze. Gli esempi seguenti aggiungono una versione minima; utilizzate l'ultima versione disponibile a meno che non abbiate un motivo per bloccarla.

Esempio
OpenTelemetry

NOTA: utilizza la versione 0.55.0 o successiva. La versione 0.55.0 ha aggiunto il supporto per il nuovo agente di OpenTelemetry intelligenza artificiale generativa, convenzioni su cui si basa il servizio di valutazione.

opentelemetry-instrumentation-langchainAggiungilo alle tue dipendenze. Il nome dell'ambito emesso è. opentelemetry.instrumentation.langchain

requirements.txt:

opentelemetry-instrumentation-langchain>=0.55.0

pyproject.toml:

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

Aggiungilo openinference-instrumentation-langchain alle tue dipendenze. Il nome dell'ambito emesso è. openinference.instrumentation.langchain

requirements.txt:

openinference-instrumentation-langchain>=0.1.62

pyproject.toml:

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

Come vengono identificati gli intervalli

L'attributo utilizzato per classificare gli intervalli differisce tra le due librerie di strumentazione.

Esempio
OpenTelemetry

La libreria di OpenTelemetry strumentazione classifica gli intervalli utilizzando l'attributo e vengono impostate anche le versioni recenti. traceloop.span.kind gen_ai.operation.name

Tipo di intervallo Attributo identificativo

Invoca l'agente

traceloop.span.kind= workflow (anche gen_ai.operation.name =) invoke_agent

Strumento di esecuzione

traceloop.span.kind= tool (anche gen_ai.operation.name =execute_tool)

Inferenza

gen_ai.operation.name = chat

OpenInference

La libreria di OpenInference strumentazione classifica gli intervalli utilizzando l'attributo. openinference.span.kind

Tipo di intervallo Attributo identificativo

Invoca l'agente

openinference.span.kind= CHAIN o AGENT

Strumento di esecuzione

openinference.span.kind = TOOL

Inferenza

openinference.span.kind = LLM

Come vengono estratti i campi di valutazione

Per l'invoke agent span, l'input e l'output non contengono un elenco pulito per messaggio. Il contenuto è invece lo stato del LangChain grafico serializzato: una stringa JSON che racchiude lo stato completo. La forma esatta di questo stato serializzato differisce tra le due librerie di strumentazione. In entrambi i casi, il servizio lo analizza per trovare il prompt dell'utente (il messaggio umano) e la risposta dell'agente (il messaggio AI).

LangGraph serializza anche i ruoli dei messaggi in più di un modulo. Un ruolo può apparire come valore minuscolo (human,ai,tool) o come nome di una classe di LangChain messaggi (HumanMessage,,). AIMessage ToolMessage Il servizio riconosce entrambi i moduli.

La posizione di questo contenuto dipende dal modo in cui è stata raccolta la telemetria. L'attributo identificativo (traceloop.span.kindoopeninference.span.kind) è presente nell'intervallo in entrambi i casi. Per ulteriori informazioni, consulta Intervalli, record di eventi e segnali di telemetria.

Dai registri degli eventi

Quando la telemetria viene suddivisa, il servizio legge il contenuto del record dell'evento correlato a ciascun intervallo:

  • Richiesta dell'utente e risposta dell'agente: dal record degli eventi di Invoke Agent Span, in and. body.input body.output

  • Chiamata allo strumento: il nome dello strumento incluso nell'intervallo di esecuzione dello strumento. Gli argomenti e i risultati dello strumento provengono dal record degli eventi di quell'intervallo, in body.input and. body.output

Per esempi, vedete Example spans with event records.

Dagli attributi span

Quando la telemetria non viene suddivisa, lo stesso contenuto rimane nell'intervallo degli attributi. Gli attributi dipendono dalla libreria di strumentazione:

  • OpenTelemetry:

    • Richiesta dell'utente e risposta dell'agente: da gen_ai.task.input e gen_ai.task.output verso l'intervallo dell'agente di invocazione.

    • Richiamata dello strumento: il nome dello strumento dagen_ai.tool.name, gli argomenti e i risultati di gen_ai.tool.call.arguments egen_ai.tool.call.result, nell'intervallo dello strumento di esecuzione.

  • OpenInference:

    • Richiesta dell'utente e risposta dell'agente: da input.value e output.value sull'intervallo dell'agente di invocazione.

    • Richiamata dello strumento: il nome dello strumento datool.name, gli argomenti e i risultati di input.value eoutput.value, nell'intervallo dello strumento di esecuzione.

Ad esempio, vedete Example spans without event records.

L'esempio si estende ai record degli eventi

Quando la telemetria viene suddivisa, l'intervallo contiene gli attributi identificativi e il contenuto risiede in un record di eventi correlato. I seguenti esempi sono tratti da un agente di LangGraph pianificazione dei viaggi distribuito su Amazon Bedrock Runtime. AgentCore Lo stesso agente è mostrato in ogni libreria di strumentazione.

Nota

Questi esempi non sono intervalli completi. Mostrano dati rappresentativi derivanti da un'interazione con un agente reale, con alcuni campi omessi e valori lunghi troncati per motivi di leggibilità.

OpenTelemetry

Esempio
Invoke agent span

L'traceloop.span.kindattributo (workflow) lo identifica come un invoke agent span; le versioni recenti della libreria impostano anche =. 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" } }

Il record dell'evento correlato contiene la conversazione. Quello di ogni messaggio content è lo stato del LangChain grafico serializzato. L'input racchiude lo stato in una chiave. inputs L'output lo avvolge in una outputs chiave, con ogni messaggio come oggetto costruttore. LangChain Il prompt dell'utente è il messaggio umano e la risposta dell'agente è il messaggio AI all'interno di quello stato serializzato.

{ "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.kindattributo (tool) lo identifica come uno strumento di esecuzione; gen_ai.tool.name contiene il nome dello strumento e =. 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" } }

Il record dell'evento correlato contiene l'input (argomenti) e l'output (risultato, serializzato come a). 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

Con la OpenInference libreria, il tipo span viene inserito nell'openinference.span.kindattributo e l'input e l'output dell'agente vengono serializzati nel record dell'evento correlato.

Esempio
Invoke agent span

L'openinference.span.kindattributo (CHAINo AGENT quando il grafico è compilato con un nome) lo identifica come un invoke agent span.

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

Il record dell'evento correlato contiene la conversazione. Il prompt dell'utente è il messaggio relativo al ruolo umano e la risposta dell'agente è il AI-role messaggio contenuto nei messaggi serializzati.

{ "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.kindattributo (TOOL) lo identifica come uno strumento di esecuzione; contiene il nome dello strumento. tool.name

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

Il record dell'evento correlato contiene l'input (argomenti) e l'output (risultato, serializzato come a). 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" } ] } } }

L'esempio si estende senza record di eventi

Quando la telemetria non viene suddivisa, lo stesso contenuto rimane negli attributi span e non viene prodotto alcun record di eventi separato. Gli esempi seguenti sono tratti da un addetto alla pianificazione dei viaggi. LangGraph Lo stesso agente è mostrato in ogni libreria di strumentazione.

Nota

Questi esempi non sono intervalli completi. Mostrano dati rappresentativi derivanti da un'interazione con un agente reale, con alcuni campi omessi e valori lunghi troncati per motivi di leggibilità.

OpenTelemetry

Esempio
Invoke agent span

L'gen_ai.task.inputattributo contiene il prompt dell'utente e l'gen_ai.task.outputattributo contiene lo stato serializzato con la risposta dell'agente. Entrambi sono lo stato del grafico serializzato LangChain .

{ "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.argumentsattributo contiene gli argomenti dello strumento e l'gen_ai.tool.call.resultattributo contiene il risultato dello strumento, serializzato come 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

Esempio
Invoke agent span

L'input.valueattributo contiene il prompt dell'utente e l'output.valueattributo contiene lo stato serializzato con la risposta dell'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

L'input.valueattributo contiene gli argomenti dello strumento e l'output.valueattributo contiene il risultato dello strumento, serializzato come 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" } }

Le migliori pratiche per gli agenti LangGraph

Il modo in cui si crea e si richiama un LangGraph agente influisce su ciò che appare nella sua telemetria e quindi sull'affidabilità della valutazione dell'agente. Le seguenti pratiche aiutano a garantire che il prompt dell'utente, la risposta dell'agente e l'attività dello strumento siano ripristinabili.

1. Scegli uno schema costruttivo per agenti

Esistono due modi comuni per creare un LangGraph agente:

  • Precostruito create_agent: il modo più rapido per iniziare. Produce un singolo intervallo di invoke agent per turno, con la conversazione passata attraverso il ciclo di esecuzione integrato del ciclo LangGraph di esecuzione. Usalo quando desideri un agente Reason-Act standard senza un flusso di controllo personalizzato.

    from langchain.agents import create_agent agent = create_agent(model=model, tools=[search_flights, book_flight])
  • Personalizzato StateGraph: ti offre il pieno controllo su nodi, bordi e routing condizionale. L'esecuzione di ogni nodo diventa un intervallo a sé stante, quindi le tracce sono più granulari. Usalo quando hai bisogno di un'orchestrazione personalizzata.

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

Entrambi i modelli vengono valutati allo stesso modo; la differenza è la granularità della traccia.

2. Usa i messaggi nel tuo stato del grafico (consigliato)

Il servizio di valutazione ricostruisce la conversazione a partire dai messaggi di input e output dell'agente. L'utilizzo di un messages campo non è obbligatorio, ma consente l'estrazione più affidabile. Per una consuetudineStateGraph, tenete la conversazione in un messages campo del vostro Stato:

  • Includi messages nel tuo Stato (consigliato). Puoi aggiungere altri campi personalizzati (come i user_id metadati). Quando messages è presente, l'estrazione standard trova direttamente il prompt dell'utente e la risposta dell'agente. Se messages è assente, il servizio torna a ricostruire la conversazione a partire da intervalli di inferenza individuali, il che è meno affidabile.

  • Aggiungi, non sostituire. Segui la LangGraph convenzione di aggiungere nuovi messaggi all'elenco anziché sovrascriverlo, in modo da conservare l'intera cronologia delle conversazioni.

  • Usa tipi di LangChain messaggi canonici (HumanMessage,,,AIMessage). ToolMessage SystemMessage La strumentazione li serializza correttamente e il servizio ne riconosce i ruoli.

3. Trasmetti il messaggio utente in un formato supportato

Quando richiami un LangGraph agente, aggiungi il messaggio dell'utente messages allo stato del grafico. LangGraph accetta il messaggio in tre formati intercambiabili e AgentCore Evaluations li supporta tutti. Ciascuno produce intervalli e record di eventi che il servizio può leggere.

  • Tuple: una (role, content) coppia:

    agent.invoke({"messages": [("user", user_message)]}, config=config)
  • LangChain oggetto messaggio: a HumanMessage (o altra classe di messaggi):

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

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

Tutti e tre i formati hanno lo stesso messages stato, quindi il prompt dell'utente e la risposta dell'agente vengono estratti in modo identico indipendentemente dalla scelta effettuata.