1. 为什么 QPS 全绿,用户还是觉得慢
如果你是从传统 Web 后端转过来做大模型应用的,大概率经历过这个场景:监控大盘上 QPS 平稳、P99 延迟 800ms、错误率 0.1%,一切指标都是绿的,但产品群里用户还在抱怨"回答太慢了""打字机卡顿"。问题出在哪?
传统 Web 服务的性能模型里,一个请求的处理成本基本是固定的——查一次数据库、渲染一个模板,耗时波动不大。但大模型应用引入了一个传统架构里不存在的变量:Token。一个对话请求的处理时间不再由"请求数"决定,而是由"输入 Token 数 + 输出 Token 数"决定。同样的 QPS,如果用户的 Prompt 从 500 Token 涨到 2000 Token,底层 GPU 的实际负载已经翻了好几倍,而 QPS 这个指标完全感知不到。
所以大模型应用的性能观测必须换一套坐标系:从"请求维度"切到"Token 维度"。这篇文章我会把性能指标拆成四层——基础设施层、推理引擎层、服务网关层、业务效果层,然后用 TaoToken 作为统一接入通道,给出可复制的配置骨架和逐层验证动作,帮你建立一条端到端的性能观测基线。适合正在做 LLM 应用落地、需要给推理服务定 SLA 的后端和算法工程师。
2. 四层指标体系:从 GPU 到用户满意度
先把全景图摆出来,后面每一层我都会给具体的采集动作。
基础设施层关注硬件状态:GPU 利用率与显存占用、GPU 功耗与温度、PCIe/NVLink 带宽、内存与磁盘 I/O。这一层是"物理上限",GPU 利用率决定了推理引擎的吞吐天花板。
推理引擎层是整条链路里最关键的一层,因为性能瓶颈基本都策源于此。核心指标有三个:TTFT(首 Token 延迟)、TPOT(每输出 Token 延迟)、Token 吞吐量。此外还有 KV Cache 命中率、队列深度与排队延迟。
服务网关层负责稳定性和成本:端到端延迟(P50/P95/P99)、请求成功率与错误码分布、并发连接数、Token 消耗速率与成本统计、限流触发次数。
业务效果层评估最终体验:任务完成率、平均对话轮次、生成质量评分、单次对话成本、用户满意度。
四层之间是层层推导的关系:基础设施层的 GPU 利用率决定推理引擎的吞吐上限;推理引擎的 TTFT/TPOT 直接决定网关层的端到端延迟;前三层的综合效果最终体现在业务效果层。下面重点拆解推理引擎层的三个核心指标。
2.1 TTFT:决定用户"感知响应速度"
TTFT(Time To First Token)是从请求到达推理引擎到第一个 Token 生成完毕的时间。它主要由 Prefill 阶段的计算时间决定——输入 Token 越多,Prefill 时间越长,因为注意力机制要对全部输入做一次前向计算。
对对话式应用来说,TTFT 决定了用户感知的"响应速度"。经验阈值:TTFT 在 500ms 以内用户感觉是"秒回";超过 2 秒用户开始明显感到等待;超过 5 秒很多人会直接刷新或放弃。优化方向包括减小最大输入长度、启用分块预填充(Chunked Prefill)、使用前缀缓存(Prefix Caching)复用系统提示词的计算结果。
2.2 TPOT:决定"打字机"流不流畅
TPOT(Time Per Output Token)是每生成一个输出 Token 的平均时间,等于 Decode 阶段耗时除以输出 Token 数。它衡量的是模型的生成速度,直接决定流式输出的流畅度。
阈值参考:TPOT 在 50ms 以内,用户几乎感觉不到延迟,接近人类阅读速度;超过 100ms 会感到"卡顿";超过 200ms 体验显著恶化,用户会怀疑是不是断流了。TPOT 受 KV Cache 竞争、批处理大小、显存带宽影响较大。
2.3 Token 吞吐:GPU 到底有没有被喂饱
Token 吞吐量是单位时间内推理引擎处理的总 Token 数(输入 + 输出),是 GPU 利用率的直接反映。粗略计算公式:
吞吐(tokens/s) = 并发请求数 × 平均输出 Token 数 / 平均端到端延迟三者之间存在一条铁律:提高并发数能提升吞吐,但会增加 TTFT(排队效应)和 TPOT(KV Cache 竞争);降低最大序列长度能改善 TTFT 和 TPOT,但会限制上下文能力。性能调优的本质就是在这三个指标之间找平衡点。
3. TaoToken 前置:统一 Key 与接入通道
在开始采集指标之前,得先有一个稳定的接入通道。我这边用的是 TaoToken 作为统一入口,好处是模型对话、编码 Agent、API 调用走同一套 Key,指标采集时不用在多个供应商之间来回切换,观测口径统一。
你需要先拿到 API Key。访问控制台创建:
https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=console然后在 API Keys 页面生成密钥:
https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api-keysAPI 的基础地址是https://taotoken.net/api(注意这个地址不加 UTM 参数,直接用于代码里的 base_url)。拿到 Key 之后,先别急着写业务代码,用一次最小请求确认通道是通的,再往上叠指标采集逻辑。
4. 可复制配置:settings.json 与 config.toml 骨架
配置分两块:一块给编码类工具(走 settings.json),一块给通用 API 客户端(走 config.toml)。两块都指向同一个 TaoToken 通道,方便统一观测。
4.1 settings.json 骨架
适用于 Claude Code 这类读取 settings.json 的工具,把模型请求指向 TaoToken:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "sk-你的TaoToken密钥", "ANTHROPIC_MODEL": "claude-sonnet-4-20250514", "API_TIMEOUT_MS": "600000" }, "permissions": { "allow": ["Bash", "Read", "Write", "Edit"] } }这里API_TIMEOUT_MS设成 600000(10 分钟)是有意为之——长上下文请求的 TTFT 可能到十几秒,默认超时太短会误判为失败,污染你的错误率指标。
4.2 config.toml 骨架
适用于通用 API 客户端或自建网关,把通道参数和指标采集开关放在一起:
[provider] name = "taotoken" base_url = "https://taotoken.net/api" api_key = "sk-你的TaoToken密钥" timeout_seconds = 600 [model] default = "claude-sonnet-4-20250514" max_input_tokens = 180000 max_output_tokens = 8192 [metrics] # 指标采集开关 enable_ttft = true enable_tpot = true enable_token_throughput = true # 按输入 Token 数分段统计 P99,避免长尾污染 latency_buckets = [0, 500, 2000, 8000, 32000] # 上报间隔(秒) report_interval = 15 [metrics.labels] env = "production" scene = "chat"latency_buckets这个配置很关键。LLM 推理的延迟分布是长尾分布,少量长上下文请求会把全局 P99 拉得很难看。按输入 Token 数分段统计,才能看出"到底是长请求慢,还是短请求也慢"。
5. 逐层采集与验证:从打点到看板
配置就位后,逐层验证采集是否生效。
5.1 推理引擎层:TTFT 与 TPOT 打点
以 Java + Micrometer 为例,核心是在一次完整请求结束后,把 TTFT、输出 Token 数、端到端延迟一起记录下来,TPOT 由(总延迟 - TTFT) / 输出Token数推导:
public void recordInferenceMetrics(String modelId, int inputTokens, int outputTokens, long ttftMs, long totalMs, boolean success) { totalInputTokens.increment(inputTokens); totalOutputTokens.increment(outputTokens); if (!success) { totalFailedRequests.increment(); return; } Timer ttftTimer = ttftTimers.computeIfAbsent(modelId, id -> Timer.builder("llm.ttft").tag("model", id) .publishPercentiles(0.5, 0.95, 0.99) .register(meterRegistry)); ttftTimer.record(ttftMs, TimeUnit.MILLISECONDS); if (outputTokens > 0 && totalMs > ttftMs) { double tpotMs = (double) (totalMs - ttftMs) / outputTokens; Timer tpotTimer = tpotTimers.computeIfAbsent(modelId, id -> Timer.builder("llm.tpot").tag("model", id) .publishPercentiles(0.5, 0.95, 0.99) .register(meterRegistry)); tpotTimer.record((long) tpotMs, TimeUnit.MILLISECONDS); } }Token 吞吐不用单独打点,Prometheus 用rate(llm.output.tokens[1m])从 Counter 直接算出来。
5.2 服务网关层:端到端延迟公式
网关层的端到端延迟要把网络和流式传输开销算进去。一个实用的经验公式:
用户感知延迟 ≈ TTFT + (输出Token数 × TPOT) + 网络RTT × 2对流式输出(SSE),首屏延迟基本等于TTFT + 一次RTT,全响应时间才是完整公式。所以实时对话场景下,优化 TTFT 比优化 TPOT 更有用户价值——用户先看到第一个字,心理上就已经"接受"了这次请求。
5.3 验证请求:确认通道与指标都通
用 curl 发一次流式请求,观察首 Token 到达时间:
curl -N https://taotoken.net/api/v1/messages \ -H "Content-Type: application/json" \ -H "x-api-key: sk-你的TaoToken密钥" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-sonnet-4-20250514", "max_tokens": 256, "stream": true, "messages": [{"role": "user", "content": "用一句话解释什么是TTFT"}] }'成功的话你会看到 SSE 事件流,第一个content_block_delta到达的时间就是 TTFT 的近似值。如果卡在连接阶段不动,先检查 Key 和 base_url;如果第一个 Token 迟迟不来但最终能出结果,多半是输入太长导致 Prefill 慢,属于正常现象,但要记进 TTFT 分段统计。
想快速验证模型本身是否正常,可以直接用模型对话页面发一条消息对比:
https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=model-chat5.4 业务效果层:别忽视的终极指标
技术指标全绿不代表业务成功。建议至少监控三个业务指标:任务完成率(用户是否在指定轮次内完成目标)、平均对话轮次(性能差会导致用户反复重试,轮次上升)、单次对话成本(Token 数 × 单价,连接技术和商业价值)。这三个指标才是性能优化的终极目标。
6. 本篇常见错排查
TTFT 忽高忽低,P99 特别难看。先看是不是长上下文请求混进来了。用latency_buckets按输入 Token 数分段,你会发现短请求的 P99 其实很稳,是少数长请求拉高了全局值。分段统计后再定 SLA,别用一个笼统的全局 P99 去告警。
GPU 利用率 100% 但吞吐上不去。100% 利用率不代表效率最高。如果满负荷下 TTFT 已经超过 SLA,那"满负荷"本身就是告警信号。更合理的指标是"有效 Token 吞吐 / GPU 利用率",即每单位 GPU 时间产出的有用 Token 数。如果这个比值在下降,说明批处理里混入了太多长序列,拖累了整体效率。
TPOT 算出来是负数或异常大。检查totalMs > ttftMs这个条件。流式场景下如果 TTFT 采集点打在了错误的位置(比如打在了网关收到请求的时刻,而不是引擎吐出第一个 Token 的时刻),会导致totalMs - ttftMs失真。TTFT 的采集点必须尽量贴近推理引擎。
告警阈值天天误报。白天和夜间的流量模式差异巨大,固定阈值必然误报。建议用滑动窗口动态阈值,比如过去 7 天同时段均值的 ±2 倍标准差。客服场景和内容生成场景的基线也不一样,按 scene 标签分开设阈值。
错误率虚高。长上下文请求超时被记成失败,会污染错误率。把超时时间调大(比如 600 秒),并把"超时"和"真实错误"分成两个指标统计,否则你会花大量时间排查根本不存在的错误。
7. 把观测基线跑起来
指标体系的最终价值不是展示,而是驱动决策——性能劣化时快速定位根因,容量不足时提前预警,架构演进时提供量化依据。落地路径建议这样走:先用 TaoToken 统一接入通道,把 settings.json 和 config.toml 配好;然后按四层逐层打点,优先把推理引擎层的 TTFT、TPOT、Token 吞吐跑通;最后接上业务效果层,让技术指标和商业价值对齐。
如果你要长期跑编码类 Agent 或高频调用,建议直接上 Coding Plan,配额和通道更稳定,指标观测口径也统一:
https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=coding-plan接入细节和参数说明可以对照官方文档:
https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc我自己的做法是:每次调整批处理参数或上下文长度上限后,固定跑一组基准请求(短/中/长三档输入各 20 次),对比 TTFT 和 TPOT 的分段 P95。这样任何一次配置变更对性能的影响都是可量化的,而不是靠"感觉好像快了点"。