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

AI服务的可观测性与运维

AI服务的可观测性与运维

当 AI 服务从开发环境走向生产,可观测性(Observability)成为运维的基石。传统的监控(CPU、内存、请求量)已不足以应对 AI 系统的复杂性,我们需要深入追踪每个 AI 交互的细节:Token 消耗、模型延迟、函数调用路径、成本分布等。同时,AI 模型的持续迭代要求我们具备A/B 测试灰度发布能力,以安全地引入新模型或提示词。

将构建一套完整的 AI 可观测性体系,涵盖 OpenTelemetry 埋点、实验框架以及成本分析工具。

1 OpenTelemetry 在 AI 链路中的埋点(Token 消耗、延迟)

1.1 为什么需要 OpenTelemetry?

OpenTelemetry(OTel)是云原生可观测性的行业标准,提供统一的 API 和 SDK 来收集追踪(Traces)指标(Metrics)日志(Logs)。对于 AI 应用,我们需要将 LLM 调用、向量检索、插件执行等环节作为 span 串联起来,形成完整的调用链,从而定位性能瓶颈和异常。

10.1.2 在 .NET 中集成 OpenTelemetry

首先安装必要的 NuGet 包:

dotnetaddpackage OpenTelemetry.Extensions.Hosting dotnetaddpackage OpenTelemetry.Instrumentation.AspNetCore dotnetaddpackage OpenTelemetry.Instrumentation.Http dotnetaddpackage OpenTelemetry.Exporter.Console dotnetaddpackage OpenTelemetry.Exporter.OpenTelemetryProtocol# 导出到 OTLP 兼容后端

在 Program.cs 中配置

