拓冰建站拓冰建站
首页 / 资讯中心 / 正文

[MAF预定义Agent中间件-04]OpenTelemetryAgent:基于Agent的调用链跟踪和性能监控

1. 当 Agent 调用链断在工具调用那一层如果你正在用 MAFMicrosoft Agent Framework搭多 Agent 系统大概率遇到过这种场景Agent 跑起来了日志也打了但一旦线上响应变慢你根本说不清时间花在哪——是模型推理慢还是工具函数卡住还是网络往返抖动。更麻烦的是当你兴冲冲接上 OpenTelemetry 想看调用链却发现 Jaeger 里只有孤零零一个invoke_agentSpan工具调用那一层凭空消失了。这就是OpenTelemetryAgent这个预定义中间件要解决的问题。它本质上是 MAF 里专门给 Agent 管道注入 Trace 和 Metrics 的中间件能自动为每次 Agent 调用生成 Span、记录耗时、统计 Token 消耗并把数据推到 Jaeger、Prometheus 这类后端。适合谁适合已经把 Agent 跑通、现在要上可观测性的团队尤其是做多轮对话、工具调用、流式输出的场景。我试过在本地把 Jaeger Prometheus Grafana 全拉起来配合 TaoToken 的统一 Key 通道跑了一遍完整链路中间踩了一个关于autoWireChatClient的坑这篇就把配置骨架、验证动作和排障过程完整拆开讲。2. TaoToken 前置统一 Key 与 API 通道在接 OpenTelemetry 之前得先让 Agent 能稳定调模型。MAF 的 Agent 底层走的是IChatClient而OpenAIClient需要一个 endpoint 和 apiKey。这里用 TaoToken 的好处是一个 Key 就能覆盖多种模型不用为每个模型单独配一套凭证Agent 中间件切换模型时也不用改代码结构。你需要准备三样东西API Key在控制台生成格式类似sk-开头Endpointhttps://taotoken.net/api模型名比如gpt-4o、claude-3-5-sonnet等按你实际订阅的填把这三个值写进.env文件代码里用DotEnv.Load()读进来避免硬编码MODELgpt-4o API_KEYsk-你的TaoToken密钥 OPENAI_URLhttps://taotoken.net/api注意OPENAI_URL不要带末尾斜杠OpenAIClientOptions.Endpoint对 URI 格式比较敏感多一个斜杠在某些版本会报Invalid endpoint。如果你还没生成 Key去控制台的 API Keys 页面创建接入细节可以对照接入文档里面有各语言的完整示例。这一步做完Agent 本身就能跑了接下来才是 OpenTelemetry 的事。3. 可复制配置TracerProvider MeterProvider Agent 中间件OpenTelemetry 在 .NET 里的核心是两个 ProviderTracerProvider管链路MeterProvider管指标。两者要用同一个serviceName否则 Jaeger 和 Prometheus 里的数据对不上号。先装 NuGet 包dotnet add package OpenTelemetry dotnet add package OpenTelemetry.Exporter.Console dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener dotnet add package OpenTelemetry.Extensions.Hosting然后是完整的配置骨架。注意AddSource和AddMeter里的名字必须和后面UseOpenTelemetry的sourceName一致using OpenTelemetry; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; var serviceName AIApp; var serviceVersion 1.0.0; using (Sdk.CreateTracerProviderBuilder() .SetResourceBuilder(ResourceBuilder.CreateDefault() .AddService(serviceName, serviceVersion: serviceVersion)) .AddSource(serviceName) .AddConsoleExporter() .AddOtlpExporter(o { o.Endpoint new Uri(http://localhost:4317); o.Protocol OpenTelemetry.Exporter.OtlpExportProtocol.Grpc; }) .Build()) using (Sdk.CreateMeterProviderBuilder() .SetResourceBuilder(ResourceBuilder.CreateDefault() .AddService(serviceName, serviceVersion: serviceVersion)) .AddMeter(serviceName) .AddConsoleExporter() .AddPrometheusHttpListener(o o.UriPrefixes [http://192.168.1.166:9464/]) .Build()) { // Agent 构建代码放这里 }Agent 侧的中间件注册是关键。UseOpenTelemetry是AIAgentBuilder的扩展方法它内部会 new 一个OpenTelemetryAgent包住内层 Agentvar agent new OpenAIClient( credential: new AzureKeyCredential(apiKey), options: new OpenAIClientOptions { Endpoint new Uri(endpoint) }) .GetChatClient(model: model) .AsIChatClient() .AsAIAgent(name: MyAgent) .AsBuilder() .UseOpenTelemetry(sourceName: serviceName) .Build();如果你要监控工具调用配置得改一下。默认autoWireChatClient: true会把OpenTelemetryChatClient插到FunctionInvokingChatClient前面导致工具调用的 Span 被吞掉。正确做法是显式注册 ChatClient 中间件再让 Agent 中间件用autoWireChatClient: truevar agent new OpenAIClient( credential: new AzureKeyCredential(apiKey), options: new OpenAIClientOptions { Endpoint new Uri(endpoint) }) .GetChatClient(model: model) .AsIChatClient() .AsBuilder() .UseOpenTelemetry(sourceName: serviceName) // ChatClient 层 .Build() .AsAIAgent(name: MyAgent, tools: [AIFunctionFactory.Create(GetWeather, nameof(GetWeather))]) .AsBuilder() .Use(inner new OpenTelemetryAgent(inner, serviceName, true)) // Agent 层 .Build();这样 Jaeger 里就能同时看到invoke_agent、chat、execute_tool三层 Span调用链才完整。4. 验证请求发起一次 Agent 调用并检查上报配置写完跑一次真实调用验证。下面这段代码手动造了三层 Activity 模拟外层业务逻辑再触发 Agentvar source new ActivitySource(serviceName); using (source.StartActivity(Agent-Server, kind: ActivityKind.Server, parentContext: default)) { await Task.Delay(200); using (source.StartActivity(Foo)) { await Task.Delay(300); using (source.StartActivity(Bar)) { await Task.Delay(400); await agent.RunAsync(苏州今天天气怎么样适合穿什么); } } }跑起来后控制台会先打印 Span 和 Metric 的原始输出。然后打开 Jaeger UIhttp://localhost:16686/选服务AIApp点 Find Traces你应该能看到一条完整的调用链Agent-Server→Foo→Bar→invoke_agent MyAgent→chat gpt-4o→execute_tool GetWeather。每个 Span 上会带这些标签用来区分 Agent 身份标签含义gen_ai.operation.name固定为invoke_agentgen_ai.provider.name模型提供商gen_ai.agent.idAgent 唯一标识gen_ai.agent.nameAgent 名称gen_ai.agent.descriptionAgent 描述指标侧打开 Prometheushttp://localhost:9090查gen_ai_client_operation_duration_seconds能看到每次调用的耗时分布。Grafana 里导入 Dashboard 后重点看四个指标gen_ai.client.operation.duration端到端延迟看 P95/P99 长尾gen_ai.client.token.usage按 input/output 拆分算成本gen_ai.client.operation.time_to_first_chunkTTFT流式场景体验核心gen_ai.client.operation.time_per_output_chunk流式输出流畅度验证成功的标志Jaeger 里 Span 层级完整Prometheus 里能查到gen_ai_client_operation_duration_seconds_count在增长Grafana 面板有曲线。5. 本篇常见错排查Span 里看不到工具调用。这是最常见的坑。原因是autoWireChatClient默认为true时OpenTelemetryChatClient被插到了FunctionInvokingChatClient前面当前 Activity 变成了chat而不是invoke_agent工具跟踪逻辑检查到不是 Agent 调用就直接跳过了。解法就是第 3 节里那个显式注册 ChatClient 中间件的写法。Prometheus 抓不到指标。检查PrometheusHttpListener的UriPrefixes是不是用了localhost。Prometheus 跑在 Docker 容器里容器内的localhost指向容器自己必须用宿主机 IP比如192.168.1.166。prometheus.yml里的 target 也要写宿主机 IPglobal: scrape_interval: 5s scrape_configs: - job_name: csharp_console_app static_configs: - targets: [192.168.1.166:9464]Jaeger 里没有数据。先确认 OTLP exporter 的 endpoint 是http://localhost:4317协议是 gRPC。如果 Jaeger 容器没映射 4317 端口加上-p 4317:4317。另外AddSource(serviceName)里的名字必须和UseOpenTelemetry(sourceName:)完全一致大小写敏感。Token 用量指标为空。gen_ai.client.token.usage依赖模型返回的 usage 字段。如果 TaoToken 通道返回的响应里没有 usage指标就不会记录。可以在控制台 exporter 的输出里确认一下ChatResponse是否带了 token 统计。流式指标缺失。time_to_first_chunk和time_per_output_chunk只在流式调用RunStreamingAsync时才有数据普通RunAsync不会产生。要验证这两个指标得改成流式调用。6. 把可观测性接进你的 Agent 工作流链路跟踪和性能监控不是跑一次就完事它得跟着你的开发流程走。我的做法是本地开发用 Console exporter 快速看输出联调环境推 Jaeger 看调用链生产环境用 Prometheus Grafana 做告警。TaoToken 的统一 Key 在这里的价值是——你换模型时不用动 OpenTelemetry 配置sourceName和中间件结构都不变只是 endpoint 后面的模型名换一下指标和链路自动跟着新模型走。如果你还在选型阶段想先验证模型对话效果可以直接在模型对话页面试要长期跑编码类 Agent、需要稳定的 Key 配额看 Coding Plan接入过程中遇到 Key 或 endpoint 问题去 API Keys 页面重新生成对照接入文档排查。把 OpenTelemetry 这套骨架搭好之后后面加 Agent、加工具、加模型监控都是自动覆盖的不用每次重新配。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门