公司动态
Microsoft Foundry 的 AI agent 可观测性:两个环境变量,无需采集器
作者来自 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 agentserver-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 tracingFoundry 使用 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.nameprovideropenai、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_interactioninvoke_agent 的子 span追踪 agent 之间的直接通信。agent_planning记录 agent 的内部规划步骤。agent.state.management覆盖上下文和 memory 操作。一个多步骤 Foundry agent 所产生的 trace 如下[invoke_agent: research-agent] ← 根整个任务 [agent_planning] ← agent 决定其处理方式 [chat: azure] ← 第一次 LLM 调用 [execute_tool: file_search] ← Foundry 内置工具调用 [chat: azure] ← 用于对结果进行推理的 LLM 调用 [agent_to_agent_interaction: summarizer] ← 交接给专家 agent [invoke_agent: summarizer] ← 嵌套的 agent 执行 [chat: azure] [chat: azure] ← 最终综合调用在 Elastic 中agent trace 会以 waterfall 的形式呈现。你可以看到每个步骤花费了多长时间、哪个步骤发生了错误以及 token 被消耗在哪里包括跨 agent 交接边界的 token。这就是我们要引入 Elastic 的数据模型。如何为 Foundry Hosted agent 添加插桩以生成 OTel tracingFoundry Hosted agent 会在由 Foundry 管理的容器中运行你的代码。因为容器由你控制所以你可以控制插桩。具体采用哪种方式取决于你使用的 framework。Framework语言关键 package插桩方法Agent FrameworkPythonazure-ai-projectsAIProjectInstrumentor().instrument()LangGraphPythonlangchain-azure-aiAzureAIOpenTelemetryTracercallbackCustom containerTypeScript / Node.jsopentelemetry/sdk-node手动startActiveSpan使用 Azure AI Projects SDKPython追踪 Agent Framework agentAgent Framework 是 Microsoft 用于在 Foundry 上构建 Hosted agent 的 framework。它通过 Foundry Responses API 进行模型推理和工具编排。要将 tracing 路由到 Elastic请在容器启动时配置 OTel SDK并添加指向 Elastic endpoint 的 OTLP exporter。安装这些 packagepip install azure-ai-projects azure-identity opentelemetry-sdk azure-core-tracing-opentelemetry opentelemetry-exporter-otlp-proto-http在 agent 容器启动时进行配置import os from azure.identity import DefaultAzureCredential from azure.ai.projects import AIProjectClient from azure.ai.projects.telemetry import AIProjectInstrumentor from opentelemetry import trace from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter # OTLPSpanExporter 会自动读取 OTEL_EXPORTER_OTLP_ENDPOINT 和 # OTEL_EXPORTER_OTLP_HEADERS因此无需显式传入 provider TracerProvider() provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter())) trace.set_tracer_provider(provider) # Tracing 默认关闭 —— 需要显式启用截至 2026 年仍处于实验性预览阶段 # 在容器环境中设置 AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACINGtrue AIProjectInstrumentor().instrument() client AIProjectClient( endpointos.getenv(AZURE_AI_FOUNDRY_PROJECT_ENDPOINT), credentialDefaultAzureCredential(), )现在通过 Responses API 发起的每次模型调用、工具调用和 agent 交接都会生成结构化的 OTel span其中包含 token 使用情况、模型身份和工具元数据。需要注意的是Azure AI Projects SDK 中的 GenAI tracing 目前处于实验性预览阶段 —— 你必须在容器环境中设置AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACINGtrue并在任何 agent 运行之前调用AIProjectInstrumentor().instrument()否则不会生成任何 span。使用 AzureAIOpenTelemetryTracerPython追踪 LangGraph agentLangGraph 是 Foundry Hosted agent 支持的 framework。Microsoft 的langchain-azure-aipackage 为 LangGraph 提供了符合 OTel 的 tracer可以生成 graph 步骤、工具调用和模型调用对应的 span。以相同方式配置 OTLP exporter然后在每次调用时将 tracer 作为 callback 附加pip install langchain-azure-ai langgraph langchain langchain-openai opentelemetry-sdk opentelemetry-exporter-otlp-proto-http azure-identityfrom langchain_azure_ai.callbacks.tracers import AzureAIOpenTelemetryTracer from opentelemetry import trace from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter # OTLPSpanExporter 会自动读取 OTEL_EXPORTER_OTLP_ENDPOINT 和 # OTEL_EXPORTER_OTLP_HEADERS因此无需显式传入 provider TracerProvider() provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter())) trace.set_tracer_provider(provider) # AzureAIOpenTelemetryTracer 是 Microsoft 为 LangChain 和 LangGraph # 提供的受支持 tracer。 # 在调用 graph 时将其作为 callback 传入 —— 它使用上面的 active TracerProvider。 azure_tracer AzureAIOpenTelemetryTracer(namemy-langgraph-agent) # app 是你编译后的 LangGraph workflow例如 workflow.compile() config {callbacks: [azure_tracer]} result app.invoke({messages: [...]}, configconfig)使用 OpenTelemetry SDKTypeScript追踪自定义 Node.js agent对于 Node.js 中的自定义 agent 架构可以直接使用 OTel SDK 包装你的编排逻辑。startActiveSpanAPI 会自动处理父子嵌套。在 callback 中创建的任何 span 都会成为当前 span 的子 span因此无需显式指定 parent references就可以获得决策树层级。import { NodeSDK } from opentelemetry/sdk-node; import { OTLPTraceExporter } from opentelemetry/exporter-trace-otlp-proto; import { trace, SpanKind, SpanStatusCode } from opentelemetry/api; // OTLPTraceExporter reads OTEL_EXPORTER_OTLP_ENDPOINT and // OTEL_EXPORTER_OTLP_HEADERS automatically, no need to pass them explicitly const sdk new NodeSDK({ traceExporter: new OTLPTraceExporter(), serviceName: my-foundry-agent, }); sdk.start(); const tracer trace.getTracer(foundry-agent, 1.0.0); // Wrap your agents entry point: everything inside becomes a child span async function runAgent(userMessage: string) { return tracer.startActiveSpan( invoke_agent my-agent, { kind: SpanKind.INTERNAL, attributes: { gen_ai.operation.name: invoke_agent, gen_ai.agent.name: my-agent, gen_ai.provider.name: azure, }, }, async (span) { try { const result await agentLoop(userMessage); span.setStatus({ code: SpanStatusCode.OK }); return result; } catch (err) { span.recordException(err as Error); span.setStatus({ code: SpanStatusCode.ERROR }); throw err; } finally { span.end(); } } ); } // Record token usage on every LLM call async function callAzureOpenAI(messages: Message[]) { return tracer.startActiveSpan( chat azure, { kind: SpanKind.CLIENT, attributes: { gen_ai.operation.name: chat, gen_ai.provider.name: azure, gen_ai.request.model: gpt-4o, }, }, async (span) { const response await client.chat.completions.create({ model: gpt-4o, messages }); span.setAttributes({ gen_ai.usage.input_tokens: response.usage.prompt_tokens, gen_ai.usage.output_tokens: response.usage.completion_tokens, gen_ai.response.model: response.model, }); span.end(); return response; } ); }如果你的 Node.js agent 直接调用 OpenAI SDKOpenTelemetry JS 还提供了 instrumentation-openai 自动插桩 package作为上述手动创建 span 的替代方案它也适用于 Azure OpenAI clients。在依赖它之前请检查其 semantic convention 版本是否与你在这里看到的版本一致LangChain 和其他框架也存在第三方 instrumentation但它们的更新程度各不相同。如何获取你的 Elastic 托管 OTLP endpoint 和 API key托管 OTLP endpointmOTLP在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 容器中设置两个环境变量export OTEL_EXPORTER_OTLP_ENDPOINThttps://your-motlp-endpoint export OTEL_EXPORTER_OTLP_HEADERSAuthorizationApiKey your-api-key注意 header 格式使用ApiKey key而不是 Bearer。而且环境变量中使用作为 header 名称和值之间的分隔符而不是:。发送到该 endpoint 的 tracing 默认会进入traces-generic.otel-defaultdata 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 中添加以下 annotationOpenTelemetry Operator 就会自动注入 SDKannotations: instrumentation.opentelemetry.io/inject-python: true你仍然需要设置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你需要将上下文放入消息本身from opentelemetry import propagate, context # 发送 agent将当前 active trace context 注入消息 carrier {} propagate.inject(carrier) await queue.send_message({ payload: task_data, trace_context: carrier, # {traceparent: 00-abc123..., tracestate: ...} }) # 接收 agent在执行任何工作之前恢复 trace context incoming_ctx propagate.extract(message[trace_context]) with context.use_context(incoming_ctx): await process_task(message[payload])完成这些配置后整个 work item 会在 Elastic 中显示为一条 trace从 orchestrator 经过每个 worker agent跨越不同的进程边界。waterfall 会准确显示每一层花费了多少时间。Foundry agent trace 在 Elastic APM 中是什么样的Trace 会在 APM UI 中以分层 waterfall 的形式呈现。对于每次 agent 调用你都会获得完整的 span tree每次 LLM 调用、工具执行和 sub-agent 调用都会嵌套在根invoke_agentspan 下。每个 chat span 都包含gen_ai.usage.input_tokens和gen_ai.usage.output_tokens因此你可以一眼看到哪些模型调用成本较高哪些属于常规调用。错误会显示为具体的失败 span并包含完整的 stack trace而不是延迟图上的一条红线。你还可以直接使用 ES|QL 查询 trace 数据。例如要查找过去一小时内所有消耗超过 20,000 个 input token 的 agent 运行FROM traces-generic.otel-default | WHERE attributes.gen_ai.operation.name invoke_agent AND timestamp NOW() - 1 hour | STATS total_input_tokens SUM(attributes.gen_ai.usage.input_tokens) BY trace.id, attributes.gen_ai.agent.name | WHERE total_input_tokens 20000 | SORT total_input_tokens DESC将 trace 结构与 token 统计结合到同一个查询中是 OTel 原生存储能够实现这一点的原因。如何对 agent trace 进行采样同时不丢失错误对于 tail-based sampling可以在 Elastic endpoint 之前添加一个本地 OTel Collector 作为中间跳转。一项合理的起始策略是保留所有错误 trace、所有慢 trace并降低常规成功 trace 的采样率# 在 Foundry Hosted agent 和 Elastic endpoint 之间运行此 Collector processors: tail_sampling: decision_wait: 10s policies: - name: errors type: status_code status_code: { status_codes: [ERROR] } - name: slow-traces type: latency latency: { threshold_ms: 5000 } - name: sample-routine type: probabilistic probabilistic: { sampling_percentage: 10 } exporters: otlp/elastic: endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT} headers: Authorization: ApiKey ${ELASTIC_API_KEY} sending_queue: enabled: true sizer: bytes queue_size: 50_000_000 block_on_overflow: true如果你直接从 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 使用AIProjectInstrumentorLangGraph 使用来自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