返回博客

我们如何把客服大模型推理成本压到$0.07/千次:Triton+FP8+Spot实例实录

2026/9/304 分钟阅读
我们如何把客服大模型推理成本压到$0.07/千次:Triton+FP8+Spot实例实录

从OOM到$0.07/千次:高吞吐AI推理的实战血泪史

做高吞吐AI推理服务,说实话,不是靠堆GPU就能搞定的。

我之前负责过很多项目,印象最深的还是去年帮一个电商客户重构他们的客服大模型。第一天上线,直接OOM(显存溢出)报警,整个服务崩了。那之后几个月,我们一直在跟延迟、吞吐量和账单赛跑。现在回头看,那段日子踩过的每一个坑,都成了后来的优化经验。

如何构建高吞吐 AI 推理服务:从模型部署到成本优化 - 插图 1

今天想聊聊这段经历——不是教科书式的流程,而是真实场景下的取舍。毕竟在真实业务里,"完美方案"往往不存在的,只有"最能打的组合"。

一、 从OOM开始:显存与部署的生死局

上线第一天,我们的模型在A100上跑着跑着就炸了。排查后发现,问题出在显存管理上:模型权重、激活值、还有推理过程中的临时缓存,全挤在一起,谁都不让步。

我当时做了几件事,顺序很关键:

先换框架。 之前我们用原生的PyTorch服务,虽然灵活,但并发控制太弱。果断切到 Triton Inference Server。为什么?它原生支持多模型热加载,而且动态批处理是它的杀手锏。

动态批处理配置是重点。 我调了很久,最终定下的参数是:

{
  "name": "customer_service_model",
  "backend": "tensorrt",
  "dynamic_batching": {
    "max_queue_delay_microseconds": 500,
    "preferred_batch_size": [4, 8, 16],
    "max_batch_size": 32
  }
}

注意:max_queue_delay_microseconds 不能设太大。我们一开始设了1000微秒,结果P99延迟在请求突增时抖动超200ms——后来发现,延迟越高,突发流量一来,队列里的请求全堆积,等待时间指数级上升。

改到500微秒后,GPU利用率从42%拉到89%,这才是关键转折点。

二、 量化不是银弹:FP8的坑我替你们踩了

模型并行、流水线批处理,这些概念大家可能都懂。但真正让我头疼的是量化。

量化确实是降本利器,但别一上来就搞FP8——我踩过坑。

当时为了追求极致性能,直接上FP8。结果呢?校准数据少20%,P99延迟直接翻倍,模型准确率还掉了1.5个点。亏大了。

修正: 截至 TensorRT 9.3,原生FP8校准在TensorRT里其实不好直接用。我们最终用的是 TRT-LLM 的量化工具链,特别是它的 calibrate.py 脚本生成 scale 文件,配合 NVIDIA 的 fp8 分支才稳定跑起来。

INT8 更稳,但 FP8 在 A100/H100 上确实能省一半显存。取舍看场景:如果延迟容忍度低,用 INT8;如果能接受一点精度损失换吞吐,再上 FP8。

代码层面,我现在这么写(示意,非直接运行代码):

# 注意:这是TRT-LLM的调用方式,非原生TensorRT API
import tensorrt_llm

builder = tensorrt_llm.Builder(...)
config = builder.net.execution_policy()
config.set_quantization("fp8")  # 实际通过TRT-LLM工具链配置

三、 并发控制:别让GPU哭

高吞吐不等于高并发。我之前犯过一个低级错误:开了Semaphore限制并发,结果GPU利用率反而低了——因为请求全卡在信号量外面,GPU闲着,请求排队着。

教训: 动态批处理比手动Semaphore管用多了。Triton 内部的批处理机制比手写asyncio更优雅,也更贴近硬件。

但如果必须控制并发(比如防止上游雪崩),我会这样写:

import asyncio
from asyncio import Semaphore

semaphore = Semaphore(50)  # 根据GPU容量定,别硬猜

async def process_request(request: dict) -> dict:
    async with semaphore:
        return await inference_model(request)

注意:semaphore的值要经过压测。我们用 locust 压测时发现,50个并发在A10上刚好打满,再多就OOM。

四、 成本优化:账单不会骗人

成本优化不是喊口号,是实打实的数字。我们当时的账单每月几万刀,看着心疼。

三个动作:

  1. Spot 实例:客服模型是非实时核心交易,用竞价实例能省70%。我们切了G5.12xlarge Spot,单价直接腰斩。
  2. HPA 自动扩缩容:K8s 里配好 HPA,CPU/GPU 利用率超过阈值就扩容,低于阈值就缩。别人工干预,人力成本高。
  3. 预留实例保底:稳定基线流量用预留实例,突发用Spot。我们算过账,预留 Instance 成本降30-40%,但灵活度差。所以比例很重要:我们70% Spot + 30% 预留。

成本追踪我写了个小工具,监控 GPU 使用时长:

class CostTracker:
    def __init__(self, gpu_type: str, hourly_rate: float):
        self.gpu_type = gpu_type
        self.hourly_rate = hourly_rate
        self.total_seconds = 0
    
    def record_usage(self, seconds: int):
        self.total_seconds += seconds
    
    def calculate_cost(self) -> float:
        return (self.total_seconds / 3600) * self.hourly_rate

这个简单,但够用。后来接了 Prometheus + Grafana,实时看账单曲线,爽。

五、 测试:不压测就是赌博

建立性能测试体系,别靠感觉。我们用 locust 压测,重点盯三个指标:

  • QPS:每秒查询数,决定吞吐量上限。
  • P99 延迟:99%的请求延迟,比平均延迟更重要——平均会被长尾掩盖。
  • GPU 利用率:低于70%说明资源浪费,高于95%说明瓶颈。

真实数据: 加了动态批处理后,QPS 涨了 3.2 倍,但 P99 延迟在请求突增时抖动超 200ms——就是上面说的 max_queue_delay_microseconds 设太激进的问题。改参数后,P99 稳定在 85ms 以内。

每次迭代前跑基准测试,对比优化前后效果。没有数据支撑的优化,都是耍流氓。

六、 结语:没有最好,只有最合适

回头看,从 OOM 到成本压到 $0.07/千次,靠的不是某个神奇技巧,而是一步步踩坑、修正、再踩坑的过程。

  • 框架选 Triton,因为它懂 GPU。
  • 量化用 TRT-LLM,别硬刚 TensorRT 原生 API。
  • 并发靠动态批处理,别手写 Semaphore。
  • 成本靠 Spot + HPA,别依赖人工。
  • 测试靠数据,别靠直觉。

每个公司、每个模型场景都不一样。我的经验不一定适合你,但希望至少让你避开几个大坑。

如果你也在折腾高吞吐 AI 推理,欢迎交流。毕竟,降低成本、提升体验,是我们这行的永恒命题。