作者:来自 Elastic Greg Crist

只需设置一次 LLM 追踪,你的 Foundry agent 的每次模型调用、工具执行和交接都会作为一条可查询的追踪记录到达 Kibana,每个跨度都包含 token 数量,并提供适用于 Agent Framework、LangGraph 和 Node.js 的代码。
使用 OTel SDK 为你的 Microsoft Foundry agent 添加插桩,设置两个环境变量,这样每次 LLM 调用、工具执行和 agent 交接都会以可查询的追踪记录进入 Kibana,每个跨度都包含 token 数量。中间无需放置 OTel Collector,数据进入时也不会被重写为专有 schema。传统 APM 假设成功响应就是正确响应。但 AI agent 可观测性不能这样做,因为一次 agent 运行可能返回 HTTP 200,却仍然给出错误答案,并且可能消耗了 40,000 个 token 才得到这个结果,所以你需要的是完整的决策树。下面提供了适用于 Agent Framework、LangGraph 和自定义 Node.js 容器的代码。
为什么 AI agent 可观测性需要不同的模型?
标准应用监控回答的是:"这个请求成功了吗?数据库查询花了多长时间?"这些问题之所以有效,是因为传统软件具有确定性:相同的输入得到相同的输出,错误具有错误代码。
AI agent 打破了这种模型。一个发送给 Foundry 托管 agent 的用户请求,可能触发十次 LLM 调用、五次工具执行、两次文件搜索以及一次 MCP server 调用,每一次都有自己的延迟和潜在故障模式。agent 可以返回 HTTP 200,却仍然产生错误答案。它可能会在同一个 工具调用 上无声地循环,直到达到 token 限制。它可能将任务交接给专家 agent,并在传输过程中丢失上下文,而这些情况都不会出现在你的错误率或 P99 延迟中。
在生产环境中,你真正需要回答的问题有所不同:
-
为什么这次运行消耗了 40,000 个 token,而平均值只有 3,000?
-
哪个工具调用导致了长尾延迟?
-
当 orchestrator 将任务交接给搜索 agent 时,追踪上下文是否成功传递?
对于这些问题,你需要能够映射 agent 决策树的分层追踪数据,而不仅仅是 I/O 边界。
Foundry Agent Service 提供了强大的内置可观测性。对于 Prompt agent,server-side tracing 是零配置的:将 Application Insights 资源连接到你的项目,Foundry 就会自动捕获输入、输出、工具调用、token 使用情况和延迟,无需修改代码。对于 Hosted agent,你需要在容器代码中添加客户端插桩,这也是本文关注的内容。
Foundry 内置 tracing 无法提供的是:针对原始 OTel 数据运行任意查询,或者将 agent tracing 与整个技术栈中其他部分的基础设施遥测数据关联起来。这正是将相同的 tracing 路由到 Elastic 后所带来的价值。Foundry 门户支持的 Instrument → Debug → Evaluate → Optimize 循环,当你可以通过针对完整 trace 数据集运行 ES|QL 查询来驱动它时,会更加强大。
关于 Application Insights 的说明:它接收 OTel tracing,但会在数据摄取时将其转换为自己的 schema。Elastic 原样存储数据(resource attributes、semantic convention 字段,以及所有其他内容都会保留),因此你可以直接查询实际产生的数据,而无需在你和数据之间增加转换层。
使用 OpenTelemetry GenAI conventions 进行 LLM tracing
Foundry 使用 OpenTelemetry 的 GenAI semantic conventions 来构建其 tracing 数据结构。三种核心 span 类型覆盖了大多数 agent 工作负载。
稳定性说明:所有 gen_ai.* span 名称和属性目前在 OpenTelemetry registry 中都带有 Development stability badge;它们仍处于 1.0 之前的阶段,并且已经发生过一次变更(例如,gen_ai.system 被重命名为 gen_ai.provider.name)。请固定你的 SDK 版本,并预计这些属性字符串在这些 conventions 达到稳定状态之前还会发生变化。
invoke_agent 包装整个 agent 执行过程。每次运行都会以其中一个 span 作为根 span。
chat 表示一次单独的 LLM API 调用。它包含 gen_ai.provider.name(provider: openai 、anthropic、aws.bedrock)、gen_ai.request.model,以及每次调用中的 gen_ai.usage.input_tokens 和 gen_ai.usage.output_tokens,这正是实现单次调用 token 成本归因的关键。
execute_tool 表示由模型触发的工具或函数调用,并嵌套在发起该调用的 chat span 下。
对于多 agent 系统,Microsoft 与 Cisco Outshift 合作扩展了这些 conventions,增加了其他 span 类型,目前已经集成到 Foundry、Agent Framework、 LangChain 、LangGraph 和 OpenAI Agents SDK 中:
-
execute_task捕获任务规划,以及工作如何在不同 agent 之间进行拆分和分配。 -
agent_to_agent_interaction(invoke_agent 的子 span)追踪 agent 之间的直接通信。 -
agent_planning记录 agent 的内部规划步骤。 -
agent.state.management覆盖上下文和 memory 操作。
一个多步骤 Foundry agent 所产生的 trace 如下:
less
`
1. [invoke_agent: research-agent] ← 根:整个任务
3. [agent_planning] ← agent 决定其处理方式
5. [chat: azure] ← 第一次 LLM 调用
7. [execute_tool: file_search] ← Foundry 内置工具调用
9. [chat: azure] ← 用于对结果进行推理的 LLM 调用
11. [agent_to_agent_interaction: summarizer] ← 交接给专家 agent
13. [invoke_agent: summarizer] ← 嵌套的 agent 执行
15. [chat: azure]
17. [chat: azure] ← 最终综合调用
`Lobster AI
在 Elastic 中,agent trace 会以 waterfall 的形式呈现。你可以看到每个步骤花费了多长时间、哪个步骤发生了错误,以及 token 被消耗在哪里,包括跨 agent 交接边界的 token。这就是我们要引入 Elastic 的数据模型。
如何为 Foundry Hosted agent 添加插桩以生成 OTel tracing
Foundry Hosted agent 会在由 Foundry 管理的容器中运行你的代码。因为容器由你控制,所以你可以控制插桩。具体采用哪种方式取决于你使用的 framework。
| Framework | 语言 | 关键 package | 插桩方法 |
|---|---|---|---|
| Agent Framework | Python | azure-ai-projects |
AIProjectInstrumentor().instrument() |
| LangGraph | Python | langchain-azure-ai |
AzureAIOpenTelemetryTracer callback |
| Custom container | TypeScript / Node.js | @opentelemetry/sdk-node |
手动 startActiveSpan |
使用 Azure AI Projects SDK(Python)追踪 Agent Framework agent
Agent Framework 是 Microsoft 用于在 Foundry 上构建 Hosted agent 的 framework。它通过 Foundry Responses API 进行模型推理和工具编排。要将 tracing 路由到 Elastic,请在容器启动时配置 OTel SDK,并添加指向 Elastic endpoint 的 OTLP exporter。
安装这些 package:
go
`pip install azure-ai-projects azure-identity opentelemetry-sdk azure-core-tracing-opentelemetry opentelemetry-exporter-otlp-proto-http`Lobster AI
在 agent 容器启动时进行配置:
python
`
1. import os
2. from azure.identity import DefaultAzureCredential
3. from azure.ai.projects import AIProjectClient
4. from azure.ai.projects.telemetry import AIProjectInstrumentor
5. from opentelemetry import trace
6. from opentelemetry.sdk.trace import TracerProvider
7. from opentelemetry.sdk.trace.export import BatchSpanProcessor
8. from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
10. # OTLPSpanExporter 会自动读取 OTEL_EXPORTER_OTLP_ENDPOINT 和
11. # OTEL_EXPORTER_OTLP_HEADERS,因此无需显式传入
13. provider = TracerProvider()
14. provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
15. trace.set_tracer_provider(provider)
17. # Tracing 默认关闭 ------ 需要显式启用(截至 2026 年仍处于实验性预览阶段)
18. # 在容器环境中设置 AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING=true
19. AIProjectInstrumentor().instrument()
21. client = AIProjectClient(
22. endpoint=os.getenv("AZURE_AI_FOUNDRY_PROJECT_ENDPOINT"),
23. credential=DefaultAzureCredential(),
24. )
`Lobster AI
现在,通过 Responses API 发起的每次模型调用、工具调用和 agent 交接都会生成结构化的 OTel span,其中包含 token 使用情况、模型身份和工具元数据。需要注意的是,Azure AI Projects SDK 中的 GenAI tracing 目前处于实验性预览阶段 ------ 你必须在容器环境中设置 AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING=true,并在任何 agent 运行之前调用 AIProjectInstrumentor().instrument(),否则不会生成任何 span。
使用 AzureAIOpenTelemetryTracer(Python)追踪 LangGraph agent
LangGraph 是 Foundry Hosted agent 支持的 framework。Microsoft 的 langchain-azure-ai package 为 LangGraph 提供了符合 OTel 的 tracer,可以生成 graph 步骤、工具调用和模型调用对应的 span。以相同方式配置 OTLP exporter,然后在每次调用时将 tracer 作为 callback 附加:
go
`pip install langchain-azure-ai langgraph langchain langchain-openai opentelemetry-sdk opentelemetry-exporter-otlp-proto-http azure-identity`Lobster AI
python
`
1. from langchain_azure_ai.callbacks.tracers import AzureAIOpenTelemetryTracer
2. from opentelemetry import trace
3. from opentelemetry.sdk.trace import TracerProvider
4. from opentelemetry.sdk.trace.export import BatchSpanProcessor
5. from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
7. # OTLPSpanExporter 会自动读取 OTEL_EXPORTER_OTLP_ENDPOINT 和
8. # OTEL_EXPORTER_OTLP_HEADERS,因此无需显式传入
10. provider = TracerProvider()
11. provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
12. trace.set_tracer_provider(provider)
14. # AzureAIOpenTelemetryTracer 是 Microsoft 为 LangChain 和 LangGraph
15. # 提供的受支持 tracer。
16. # 在调用 graph 时将其作为 callback 传入 ------ 它使用上面的 active TracerProvider。
17. azure_tracer = AzureAIOpenTelemetryTracer()
19. # app 是你编译后的 LangGraph workflow(例如 workflow.compile())
20. config = {"callbacks": [azure_tracer]}
21. result = app.invoke({"messages": [...]}, config=config)
`Lobster AI
使用 OpenTelemetry SDK(TypeScript)追踪自定义 Node.js agent
对于 Node.js 中的自定义 agent 架构,可以直接使用 OTel SDK 包装你的编排逻辑。startActiveSpan API 会自动处理父子嵌套。在 callback 中创建的任何 span 都会成为当前 span 的子 span,因此无需显式指定 parent references,就可以获得决策树层级。
php
`
1. import { NodeSDK } from "@opentelemetry/sdk-node";
2. import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
3. import { trace, SpanKind, SpanStatusCode } from "@opentelemetry/api";
5. // OTLPTraceExporter reads OTEL_EXPORTER_OTLP_ENDPOINT and
6. // OTEL_EXPORTER_OTLP_HEADERS automatically, no need to pass them explicitly
7. const sdk = new NodeSDK({
8. traceExporter: new OTLPTraceExporter(),
9. serviceName: "my-foundry-agent",
10. });
11. sdk.start();
13. const tracer = trace.getTracer("foundry-agent", "1.0.0");
15. // Wrap your agent's entry point: everything inside becomes a child span
16. async function runAgent(userMessage: string) {
17. return tracer.startActiveSpan(
18. "invoke_agent my-agent",
19. {
20. kind: SpanKind.INTERNAL,
21. attributes: {
22. "gen_ai.operation.name": "invoke_agent",
23. "gen_ai.agent.name": "my-agent",
24. "gen_ai.provider.name": "azure",
25. },
26. },
27. async (span) => {
28. try {
29. const result = await agentLoop(userMessage);
30. span.setStatus({ code: SpanStatusCode.OK });
31. return result;
32. } catch (err) {
33. span.recordException(err as Error);
34. span.setStatus({ code: SpanStatusCode.ERROR });
35. throw err;
36. } finally {
37. span.end();
38. }
39. }
40. );
41. }
43. // Record token usage on every LLM call
44. async function callAzureOpenAI(messages: Message[]) {
45. return tracer.startActiveSpan(
46. "chat azure",
47. {
48. kind: SpanKind.CLIENT,
49. attributes: {
50. "gen_ai.operation.name": "chat",
51. "gen_ai.provider.name": "azure",
52. "gen_ai.request.model": "gpt-4o",
53. },
54. },
55. async (span) => {
56. const response = await client.chat.completions.create({ model: "gpt-4o", messages });
57. span.setAttributes({
58. "gen_ai.usage.input_tokens": response.usage.prompt_tokens,
59. "gen_ai.usage.output_tokens": response.usage.completion_tokens,
60. "gen_ai.response.model": response.model,
61. });
62. span.end();
63. return response;
64. }
65. );
66. }
`Lobster AI收起代码块
如果你的 Node.js agent 直接调用 OpenAI SDK,OpenTelemetry JS 还提供了 instrumentation-openai 自动插桩 package,作为上述手动创建 span 的替代方案(它也适用于 Azure OpenAI clients)。在依赖它之前,请检查其 semantic convention 版本是否与你在这里看到的版本一致;LangChain 和其他框架也存在第三方 instrumentation,但它们的更新程度各不相同。
如何获取你的 Elastic 托管 OTLP endpoint 和 API key
托管 OTLP endpoint(mOTLP)在 Elastic Cloud Serverless 和 Elastic Cloud Hosted 上通常都已正式可用。它不适用于 self-managed、ECE 或 ECK 部署。对于这些部署,请改用 EDOT Collector 作为 gateway。
Serverless: 登录 Elastic Cloud → 找到你的 project → Manage → Application endpoints, cluster and component IDs → Ingest 。复制 endpoint 值。或者,在你的 project 中进入 Add data → Applications → OpenTelemetry,这里也会为你生成预配置的 API key。
Elastic Cloud Hosted: 登录 → Hosted deployments → Manage → Application endpoints → Managed OTLP。复制 public endpoint 值。
API key 必须在 APM application 上具有 event:write 权限。Elastic Cloud quickstart 向导会为你生成此权限。然后在 Foundry Hosted agent 容器中设置两个环境变量:
ini
`
1. export OTEL_EXPORTER_OTLP_ENDPOINT="https://<your-motlp-endpoint>"
2. export OTEL_EXPORTER_OTLP_HEADERS="Authorization=ApiKey <your-api-key>"
`Lobster AI
注意 header 格式:使用 ApiKey <key>,而不是 Bearer。而且环境变量中使用 = 作为 header 名称和值之间的分隔符,而不是 :。
发送到该 endpoint 的 tracing 默认会进入 traces-generic.otel-default data stream。在 Kibana 中,可以通过 Observability → APM → Traces 找到它们,也可以直接使用 ES|QL 针对 traces-generic.otel-* 进行查询。无需 OTel Collector,也无需 schema 转换。
使用 OpenTelemetry Operator 在 AKS 上自动为 Foundry agent 添加插桩
Foundry Hosted agent 支持自带 VNet,并且可以在 AKS 上运行容器工作负载。如果你采用这种配置,可以完全跳过 SDK 级别的 OTLP 配置。在 pod spec 中添加以下 annotation,OpenTelemetry Operator 就会自动注入 SDK:
bash
`
1. annotations:
2. instrumentation.opentelemetry.io/inject-python: "true"
`Lobster AI
你仍然需要设置 OTEL_EXPORTER_OTLP_ENDPOINT 和 OTEL_EXPORTER_OTLP_HEADERS,让它们指向 Elastic。SDK 的连接配置由 Operator 为你处理。
对于运行在 Azure Container Apps 中的 agent,平台内置的托管 OTel Collector 默认会将 tracing 路由到 Application Insights。如果还希望将 tracing 发送到 Elastic,可以结合使用上面示例中的 SDK 级配置和 Foundry 的内置可观测性,或者运行一个配置了指向 Elastic endpoint 的 OTLP exporter 的 sidecar OTel Collector。
如何让多 agent 交接保持在同一条 trace 中
如果你的架构涉及 orchestrator 将任务委派给专家 agent,你希望所有这些操作显示为一条连接起来的 trace,而不是 N 个互不关联的片段。
OTel 通过 W3C TraceContext 标准(traceparent 和 tracestate header)来处理这一问题。对于基于 HTTP 的 agent 间调用,SDK 会自动传播这些上下文。对于基于 queue 的交接(Service Bus、Event Hubs),你需要将上下文放入消息本身:
php
`
1. from opentelemetry import propagate, context
3. # 发送 agent:将当前 active trace context 注入消息
4. carrier = {}
5. propagate.inject(carrier)
7. await queue.send_message({
8. "payload": task_data,
9. "trace_context": carrier, # {"traceparent": "00-abc123...", "tracestate": "..."}
10. })
12. # 接收 agent:在执行任何工作之前恢复 trace context
13. incoming_ctx = propagate.extract(message["trace_context"])
15. with context.use_context(incoming_ctx):
16. await process_task(message["payload"])
`Lobster AI
完成这些配置后,整个 work item 会在 Elastic 中显示为一条 trace,从 orchestrator 经过每个 worker agent,跨越不同的进程边界。waterfall 会准确显示每一层花费了多少时间。
Foundry agent trace 在 Elastic APM 中是什么样的
Trace 会在 APM UI 中以分层 waterfall 的形式呈现。对于每次 agent 调用,你都会获得完整的 span tree:每次 LLM 调用、工具执行和 sub-agent 调用都会嵌套在根 invoke_agent span 下。每个 chat span 都包含 gen_ai.usage.input_tokens 和 gen_ai.usage.output_tokens,因此你可以一眼看到哪些模型调用成本较高,哪些属于常规调用。错误会显示为具体的失败 span,并包含完整的 stack trace,而不是延迟图上的一条红线。
你还可以直接使用 ES|QL 查询 trace 数据。例如,要查找过去一小时内所有消耗超过 20,000 个 input token 的 agent 运行:
sql
`
1. FROM traces-generic.otel-default
2. | WHERE attributes.gen_ai.operation.name == "invoke_agent"
3. AND @timestamp > NOW() - 1 hour
4. | STATS total_input_tokens = SUM(attributes.gen_ai.usage.input_tokens)
5. BY trace.id, attributes.gen_ai.agent.name
6. | WHERE total_input_tokens > 20000
7. | SORT total_input_tokens DESC
`Lobster AI
将 trace 结构与 token 统计结合到同一个查询中,是 OTel 原生存储能够实现这一点的原因。
如何对 agent trace 进行采样,同时不丢失错误
对于 tail-based sampling,可以在 Elastic endpoint 之前添加一个本地 OTel Collector 作为中间跳转。一项合理的起始策略是保留所有错误 trace、所有慢 trace,并降低常规成功 trace 的采样率:
yaml
`
1. # 在 Foundry Hosted agent 和 Elastic endpoint 之间运行此 Collector
2. processors:
3. tail_sampling:
4. decision_wait: 10s
5. policies:
6. - name: errors
7. type: status_code
8. status_code: { status_codes: [ERROR] }
9. - name: slow-traces
10. type: latency
11. latency: { threshold_ms: 5000 }
12. - name: sample-routine
13. type: probabilistic
14. probabilistic: { sampling_percentage: 10 }
16. exporters:
17. otlp/elastic:
18. endpoint: "${OTEL_EXPORTER_OTLP_ENDPOINT}"
19. headers:
20. Authorization: "ApiKey ${ELASTIC_API_KEY}"
21. sending_queue:
22. enabled: true
23. sizer: bytes
24. queue_size: 50_000_000
25. block_on_overflow: true
`Lobster AI
如果你直接从 SDK 发送而不使用 Collector,可以从 100% sampling 开始。Agent trace 的数据量通常比预期小。运行一周后评估存储成本,然后再从那里开始调整。对于大多数 Foundry Hosted agent 工作负载,直接使用 SDK 是合适的起点。
如何使用 agent trace 进行评估和优化
Build session 演示将 tracing 描述为一个四步生产循环:插桩、调试、评估、优化。调试是最直接的收益:当 agent 运行失败或产生错误答案时,trace 会准确显示是哪个 span 引入了问题。但评估和优化步骤才是这个循环能够不断产生累积价值的地方。
当 tracing 流入 Elastic 后,你可以识别出哪些 agent 运行产生了错误或低质量的输出,然后直接将这些 trace 提取到 Foundry 中的评估工作流。Trace 提供了评估所需的完整上下文(prompt、工具调用、模型的推理路径),从而可以评估质量并发现回归。从这里开始,优化目标就非常具体:这个工具调用很慢,这个模型相对于其输出质量来说成本太高,这个规划步骤在每个请求中都不必要地运行。
Foundry 的 agent optimizer 可以使用 trace 数据自动改进 agent instructions。Elastic 则提供查询层,让你首先找到值得优化的 trace。
开始追踪 Foundry agent 所需要的条件
你需要四样东西:一个 Elastic Cloud Serverless project(其中包含托管 OTLP endpoint)、从 Project Management → Edit alias 获取的 endpoint URL 和 API key、与你的技术栈匹配的插桩(Agent Framework 使用 AIProjectInstrumentor,LangGraph 使用来自 langchain-azure-ai 的 AzureAIOpenTelemetryTracer,或者自定义容器代码直接使用 OTel SDK),以及运行一次 agent 来验证 trace 是否出现在 Kibana 的 APM 视图中。
当第一条 trace 准确告诉你究竟是哪个工具调用导致了那次 45 秒的超时时,你就会发现这些配置是值得的。
更多资源: Microsoft Foundry Agent Service 概览 | 在 Foundry 中设置 tracing | 各 framework 的 tracing 集成 | Elastic 托管 OTLP endpoint 文档 | OpenTelemetry GenAI semantic conventions | Build 2026 DEM341:任何 agent,任何 cloud
原文:AI agent observability for Microsoft Foundry in Elastic | Elastic Observability Labs