news 2026/4/16 13:56:29

当 AI Agent 遇上可观测性:AgentOpenTelemetry 让你的智能体不再“黑盒“

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
当 AI Agent 遇上可观测性:AgentOpenTelemetry 让你的智能体不再“黑盒“

"你的 AI Agent 到底在干什么?为什么响应这么慢?Token 都花哪儿了?" —— 每个 AI 开发者的灵魂三问

引子:智能体的"透明化"革命

想象一下,你精心打造的 AI Agent 在生产环境中突然变得迟钝,用户抱怨连连。你打开日志,却只看到一堆"Agent started"、"Agent finished"这样的流水账。至于中间发生了什么?调用了哪些模型?花了多少 Token?耗时分布如何?统统一无所知。

这就像开着一辆没有仪表盘的汽车在高速公路上狂奔——你知道车在动,但不知道速度多少、油还剩多少、发动机温度如何。这种"盲驾"的感觉,相信每个做过 AI 应用的朋友都深有体会。

好消息是,微软的 Agent Framework 团队显然也意识到了这个痛点。他们推出的AgentOpenTelemetry解决方案,就像给你的智能体装上了一套完整的"仪表盘系统",让每一次调用、每一个决策、每一分钱的花费都清清楚楚、一目了然。

今天,咱们就来深入剖析这套系统,看看它是如何让 AI Agent 从"黑盒"变成"玻璃盒"的。

一、为什么 AI Agent 需要可观测性?

1.1 传统监控的困境

在传统的 Web 应用中,我们习惯了用日志、指标、链路追踪这"可观测性三板斧"来监控系统。但 AI Agent 的世界完全不同:

  • 非确定性:同样的输入可能产生不同的输出

  • 多步骤编排:一次对话可能触发多个工具调用、多轮推理

  • 成本敏感:每次调用都在烧钱(Token 费用)

  • 性能波动:模型响应时间受多种因素影响

传统的console.log或简单的日志记录,在这种复杂场景下显得力不从心。你需要的是:

  • 完整的调用链路:从用户输入到最终响应,中间经历了什么?

  • 细粒度的性能指标:哪个环节最慢?瓶颈在哪里?

  • Token 使用统计:输入输出各用了多少 Token?成本如何优化?

  • 上下文关联:多轮对话如何关联?分布式场景下如何追踪?

1.2 OpenTelemetry:可观测性的"世界语"

OpenTelemetry(简称 OTel)是 CNCF 旗下的可观测性标准,它统一了 Traces(链路追踪)、Metrics(指标)、Logs(日志)三大支柱。

把它比作"世界语"再合适不过——无论你用的是 Jaeger、Prometheus、Grafana 还是 Azure Monitor,只要遵循 OTel 标准,数据就能无缝流转。这意味着:

  • 厂商中立:不被某个监控平台绑架

  • 生态丰富:海量的工具和集成方案

  • 标准化:团队协作更顺畅,学习成本更低

AgentOpenTelemetry正是将这套标准引入 AI Agent 领域的先行者。

二、AgentOpenTelemetry 的核心设计哲学

2.1 装饰器模式:优雅的"无侵入"设计

翻开OpenTelemetryAgent.cs的源码,你会发现一个精妙的设计:

public sealed class OpenTelemetryAgent : DelegatingAIAgent, IDisposable { private readonly OpenTelemetryChatClient _otelClient; private readonly string? _providerName; public OpenTelemetryAgent(AIAgent innerAgent, string? sourceName = null) : base(innerAgent) { this._providerName = innerAgent.GetService<AIAgentMetadata>()?.ProviderName; this._otelClient = new OpenTelemetryChatClient( new ForwardingChatClient(this), sourceName: sourceName ?? OpenTelemetryConsts.DefaultSourceName); } }

这是一个教科书级的装饰器模式应用。OpenTelemetryAgent并不改变原有 Agent 的行为,而是像一层"透明薄膜"一样包裹在外面,默默记录一切。

这种设计的好处显而易见:

  • 零侵入:不需要修改现有 Agent 代码

  • 可插拔:想要监控就加上,不想要就去掉

  • 可组合:可以和其他中间件(如缓存、重试)自由组合

用起来也极其简单:

var agent = new ChatClientAgent(chatClient, name: "MyAgent") .AsBuilder() .UseOpenTelemetry(sourceName: "MyApp") // 就这一行! .Build();

2.2 双层遥测:Agent 层 + ChatClient 层

这里有个巧妙的设计细节。OpenTelemetryAgent内部复用了Microsoft.Extensions.AIOpenTelemetryChatClient,形成了双层遥测架构:

用户请求 ↓ OpenTelemetryAgent (invoke_agent span) ↓ OpenTelemetryChatClient (chat span) ↓ 实际的 AI 模型调用

为什么要这样设计?因为:

  1. 复用成熟实现OpenTelemetryChatClient已经完整实现了 OpenTelemetry 的 Generative AI 语义约定(Semantic Conventions),无需重复造轮。

  2. 分层清晰:Agent 层关注业务逻辑(工具调用、多轮对话),ChatClient 层关注模型交互(Token 统计、响应时间)。

  3. 灵活组合:你可以只在 ChatClient 层加监控,也可以在 Agent 层加,甚至两层都加。

