LLM应用性能测试:TTFT/TPOT指标拆解与缓存感知的压测落地

发布时间:2026/9/1 23:34:58

LLM应用性能测试:TTFT/TPOT指标拆解与缓存感知的压测落地
AI应用的质量保障大家盯着RAG检索命中率、回答忠实度这些对不对的指标但快不快——性能测试往往是最晚补的一课。等线上用户开始骂转圈圈才想起来压测已经晚了。LLM应用的性能测试和传统Web压测有本质区别输出是流式的延迟被拆成TTFT和TPOT两段结果受prompt缓存影响极大冷热缓存下TTFT能差一个数量级压测还会真实烧钱一次并发压测跑掉几百刀是常态。这篇文章把指标选型、压测架构、缓存感知设计和踩过的坑一次讲清楚。一、先拆指标TTFT和TPOT是两回事传统Web压测看响应时间LLM应用必须拆开看因为用户感知完全不同指标定义用户感知典型量级TTFT首token延迟请求发出到第一个token返回有没有反应300ms-3sTPOT每输出token耗时流式节奏打字快不快20-80ms/tokenE2E整轮完成时间总等待TTFT TPOT×长度Queue Time排队时间网关/推理服务内部高峰期卡住0-10sCache Hit Rate输入token缓存命中率影响TTFT和成本30%-90%关键认知TTFT是缓存和算力调度问题TPOT是推理引擎和带宽问题。优化手段完全不同——TTFT靠prompt缓存、前缀复用、更快的调度器TPOT靠小模型、量化、投机解码。不拆开测你根本不知道瓶颈在哪。下面是流式计时的最小实现用OpenAI SDK的流式接口直接量TTFT和TPOTimport time from openai import OpenAI client OpenAI() def measure_stream(model: str, messages: list, expected_tokens: int 200): t0 time.perf_counter() ttft None tokens 0 first_byte None stream client.chat.completions.create( modelmodel, messagesmessages, streamTrue, stream_options{include_usage: True} ) for chunk in stream: if ttft is None and chunk.choices and chunk.choices[0].delta.content: ttft time.perf_counter() - t0 # 首token延迟 if chunk.choices and chunk.choices[0].delta.content: tokens 1 if chunk.usage: # 流式结束携带usage first_byte time.perf_counter() - t0 tpot (first_byte - ttft) / max(tokens, 1) * 1000 # ms/token return {ttft_ms: round(ttft * 1000, 1), tpot_ms: round(tpot, 1), tokens: tokens, cache_hit: chunk.usage.prompt_tokens_details.cached_tokens 0} print(measure_stream(gpt-4o-mini, [{role: user, content: 写一段200字的RAG架构说明}]))注意两个细节一是必须用流式接口非流式只能拿到E2ETTFT完全不可见二是读prompt_tokens_details.cached_tokens顺手把缓存命中情况一起记下来后面分析数据时靠它分组。二、压测架构网关层压真实Provider层压Mock压测要分两层不要一上来就打真实模型API第一层网关/应用层自己可控的部分。用k6打HTTP接口验证的是你的编排逻辑、检索链路、限流、队列这一层可以放心跑高并发// k6 脚本对RAG网关做流式压测校验SSE与延迟SLO import http from k6/http; import { check, sleep } from k6; import { Rate, Trend } from k6/metrics; const ttft new Trend(ttft_ms, true); const failRate new Rate(ttft_slo_fail); export const options { scenarios: { ramp: { executor: ramping-vus, stages: [ { duration: 2m, target: 20 }, // 预热 { duration: 5m, target: 50 }, // 峰值 { duration: 2m, target: 0 }, // 消退 ], }, }, thresholds: { ttft_ms: [p(95)1500], // TTFT p95 1.5s failRate: [rate0.01], // SLO失败率 1% }, }; export default function () { const payload JSON.stringify({ question: 某产品线7月的退款率趋势, top_k: 5 }); const res http.post(http://gateway:8080/v1/rag/stream, payload, { headers: { Content-Type: application/json }, }); // 网关返回SSE流解析首个 data: 块出现的时间 const firstChunk res.body.indexOf(data:); const t firstChunk 0 ? firstChunk : res.timings.duration; ttft.add(t); failRate.add(t 1500); check(res, { status 200: (r) r.status 200 }); sleep(1); }第二层Provider层模型推理。真实API并发压测有两个问题贵、且会被限流(429)干扰结果。正确做法是搭一个record/replay的mock provider——用真实请求录一段响应含token节奏压测时按录制的节奏回放。这样压测可重复缓存变量被隔离不烧钱可以放心压到500并发网关层的排队、超时、重试逻辑照样被测到。录制时把TTFT和TPOT的分布也录下来回放时按分位数采样比均匀回放更接近真实。三、缓存感知不分开冷热缓存数据全是假的这是LLM压测和传统压测最大的分水岭。三家主流API的缓存机制差异很大厂商缓存方式命中收益注意点OpenAI自动无需配置输入token约50%折扣TTFT显著下降前缀≥1024 token才触发命中与否看cached_tokensAnthropic显式cache_control断点缓存段输入约90%折扣长prompt TTFT可降80%TTL 5分钟需在system prompt埋断点Gemini隐式1小时TTL命中段计费大幅降低前缀需≥1024 token同一个系统提示词比如塞了产品知识库的system prompt冷缓存TTFT可能1.8s热缓存直接掉到300ms。把冷热混在一起压p95会被冷缓存污染你会误判网关性能全用热缓存压又会把网关的问题掩盖掉。正确做法是分两个场景# 压测场景按缓存状态拆分分别出报告 SCENARIOS { cold_cache: {prompt: random_system_prompt(), warmup: 0}, # 每次换前缀模拟冷启动 warm_cache: {prompt: FIXED_SYSTEM_PROMPT, warmup: 2}, # 固定前缀先跑2轮暖缓存 } def gen_prompt(scenario: str) - list: if scenario cold_cache: return [{role: system, content: f你是客服助手。会话ID: {uuid4().hex}}, {role: user, content: USER_QUESTION}] return [{role: system, content: FIXED_SYSTEM_PROMPT}, {role: user, content: USER_QUESTION}]Anthropic侧要主动埋缓存断点长文档场景收益最大from anthropic import Anthropic client Anthropic() resp client.messages.create( modelclaude-sonnet-4-5, max_tokens1024, system[ {type: text, text: KNOWLEDGE_BASE, cache_control: {type: ephemeral}}, {type: text, text: 你是资深客服回答要简洁。}, ], messages[{role: user, content: question}], ) print(resp.usage) # cache_read_input_tokens / cache_creation_input_tokens报告里务必给两组数字TTFT_cold_p95和TTFT_warm_p95上线前的SLO以冷缓存为准用户第一次访问就是冷缓存容量规划用热缓存稳态流量下大部分是热缓存。四、踩坑记录1. 429重试风暴制造延迟假象。并发一高provider开始限流客户端SDK自动重试重试请求又挤占配额延迟曲线变成锯齿状p99直接爆表。这不是性能问题是重试策略问题。压测前先确认指数退避抖动上限设了没重试是否只对可重试错误生效gateway有没有独立的队列和熔断2. 非流式压测完全掩盖TTFT。有人图省事用普通HTTP压测工具打非流式接口拿到的E2E里TTFT和生成时间混在一起根本定位不了问题。LLM应用的压测脚本必须消费流。3. 长短prompt混跑稀释TPOT。短问答TPOT 30ms长文生成TPOT 55ms混在一起平均40ms看起来还行实际上长文场景已经超SLO。按用例类型分组出报告别只给一个总均值。4. 缓存污染让压测越跑越快。同一个system prompt跑10分钟命中率从0爬到80%TTFT曲线一路向下你以为系统变好了其实是缓存热了。压测时长超过缓存TTL(5分钟)后务必分段看数据或干脆按缓存状态分场景。5. 压测账单。一次50并发、5分钟、gpt-4o级别的压测账单可能几百刀。Provider层用mock需要真实数据时用mini档模型或降采样比如10%流量走真实API。一个排查实例TTFT从1.2s劣化到3.4s。某RAG客服应用压测数据里TTFT p95从1.2s涨到3.4s第一反应是模型变慢了。分缓存状态一看冷缓存场景TTFT没变化热缓存场景劣化严重——说明问题不在推理在网关。顺着trace查发现是新增的会话摘要中间件把整段历史对话拼进了system prompt前缀长度翻倍导致缓存命中失效命中率从82%掉到31%。修复方式是摘要前置缓存断点下沉TTFT p95回到900ms。这个案例说明两件事指标不按缓存分组问题定位会走弯路压测数据要能回溯到单次请求的trace否则只能靠猜。五、线上观测压测数据和线上数据对不上等于白测压测只是实验室结果线上真实流量才是最终裁判。建议把压测和线上观测打通共用同一套指标定义压测用k6出报告线上用Langfuse/LangSmith/Phoenix这类工具盯分位数。线上至少盯四个数TTFT p95分冷热缓存、TPOT p95、缓存命中率、单次请求成本。告警阈值不要用均值用p95p99双阈值——均值在LLM场景下几乎没有意义长尾才是用户真实体验。一个容易被忽略的点线上流量的prompt长度分布和压测脚本要一致。压测用的都是几百token的短问题线上全是带长文档的RAG查询TPOT和TTFT会系统性偏高。定期从线上采样真实prompt脱敏后回放进压测集让实验室和线上的差距可控。六、工具选型与SLO基线工具适用层特点k6网关层JS脚本、内置阈值和分位数适合进CILocust网关/自定义客户端Python适合写复杂LLM客户端逻辑Gatling网关层Scala高并发场景性能好Langfuse / LangSmith观测线上真实流量的延迟分位数、成本、缓存命中率Phoenix (Arize)观测追踪评测一体可查单次请求的token级耗时工具链搭配建议k6做压测Langfuse类工具做线上观测两者共用同一套指标名TTFT/TPOT/缓存命中率压测数据和线上数据才能对得上。给一套可落地的初始SLO按业务调整TTFT p95 1.5s冷缓存、TPOT p95 80ms/token、错误率 0.5%、单次问答成本 $0.05。压测报告里除了达标情况必须附缓存命中率和成本两个数——LLM应用的性能、成本、质量是三角关系只看延迟会漏掉一半问题。总结与进阶方向LLM应用性能测试的要点就四句话指标拆到TTFT/TPOT两级压测分层网关打真实、Provider打mock缓存状态分开测报告绑定成本。把这套跑通线上转圈圈的问题基本能提前暴露。进阶方向① 压测进CI模型版本升级、prompt模板改动时自动跑性能回归金丝雀对比新旧版本的TTFT分布② 基于OpenTelemetry GenAI语义约定gen_ai.*属性把压测和线上追踪打通单次请求从网关到provider的全链路耗时可视化③ 把性能SLO和成本SLO一起做预算管理超预算自动告警。如果你也在做 AI 应用RAG / Agent / LLM不知道质量怎么测——我最近在给 AI 应用做免费质量体检出一份可执行的测评报告检索命中率、回答忠实度、噪声敏感度等维度感兴趣可以直接私信我。