usingOpenTelemetry;usingOpenTelemetry.Metrics;usingOpenTelemetry.Trace;usingOpenTelemetry.Resources;varserviceName="EnterpriseAI";varserviceVersion="1.0.0";builder.Services.AddOpenTelemetry().WithTracing(tracing=>tracing.AddSource(serviceName).AddAspNetCoreInstrumentation().AddHttpClientInstrumentation().AddSource("Microsoft.SemanticKernel")// 自动捕获 SK 的 spans.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(serviceName,serviceVersion:serviceVersion)).AddOtlpExporter())// 发送到 Jaeger、Aspire Dashboard 等.WithMetrics(metrics=>metrics.AddAspNetCoreInstrumentation().AddHttpClientInstrumentation().AddMeter(serviceName).AddPrometheusExporter());// 可选:暴露 Prometheus 端点
1.3 为 AI 组件添加自定义 Span

我们需要在关键位置创建自定义 Span,记录模型调用、Token 用量等关键信息。

创建 ActivitySource

publicstaticclassAIDiagnostics{publicstaticreadonlyActivitySourceActivitySource=new("EnterpriseAI.AI","1.0.0");}

在 Semantic Kernel 调用中添加 Span

由于 Semantic Kernel 内部已支持 OpenTelemetry(通过HttpClient的自动追踪),但我们可以包装 Invoke 方法以添加更细粒度的信息。

publicclassObservableKernel{privatereadonlyKernel_kernel;publicasyncTask<string>InvokePromptWithTelemetryAsync(stringprompt,KernelArguments?arguments=null){usingvaractivity=AIDiagnostics.ActivitySource.StartActivity("AI Invoke",ActivityKind.Server);activity?.SetTag("prompt",prompt);activity?.SetTag("arguments",arguments?.ToString());varstopwatch=Stopwatch.StartNew();try{varresult=await_kernel.InvokePromptAsync(prompt,arguments);stopwatch.Stop();activity?.SetTag("duration_ms",stopwatch.ElapsedMilliseconds);activity?.SetTag("output_length",result.Length);// 记录 Token 用量(需从响应中提取)// 注意:OpenAI SDK 返回的 Completions 对象包含 Usage 信息// 这里仅示例,实际需要从 Kernel 返回的结果中解析returnresult;}catch(Exceptionex){activity?.SetStatus(ActivityStatusCode.Error,ex.Message);throw;}}}

对于 ONNX Runtime 推理,也可以添加类似的 Span。

在插件方法中添加 Span

publicclassWeatherPlugin{[KernelFunction]publicasyncTask<string>GetWeatherAsync(stringcity){usingvaractivity=AIDiagnostics.ActivitySource.StartActivity("WeatherPlugin.GetWeather");activity?.SetTag("city",city);try{varweather=await_weatherService.GetAsync(city);activity?.SetTag("result",weather);returnweather;}catch(Exceptionex){activity?.SetStatus(ActivityStatusCode.Error,ex.Message);throw;}}}
1.4 记录 Token 消耗与成本

Token 消耗是 LLM 调用的核心指标。Azure OpenAI 和 OpenAI 的响应中都包含Usage对象。在 Semantic Kernel 中,可以通过配置OpenAIPromptExecutionSettings并捕获响应来提取。

自定义 DelegatingHandler 捕获响应

publicclassTokenUsageHandler:DelegatingHandler{publicstaticreadonlyMeterMeter=new("EnterpriseAI.AI");publicstaticreadonlyCounter<int>InputTokenCounter=Meter.CreateCounter<int>("ai.input_tokens");publicstaticreadonlyCounter<int>OutputTokenCounter=Meter.CreateCounter<int>("ai.output_tokens");publicstaticreadonlyHistogram<double>RequestDuration=Meter.CreateHistogram<double>("ai.request_duration");protectedoverrideasyncTask<HttpResponseMessage>SendAsync(HttpRequestMessagerequest,CancellationTokencancellationToken){varstopwatch=Stopwatch.StartNew();varresponse=awaitbase.SendAsync(request,cancellationToken);stopwatch.Stop();// 尝试解析响应中的 usagevarcontent=awaitresponse.Content.ReadAsStringAsync();if(response.IsSuccessStatusCode&&TryExtractUsage(content,outvarinputTokens,outvaroutputTokens)){InputTokenCounter.Add(inputTokens);OutputTokenCounter.Add(outputTokens);RequestDuration.Record(stopwatch.Elapsed.TotalSeconds);}// 重新包装内容流,因为已经读取了一次response.Content=newStringContent(content);returnresponse;}privateboolTryExtractUsage(stringjson,outintinputTokens,outintoutputTokens){// 解析 OpenAI/Azure OpenAI 响应格式// 示例:{"usage":{"prompt_tokens":10,"completion_tokens":20,"total_tokens":30}}inputTokens=outputTokens=0;try{vardoc=JsonDocument.Parse(json);if(doc.RootElement.TryGetProperty("usage",outvarusage)){inputTokens=usage.GetProperty("prompt_tokens").GetInt32();outputTokens=usage.GetProperty("completion_tokens").GetInt32();returntrue;}}catch{}returnfalse;}}

在注册 HttpClient 时添加此 Handler:

builder.Services.AddHttpClient("AzureOpenAI").AddHttpMessageHandler<TokenUsageHandler>();
1.5 集成可观测性后端
  • 本地开发:使用 .NET Aspire Dashboard 或 Jaeger 查看 traces。
  • 生产环境:导出到 Azure Application Insights、AWS X-Ray、Datadog 或 Prometheus + Grafana。

导出到 Application Insights(Azure Monitor):

dotnetaddpackage OpenTelemetry.Exporter.AzureMonitor

配置:

builder.Services.AddOpenTelemetry().WithTracing(tracing=>tracing.AddAzureMonitorTraceExporter(options=>{options.ConnectionString="<Your Application Insights Connection String>";}));

2 模型的 A/B 测试与灰度发布策略

2.1 为什么需要 A/B 测试?

模型迭代速度快,新模型可能在准确率、延迟、成本上有差异。直接全量替换风险极高。通过 A/B 测试,我们可以:

  • 比较不同模型版本的效果(如 GPT-3.5 vs GPT-4)
  • 验证新提示词模板的效果
  • 评估不同部署后端(如本地 vs 云端)的性能
2.2 构建实验框架

设计一个简单的实验框架,支持按用户、租户或随机流量分配变体。

定义实验配置

publicclassExperimentConfig{publicstringName{get;set;}="model_ab_test";publicList<Variant>Variants{get;set;}=new();publicstringDefaultVariant{get;set;}}publicclassVariant{publicstringName{get;set;}publicintWeight{get;set;}// 流量权重百分比publicstringModelName{get;set;}publicstringPromptTemplate{get;set;}publicDictionary<string,object>Parameters{get;set;}}

配置存储在appsettings.json

{"Experiments":{"model_ab_test":{"Variants":[{"Name":"gpt35","Weight":50,"ModelName":"gpt-3.5-turbo","PromptTemplate":"default"},{"Name":"gpt4","Weight":50,"ModelName":"gpt-4o","PromptTemplate":"default"}],"DefaultVariant":"gpt35"}}}

实验分配器

publicinterfaceIExperimentAssigner{VariantGetVariant(stringexperimentName,stringuserId);}publicclassRandomExperimentAssigner:IExperimentAssigner{privatereadonlyIConfiguration_config;privatereadonlyRandom_random=new();publicVariantGetVariant(stringexperimentName,stringuserId){varexpConfig=_config.GetSection($"Experiments:{experimentName}").Get<ExperimentConfig>();if(expConfig==null)returnnull;// 可以基于 userId 实现确定性哈希,保证同一用户始终看到同一变体varhash=(userId?.GetHashCode()??0)%100;intcumulative=0;foreach(varvariantinexpConfig.Variants){cumulative+=variant.Weight;if(hash<cumulative)returnvariant;}returnexpConfig.Variants.FirstOrDefault(v=>v.Name==expConfig.DefaultVariant);}}

在服务中使用

publicclassChatService{privatereadonlyIExperimentAssigner_assigner;privatereadonlyKernelFactory_kernelFactory;publicasyncTask<string>ChatAsync(stringuserMessage,stringuserId){varvariant=_assigner.GetVariant("model_ab_test",userId);varkernel=_kernelFactory.Create(variant);varresult=awaitkernel.InvokePromptAsync(variant.PromptTemplate,new(){["input"]=userMessage});// 记录实验数据(见下节)returnresult;}}
2.3 灰度发布策略

灰度发布是逐步将流量切换到新版本的过程。与 A/B 测试不同,灰度通常有一个明确的目标(全量切换)。我们可以利用相同的实验框架,通过调整权重实现:

  1. 初始:新模型权重 0%,旧模型 100%。
  2. 灰度 1%:新模型 1%,旧模型 99%。
  3. 灰度 10%:新模型 10%,旧模型 90%。
  4. …直至 100%。

自动化灰度:结合监控指标(错误率、延迟),当新版本指标异常时自动回滚。

2.4 实验数据收集与评估

A/B 测试需要收集关键指标,如:

  • 业务指标:用户满意度(点赞/点踩)、任务完成率
  • 技术指标:延迟、Token 消耗、错误率
  • 成本指标:每次请求的成本

将实验变体信息添加到 OpenTelemetry span 的标签中:

usingvaractivity=AIDiagnostics.ActivitySource.StartActivity("Chat");activity?.SetTag("experiment","model_ab_test");activity?.SetTag("variant",variant.Name);

在后端(如 Application Insights)中,可以通过这些标签进行分组分析,比较不同变体的表现。

3 成本控制:Token 经济学与缓存策略

3.1 Token 成本构成

LLM 的成本与 Token 数量直接相关:

  • 输入 Token:提示词、历史对话、检索的上下文
  • 输出 Token:模型生成的回答
3.2 缓存策略

1. 精确缓存(Exact Match Cache)

对于完全相同的用户问题,直接返回缓存的结果。适用于 FAQ 类场景。

publicclassExactMatchCache{privatereadonlyIMemoryCache_cache;privatereadonlyTimeSpan_ttl;publicasyncTask<string>GetOrAddAsync(stringuserInput,Func<Task<string>>factory){returnawait_cache.GetOrCreateAsync(userInput,asyncentry=>{entry.AbsoluteExpirationRelativeToNow=_ttl;returnawaitfactory();});}}

2. 语义缓存(Semantic Cache)

对于语义相似但表述不同的问题,可以基于向量相似度判断是否命中缓存。将问题向量化,计算与缓存项的相似度,超过阈值则返回。

publicclassSemanticCache{privatereadonlyIVectorDatabase_vectorDb;privatereadonlyITextEmbeddingGenerationService_embeddingService;publicasyncTask<string?>GetAsync(stringuserInput,floatsimilarityThreshold=0.95f){varembedding=await_embeddingService.GenerateEmbeddingAsync(userInput);varresults=await_vectorDb.SearchAsync(embedding.ToArray(),topK:1);if(results.Any()&&results[0].Score>=similarityThreshold){returnresults[0].Metadata["response"];}returnnull;}publicasyncTaskSetAsync(stringuserInput,stringresponse){varembedding=await_embeddingService.GenerateEmbeddingAsync(userInput);await_vectorDb.AddAsync(Guid.NewGuid().ToString(),response,embedding.ToArray(),metadata:newDictionary<string,object>{["response"]=response});}}

3. 提示词压缩

对于包含大量上下文的 RAG 应用,可以对检索到的内容进行摘要或提取关键句子,减少输入 Token。

publicasyncTask<string>SummarizeContextAsync(stringcontext,intmaxTokens){varprompt=$"Summarize the following context in less than{maxTokens}tokens:\n{context}";varsummary=await_kernel.InvokePromptAsync(prompt);returnsummary;}
3.3 模型选择策略

根据任务复杂度动态选择模型:

  • 简单任务(如情感分析、分类):使用轻量级模型(GPT-3.5-turbo 或本地小模型)
  • 复杂任务(如代码生成、逻辑推理):使用 GPT-4o
  • 可批处理的任务:使用异步批处理模式,降低单次调用成本
3.4 成本监控与告警

在 OpenTelemetry 指标中增加成本指标:

publicstaticclassCostMetrics{publicstaticreadonlyHistogram<double>RequestCost=Meter.CreateHistogram<double>("ai.request_cost","USD");}// 在 TokenUsageHandler 中计算成本并记录varcost=(inputTokens*0.000005)+(outputTokens*0.000015);// 示例单价CostMetrics.RequestCost.Record(cost,newTagList{{"model",modelName}});

在 Prometheus/Grafana 中建立成本仪表板,并设置预算告警(如日成本超过 $100 时触发)。

3.5 限流与配额管理

防止单个用户或租户滥用导致成本激增:

publicclassRateLimiter{privatereadonlyIMemoryCache_cache;publicboolIsAllowed(stringuserId,intmaxTokensPerDay){varkey=$"user_tokens_{userId}";vartodayTokens=_cache.Get<int>(key);if(todayTokens>maxTokensPerDay)returnfalse;// 在请求后增加 Token 计数returntrue;}publicvoidRecordTokens(stringuserId,inttokens){varkey=$"user_tokens_{userId}";_cache.Set(key,_cache.Get<int>(key)+tokens,TimeSpan.FromDays(1));}}

总结

  • OpenTelemetry 埋点:为 AI 链路添加分布式追踪和指标,记录 Token 消耗和延迟。
  • A/B 测试与灰度发布:通过实验框架安全地迭代模型和提示词,降低变更风险。
  • 成本控制:通过缓存、模型选择、限流等策略,在保证体验的前提下优化成本。
http://www.jsqmd.com/news/596450/

相关文章:

  • 通义千问3-Embedding-4B实战:3步搭建个人语义搜索系统,开箱即用
  • 3大核心功能让新手轻松玩转《杀戮尖塔》模组加载器
  • ai辅助开发:让快马平台智能解决多设备db9接口集成与信号处理难题
  • 突破硬件限制:OpenCore Legacy Patcher实现老旧Mac现代化升级的完整方案
  • 实战项目开发:在快马平台从零到一构建并部署一个可用的博客系统API
  • NHSE:打造你的专属动森岛屿,存档编辑工具全攻略
  • Nunchaku-FLUX.1-dev多尺寸生成指南:512x512标准图、768x512横版海报适配
  • 如何用极速搜索工具提升Linux文件检索效率?FSearch让系统工具不再等待
  • 3步打造专业级英雄联盟辅助工具:ChampR从入门到精通
  • 3种高效方案解决Switch游戏安装难题:Awoo Installer全技能指南
  • DeepSeek-R1-Distill-Qwen-7B快速体验:Ollama一键安装,智能问答实战教程
  • AGC电路设计避坑指南:用1N4148二极管实现THD<0.1%的自动增益控制
  • 数字波束形成中的导向矢量与FFT方法:原理对比与场景应用
  • 解决正点原子Kernel编译中arm-linux-gnueabihf-gcc缺失问题
  • Transformer 论文阅读笔记
  • RPG Maker MV Decrypter:游戏资源提取与加密解析的创新方法与实战价值
  • 告别手写代码:ImStudio可视化GUI设计器如何让Dear ImGui开发效率提升300%
  • 实测通义千问2.5-7B-Instruct工具调用:轻松构建你的第一个AI Agent
  • ReactNative for OpenHarmony项目鸿蒙化三方库:react-native-flash-message — 闪现消息组件
  • 如何打造专属家庭电视直播系统:从技术实现到个性化体验
  • 3大突破!res-downloader突破限制高效获取音乐资源实战案例
  • 网站推广SEO的技巧有哪些_网站推广SEO需要哪些硬件和软件配置
  • Ostrakon-VL 扫描终端入门:Windows 系统下快速部署与测试指南
  • OFA-Image-Caption模型在VMware虚拟机中的开发测试环境搭建
  • LFM2.5-1.2B-Thinking-GGUF快速上手:用Postman保存/generate请求模板集
  • Pixel Script Temple部署教程:ARM服务器(如NVIDIA Grace)上Qwen2.5量化部署
  • 北京专业SEO优化公司如何进行内容营销
  • C#面试必问:垃圾回收(GC)的10个实战避坑指南
  • NomNom存档编辑器:突破《无人深空》游戏体验边界的核心功能与创新价值
  • OpenClaw低代码扩展:Qwen3-32B镜像与Node-RED可视化编排整合