Microsoft Foundry 的 AI agent 可观测性:两个环境变量,无需采集器
英文标题:Microsoft Foundry 的 AI agent 可观测性:两个环境变量,无需采集器
作者:来自 Elastic Greg Crist 只需设置一次 LLM 追踪,你的 Foundry agent 的每次模型调用、工具执行和交接都会作为一条可查询的追踪记录到达 Kibana,每个跨度都
11.3 KB
15.0 KB
中文
作者:来自 Elastic Greg Crist 只需设置一次 LLM 追踪,你的 Foundry agent 的每次模型调用、工具执行和交接都会作为一条可查询的追踪记录到达 Kibana,每个跨度都
中文全文翻译正在补齐,可先查看下方英文原文。
English / 原文
作者:来自 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 如下:
`
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.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:
`pip install azure-ai-projects azure-identity opentelemetry-sdk azure-core-tracing-opentelemetry opentelemetry-exporter-otlp-proto-http`Lobster AI
在 agent 容器启动时进行配置:
`
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.
11.
13. provider = TracerProvider()
14. provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
15. trace.set_tracer_provider(provider)
17.
18.
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 附加:
`pip install langchain-azure-ai langgraph langchain langchain-openai opentelemetry-sdk opentelemetry-exporter-otlp-proto-http azure-identity`Lobster AI
`
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.
8.
10. provider = TracerProvider()
11. provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
12. trace.set_tracer_provider(provider)
14.
15.
16.
17. azure_tracer = AzureAIOpenTelemetryTracer()
19.
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,就可以获得决策树层级。
`
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.
6.
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.
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.
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。在依赖它之前,请检查其 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 容器中设置两个环境变量:
`
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:
`
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),你需要将上下文放入消息本身:
`
1. from opentelemetry import propagate, context
3.
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.
13. incoming_ctx = propagate.extract(message["trace_context"])
15. with context.use_context(incoming_ctx):
16. await process_task(message["payload"])
`Lobster AI - 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 的采样率:
`
1.
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