推理 API 防滥用实战:基于 Token 消耗的异常检测和自动限流 推理 API 防滥用实战基于 Token 消耗的异常检测和自动限流一、Token 账单失控凌晨的异常消耗告警早晨 7 点打开 GPU 资源监控面板。昨夜 23 点到凌晨 3 点推理 API 的 Token 消耗量是平时峰值的 6 倍但请求量只增加了 20%。排查发现一个用户的 prompt 从正常长度 200 token 变成了每次 8000 token 的重复文本输入模型一直在生成对不起请重新输入的回复。这不是 DDoS而是更隐蔽的 API 滥用。由恶意或故障客户端导致的 Token 消耗剧增不会触发传统的 QPS 限流——请求量正常——但花费的 GPU 算力和费用远超正常水平。在按 Token 计费的模式下这种滥用会直接转化为财务损失。传统 API 限流基于 QPS每秒请求数和并发连接数。但推理 API 的成本单位是 TokenQPS 限流无法反映真实消耗。需要基于 Token 消耗量的限流和异常检测机制。二、分层限流架构QPS → Token Rate → 异常检测一个实用的推理 API 防滥用架构应该是三层限流每层从不同维度控制资源消耗。flowchart TD A[客户端请求到达] -- B[L1: 请求级限流] B --|超出 QPS| R1[429: Too Many Requests] B --|通过| C[L2: Token 速率限制] C --|超出 Token/Min| R2[429: Token Quota Exceeded] C --|通过| D[L3: 行为异常检测] D --|异常模式| E[自动降级或阻断] D --|正常| F[推理处理] F -- G[返回结果] subgraph 异常模式 H[输入 Token 暴增] I[输出循环/重复] J[高频重试无效请求] end H -- D I -- D J -- DL1 请求级限流按用户的 API Key 做滑动窗口 QPS 限制。这是最传统的限流但不区分请求的实际成本。一个简单 prompt 和一个 4000 token 的长 prompt 在 QPS 限流下被等同对待。L2 Token 速率限制按每分钟 Token 消耗量做限流。统计维度包括输入 Token 和输出 Token。计算方式是时间窗口内的累计 Token 消耗超过用户配额的阈值立即返回 429。这比 QPS 限流更能反映真实成本。L3 行为异常检测实时分析请求特征识别异常模式。包括输入 Token 量突增相比历史均值突然 10 倍以上输出重复率过高生成的文本中同一 token 序列反复出现无效请求比例过高请求被内容安全拦截或模型拒绝的比例异常三、Go 实现的三层防滥用中间件package abuseguard import ( context sync time ) // TokenRateLimiter 基于 Token 消耗速率的限流器 type TokenRateLimiter struct { mu sync.Mutex // users 存储每用户的令牌桶 // Key: API Key, Value: 令牌桶当前容量 users map[string]*TokenBucket // maxTokensPerMin 每分钟最大 Token 配额 maxTokensPerMin int64 // refillInterval 令牌补充间隔 refillInterval time.Duration } // TokenBucket 用户级令牌桶 type TokenBucket struct { tokens float64 // 当前可用 Token 数 lastRefill time.Time capacity float64 // 桶容量 突发峰值容忍度 } // Allow 检查请求的 Token 消耗是否在配额内 // consumedTokens 输入 Token 数 预期输出 Token 数 func (r *TokenRateLimiter) Allow( apikey string, consumedTokens int64, ) bool { r.mu.Lock() defer r.mu.Unlock() bucket, exists : r.users[apikey] if !exists { bucket TokenBucket{ tokens: float64(r.maxTokensPerMin), capacity: float64(r.maxTokensPerMin) * 2, // 2 倍突发 lastRefill: time.Now(), } r.users[apikey] bucket } // 按时间比例补充 Token now : time.Now() elapsed : now.Sub(bucket.lastRefill).Minutes() bucket.tokens elapsed * float64(r.maxTokensPerMin) if bucket.tokens bucket.capacity { bucket.tokens bucket.capacity } bucket.lastRefill now // 请求的 Token 消耗是否超过可用 Token if bucket.tokens float64(consumedTokens) { return false // 配额不足 } bucket.tokens - float64(consumedTokens) return true } // AbuseDetector 基于行为特征的异常检测器 type AbuseDetector struct { mu sync.RWMutex // userStats 每用户的统计窗口 userStats map[string]*UserStats // windowSize 统计窗口大小 windowSize time.Duration } // UserStats 用户在窗口内的行为统计 type UserStats struct { avgInputTokens float64 inputTokenSamples int invalidReqRatio float64 lastSeen time.Time } // DetectAnomaly 检测请求是否异常 // 返回异常类型nil 表示正常 func (d *AbuseDetector) DetectAnomaly( apikey string, req *RequestMeta, ) *AnomalyResult { d.mu.Lock() defer d.mu.Unlock() stats, exists : d.userStats[apikey] if !exists { d.userStats[apikey] UserStats{ avgInputTokens: float64(req.InputTokens), inputTokenSamples: 1, lastSeen: time.Now(), } return nil } // 检查1: 输入 Token 暴增 // 与历史均值的比值超过 10 倍判定为异常 if stats.inputTokenSamples 5 req.InputTokens int64(stats.avgInputTokens*10) { return AnomalyResult{ Type: input_token_spike, Detail: fmt.Sprintf( 输入 Token %d 超出历史均值 %.0f 的 10 倍, req.InputTokens, stats.avgInputTokens, ), } } // 检查2: 输出重复率过高 if req.RepeatedTokensRatio 0.8 { return AnomalyResult{ Type: output_loop, Detail: fmt.Sprintf( 输出重复率 %.1f%% 超过 80%% 阈值, req.RepeatedTokensRatio*100, ), } } // 更新滑动窗口统计 alpha : 0.3 // EMA 平滑系数 stats.avgInputTokens alpha*float64(req.InputTokens) (1-alpha)*stats.avgInputTokens stats.inputTokenSamples stats.lastSeen time.Now() return nil // 行为正常 }设计要点令牌桶算法比固定窗口更公平——允许短时间的突发流量但限制长期平均速率EMA指数移动平均计算历史行为的滑动窗口均值对近期行为权重更高异常检测结果不直接阻断而是写入请求 Context由上游的决策层决定响应策略四、限流策略的权衡防护 vs 用户体验误判正常波动。用户在非高峰时段集中提交长期累积的任务天然会产生突发 Token 消耗。单纯基于 Token 速率的限流会误判。需要结合时间段和用户历史行为做动态阈值调整——白天的营销活动与深夜的恶意行为应该有不同判断标准。重试风暴。当客户端收到 429 后常见的错误处理是重试。如果不加退避策略大量 429 响应本身会产生新的流量压力。API 必须在 429 响应中返回Retry-After头指示客户端何时重试。配额设计。Token 配额如果太低正常使用也会被限制。太高则失去防护意义。建议基于用户历史消耗的 P95 值设置基准配额突发峰值为 2 倍基准。按月对超过 P95 的用户单独审查。不适合 Token 速率限流的场景实时对话场景用户期望即时响应限流延迟不可接受模型调优场景研发人员可能频繁测试不同 prompt批量推理场景单次请求的 Token 消耗量天然很高五、总结推理 API 防滥用的三层架构请求级限流QPS 滑动窗口传统但必要的入口控制Token 速率限制按每分钟 Token 消耗量的令牌桶限流行为异常检测识别输入暴增、输出循环、高无效率等异常模式核心原则是用 Token 消耗量而非请求次数度量 API 的使用成本。令牌桶算法平衡了速率控制和突发容忍。异常检测用 EMA 滑动窗口建立行为基线偏差超过 10 倍时触发告警或降级。