当前位置: 首页 > news >正文

构建基础设施

为了显示基于调用链的跟踪信息,我们在本地安装了Jaeger。为了收集和展示性能指标,我们使用了PrometheusGrafana。我们采用最简单的方式,通过在本地创建相映的Docker容器来搭建这些服务。如果希望在Windows上执行相应的命令,将换行符从\改为^即可。

Jageer:

docker run -d --name jaeger \ -p 16686:16686 \ -p 4317:4317 \ jaegertracing/all-in-one:latest

Prometheus:

docker run -d --name prometheus \ -p 9090:9090 \ -v /c/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml \ prom/prometheus:latest

其中c:\prometheus\prometheus.yml的内容如下:

global: scrape_interval: 5s scrape_configs: - job_name: 'prometheus' static_configs: - targets: ['localhost:9090'] - job_name: 'csharp_console_app' static_configs: - targets: ['192.168.1.166:9464']
  • 192.168.1.166是本地的IP地址,9464是接下来创建的应用暴露的端口,用于输出性能指标信息。

Grafana:

docker run -d --name grafana \ -p 3000:3000 \ grafana/grafana:latest

然后添加针对Prometheushttp://192.168.1.166:9090的连接。我针对OpenTelemetryChatClient输出的指标创建了一个简单Dashboard,可以通过这里下载并导入。

2. 构建一个简单的Agent应用

我们创建一个简单的Console应用,并添加针对OpenTelemetry.NET相关的NuGet包:

  • OpenTelemetry
  • OpenTelemetry.Exporter.Console
  • OpenTelemetry.Exporter.OpenTelemetryProtocol
  • OpenTelemetry.Exporter.Prometheus.HttpListener
  • OpenTelemetry.Extensions.Hosting

如下所示的是完整的演示程序。最外层的两个using块分别创建了TracerProviderMeterProvider,前者用于链路跟踪,后者用于性能指标的收集,两者设置了相同的服务名称(AIApp)和版本(1.0.0)。对于Trace,我们添加了ConsoleOTLP两种Exporter,后者将数据发送到Jaeger。对于Metrics,我们添加了ConsolePrometheusHttpListener两种Exporter,后者在http://192.168.1.166:9464/暴露性能指标,供Prometheus收集。

using Azure; using dotenv.net; using Microsoft.Agents.AI; using Microsoft.Extensions.AI; using OpenAI; using OpenTelemetry; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; using System.Diagnostics; DotEnv.Load(); var model = Environment.GetEnvironmentVariable("MODEL")!; var apiKey = Environment.GetEnvironmentVariable("API_KEY")!; var endpoint = Environment.GetEnvironmentVariable("OPENAI_URL")!; var serviceName = "AIApp"; var servceVersion = "1.0.0"; using (Sdk.CreateTracerProviderBuilder() .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(serviceName, serviceVersion: servceVersion)) .AddSource(serviceName) .AddConsoleExporter() .AddOtlpExporter(options => { options.Endpoint = new Uri("http://localhost:4317"); options.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.Grpc; }) .Build()) using (Sdk.CreateMeterProviderBuilder() .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(serviceName, serviceVersion: servceVersion)) .AddMeter(serviceName) .AddConsoleExporter() .AddPrometheusHttpListener(options => { options.UriPrefixes = ["http://192.168.1.166:9464/"]; }).Build()) { var chatClient = new OpenAIClient( credential: new AzureKeyCredential(apiKey), options: new OpenAIClientOptions { Endpoint = new Uri(endpoint) }) .GetChatClient(model: model) .AsIChatClient() .AsBuilder() .UseOpenTelemetry(sourceName: serviceName) .Build(); string[] queries = [ "What is the capital of France?", "Who won the FIFA World Cup in 2018?", "What is the largest mammal on Earth?" ]; var random = new Random(); var source = new ActivitySource(serviceName); for (int i = 0; i < 30; i++) { using (source.StartActivity("Agent-Server", kind: ActivityKind.Server, parentContext: default)) { await Task.Delay(random.Next(100, 1000)); using (source.StartActivity("Foo")) { await Task.Delay(random.Next(100, 1000)); using (source.StartActivity("Bar")) { await Task.Delay(random.Next(100, 1000)); await chatClient.GetResponseAsync(queries[random.Next(queries.Length)]); } } } await Task.Delay(random.Next(3000, 5000)); } Console.ReadLine(); }

using块中,我们创建了用来调用LLM的IChatClient对象。具体来说,我们首先创建了一个OpenAIClient,并通过GetChatClient方法获取了一个针对聊天模型的客户端。然后我们将其转换为IChatClient,并使用AsBuilder方法创建了一个可配置的构建器。在构建器上,我们调用UseOpenTelemetry方法,指定了与TracerProviderMeterProvider相同的sourceName(AIApp),以启用链路跟踪和性能指标的收集。最后,我们调用Build方法构建了最终的IChatClient对象。

为了模拟一段持续的调用,我们在一个循环中随机选择了三个问题,并调用了GetResponseAsync方法。为了模拟一段完整的调用链,我们利用创建的ActivitySource(将服务名称作为sourceName)手动创建了三个不同层级的Activity,分别命名为Agent-ServerFooBar,它们表示LLM调用外层的操作。