相关新闻

低价DeepSeek API站点接入验证与排查指南

低价DeepSeek API站点接入验证与排查指南

2026/9/1 23:34:58

一个 DeepSeek API 站点,价格只要官方渠道的 0.15 倍,也就是打 1.5 折。光看这个数字,很多人第一反应是“便宜到离谱”,第二反应是“是不是有坑”。我的判断是:低价站点不是不能用,但要把它当生产依赖&…

[光学原理与应用-601]:为什么两束光(光子)之间,在真空里无法直接互相影响、互相改变

[光学原理与应用-601]:为什么两束光(光子)之间,在真空里无法直接互相影响、互相改变

2026/9/1 23:34:58

为什么两束光(光子)之间,在真空里无法直接互相影响、互相改变核心结论:真空中两束光相遇,只是简单叠加、穿过彼此,不会改变对方的频率、方向、能量;光子和光子之间没有直接作用力。只有穿过介质…

Python跨平台IP配置工具(Windows/Linux通用)完整源码+使用教程

Python跨平台IP配置工具(Windows/Linux通用)完整源码+使用教程

2026/9/1 23:34:58

一、前言 日常运维、局域网调试、多网络环境切换场景下,手动修改电脑IP地址、子网掩码、网关、DNS十分繁琐,且Windows、Linux系统配置命令不统一,新手极易操作出错。 为此,我用Python开发了一款跨平台智能IP配置工具&#xff0c…

