AI 服务质量定义:SLO 要把推理延迟、可用率和准确率都算进去
AI 服务质量定义SLO 要把推理延迟、可用率和准确率都算进去一、99.9% 可用率不等于用户满意——AI 服务的质量是三维的传统 Web 服务的 SLO 很简单可用率。99.9% 的请求返回 2xx就算达标了。金丝雀发布看错误率Prometheus 告警绑定 5xx 比例一切清晰明了。但 AI 推理服务不是这样。一个请求可能返回 200 OK但生成的回答完全错误。从 HTTP 状态码的角度看它是成功的从用户的角度看它是失败的。如果按传统可用率定义你的推理服务可能有 99.95% 的可用率——但其中 15% 的请求返回了错误或低质量的答案而你的监控大盘对此一无所知。这就是 AI 服务质量定义的独特挑战质量是多维的。延迟响应的速度、可用率服务的可达性、准确率回答的正确性必须同时纳入 SLO。三个维度中的任何一个不达标都应该触发告警并阻止上线。二、三维 SLO 模型的定义与计算框架将 AI 服务质量定义为三个维度的四组指标graph TD subgraph 请求入口 A[用户请求] -- B{SLO 判定引擎} end subgraph 维度1_延迟 B -- C[P50 延迟] B -- D[P99 延迟] B -- E[首Token时间 TTFT] end subgraph 维度2_可用率 B -- F[HTTP 2xx 率] B -- G[推理成功率] B -- H[Token 生成完成率] end subgraph 维度3_准确率 B -- I[用户反馈分] B -- J[自动评估分] B -- K[安全拒绝率] end subgraph SLO_汇总 C -- L[每日 SLO 合规度] D -- L E -- L F -- L G -- L H -- L I -- L J -- L K -- L L -- M{任意维度 目标?} M --|是| N[触发告警 阻断发布] M --|否| O[正常] end三维 SLO 的具体定义延迟 SLOP99 端到端延迟 ≤ 5s滚动窗口 30 天达标率 ≥ 99%。但 P99 还不够——对于流式推理场景首 Token 时间TTFT必须单独设 SLO如 P95 TTFT ≤ 500ms。用户在打字时感受到的是第一句话出现前的等待。可用率 SLOHTTP 2xx 率 ≥ 99.9% 且推理成功率 ≥ 99.5%。区分HTTP 200 但推理失败和HTTP 500前者在传统监控中是不可见的。准确率 SLO自动评估分如 Rouge-L、人工评判通过率的 30 天滑动均值不低于历史基线的 95%。这是最难量化但最重要的维度——它直接定义了用户感知到的质量。三、三维 SLO 监控的生产级实现以下 Go 代码实现了 SLO 的实时追踪和燃尽图计算package main import ( fmt sync time ) // SLODimension 定义 SLO 的三个维度 type SLODimension string const ( DimLatency SLODimension latency DimAvailability SLODimension availability DimAccuracy SLODimension accuracy ) // SLOIndicator 单个 SLO 指标的追踪器 type SLOIndicator struct { Name string TargetRate float64 // 目标达标率如 0.99 表示 99% Window time.Duration // 滑动窗口大小如 30*24*time.Hour } // SLOTracker 三维 SLO 追踪器协程安全 type SLOTracker struct { mu sync.RWMutex indicators map[SLODimension][]SLOIndicator buckets map[string]*TokenBucket // key: 维度_指标_日期 window time.Duration alertFunc func(dimension SLODimension, indicator string, current float64, target float64) } // TokenBucket 日级 SLO Token 桶追踪好事件/总事件比率 type TokenBucket struct { GoodEvents int64 TotalEvents int64 } // SLOReport 单次 SLO 评估报告 type SLOReport struct { Dimension SLODimension Indicator string Compliance float64 // 当前合规度 Target float64 // 目标合规度 Healthy bool BurnRate float64 // 燃尽率消耗错误预算的速率 } func NewSLOTracker(window time.Duration, alertFunc func(SLODimension, string, float64, float64)) *SLOTracker { return SLOTracker{ indicators: map[SLODimension][]SLOIndicator{ DimLatency: { {Name: p99_e2e_latency, TargetRate: 0.99, Window: window}, {Name: p95_ttft, TargetRate: 0.99, Window: window}, }, DimAvailability: { {Name: http_2xx_rate, TargetRate: 0.999, Window: window}, {Name: inference_success_rate, TargetRate: 0.995, Window: window}, }, DimAccuracy: { {Name: auto_eval_pass_rate, TargetRate: 0.95, Window: window}, {Name: user_thumb_up_rate, TargetRate: 0.90, Window: window}, }, }, buckets: make(map[string]*TokenBucket), window: window, alertFunc: alertFunc, } } // Record 记录一次请求的三维 SLO 事件 func (t *SLOTracker) Record(latencyOK, availOK, accuracyOK bool) { t.mu.Lock() defer t.mu.Unlock() dateKey : time.Now().Format(2006-01-02) // 记录延迟维度 t.recordEvent(DimLatency, p99_e2e_latency, dateKey, latencyOK) // 记录可用性维度 t.recordEvent(DimAvailability, http_2xx_rate, dateKey, availOK) // 记录准确率维度 t.recordEvent(DimAccuracy, auto_eval_pass_rate, dateKey, accuracyOK) } func (t *SLOTracker) recordEvent(dim SLODimension, indicator, dateKey string, good bool) { key : string(dim) _ indicator _ dateKey bucket, ok : t.buckets[key] if !ok { bucket TokenBucket{} t.buckets[key] bucket } bucket.TotalEvents if good { bucket.GoodEvents } } // Evaluate 评估当前 SLO 合规度返回各维度报告 func (t *SLOTracker) Evaluate() []SLOReport { t.mu.RLock() defer t.mu.RUnlock() var reports []SLOReport cutoff : time.Now().Add(-t.window).Format(2006-01-02) for dim, indicators : range t.indicators { for _, ind : range indicators { // 聚合滑动窗口内的所有日桶 var totalGood, totalEvents int64 for key, bucket : range t.buckets { if !t.matchesIndicator(key, string(dim), ind.Name, cutoff) { continue } totalGood bucket.GoodEvents totalEvents bucket.TotalEvents } compliance : 1.0 // 无数据时默认达标 if totalEvents 0 { compliance float64(totalGood) / float64(totalEvents) } burnRate : 0.0 errorBudget : 1.0 - ind.TargetRate if errorBudget 0 { burnRate (ind.TargetRate - compliance) / errorBudget } report : SLOReport{ Dimension: dim, Indicator: ind.Name, Compliance: compliance, Target: ind.TargetRate, Healthy: compliance ind.TargetRate, BurnRate: burnRate, } reports append(reports, report) if !report.Healthy t.alertFunc ! nil { t.alertFunc(dim, ind.Name, compliance, ind.TargetRate) } } } return reports } func (t *SLOTracker) matchesIndicator(key, dim, indicator, cutoff string) bool { // key 格式: latency_p99_e2e_latency_2026-07-01 expected : dim _ indicator _ if len(key) len(expected) { return false } if key[:len(expected)] ! expected { return false } dateStr : key[len(expected):] return dateStr cutoff } // ---- 使用示例 ---- func main() { tracker : NewSLOTracker(30*24*time.Hour, func(dim SLODimension, ind string, cur, tgt float64) { fmt.Printf([ALERT] %s/%s 合规度 %.2f%% 目标 %.2f%%\n, dim, ind, cur*100, tgt*100) }) // 模拟记录请求事件 tracker.Record(true, true, true) // 全面达标 tracker.Record(false, true, true) // 延迟超标 tracker.Record(true, false, true) // 可用性超标 tracker.Record(true, true, false) // 准确率超标 reports : tracker.Evaluate() for _, r : range reports { status : HEALTHY if !r.Healthy { status UNHEALTHY } fmt.Printf([%s] %s/%s: %.2f%% (目标 %.2f%%) 燃尽率: %.2f\n, status, r.Dimension, r.Indicator, r.Compliance*100, r.Target*100, r.BurnRate) } }设计考量燃尽率Burn Rate这是 Google SRE 的核心概念。燃尽率 (目标 - 当前) / 错误预算。燃尽率 1 意味着正在以超过预算的速度消耗错误配额。燃尽率 14.4 意味着一天就烧掉了一个月 2% 的错误预算——这应该触发 P1 告警。无数据默认达标一个新上线的服务缺乏历史数据不能因为没有数据就判定 SLO 失败。这个默认值应该随着服务成熟逐步收紧。原子记录单次请求的三个维度通过一次Record调用原子记录避免维度间的数据不一致。四、SLO 定义的陷阱不要把 SLO 写成 SLASLO 与 SLA 的本质区别。SLO 是内部目标没达到说明需要改进。SLA 是外部合同没达到意味着赔钱。很多团队直接把99.9% 可用率从 SLO 写成 SLA 对外承诺这是给自己挖坑。准确率的 SLO 更应该保守——自动评估分的 95% 基线不是法律承诺是一个需要持续优化的内部目标。准确率 SLO 的量化困境。你怎么知道模型的回答是准确的自动评估分如 Rouge-L、GPT-Eval只是近似值。人工标注成本极高。一个务实的方案是用自动评估分做日常 SLO 追踪月度随机抽样 200 条做人工标注作为真值校准修正自动评估的系统性偏差。SLO 的好天气偏误。在流量平稳、无突发事件时SLO 看起来总是绿的。但真正检验 SLO 的是异常时刻模型升级、依赖服务故障、突发流量。建议在 SLO 报告旁边附上压力测试下的 SLO 表现——在混沌实验期间的三维达标率比平静期的达标率更有说服力。多租户差异化。不同客户的 SLO 期望不同。付费用户的延迟容忍度远低于免费用户对准确率的要求也更高。SLO 追踪器应该支持按租户、按 API Key 分组统计而不是只看全局平均。五、总结AI 服务的质量不是一维的。将延迟、可用率和准确率同时纳入 SLO 体系才能在监控大盘上看到服务质量的完整画像。落地三步第一步定义延迟P99 ≤ 5s、可用率≥ 99.9%、准确率≥ 基线 95%的 SLO 基线第二步在推理服务的中间件层接入本文的 SLOTracker记录每次请求的三维事件第三步在 Grafana 上构建燃尽率大盘设置燃尽率 10 时的 P1 告警。基础设施不需要漂亮话。它需要的是当你看着监控大盘时每一个绿色的指标背后都有一个经得起推敲的定义。