本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
为评估设置 LangGraph 遥测 AgentCore
本页介绍如何对LangGraph代理进行仪器测量、如何识别跨度以及如何提取评估字段。 AgentCore 评估支持在 Python 中内置的 LangGraph 代理 TypeScript;本页分别介绍每种语言,包括 Python 代理支持和TypeScript 代理支持。最后,它介绍了构建 LangGraph 代理LangGraph 代理最佳实践的最佳实践,以便对其进行可靠评估。
主题
Python 代理支持
Python LangGraph 代理在作用域名称 opentelemetry.instrumentation.langchain (OpenTelemetry) 或 openinference.instrumentation.langchain (OpenInference) 下发出跨度。
为您的代理人提供仪器
您可以使用两个仪器库中的任何一个来对 LangGraph 代理进行仪器:OpenTelemetry(opentelemetry-instrumentation-langchain) 或 OpenInference (openinference-instrumentation-langchain)。亚马逊基岩 AgentCore 评估支持这两个库。这些库发出不同的范围名称并使用不同的跨度属性。评估服务从每种方法中提取相同的值。
当您的代理使用 AWS 发行版 OpenTelemetry (ADOT) 运行时,例如在 Amazon Bedrock AgentCore Runtime 上,您无需添加显式的检测代码。将仪器库添加到项目的依赖项中就足够了。ADOT 在启动时发现它并自动将其激活。
为你想要的依赖项路径添加工具库。以下示例固定了最低版本;除非您有理由固定,否则请使用最新的可用版本。
例
- OpenTelemetry
-
注意:使用版本0.55.0或更高版本。0.55.0版本增加了对 GitHub 网站上较新的 OpenTelemetry Generative-AI代理跨度约定的支持,评估服务依赖该协议。
将 opentelemetry-instrumentation-langchain 添加到依赖项。发出的范围名称是。opentelemetry.instrumentation.langchain
requirements.txt:
opentelemetry-instrumentation-langchain>=0.55.0
pyproject.toml:
[project]
dependencies = [
"opentelemetry-instrumentation-langchain>=0.55.0",
]
- OpenInference
-
将 openinference-instrumentation-langchain 添加到依赖项。发出的范围名称是。openinference.instrumentation.langchain
requirements.txt:
openinference-instrumentation-langchain>=0.1.62
pyproject.toml:
[project]
dependencies = [
"openinference-instrumentation-langchain>=0.1.62",
]
仪器仪表是设置可观测性的一个步骤。要导出遥测数据进行评估,请在设置可观测性中完成完整设置。
如何识别跨度
两个仪器库中用于对跨度进行分类的属性有所不同。
例
- OpenTelemetry
-
OpenTelemetry 仪器库使用该traceloop.span.kind属性对跨度进行分类,最新版本也进行了设置。gen_ai.operation.name
| 跨度类型 |
识别属性 |
|
调用代理
|
traceloop.span.kind=workflow(也是 gen_ai.operation.name =invoke_agent)
|
|
执行工具
|
traceloop.span.kind=tool(也是 gen_ai.operation.name =execute_tool)
|
|
推理
|
gen_ai.operation.name = chat
|
- OpenInference
-
OpenInference 仪器库使用该属性对跨度进行分类。openinference.span.kind
| 跨度类型 |
识别属性 |
|
调用代理
|
openinference.span.kind= CHAIN 或 AGENT
|
|
执行工具
|
openinference.span.kind = TOOL
|
|
推理
|
openinference.span.kind = LLM
|
对于调用代理跨度,输入和输出不包含干净的每条消息列表。相反,内容是序列化的 LangChain 图形状态:包装完整状态的 JSON 字符串。这种序列化状态的确切形状在两个仪器库之间有所不同。在这两种情况下,服务都会对其进行解析以找到用户提示(人工消息)和代理响应(AI 消息)。
LangGraph 还以多种形式序列化消息角色。角色可以显示为小写值 (human,ai,tool) 或 LangChain 消息类名 (HumanMessage,AIMessage,ToolMessage)。该服务可以识别这两种形式。
此内容的位置取决于遥测数据的收集方式。在这两种情况下,识别属性(traceloop.span.kind或openinference.span.kind)都处于跨度内。有关更多信息,请参阅遥测设置和交付。
通过拆分遥测,该服务从与每个跨度相关的事件记录中读取内容:
有关更多信息,请参见分割遥测中的跨度示例。
使用统一的遥测技术,相同的内容将作为属性保留在跨度上。这些属性取决于仪器库:
-
OpenTelemetry:
-
OpenInference:
有关更多信息,请参阅统一遥测中的跨度示例。
分体遥测中的跨度示例
使用分离式遥测,跨度携带识别属性,内容存在于相关的事件记录中。以下示例来自部署在亚马逊 Bedro LangGraph AgentCore ck Runtime 上的 Python 旅行计划代理。每个仪器库下方都显示相同的代理。
这些例子并不完整。它们显示来自真实代理交互的代表性数据,为了便于阅读,省略了一些字段并截断了长值。
OpenTelemetry
例
- Invoke agent span
-
traceloop.span.kind属性 (workflow) 将其标识为调用代理跨度;最近的库版本也将其设置为 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"
}
}
相关的事件记录承载着对话。每条消息content都是序列化的 LangChain 图形状态。输入将状态封装在按inputs键下。输出将其包装在一个outputs键下,每条消息作为 LangChain 构造器对象。用户提示是人工消息,代理响应是该序列化状态下的 AI 消息。
{
"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
-
traceloop.span.kind属性 (tool) 将其标识为执行工具跨度;gen_ai.tool.name包含工具名称和 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"
}
}
相关事件记录包含工具输入(参数)和输出(结果,序列化为 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
在 OpenInference 库中,跨度类型包含在openinference.span.kind属性中,代理输入和输出在相关的事件记录中序列化。
例
- Invoke agent span
-
该openinference.span.kind属性(CHAIN或使用名称编译图表AGENT时)将其标识为调用代理跨度。
{
"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"
}
}
相关的事件记录承载着对话。用户提示是人类角色消息,代理响应是序列化 AI-role 消息中的消息。
{
"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
-
openinference.span.kind属性 (TOOL) 将其标识为执行工具跨度;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"
}
}
相关事件记录包含工具输入(参数)和输出(结果,序列化为 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"
}
]
}
}
}
统一遥测中的示例跨度
使用统一的遥测技术,跨度属性上的内容相同,不会生成单独的事件记录。以下示例来自 Python LangGraph 旅行计划代理。每个仪器库下方都显示相同的代理。
这些例子并不完整。它们显示来自真实代理交互的代表性数据,为了便于阅读,省略了一些字段并截断了长值。
OpenTelemetry
例
- Invoke agent span
-
该gen_ai.task.input属性保存用户提示,该gen_ai.task.output属性保存代理响应的序列化状态。两者都是序列化 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
-
该gen_ai.tool.call.arguments属性保存工具参数,该gen_ai.tool.call.result属性保存工具结果,序列化为 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
例
- Invoke agent span
-
该input.value属性保存用户提示,该output.value属性保存代理响应的序列化状态。
{
"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
-
该input.value属性保存工具参数,该output.value属性保存工具结果,序列化为 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 代理支持
TypeScript LangGraph 代理发出的跨度类型与 Python 代理相同,因此评估服务以相同的方式读取它。有三个 TypeScript 仪器库,每个都有自己的范围名称和跨度分类约定。
为您的代理人提供仪器
为你想要的 TypeScript 依赖项路径添加工具库。以下示例固定了最低版本;除非您有理由固定,否则请使用最新的可用版本。
例
- ADOT (OpenTelemetry)
-
对于 ADOT 上的 TypeScript 代理,将 AWS 发行版节点自动仪表包 (@aws/aws-distro-opentelemetry-node-autoinstrumentation) 添加到您的依赖项中。它包括内置 LangChain 工具,该工具在启动时激活并发出范围名称。@aws/aws-distro-opentelemetry-instrumentation-langchain
package.json:
{
"dependencies": {
"@aws/aws-distro-opentelemetry-node-autoinstrumentation": "^0.12.0"
}
}
- Traceloop (OpenTelemetry)
-
将 Traceloop LangChain 工具 (@traceloop/instrumentation-langchain) 添加到您的依赖项中。发出的范围名称是。@traceloop/instrumentation-langchain
package.json:
{
"dependencies": {
"@traceloop/instrumentation-langchain": "^0.27.0"
}
}
- OpenInference
-
将 @arizeai/openinference-instrumentation-langchain 添加到依赖项。发出的范围名称是。@arizeai/openinference-instrumentation-langchain
package.json:
{
"dependencies": {
"@arizeai/openinference-instrumentation-langchain": "^4.0.14"
}
}
仪器仪表是设置可观测性的一个步骤。要导出遥测数据进行评估,请在设置可观测性中完成完整设置。
如何识别跨度
跨度识别取决于仪器库:
-
ADOT (OpenTelemetry): AWS 发行版节点自动仪表包 (@aws/aws-distro-opentelemetry-node-autoinstrumentation),它发出的范围名称@aws/aws-distro-opentelemetry-instrumentation-langchain集合 gen_ai.operation.name (invoke_agent,execute_tool,chat),与其他框架相同。 ADOT-native
-
Traceloop (OpenTelemetry):来自 Traceloop (@traceloop/instrumentation-langchain) 集合的 OpenTelemetry JS 库traceloop.span.kind(workflow用于调用代理跨度,task用于工具跨度),与 Python 库相匹配。 OpenTelemetry 请参阅 Python 代理支持如何识别跨度下如何识别跨度。
-
OpenInference: OpenInference JS 库 (@arizeai/openinference-instrumentation-langchain) 集openinference.span.kind(CHAIN或AGENT、TOOL、LLM),与 Python OpenInference 库相同。
字段提取依赖于仪器库:
-
ADOT (OpenTelemetry):调用代理跨度是一个结构容器,对话内容以部分gen_ai.input.messages格式和属性存在于推断 (chat) 跨度上。gen_ai.output.messages
-
Traceloop (OpenTelemetry):使用 Traceloop 的 OpenTelemetry JS 库,对话处于traceloop.entity.input和traceloop.entity.output属性,处于序列化状态。 LangChain 这与 Python OpenTelemetry 库相匹配;请参阅 Python 代理支持下如何提取评估字段。
-
OpenInference: 在 OpenInference JS 库中,对话位于input.value和output.value属性中,推理消息也出现在索引llm.input_messages.*和llm.output_messages.*属性上。这与 Python OpenInference 库相匹配。
来自代理的示例跨度 TypeScript
以下示例来自部署在具有统一遥测功能的 Amazon TypeScript LangGraph Bedrock AgentCore Runtime 上的旅行计划代理。三个 TypeScript 仪器库下都显示了相同的代理。
这些例子并不完整。它们显示来自真实代理交互的代表性数据,为了便于阅读,省略了一些字段并截断了长值。
OpenTelemetry (ADOT 原生)
使用 ADOT-native 库(来自 AWS Distro Node autoInstrumentation 软件包@aws/aws-distro-opentelemetry-node-autoinstrumentation,发出范围名称@aws/aws-distro-opentelemetry-instrumentation-langchain),调用代理跨度是一个结构容器,对话内容以零件格式和属性存在于推断 (chat) 跨度上。gen_ai.input.messages gen_ai.output.messages
例
- Invoke agent span
-
gen_ai.operation.name属性 (invoke_agent) 将其标识为调用代理跨度。该跨度带有代理名称和型号,但没有对话内容。
{
"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
-
gen_ai.operation.name属性 (execute_tool) 将其标识为执行工具跨度;gen_ai.tool.name包含工具名称。gen_ai.tool.call.arguments和gen_ai.tool.call.result属性用于保存工具参数和结果。
{
"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
-
gen_ai.operation.name属性 (chat) 将其标识为推理跨度。gen_ai.input.messages和gen_ai.output.messages属性以零件格式保存对话,并gen_ai.system_instructions保存系统提示。
{
"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 (@traceloop/instrumentation-langchain) 的 OpenTelemetry JS 库,跨度类型包含在traceloop.span.kind属性中(workflow对于调用代理跨度,task对于工具跨度),在调用代理跨度workflow上gen_ai.operation.name为 =。对话处于traceloop.entity.input和traceloop.entity.output属性,处于序列化 LangChain 状态。
例
- Invoke agent span
-
traceloop.span.kind属性 (workflow) 将其标识为调用代理跨度。traceloop.entity.input和traceloop.entity.output属性保持序列化 LangChain 状态,从中解析用户提示(人工消息)和代理响应(AI 消息)。
{
"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
-
traceloop.span.kind属性 (task) 将其标识为执行工具跨度。traceloop.entity.input和traceloop.entity.output属性用于保存工具参数和结果。
{
"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
在 OpenInference JS 库 (@arizeai/openinference-instrumentation-langchain) 中,跨度类型包含在openinference.span.kind属性中。对话内容位于input.value和output.value属性中,推理消息也出现在索引llm.input_messages.*和llm.output_messages.*属性上。
例
- Invoke agent span
-
openinference.span.kind属性 (CHAIN) 将其标识为调用代理跨度。input.value和output.value属性保持序列化 LangChain 状态。
{
"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
-
openinference.span.kind属性 (TOOL) 将其标识为执行工具跨度;tool.name包含工具名称。input.value和output.value属性保存工具参数和结果(序列化为 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
-
openinference.span.kind属性 (LLM) 将其标识为推理跨度。这些llm.input_messages.*属性保存系统提示和用户提示,llm.output_messages.*属性保存代理响应。
{
"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"
}
}
LangGraph 代理最佳实践
构建和调用 LangGraph 代理的方式会影响其遥测数据中显示的内容,从而影响评估代理的可靠性。以下做法有助于确保用户提示、代理响应和工具活动均可恢复。
1. 选择代理构造模式
构建 LangGraph 代理有两种常用方法:
-
预建 create_agent:最快的入门方式。它每回合生成一个调用代理跨度,对话通过内置 LangGraph的执行循环。当你想要一个没有自定义控制流程的标准 reason-act 代理时,请使用此选项。
from langchain.agents import create_agent
agent = create_agent(model=model, tools=[search_flights, book_flight])
-
自定义 StateGraph:使您可以完全控制节点、边和条件路由。每个节点的执行都会变成自己的跨度,因此跟踪更加精细。当你需要自定义编排时使用它。
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()
两种模式的评估方法相同;区别在于跟踪的粒度。
2. 在图表状态下使用消息(推荐)
评估服务根据代理的输入和输出消息重构对话。使用messages字段不是强制性的,但它可以实现最可靠的提取。对于自定义StateGraph,请将对话保留在您所在州的某个messages字段中:
-
包括messages在您所在的州(推荐)。您可以添加其他自定义字段(例如user_id或元数据)。当messages存在时,标准提取会直接找到用户提示和代理响应。如果messages不存在,该服务将退回到从个人推理跨度中重建对话,这不太可靠。
-
追加,不要替换。遵循向列表添加新消息而不是覆盖列表的 LangGraph 惯例,这样可以保留完整的对话历史记录。
-
使用规范 LangChain 消息类型 (HumanMessage、、AIMessageToolMessage、SystemMessage)。仪器可以正确地对它们进行序列化,并且服务可以识别它们的角色。
3. 以支持的格式传递用户消息
调用 LangGraph 代理时,将用户消息添加到图表的messages状态。 LangGraph 接受三种可互换格式的消息, AgentCore 评估支持所有格式。每个都生成服务可以读取的跨度和事件记录。
-
元组:一(role, content)对:
agent.invoke({"messages": [("user", user_message)]}, config=config)
-
LangChain 消息对象:aHumanMessage(或其他消息类):
from langchain_core.messages import HumanMessage
agent.invoke({"messages": [HumanMessage(content=user_message)]}, config=config)
-
字典:一本{"role", "content"}字典:
agent.invoke({"messages": [{"role": "user", "content": user_message}]}, config=config)
所有三种格式都会产生相同的messages状态,因此无论您选择哪种格式,提取的用户提示和代理响应都是相同的。