【单片机课程设计/毕业设计】基于 STM32 或 51 单片机的声光报警智能恒温出水系统设计 基于 STM32 或 51 单片机的手机 APP 远程饮水控制终端设计(024805)

【单片机课程设计/毕业设计】基于 STM32 或 51 单片机的声光报警智能恒温出水系统设计 基于 STM32 或 51 单片机的手机 APP 远程饮水控制终端设计(024805)

2026/9/2 0:35:01

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

【单片机课程设计/毕业设计】基于 STM32 单片机的移动端联动安全监护硬件系统设计 基于 STM32 单片机的跌倒识别与多场景预警终端设计(024705)

【单片机课程设计/毕业设计】基于 STM32 单片机的移动端联动安全监护硬件系统设计 基于 STM32 单片机的跌倒识别与多场景预警终端设计(024705)

2026/9/2 0:35:01

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

【单片机课程设计/毕业设计】基于 ESP8266WiFi 模块的生理数据采集报警平台设计与实现 面向居家健康的单片机体征检测与声光报警系统设计(024105)

【单片机课程设计/毕业设计】基于 ESP8266WiFi 模块的生理数据采集报警平台设计与实现 面向居家健康的单片机体征检测与声光报警系统设计(024105)

2026/9/2 0:35:01

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

【单片机课程设计/毕业设计】基于蓝牙移动端的输液滴速液位温度一体化监护系统设计 单片机控制下 PTC 加热输液辅助监测报警系统设计与实现(024005)