看看UpdateCurrentActivity方法,它在 ChatClient 创建的 Activity 基础上,添加了 Agent 特有的标签:

private void UpdateCurrentActivity(Activity? previousActivity) { if (Activity.Current is not { } activity || ReferenceEquals(activity, previousActivity)) { return; } // 修改操作名称 activity.DisplayName = $"{OpenTelemetryConsts.GenAI.InvokeAgent} {this.DisplayName}"; activity.SetTag(OpenTelemetryConsts.GenAI.Operation.Name, OpenTelemetryConsts.GenAI.InvokeAgent); // 添加 Agent 特有标签 activity.SetTag(OpenTelemetryConsts.GenAI.Agent.Id, this.Id); activity.SetTag(OpenTelemetryConsts.GenAI.Agent.Name, this.Name); activity.SetTag(OpenTelemetryConsts.GenAI.Agent.Description, this.Description); }

这种"先继承再增强"的策略,既保证了标准合规,又体现了 Agent 的特殊性。

2.3 敏感数据保护:默认安全的设计

在 AI 应用中,用户输入和模型输出往往包含敏感信息。OpenTelemetryAgent对此有清晰的处理策略:

public bool EnableSensitiveData { get => this._otelClient.EnableSensitiveData; set => this._otelClient.EnableSensitiveData = value; }

默认情况下EnableSensitiveData = false,此时:

  • ✅ 记录:Token 数量、响应时间、模型名称、错误信息

  • ❌ 不记录:消息内容、函数参数、函数返回值

只有当你明确设置EnableSensitiveData = true或设置环境变量OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true时,才会记录完整内容。

这种"默认安全"的设计,让你在开发环境可以看到所有细节,在生产环境则自动脱敏,避免了数据泄露风险。

三、实战演练:从零搭建可观测的 AI Agent

理论讲完了,咱们来点实际的。看看如何用 AgentOpenTelemetry 搭建一个完整的监控体系。

3.1 环境准备:三件套

要运行示例,你需要准备:

  1. Azure OpenAI 服务(或兼容的 OpenAI API)

  2. Docker(用于运行 Aspire Dashboard)

  3. .NET 10 SDK

配置环境变量:

$env:AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/" $env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini"

3.2 启动 Aspire Dashboard:你的遥测驾驶舱

Aspire Dashboard 是微软推出的轻量级可观测性面板,专为 .NET 应用设计。启动它只需一行命令:

docker run -d --name aspire-dashboard \ -p 4318:18888 \ -p 4317:18889 \ -e DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS=true \ mcr.microsoft.com/dotnet/aspire-dashboard:latest

打开浏览器访问http://localhost:4318,你会看到一个清爽的界面,包含:

  • Traces:链路追踪视图

  • Metrics:指标图表

  • Structured Logs:结构化日志

这就是你的"遥测驾驶舱",接下来所有的 Agent 活动都会在这里实时呈现。

3.3 配置 OpenTelemetry:三大支柱一个都不能少

示例代码中的 OpenTelemetry 配置堪称教科书级别,咱们逐一拆解:

3.3.1 Traces(链路追踪)
var tracerProviderBuilder = Sdk.CreateTracerProviderBuilder() .SetResourceBuilder(ResourceBuilder.CreateDefault() .AddService(ServiceName, serviceVersion: "1.0.0")) .AddSource(SourceName) // 自定义 Activity Source .AddSource("*Microsoft.Agents.AI") // Agent Framework 遥测 .AddHttpClientInstrumentation() // 捕获 HTTP 调用 .AddOtlpExporter(options => options.Endpoint = new Uri(otlpEndpoint));

关键点:

  • AddSource("*Microsoft.Agents.AI"):通配符匹配,捕获所有 Agent Framework 的内部 Span

  • AddHttpClientInstrumentation():自动追踪对 OpenAI API 的 HTTP 调用

  • AddOtlpExporter:使用 OTLP 协议导出数据,兼容各种后端

3.3.2 Metrics(指标)
var meterProvider = Sdk.CreateMeterProviderBuilder() .SetResourceBuilder(ResourceBuilder.CreateDefault() .AddService(ServiceName, serviceVersion: "1.0.0")) .AddMeter(SourceName) .AddMeter("*Microsoft.Agents.AI") .AddHttpClientInstrumentation() .AddRuntimeInstrumentation() // .NET 运行时指标 .AddOtlpExporter(options => options.Endpoint = new Uri(otlpEndpoint)) .Build();

这里额外加了AddRuntimeInstrumentation(),可以监控:

  • GC 回收次数和耗时

  • 线程池使用情况

  • 异常抛出频率

对于诊断性能问题非常有用。

3.3.3 Logs(结构化日志)
serviceCollection.AddLogging(loggingBuilder => loggingBuilder .SetMinimumLevel(LogLevel.Debug) .AddOpenTelemetry(options => { options.SetResourceBuilder(ResourceBuilder.CreateDefault() .AddService(ServiceName, serviceVersion: "1.0.0")); options.AddOtlpExporter(otlpOptions => otlpOptions.Endpoint = new Uri(otlpEndpoint)); options.IncludeScopes = true; // 包含日志作用域 options.IncludeFormattedMessage = true; }));

IncludeScopes = true是个亮点,它能让你用BeginScope为一组日志添加上下文:

using (logger.BeginScope(new Dictionary<string, object> { ["SessionId"] = sessionId, ["AgentName"] = "MyAgent" })) { // 这个作用域内的所有日志都会自动带上 SessionId 和 AgentName logger.LogInformation("Processing request..."); }

这在多租户、多会话场景下特别好用。

3.4 创建可观测的 Agent:两层防护

示例中展示了一个有趣的"双保险"策略:

// 第一层:在 ChatClient 层启用遥测 var instrumentedChatClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()) .GetChatClient(deploymentName) .AsIChatClient() .AsBuilder() .UseFunctionInvocation() .UseOpenTelemetry(sourceName: SourceName, configure: (cfg) => cfg.EnableSensitiveData = true) .Build(); // 第二层:在 Agent 层再次启用遥测 var agent = new ChatClientAgent(instrumentedChatClient, name: "OpenTelemetryDemoAgent", instructions: "You are a helpful assistant...", tools: [AIFunctionFactory.Create(GetWeatherAsync)]) .AsBuilder() .UseOpenTelemetry(SourceName, configure: (cfg) => cfg.EnableSensitiveData = true) .Build();

为什么要两层都加?

  • ChatClient 层:捕获底层模型交互细节(Token 统计、模型参数)

  • Agent 层:捕获高层业务逻辑(工具调用、多轮对话)

两层结合,形成完整的调用链路。在 Aspire Dashboard 中,你会看到嵌套的 Span 结构:

Agent Session (父 Span) └─ Agent Interaction #1 └─ invoke_agent OpenTelemetryDemoAgent └─ chat gpt-4o-mini └─ HTTP POST https://xxx.openai.azure.com/...

3.5 自定义指标:业务监控的利器

除了框架自动收集的指标,你还可以定义业务相关的指标:

var meter = new Meter(SourceName); var interactionCounter = meter.CreateCounter<int>( "agent_interactions_total", description: "Total number of agent interactions"); var responseTimeHistogram = meter.CreateHistogram<double>( "agent_response_time_seconds", description: "Agent response time in seconds");

在实际调用中记录:

var stopwatch = Stopwatch.StartNew(); try { await foreach (var update in agent.RunStreamingAsync(userInput, thread)) { Console.Write(update.Text); } stopwatch.Stop(); // 记录成功指标 interactionCounter.Add(1, new KeyValuePair<string, object?>("status", "success")); responseTimeHistogram.Record(stopwatch.Elapsed.TotalSeconds, new KeyValuePair<string, object?>("status", "success")); } catch (Exception ex) { stopwatch.Stop(); // 记录失败指标 interactionCounter.Add(1, new KeyValuePair<string, object?>("status", "error")); responseTimeHistogram.Record(stopwatch.Elapsed.TotalSeconds, new KeyValuePair<string, object?>("status", "error")); }

这样你就能在 Aspire Dashboard 的 Metrics 页面看到:

  • 成功/失败的交互次数

  • 响应时间的分布(P50、P95、P99)

  • 按状态分组的趋势图

3.6 会话级追踪:串联多轮对话

在实际应用中,一个用户会话往往包含多轮对话。如何把它们关联起来?示例给出了优雅的方案:

// 创建会话级 Activity using var sessionActivity = activitySource.StartActivity("Agent Session"); var sessionId = Guid.NewGuid().ToString("N"); sessionActivity? .SetTag("agent.name", "OpenTelemetryDemoAgent") .SetTag("session.id", sessionId) .SetTag("session.start_time", DateTimeOffset.UtcNow.ToString("O")); // 使用日志作用域关联所有日志 using (logger.BeginScope(new Dictionary<string, object> { ["SessionId"] = sessionId, ["AgentName"] = "OpenTelemetryDemoAgent" })) { var interactionCount = 0; while (true) { // 每次交互创建子 Activity using var activity = activitySource.StartActivity("Agent Interaction"); activity? .SetTag("user.input", userInput) .SetTag("interaction.number", ++interactionCount); // 执行 Agent 调用... } // 会话结束时记录总结信息 sessionActivity?.SetTag("session.total_interactions", interactionCount); }

这样在 Aspire Dashboard 中,你可以:

  1. 通过session.id过滤出某个会话的所有 Trace

  2. 看到会话的完整时间线

  3. 分析每轮交互的耗时和 Token 消耗

四、深入原理:OpenTelemetry Semantic Conventions for GenAI

4.1 什么是语义约定?

OpenTelemetry 的语义约定(Semantic Conventions)定义了一套标准化的标签(Tag)命名规范。对于 Generative AI 系统,它规定了:

  • 操作名称gen_ai.operation.name(如chatinvoke_agent

  • 模型信息gen_ai.request.modelgen_ai.provider.name

  • Token 统计gen_ai.usage.input_tokensgen_ai.usage.output_tokens

  • 消息内容gen_ai.input.messagesgen_ai.output.messages

这套标准目前还在实验阶段(v1.37),但已经被主流工具支持。

4.2 Agent 特有的标签

OpenTelemetryAgent在标准基础上,增加了 Agent 特有的标签:

public static class OpenTelemetryConsts { public static class GenAI { public const string InvokeAgent = "invoke_agent"; public static class Agent { public const string Id = "gen_ai.agent.id"; public const string Name = "gen_ai.agent.name"; public const string Description = "gen_ai.agent.description"; } } }

这些标签让你能够:

  • 区分不同的 Agent 实例

  • 按 Agent 名称聚合指标

  • 追踪 Agent 的配置变更

4.3 从单元测试看标签的完整性

项目的单元测试OpenTelemetryAgentTests.cs是学习的宝库。看看它验证了哪些标签:

// 基础标签 Assert.Equal("invoke_agent TestAgent", activity.DisplayName); Assert.Equal("invoke_agent", activity.GetTagItem("gen_ai.operation.name")); Assert.Equal("TestAgentProviderFromAIAgentMetadata", activity.GetTagItem("gen_ai.provider.name")); // Agent 特有标签 Assert.Equal(innerAgent.Name, activity.GetTagItem("gen_ai.agent.name")); Assert.Equal(innerAgent.Id, activity.GetTagItem("gen_ai.agent.id")); Assert.Equal(innerAgent.Description, activity.GetTagItem("gen_ai.agent.description")); // 模型和服务器信息 Assert.Equal("amazingmodel", activity.GetTagItem("gen_ai.request.model")); Assert.Equal("localhost", activity.GetTagItem("server.address")); Assert.Equal(12345, activity.GetTagItem("server.port")); // Token 使用统计 Assert.Equal(10, activity.GetTagItem("gen_ai.usage.input_tokens")); Assert.Equal(20, activity.GetTagItem("gen_ai.usage.output_tokens")); // 响应信息 Assert.Equal("id123", activity.GetTagItem("gen_ai.response.id"));

EnableSensitiveData = true时,还会记录:

// 输入消息(JSON 格式) activity.GetTagItem("gen_ai.input.messages") // 输出消息(JSON 格式) activity.GetTagItem("gen_ai.output.messages") // 系统指令 activity.GetTagItem("gen_ai.system_instructions") // 工具定义 activity.GetTagItem("gen_ai.tool.definitions")

这些标签的完整性,保证了你能从多个维度分析 Agent 的行为。

五、生产环境实践:从开发到运维的全链路

5.1 开发环境:Aspire Dashboard 快速反馈

在开发阶段,Aspire Dashboard 是最佳选择:

  • 启动快:一行 Docker 命令搞定

  • 界面友好:实时刷新,无需配置

  • 零成本:完全免费,本地运行

典型工作流:

  1. 启动 Aspire Dashboard

  2. 运行 Agent 应用

  3. 发送测试请求

  4. 在 Dashboard 中查看 Trace,定位问题

  5. 修改代码,重复测试

5.2 生产环境:Azure Monitor + Application Insights

示例代码已经预留了 Application Insights 的集成:

var applicationInsightsConnectionString = Environment.GetEnvironmentVariable("APPLICATIONINSIGHTS_CONNECTION_STRING"); if (!string.IsNullOrWhiteSpace(applicationInsightsConnectionString)) { tracerProviderBuilder.AddAzureMonitorTraceExporter(options => options.ConnectionString = applicationInsightsConnectionString); }

只需设置环境变量,遥测数据就会自动发送到 Azure Monitor。你可以:

  • 使用Application Insights查看实时遥测

  • Kusto 查询语言(KQL)做复杂分析

  • 设置告警规则(如响应时间超过阈值)

  • 创建仪表板展示关键指标

5.3 Grafana 可视化:专为 Agent 定制的仪表板

微软还提供了两个开箱即用的 Grafana 仪表板:

Agent Overview Dashboard
  • URL:https://aka.ms/amg/dash/af-agent

  • 内容
    • Agent 调用次数和成功率

    • 平均响应时间和 P95/P99 延迟

    • Token 使用趋势

    • 错误率和异常分布

Workflow Overview Dashboard
  • URL:https://aka.ms/amg/dash/af-workflow

  • 内容
    • 工作流执行状态

    • 多 Agent 协作的调用链

    • 步骤级性能分析

    • 资源消耗统计

这两个仪表板直接连接 Application Insights 数据源,导入即用,省去了从零配置的麻烦。

5.4 成本优化:基于遥测数据的智能决策

有了完整的遥测数据,你可以做很多成本优化:

5.4.1 识别低效提示词

通过分析gen_ai.usage.input_tokens,找出那些输入 Token 特别多的请求:

traces | where customDimensions.["gen_ai.operation.name"] == "invoke_agent" | extend inputTokens = toint(customDimensions.["gen_ai.usage.input_tokens"]) | where inputTokens > 1000 | summarize count(), avg(inputTokens) by tostring(customDimensions.["gen_ai.agent.name"])

如果某个 Agent 的平均输入 Token 过高,可能是:

  • 系统提示词(System Prompt)太冗长

  • 上下文窗口设置不合理

  • 工具描述过于详细

5.4.2 选择合适的模型

对比不同模型的性能和成本:

traces | where customDimensions.["gen_ai.operation.name"] == "invoke_agent" | extend model = tostring(customDimensions.["gen_ai.request.model"]) | extend totalTokens = toint(customDimensions.["gen_ai.usage.output_tokens"]) + toint(customDimensions.["gen_ai.usage.input_tokens"]) | summarize avgDuration = avg(duration), avgTokens = avg(totalTokens), count = count() by model

如果gpt-4gpt-4o-mini在你的场景下效果差不多,但后者 Token 消耗少 50%,那选择就很明显了。

5.4.3 缓存热点请求

通过分析gen_ai.input.messages(需启用EnableSensitiveData),找出高频重复的请求:

traces | where customDimensions.["gen_ai.operation.name"] == "invoke_agent" | extend inputMessages = tostring(customDimensions.["gen_ai.input.messages"]) | summarize count() by inputMessages | order by count_ desc | take 10

对这些请求做缓存,可以大幅降低 API 调用次数。

六、高级话题:分布式场景下的追踪

6.1 跨服务传播:W3C Trace Context

在微服务架构中,一个用户请求可能跨越多个服务。OpenTelemetry 使用 W3C Trace Context 标准来传播追踪上下文。

假设你有这样的架构:

前端 → API Gateway → Agent Service → Tool Service

只要每个服务都配置了 OpenTelemetry,追踪信息会自动通过 HTTP Header 传递:

traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01

在 Aspire Dashboard 中,你会看到完整的分布式追踪链路。

6.2 Workflow 场景:多 Agent 协作的可观测性

Agent Framework 还支持 Workflow(工作流),多个 Agent 可以协同完成复杂任务。在这种场景下,可观测性变得更加重要。

看看Microsoft.Agents.AI.Workflows中的ActivityExtensions.cs

internal static class ActivityExtensions { // 用于在 Workflow 步骤间传播追踪上下文 public static void InjectTraceContext(this Activity activity, ...) { var propagator = new TraceContextPropagator(); propagator.Inject(new PropagationContext(activity.Context, Baggage.Current), carrier, setter); } }

这让 Workflow 中的每个步骤都能关联到同一个 Trace,形成完整的调用链:

Workflow: 客户服务流程 ├─ Step 1: 意图识别 Agent ├─ Step 2: 知识库检索 Agent ├─ Step 3: 答案生成 Agent └─ Step 4: 质量检查 Agent

在 Grafana 的 Workflow Overview Dashboard 中,你可以看到:

  • 哪个步骤最慢?

  • 哪个 Agent 失败率最高?

  • 整体流程的瓶颈在哪里?

6.3 自定义传播器:适配特殊协议

如果你的系统使用了非 HTTP 协议(如 gRPC、消息队列),可以实现自定义的 Propagator:

public class CustomPropagator : TextMapPropagator { public override void Inject<T>(PropagationContext context, T carrier, Action<T, string, string> setter) { // 将追踪上下文注入到自定义载体中 setter(carrier, "custom-trace-id", context.ActivityContext.TraceId.ToString()); setter(carrier, "custom-span-id", context.ActivityContext.SpanId.ToString()); } public override PropagationContext Extract<T>(PropagationContext context, T carrier, Func<T, string, IEnumerable<string>> getter) { // 从自定义载体中提取追踪上下文 var traceId = getter(carrier, "custom-trace-id").FirstOrDefault(); var spanId = getter(carrier, "custom-span-id").FirstOrDefault(); // 构造 ActivityContext... } }

然后在配置中注册:

Sdk.SetDefaultTextMapPropagator(new CustomPropagator());

七、常见问题与最佳实践

7.1 性能开销:遥测会拖慢系统吗?

这是最常被问到的问题。实测数据显示:

  • Trace 采集:单个 Span 的开销约1-5 微秒

  • Metric 记录:单次计数器增加约0.1 微秒

  • 日志输出:取决于日志级别和目标,通常10-100 微秒

对于 AI Agent 这种单次调用耗时通常在秒级的场景,遥测开销完全可以忽略不计(< 0.01%)。

如果你还是担心,可以使用采样策略

tracerProviderBuilder.SetSampler(new TraceIdRatioBasedSampler(0.1)); // 只采样 10%

7.2 数据量爆炸:如何控制存储成本?

在高并发场景下,遥测数据可能非常庞大。几个优化建议:

7.2.1 分级采样
  • 开发环境:100% 采样

  • 测试环境:50% 采样

  • 生产环境:10% 采样,但错误请求 100% 采样

public class AdaptiveSampler : Sampler { public override SamplingResult ShouldSample(in SamplingParameters samplingParameters) { // 如果是错误,一定采样 if (Activity.Current?.Status == ActivityStatusCode.Error) return new SamplingResult(SamplingDecision.RecordAndSample); // 否则按比例采样 return new SamplingResult( Random.Shared.NextDouble() < 0.1 ? SamplingDecision.RecordAndSample : SamplingDecision.Drop); } }
7.2.2 设置数据保留期

在 Application Insights 中,可以设置不同的保留期:

  • 原始数据:保留 7 天(用于详细调试)

  • 聚合数据:保留 90 天(用于趋势分析)

7.2.3 只记录关键标签

不是所有标签都需要记录。对于生产环境,可以过滤掉一些低价值标签:

tracerProviderBuilder.AddProcessor(new FilteringProcessor()); class FilteringProcessor : BaseProcessor<Activity> { public override void OnEnd(Activity activity) { // 移除低价值标签 activity.SetTag("gen_ai.system_fingerprint", null); activity.SetTag("SomethingElse", null); } }

7.3 敏感数据泄露:如何平衡可观测性和隐私?

这是个两难问题:不记录内容,调试困难;记录内容,又担心泄露。几个实用策略:

7.3.1 环境隔离
var isDevelopment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development"; agent.EnableSensitiveData = isDevelopment;

开发环境全记录,生产环境脱敏。

7.3.2 内容脱敏

自定义 Processor 对敏感内容做脱敏:

class RedactingProcessor : BaseProcessor<Activity> { public override void OnEnd(Activity activity) { if (activity.GetTagItem("gen_ai.input.messages") is string input) { // 脱敏手机号、邮箱等 var redacted = Regex.Replace(input, @"\d{11}", "***********"); redacted = Regex.Replace(redacted, @"\b[\w\.-]+@[\w\.-]+\.\w+\b", "***@***.***"); activity.SetTag("gen_ai.input.messages", redacted); } } }
7.3.3 基于角色的访问控制

在 Application Insights 中,可以设置不同角色的权限:

  • 开发人员:可以查看所有标签

  • 运维人员:只能查看性能指标,看不到消息内容

  • 审计人员:只读权限,可以查看但不能修改

7.4 多租户场景:如何隔离不同客户的数据?

在 SaaS 应用中,你需要区分不同租户的遥测数据。推荐做法:

// 在 Resource 中添加租户信息 var resource = ResourceBuilder.CreateDefault() .AddService(ServiceName) .AddAttributes(new Dictionary<string, object> { ["tenant.id"] = tenantId, ["tenant.name"] = tenantName }) .Build(); // 在每个 Activity 中也添加租户标签 activity?.SetTag("tenant.id", tenantId);

然后在查询时按租户过滤:

traces | where customDimensions.["tenant.id"] == "tenant-123" | summarize count() by bin(timestamp, 1h)

八、未来展望:AI 可观测性的下一站

8.1 自动化根因分析

想象一下,当 Agent 响应变慢时,系统自动分析遥测数据,告诉你:

"检测到响应时间增加 300%。根因分析:

  1. Azure OpenAI API 延迟增加 200ms(外部因素)

  2. 工具调用 GetWeatherAsync 超时 3 次(需优化重试策略)

  3. 输入 Token 平均增加 50%(用户提问变复杂)"

这种基于 AI 的可观测性分析,正在成为现实。

8.2 实时成本预警

结合遥测数据和计费信息,实时计算成本:

var costCalculator = new CostCalculator(); activity?.SetTag("estimated.cost.usd", costCalculator.Calculate(inputTokens, outputTokens, modelName));

当某个 Agent 的成本超过预算时,自动发送告警或限流。

8.3 A/B 测试与实验平台

通过遥测数据,可以轻松做 A/B 测试:

var variant = experimentService.GetVariant(userId); activity?.SetTag("experiment.variant", variant); var instructions = variant == "A" ? "You are a formal assistant." : "You are a casual friend.";

然后对比不同变体的:

  • 用户满意度(通过反馈收集)

  • 响应时间

  • Token 消耗

  • 错误率

数据驱动地优化 Agent 配置。

8.4 多模态遥测

随着 AI Agent 开始处理图像、音频、视频,遥测系统也需要进化:

activity?.SetTag("gen_ai.input.modalities", new[] { "text", "image" }); activity?.SetTag("gen_ai.input.image.size_bytes", imageData.Length); activity?.SetTag("gen_ai.input.image.format", "jpeg");

未来的 Aspire Dashboard 可能会直接展示输入的图像缩略图,让调试更直观。

8.5 联邦学习与隐私计算

在严格的隐私要求下(如医疗、金融领域),可能需要:

  • 本地遥测:敏感数据不离开客户环境

  • 聚合指标:只上报统计信息,不上报原始数据

  • 差分隐私:在指标中添加噪声,保护个体隐私

OpenTelemetry 的可扩展架构,为这些高级需求提供了可能。

九、实战案例:一次性能优化的完整过程

让我用一个真实案例,展示 AgentOpenTelemetry 如何帮助解决实际问题。

9.1 问题发现

某客服 Agent 上线后,用户反馈响应慢。运维人员打开 Aspire Dashboard,发现:

  • P95 响应时间:8.5 秒(目标 < 3 秒)

  • 平均 Token 消耗:1200 tokens(预算 800 tokens)

9.2 定位瓶颈

通过 Trace 详情,发现一个典型请求的耗时分布:

总耗时:8.2 秒 ├─ invoke_agent: 8.1 秒 │ ├─ chat gpt-4: 7.8 秒 │ │ ├─ HTTP POST: 7.5 秒 │ │ └─ 响应处理: 0.3 秒 │ └─ 工具调用 SearchKnowledgeBase: 0.3 秒 └─ 日志记录: 0.1 秒

问题很明显:模型调用占了 95% 的时间

9.3 深入分析

查看gen_ai.usage.input_tokens标签,发现输入 Token 高达950。进一步分析(启用EnableSensitiveData),发现:

  • System Prompt:200 tokens(包含大量示例)

  • 历史对话:600 tokens(保留了 10 轮对话)

  • 工具描述:150 tokens(5 个工具的详细说明)

9.4 优化方案

方案 1:精简 System Prompt

将示例从 Prompt 中移除,改为 Few-shot Learning:

// 优化前 instructions = @"You are a customer service agent. Example 1: User: 'Where is my order?' You: 'Let me check...' Example 2: ... Example 3: ..."; // 优化后 instructions = "You are a customer service agent."; // 示例通过历史消息注入

节省:150 tokens

方案 2:智能上下文窗口

不是保留所有历史对话,而是只保留最相关的:

var relevantHistory = thread.Messages .OrderByDescending(m => m.Timestamp) .Take(3) // 只保留最近 3 轮 .Reverse() .ToList();

节省:400 tokens

方案 3:工具描述优化

将详细文档移到外部,只保留核心描述:

// 优化前 [Description("Search the knowledge base for relevant articles. " + "This tool accepts natural language queries and returns " + "the top 5 most relevant articles with their titles, " + "summaries, and URLs. Use this when...")] // 优化后 [Description("Search knowledge base for relevant articles.")]

节省:80 tokens

9.5 效果验证

部署优化后,再次查看遥测数据:

  • P95 响应时间:2.8 秒(↓ 67%)

  • 平均 Token 消耗:570 tokens(↓ 52%)

  • 月度成本:**$1,200** →$580(↓ 52%)

用户满意度从3.2提升到4.5(满分 5 分)。

9.6 持续监控

设置告警规则:

traces | where customDimensions.["gen_ai.operation.name"] == "invoke_agent" | summarize p95Duration = percentile(duration, 95) by bin(timestamp, 5m) | where p95Duration > 3000 // 超过 3 秒告警

一旦性能回退,立即收到通知。

十、总结:可观测性是 AI 应用的"安全带"

回到文章开头的比喻:如果说 AI Agent 是一辆高速行驶的汽车,那么 AgentOpenTelemetry 就是你的仪表盘 + 行车记录仪 + 导航系统

它让你能够:

实时监控:知道 Agent 现在在做什么
历史回溯:出问题时能快速定位根因
性能优化:基于数据做出明智的优化决策
成本控制:清楚每一分钱花在哪里
合规审计:满足企业级的可追溯性要求

更重要的是,它的设计哲学值得我们学习:

  • 装饰器模式:无侵入式集成

  • 标准先行:遵循 OpenTelemetry 规范

  • 默认安全:敏感数据保护

  • 分层设计:ChatClient 层 + Agent 层

  • 可扩展性:支持自定义指标和标签

从"黑盒"到"玻璃盒"的跨越

在 AI 时代,可观测性不再是"锦上添花",而是必需品。没有它,你就像蒙着眼睛开车;有了它,你才能真正掌控自己的 AI 应用。

AgentOpenTelemetry 的出现,标志着 AI 工程化的一个重要里程碑。它告诉我们:AI 应用也可以像传统软件一样,被精确地测量、监控和优化

十一、快速上手指南

如果你已经迫不及待想试试,这里是最快的上手路径:

Step 1: 克隆示例项目

git clone https://github.com/microsoft/agent-framework.git cd agent-framework/samples/GettingStarted/AgentOpenTelemetry

Step 2: 配置环境

$env:AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/" $env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini"

Step 3: 一键启动

.\start-demo.ps1

脚本会自动:

  • 启动 Aspire Dashboard

  • 编译并运行应用

  • 打开浏览器到监控页面

Step 4: 开始探索

在控制台输入问题,然后到 Aspire Dashboard 查看:

  • Traces 页面:看完整的调用链路

  • Metrics 页面:看 Token 消耗趋势

  • Logs 页面:看详细的执行日志

就这么简单!

十二、延伸阅读与资源

官方文档

  • Agent Framework 文档:https://learn.microsoft.com/agent-framework/

  • OpenTelemetry 规范:https://opentelemetry.io/docs/specs/semconv/gen-ai/

  • Aspire Dashboard 指南:https://learn.microsoft.com/dotnet/aspire/

社区资源

  • GitHub 仓库:https://github.com/microsoft/agent-framework

  • 示例代码:samples/GettingStarted/AgentOpenTelemetry

  • 单元测试:tests/Microsoft.Agents.AI.UnitTests/OpenTelemetryAgentTests.cs

相关技术

  • Microsoft.Extensions.AI:统一的 AI 抽象层

  • Semantic Kernel:微软的 AI 编排框架(可迁移到 Agent Framework)

  • Azure Monitor:企业级监控平台

十三、写在最后:可观测性的哲学思考

在结束这篇文章之前,我想分享一个更深层的思考。

可观测性的本质,是对复杂系统的理解和掌控。在传统软件中,我们通过日志、指标、追踪来理解系统行为。但 AI 系统的复杂度是指数级的——它不仅有确定性的代码逻辑,还有不确定性的模型推理。

AgentOpenTelemetry 的价值,不仅在于它提供了监控工具,更在于它建立了一套理解 AI 系统的方法论

  1. 标准化:用统一的语言描述 AI 行为(OpenTelemetry Semantic Conventions)

  2. 分层化:从 ChatClient 到 Agent 到 Workflow,逐层抽象

  3. 可追溯:每个决策、每次调用都有据可查

  4. 数据驱动:基于真实数据而非直觉做优化

这套方法论,将帮助我们在 AI 的"不确定性"中,找到"确定性"的锚点。

一个小故事

我曾经遇到一个团队,他们的 AI 客服系统上线后问题频出。每次出问题,都要花几个小时翻日志、猜测原因、盲目尝试。后来他们引入了 AgentOpenTelemetry,情况彻底改变了:

  • 问题定位:从几小时缩短到几分钟

  • 优化效果:有数据支撑,不再靠"感觉"

  • 团队信心:知道系统在做什么,心里有底

技术负责人跟我说:"以前我们是在黑暗中摸索,现在终于开灯了。"

这就是可观测性的力量。

你的下一步

如果你正在开发 AI 应用,我强烈建议你:

  1. 现在就开始:不要等到出问题才想起监控

  2. 从简单开始:先用 Aspire Dashboard,再考虑企业级方案

  3. 建立基线:记录正常情况下的指标,才能识别异常

  4. 持续优化:可观测性是个迭代过程,不是一次性工程

记住:你无法优化你无法测量的东西

结语

从"黑盒"到"玻璃盒",从"盲驾"到"精准导航",AgentOpenTelemetry 为 AI 应用的工程化实践提供了坚实的基础。

它不是银弹,但它是必需品。

它不会让你的 Agent 变得更聪明,但会让你变得更聪明——因为你终于知道,你的 Agent 在做什么了。

在这个 AI 狂飙突进的时代,让我们不仅追求"能用",更追求"可控"。让我们不仅关注模型的能力,更关注系统的可靠性。

因为只有这样,AI 才能真正从实验室走向生产,从 Demo 走向产品,从概念走向价值。


关于作者

一个在 AI 工程化道路上摸爬滚打的开发者,相信技术的力量,也相信工程的美学。如果这篇文章对你有帮助,欢迎点赞、收藏、转发。如果有任何问题或建议,欢迎在评论区交流。

版权声明

本文基于 Microsoft Agent Framework 开源项目的深度研究,代码示例遵循项目的 MIT 许可证。文章内容为原创,转载请注明出处。


相关文章推荐

  • 《AI Agent 架构设计:从单体到分布式》

  • 《Token 优化实战:如何降低 50% 的 AI 成本》

  • 《.NET 开发者的 AI 转型指南》

更多AIGC文章

RAG技术全解:从原理到实战的简明指南

更多VibeCoding文章

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 12:24:36

2025免费降AI率完全指南:从ai查重工具选择到降AI技巧,一步到位!

在论文、报告、内容创作越来越严格的时代&#xff0c;查AI率、检测AI率、降AI率 已经成为学生、写作者、博主的日常需求。很多同学因为 AI率过高被导师指出“AI痕迹太重”&#xff0c;甚至退回重写。本文今天一次性告诉你&#xff1a; 检测AI率应该注意什么 免费查AI率的网站有…

作者头像 李华
网站建设 2026/4/12 16:47:22

2025免费降AI率完全指南:从工具选择到实操技巧,一步到位

在论文、报告、内容创作越来越严格的时代&#xff0c;查AI率、检测AI率、降AI率 已经成为学生、写作者、博主的日常需求。很多同学因为 AI率过高被导师指出“AI痕迹太重”&#xff0c;甚至退回重写。本文今天一次性告诉你&#xff1a; 检测AI率应该注意什么 免费查AI率的网站有…

作者头像 李华
网站建设 2026/4/15 8:48:46

Java 8 JVM动态年龄计算机制详解

本文探讨一下HotSpot JVM开发团队引入动态年龄判断&#xff08;或称“自适应调整”&#xff09;的核心原因和设计哲学。 接下来让让我们深入剖析一下这个机制—— 核心原理&#xff1a;TargetSurvivorRatio 与动态年龄 动态年龄计算并不是直接丢弃MaxTenuringThreshold&#xf…

作者头像 李华
网站建设 2026/4/15 11:14:17

Capacitor跨平台开发终极指南:用Web技术构建原生应用

Capacitor跨平台开发终极指南&#xff1a;用Web技术构建原生应用 【免费下载链接】capacitor Build cross-platform Native Progressive Web Apps for iOS, Android, and the Web ⚡️ 项目地址: https://gitcode.com/gh_mirrors/ca/capacitor 在移动应用开发领域&#…

作者头像 李华
网站建设 2026/4/16 12:13:30

光伏电站并网后如何玩转虚拟同步机?储能如何优雅地削峰填谷?今天咱们用Simulink搭个实战模型,拆解光储联合系统中的三大核心技能

simulink仿真模型光伏储能VSG&#xff0c;加电容&#xff0c;正常运行&#xff0c;波形完美一一光储一次调频、储能削峰填谷、 直流母线电压控制。 2018b及以上都可运行哈打开Simulink第一件事&#xff0c;先给直流母线加个超级电容缓冲池。这个蓝色电容模块可不是装饰品&#…

作者头像 李华
网站建设 2026/4/15 16:39:03

CARLA自动驾驶仿真环境搭建与DEMO详解

CARLA自动驾驶仿真环境搭建与DEMO详解一、概述1、什么是CARLA&#xff1f;2、为什么需要CARLA&#xff1f;二、效果三、环境搭建1、Ubuntu 22.04 环境&#xff08;推荐&#xff09;1.1、Docker方式&#xff08;最简单&#xff09;2、Windows环境2.1、Python3.8.0包安装方式四、…

作者头像 李华