

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 为评估设置 LangGraph 遥测 AgentCore
<a name="supported-frameworks-langgraph"></a>

本页介绍如何对[LangGraph](https://langchain-ai.github.io/langgraph/)代理进行仪器测量、如何识别跨度以及如何提取评估字段。 AgentCore 评估支持在 Python 中内置的 LangGraph 代理 TypeScript；本页分别介绍每种语言，包括 [ Python 代理支持](#langgraph-python)和[TypeScript 代理支持](#langgraph-typescript)。最后，它介绍了构建 LangGraph 代理[LangGraph 代理最佳实践](#langgraph-best-practices)的最佳实践，以便对其进行可靠评估。

 **主题** 
+  [Python 代理支持 ](#langgraph-python) 
  +  [为您的代理人提供仪器 ](#langgraph-instrument) 
  +  [如何识别跨度 ](#langgraph-span-identification) 
  +  [如何提取评估字段 ](#langgraph-extraction) 
    +  [来自事件记录 ](#langgraph-extraction-event-records) 
    +  [来自跨度属性 ](#langgraph-extraction-attributes) 
  +  [分体遥测中的跨度示例 ](#langgraph-examples-split) 
  +  [统一遥测中的示例跨度 ](#langgraph-examples-unified) 
+  [TypeScript 代理支持 ](#langgraph-typescript) 
  +  [为您的代理人提供仪器 ](#langgraph-typescript-instrument) 
  +  [如何识别跨度 ](#langgraph-typescript-span-identification) 
  +  [如何提取评估字段 ](#langgraph-typescript-extraction) 
  +  [来自代理的示例跨度 TypeScript ](#langgraph-examples-typescript) 
+  [ LangGraph 代理最佳实践 ](#langgraph-best-practices) 

## Python 代理支持
<a name="langgraph-python"></a>

Python LangGraph 代理在作用域名称 `opentelemetry.instrumentation.langchain` (OpenTelemetry) 或 `openinference.instrumentation.langchain` (OpenInference) 下发出跨度。

### 为您的代理人提供仪器
<a name="langgraph-instrument"></a>

您可以使用两个仪器库中的任何一个来对 LangGraph 代理进行仪器：**OpenTelemetry**(`opentelemetry-instrumentation-langchain`) 或 ** OpenInference ** (`openinference-instrumentation-langchain`)。亚马逊基岩 AgentCore 评估支持这两个库。这些库发出不同的范围名称并使用不同的跨度属性。评估服务从每种方法中提取相同的值。

当您的代理使用 Amazon 发行版 OpenTelemetry (ADOT) 运行时，例如在 Amazon Bedrock AgentCore Runtime 上，您无需添加显式的检测代码。将仪器库添加到项目的依赖项中就足够了。ADOT 在启动时发现它并自动将其激活。

为你想要的依赖项路径添加工具库。以下示例固定了最低版本；除非您有理由固定，否则请使用最新的可用版本。

**Example**  
注意：使用版本`0.55.0`或更高版本。0.55.0版本增加了对 GitHub 网站上较新的 OpenTelemetry [ Generative-AI代理跨度约定的](https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/gen-ai-agent-spans.md)支持，评估服务依赖该协议。  
将 `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-instrumentation-langchain` 添加到依赖项。发出的范围名称是。`openinference.instrumentation.langchain`  
 `requirements.txt`:  

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

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

**注意**  
仪器仪表是设置可观测性的一个步骤。要导出遥测数据进行评估，请在设置可观[测性中](supported-frameworks-telemetry.md#supported-frameworks-setup)完成完整设置。

### 如何识别跨度
<a name="langgraph-span-identification"></a>

两个仪器库中用于对跨度进行分类的属性有所不同。

**Example**  
 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.span.kind`  


| 跨度类型 | 识别属性 | 
| --- | --- | 
| 调用代理 |  `openinference.span.kind`= `CHAIN` 或 `AGENT`  | 
| 执行工具 |  `openinference.span.kind` = `TOOL`  | 
| 推理 |  `openinference.span.kind` = `LLM`  | 

### 如何提取评估字段
<a name="langgraph-extraction"></a>

对于调用代理跨度，输入和输出不包含干净的每条消息列表。相反，内容是**序列化的 LangChain 图形状态**：包装完整状态的 JSON 字符串。这种序列化状态的确切形状在两个仪器库之间有所不同。在这两种情况下，服务都会对其进行解析以找到用户提示（人工消息）和代理响应（AI 消息）。

LangGraph 还以多种形式序列化消息角色。角色可以显示为小写值 (`human`,`ai`,`tool`) 或 LangChain 消息类名 (`HumanMessage`,`AIMessage`,`ToolMessage`)。该服务可以识别这两种形式。

此内容的位置取决于遥测数据的收集方式。在这两种情况下，识别属性（`traceloop.span.kind`或`openinference.span.kind`）都处于跨度内。有关更多信息，请参阅[遥测设置和交付](supported-frameworks-telemetry.md)。

#### 来自事件记录
<a name="langgraph-extraction-event-records"></a>

通过拆分遥测，该服务从与每个跨度相关的事件记录中读取内容：
+  **用户提示**和**代理响应**：来自调用代理范围的事件记录，在`body.input`和中`body.output`。
+  **工具调用**：执行工具跨度中的工具名称。工具参数和结果来自该跨度的事件记录，以`body.input`和为单位`body.output`。

有关更多信息，请参见分割遥测[中的跨度](#langgraph-examples-split)示例。

#### 来自跨度属性
<a name="langgraph-extraction-attributes"></a>

使用统一的遥测技术，相同的内容将作为属性保留在跨度上。这些属性取决于仪器库：
+  **OpenTelemetry**:
  +  **用户提示**和**代理响应**：从`gen_ai.task.input`调用代理跨度开始。`gen_ai.task.output`
  +  **工具调用**：执行工具跨度上的工具名称`gen_ai.tool.call.arguments`以及来自和`gen_ai.tool.call.result`的参数和结果。`gen_ai.tool.name`
+  **OpenInference**:
  +  **用户提示**和**代理响应**：从`input.value`调用代理跨度开始。`output.value`
  +  **工具调用**：执行工具跨度上的工具名称`input.value`以及来自和`output.value`的参数和结果。`tool.name`

有关更多信息，请参阅统一遥测[中的跨度](#langgraph-examples-unified)示例。

### 分体遥测中的跨度示例
<a name="langgraph-examples-split"></a>

使用分离式遥测，跨度携带识别属性，内容存在于相关的事件记录中。以下示例来自部署在亚马逊 Bedro LangGraph AgentCore ck Runtime 上的 Python 旅行计划代理。每个仪器库下方都显示相同的代理。

**注意**  
这些例子并不完整。它们显示来自真实代理交互的代表性数据，为了便于阅读，省略了一些字段并截断了长值。

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

**Example**  
`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"
        }
      ]
    }
  }
}
```
`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
<a name="langgraph-examples-openinference"></a>

在 OpenInference 库中，跨度类型包含在`openinference.span.kind`属性中，代理输入和输出在相关的事件记录中序列化。

**Example**  
该`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"
        }
      ]
    }
  }
}
```
`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"
        }
      ]
    }
  }
}
```

### 统一遥测中的示例跨度
<a name="langgraph-examples-unified"></a>

使用统一的遥测技术，跨度属性上的内容相同，不会生成单独的事件记录。以下示例来自 Python LangGraph 旅行计划代理。每个仪器库下方都显示相同的代理。

**注意**  
这些例子并不完整。它们显示来自真实代理交互的代表性数据，为了便于阅读，省略了一些字段并截断了长值。

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

**Example**  
该`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"
  }
}
```
该`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
<a name="langgraph-examples-unified-openinference"></a>

**Example**  
该`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"
  }
}
```
该`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 代理支持
<a name="langgraph-typescript"></a>

 TypeScript LangGraph 代理发出的跨度类型与 Python 代理相同，因此评估服务以相同的方式读取它。有三个 TypeScript 仪器库，每个都有自己的范围名称和跨度分类约定。

### 为您的代理人提供仪器
<a name="langgraph-typescript-instrument"></a>

为你想要的 TypeScript 依赖项路径添加工具库。以下示例固定了最低版本；除非您有理由固定，否则请使用最新的可用版本。

**Example**  
对于 ADOT 上的 TypeScript 代理，将 Amazon 发行版节点自动仪表包 (`@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 LangChain 工具 (`@traceloop/instrumentation-langchain`) 添加到您的依赖项中。发出的范围名称是。`@traceloop/instrumentation-langchain`  
 `package.json`:  

```
{
  "dependencies": {
    "@traceloop/instrumentation-langchain": "^0.27.0"
  }
}
```
将 `@arizeai/openinference-instrumentation-langchain` 添加到依赖项。发出的范围名称是。`@arizeai/openinference-instrumentation-langchain`  
 `package.json`:  

```
{
  "dependencies": {
    "@arizeai/openinference-instrumentation-langchain": "^4.0.14"
  }
}
```

**注意**  
仪器仪表是设置可观测性的一个步骤。要导出遥测数据进行评估，请在设置可观[测性中](supported-frameworks-telemetry.md#supported-frameworks-setup)完成完整设置。

### 如何识别跨度
<a name="langgraph-typescript-span-identification"></a>

跨度识别取决于仪器库：
+  **ADOT (OpenTelemetry)**： Amazon 发行版节点自动仪表包 (`@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 代理支持[如何识别跨度](#langgraph-span-identification)下](#langgraph-python)如何识别跨度。
+  **OpenInference**: OpenInference JS 库 (`@arizeai/openinference-instrumentation-langchain`) 集`openinference.span.kind`（`CHAIN`或`AGENT`、`TOOL`、`LLM`），与 Python OpenInference 库相同。

### 如何提取评估字段
<a name="langgraph-typescript-extraction"></a>

字段提取依赖于仪器库：
+  **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 代理支持[下](#langgraph-extraction)如何提取评估字段](#langgraph-python)。
+  **OpenInference**: 在 OpenInference JS 库中，对话位于`input.value`和`output.value`属性中，推理消息也出现在索引`llm.input_messages.*`和`llm.output_messages.*`属性上。这与 Python OpenInference 库相匹配。

### 来自代理的示例跨度 TypeScript
<a name="langgraph-examples-typescript"></a>

以下示例来自部署在具有统一遥测功能的 Amazon TypeScript LangGraph Bedrock AgentCore Runtime 上的旅行计划代理。三个 TypeScript 仪器库下都显示了相同的代理。

**注意**  
这些例子并不完整。它们显示来自真实代理交互的代表性数据，为了便于阅读，省略了一些字段并截断了长值。

#### OpenTelemetry （ADOT 原生）
<a name="langgraph-examples-typescript-adot"></a>

使用 ADOT-native 库（来自 Amazon Distro Node autoInstrumentation 软件包`@aws/aws-distro-opentelemetry-node-autoinstrumentation`，发出范围名称`@aws/aws-distro-opentelemetry-instrumentation-langchain`），调用代理跨度是一个结构容器，对话内容以零件格式和属性存在于推断 (`chat`) 跨度上。`gen_ai.input.messages` `gen_ai.output.messages`

**Example**  
`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"
  }
}
```
`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"
  }
}
```
`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 （追踪循环）
<a name="langgraph-examples-typescript-traceloop"></a>

使用来自 Traceloop (`@traceloop/instrumentation-langchain`) 的 OpenTelemetry JS 库，跨度类型包含在`traceloop.span.kind`属性中（`workflow`对于调用代理跨度，`task`对于工具跨度），在调用代理跨度`workflow`上`gen_ai.operation.name`为 =。对话处于`traceloop.entity.input`和`traceloop.entity.output`属性，处于序列化 LangChain 状态。

**Example**  
`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"
  }
}
```
`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
<a name="langgraph-examples-typescript-openinference"></a>

在 OpenInference JS 库 (`@arizeai/openinference-instrumentation-langchain`) 中，跨度类型包含在`openinference.span.kind`属性中。对话内容位于`input.value`和`output.value`属性中，推理消息也出现在索引`llm.input_messages.*`和`llm.output_messages.*`属性上。

**Example**  
`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"
  }
}
```
`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"
  }
}
```
`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 代理最佳实践
<a name="langgraph-best-practices"></a>

构建和调用 LangGraph 代理的方式会影响其遥测数据中显示的内容，从而影响评估代理的可靠性。以下做法有助于确保用户提示、代理响应和工具活动均可恢复。

### 1. 选择代理构造模式
<a name="langgraph-bp-construction"></a>

构建 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. `在图表状态下使用`消息（推荐）
<a name="langgraph-bp-messages"></a>

评估服务根据代理的输入和输出消息重构对话。使用`messages`字段不是强制性的，但它可以实现最可靠的提取。对于自定义`StateGraph`，请将对话保留在您所在州的某个`messages`字段中：
+  **包括`messages`在您所在的州（推荐）。**您可以添加其他自定义字段（例如`user_id`或元数据）。当`messages`存在时，标准提取会直接找到用户提示和代理响应。如果`messages`不存在，该服务将退回到从个人推理跨度中重建对话，这不太可靠。
+  **追加，不要替换。**遵循向列表添加新消息而不是覆盖列表的 LangGraph 惯例，这样可以保留完整的对话历史记录。
+  **使用规范 LangChain 消息类型 ** (`HumanMessage`、、`AIMessage``ToolMessage`、`SystemMessage`)。仪器可以正确地对它们进行序列化，并且服务可以识别它们的角色。

### 3. 以支持的格式传递用户消息
<a name="langgraph-bp-invocation"></a>

调用 LangGraph 代理时，将用户消息添加到图表的`messages`状态。 LangGraph 接受三种可互换格式的消息， AgentCore 评估支持所有格式。每个都生成服务可以读取的跨度和事件记录。
+  **元组**：一`(role, content)`对：

  ```
  agent.invoke({"messages": [("user", user_message)]}, config=config)
  ```
+  **LangChain 消息对象**：a`HumanMessage`（或其他消息类）：

  ```
  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`状态，因此无论您选择哪种格式，提取的用户提示和代理响应都是相同的。