【单片机课程设计/毕业设计】基于蓝牙移动端的输液滴速液位温度一体化监护系统设计 单片机控制下 PTC 加热输液辅助监测报警系统设计与实现(024005)

2026/9/2 0:35:01

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

【单片机课程设计/毕业设计】基于 STM32 或 51 单片机的蓝牙移动端饲喂管控系统设计 基于单片机的时钟驱动智能喂食加水设备设计与实现(023905)

【单片机课程设计/毕业设计】基于 STM32 或 51 单片机的蓝牙移动端饲喂管控系统设计 基于单片机的时钟驱动智能喂食加水设备设计与实现(023905)

2026/9/2 0:35:00

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

拉格朗日乘数法精讲:从约束优化到支持向量机实战

拉格朗日乘数法精讲:从约束优化到支持向量机实战

2026/9/2 0:25:00

1. 背景与核心概念如果你正在学习机器学习,可能在看书或看视频时,突然看到“拉格朗日乘数法”这个词。很多初学者在这里卡住,原因是它前面连着高等数学,后面又连着支持向量机推导,硬啃的话容易劝退。但其实&#xff0c…

备战数据库管理工程师校招:索引、事务、备份恢复核心考点解析

备战数据库管理工程师校招:索引、事务、备份恢复核心考点解析

2026/9/1 1:53:39

每年校招季我都会接触不少准备数据库方向笔试的同学,看到最多的状态就是:简历上写着“熟悉 MySQL”“了解索引优化”,一碰到数据库管理工程师的笔试卷,却在索引、事务、锁、备份恢复这些题目上翻车。网易这套 2018 校园招聘数据库…

数字电路时序基石:深入理解建立时间与保持时间

数字电路时序基石:深入理解建立时间与保持时间

2026/9/1 9:55:14

1. 这不是“背公式”的事:时间参数到底在约束什么你翻过数字电路教材,一定见过这两个词:建立时间(Setup Time)和保持时间(Hold Time)。它们常被并列写在触发器(Flip-Flop&#xff09…

蓝桥杯国赛超声波测距机:从单片机原理到嵌入式系统实战

蓝桥杯国赛超声波测距机:从单片机原理到嵌入式系统实战

2026/9/1 23:49:08

1. 项目缘起:从赛题到超声波测距机的诞生第八届蓝桥杯单片机设计与开发国赛的题目,我至今记忆犹新。它没有直接给出一个花哨的名字,而是用“超声波测距机”这个朴实无华的功能描述,精准地勾勒出了考核的核心。对于当时备赛的我而言…

单片机毕业设计-基于单片机与蓝牙通讯的输液状态监测终端设计与开发 基于 STM32 或 51 单片机的液位‑滴速‑温度多参数输液监护装置设计(024005)

单片机毕业设计-基于单片机与蓝牙通讯的输液状态监测终端设计与开发 基于 STM32 或 51 单片机的液位‑滴速‑温度多参数输液监护装置设计(024005)

2026/9/2 0:04:59

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

DeepSeek字幕翻译实战:从API调用到批量SRT转中文的完整方案

DeepSeek字幕翻译实战:从API调用到批量SRT转中文的完整方案

2026/9/2 0:04:59

这次我们来看一个很实用的 DeepSeek 落地场景:用 DeepSeek 把英文视频字幕自动翻译成中文。具体案例是《恶魔君》1989 年第 28 集的英转中字幕任务,标题写得很直白,但背后其实是一整套可以复用的技术流程:字幕解析、模型调用、批量…

用Python搭建搞笑语音助手:从语音识别到语音合成全教程

用Python搭建搞笑语音助手:从语音识别到语音合成全教程

2026/9/2 0:04:59

当你家里摆着一台天猫精灵,却总希望语音助手偶尔“不正经”一点,不用官方腔回答问题,而是张口就接几句搞笑段子,会是什么体验?我最近动手验证了一下这个想法——没有去改装任何市面上现有的智能音箱,而是直…

远程协作的工作台整理

远程协作的工作台整理

2026/9/1 0:03:36

远程协作的工作台整理远程协作的核心不是再加一个工具,而是让交接信息足够完整。异步任务要写明目标、输入位置、完成标准和需要决策的人。 工作台的最小配置 将日程、待办、代码和沟通入口收拢到少数固定位置;通知按紧急程度分层。工作台不需要模仿办公…

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

2026/9/1 0:03:36

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

2026/9/1 0:03:36

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…