![[MAF預定義Agent中間件-04]OpenTelemetryAgent:基于Agent的調用鏈跟蹤和性能監(jiān)控](http://pic.xiahunao.cn/yaotu/[MAF預定義Agent中間件-04]OpenTelemetryAgent:基于Agent的調用鏈跟蹤和性能監(jiān)控)
1. 當 Agent 調用鏈斷在工具調用那一層如果你正在用 MAFMicrosoft Agent Framework搭多 Agent 系統(tǒng)大概率遇到過這種場景Agent 跑起來了日志也打了但一旦線上響應變慢你根本說不清時間花在哪——是模型推理慢還是工具函數(shù)卡住還是網(wǎng)絡往返抖動。更麻煩的是當你興沖沖接上 OpenTelemetry 想看調用鏈卻發(fā)現(xiàn) Jaeger 里只有孤零零一個invoke_agentSpan工具調用那一層憑空消失了。這就是OpenTelemetryAgent這個預定義中間件要解決的問題。它本質上是 MAF 里專門給 Agent 管道注入 Trace 和 Metrics 的中間件能自動為每次 Agent 調用生成 Span、記錄耗時、統(tǒng)計 Token 消耗并把數(shù)據(jù)推到 Jaeger、Prometheus 這類后端。適合誰適合已經(jīng)把 Agent 跑通、現(xiàn)在要上可觀測性的團隊尤其是做多輪對話、工具調用、流式輸出的場景。我試過在本地把 Jaeger Prometheus Grafana 全拉起來配合 TaoToken 的統(tǒng)一 Key 通道跑了一遍完整鏈路中間踩了一個關于autoWireChatClient的坑這篇就把配置骨架、驗證動作和排障過程完整拆開講。2. TaoToken 前置統(tǒng)一 Key 與 API 通道在接 OpenTelemetry 之前得先讓 Agent 能穩(wěn)定調模型。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 頁面創(chuàng)建接入細節(jié)可以對照接入文檔里面有各語言的完整示例。這一步做完Agent 本身就能跑了接下來才是 OpenTelemetry 的事。3. 可復制配置TracerProvider MeterProvider Agent 中間件OpenTelemetry 在 .NET 里的核心是兩個 ProviderTracerProvider管鏈路MeterProvider管指標。兩者要用同一個serviceName否則 Jaeger 和 Prometheus 里的數(shù)據(jù)對不上號。先裝 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();如果你要監(jiān)控工具調用配置得改一下。默認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. 驗證請求發(fā)起一次 Agent 調用并檢查上報配置寫完跑一次真實調用驗證。下面這段代碼手動造了三層 Activity 模擬外層業(yè)務邏輯再觸發(fā) 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 上會帶這些標簽用來區(qū)分 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 節(jié)里那個顯式注冊 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 里沒有數(shù)據(jù)。先確認 OTLP exporter 的 endpoint 是http://localhost:4317協(xié)議是 gRPC。如果 Jaeger 容器沒映射 4317 端口加上-p 4317:4317。另外AddSource(serviceName)里的名字必須和UseOpenTelemetry(sourceName:)完全一致大小寫敏感。Token 用量指標為空。gen_ai.client.token.usage依賴模型返回的 usage 字段。如果 TaoToken 通道返回的響應里沒有 usage指標就不會記錄??梢栽诳刂婆_ exporter 的輸出里確認一下ChatResponse是否帶了 token 統(tǒng)計。流式指標缺失。time_to_first_chunk和time_per_output_chunk只在流式調用RunStreamingAsync時才有數(shù)據(jù)普通RunAsync不會產(chǎn)生。要驗證這兩個指標得改成流式調用。6. 把可觀測性接進你的 Agent 工作流鏈路跟蹤和性能監(jiān)控不是跑一次就完事它得跟著你的開發(fā)流程走。我的做法是本地開發(fā)用 Console exporter 快速看輸出聯(lián)調環(huán)境推 Jaeger 看調用鏈生產(chǎn)環(huán)境用 Prometheus Grafana 做告警。TaoToken 的統(tǒng)一 Key 在這里的價值是——你換模型時不用動 OpenTelemetry 配置sourceName和中間件結構都不變只是 endpoint 后面的模型名換一下指標和鏈路自動跟著新模型走。如果你還在選型階段想先驗證模型對話效果可以直接在模型對話頁面試要長期跑編碼類 Agent、需要穩(wěn)定的 Key 配額看 Coding Plan接入過程中遇到 Key 或 endpoint 問題去 API Keys 頁面重新生成對照接入文檔排查。把 OpenTelemetry 這套骨架搭好之后后面加 Agent、加工具、加模型監(jiān)控都是自動覆蓋的不用每次重新配。