3. 结果展示

运行程序之后,我们可以在控制台上看到链路跟踪和性能指标的输出。同时,在Jaeger的UI界面http://localhost:16686/上,我们可以看到针对Agent-Server操作的调用链信息,如下图所示:

打开Grafana的Dashboard,我们可以看到针对LLM调用的性能指标,其中包括请求和响应Token的消耗、调用LLM的延时、成功调用的比例和错误分布等。

4. OpenTelemetryChatClient

和我们演示的程序一样,OpenTelemetryChatClient也是使用ActivitySource创建的Activity来表示针对LLM的调用。创建这个ActivitySource指定的名称来源于OpenTelemetryChatClient构造函数中的sourceName参数,在OpenTelemetry的语境中将它视为服务名称。如果没有显示指定sourceNameOpenTelemetryChatClient会使用默认的名称"Experimental.Microsoft.Extensions.AI"。

public sealed partial class OpenTelemetryChatClient : DelegatingChatClient { public OpenTelemetryChatClient( IChatClient innerClient, ILogger? logger = null, string? sourceName = null); public JsonSerializerOptions JsonSerializerOptions { get; set; } public bool EnableSensitiveData { get; set; } = TelemetryHelpers.EnableSensitiveDataDefault; public override async Task<ChatResponse> GetResponseAsync( IEnumerable<ChatMessage> messages, ChatOptions? options = null, CancellationToken cancellationToken = default); public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync( IEnumerable<ChatMessage> messages, ChatOptions? options = null, CancellationToken cancellationToken = default); }

EnableSensitiveData属性用于控制是否允许在Trace数据中包含一些敏感数据,这个属性的默认值来源于针对环境变量OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT的设置。如果两者均为设置,默认不捕获敏感数据。如果这个属性被设置为true,调用LLM的请求和响应消息会被序列化并作为操作的标签进行输出,JsonSerializerOptions属性就是用来控制这个序列化过程的行为的。

在重写的GetResponseAsyncGetStreamingResponseAsync方法中,OpenTelemetryChatClient会创建一个新的Activity来表示针对IChatClient的调用。如果能够从ChatOptions中提取名称的名称(对应ModelId属性),此操作被命名为“chat {model-name}”,否则被命名为“chat”。创建的Activity会被设置一系列丰富的标签来描述此次调用。对于我们前面的演示程序,OpenTelemetryChatClient创建的跟踪操作包含的标签体现在如下这张针对Jaeger的截图上。

http://www.jsqmd.com/news/1092414/

相关文章:

  • 打造AI时代不可替代的高语境资产
  • 从PV、EV、AC到EAC、ETC:一文掌握项目成本绩效的核心公式与实战解读
  • YimMenu终极指南:免费GTA5菜单工具完整使用教程与安全防护
  • 喜保宁与氯巴占联用还是单用,难治性局灶发作治疗策略解析
  • 一键修复Windows软件兼容性问题:VisualCppRedist AIO完整解决方案指南
  • Xilinx FPGA LVDS接口设计:从IBUFDS到自环测试的工程实践
  • 正负样本比例不平衡
  • SVGnest 矢量嵌套算法深度解析:开源CNC材料优化终极指南
  • FastApiAdmin 后端接口开发好了,前端管理界面怎么调用与显示?
  • 还在愁毕业论文写不完?9款AI论文网站一键生成逻辑连贯初稿!
  • 从LC谐振到ADC采样:OPA4377运放电路在电磁信号处理中的核心作用
  • 革命性Beat Saber一站式管理方案:BSManager深度解析与实战指南
  • USB-I2C/GPIO适配器EVM硬件解析与实战调试指南
  • ChatGPT Plus用户流失率骤升23%的背后:不是价格问题,而是这1个被忽略的免费替代路径(企业级实测可用)
  • Python构建XSS检测系统:从原理到实践的动态验证与载荷变异
  • Moneta Markets亿汇:“通胀压力扰动科技资产”
  • VMPDump:如何快速掌握逆向工程中的动态脱壳与导入修复技术
  • 如何完整恢复老旧iOS设备:5步快速降级与越狱教程
  • 近期零基础学量化,先让 AI 帮你整理表达
  • 肿瘤标志物联合检测的科研进阶工具——多因子检测试剂盒
  • 如何快速解决Windows驱动签名验证问题:DSEFix完整使用指南
  • 打破进口垄断!云克隆推出肠道七因子高通量检测全新方案
  • TI TAS2559智能功放评估板硬件解析与上手指南
  • Java毕设项目:基于 Spring Boot 的电影评价与购票联动系统 影院票务数据统计与订单管理系统设计 (源码+文档,讲解、调试运行,定制等)
  • HarmonyOS技术精讲-应用间跳转:精确控制跳转目标(显式跳转)
  • 【Vid-Agent】长视频理解VideoTemp-o3框架
  • 训练 = 一个反复打分与改作业的循环
  • 从像素到光点:基于SSD1306 OLED的动态光源控制与传感应用
  • HarmonyOS技术精讲-应用间跳转:典型场景二——地图导航与位置服务
  • 当 leader 被隔离: etcd 网络分区深度分析