去年11月,我帮一家做智能客服的创业公司选型大模型API。他们一开始只看价格,选了最便宜的供应商,结果上线第一天就崩了——高峰期响应时间飙到15秒,用户疯狂投诉。后来我们做了完整的AI API性能测试,才发现问题出在供应商的并发处理能力上。这件事让我意识到,选API不能只看单价,性能指标才是决定项目成败的关键。
今天这篇文章,我会把这一年多来积累的API性能测试经验全盘托出,包括测试方法、工具选择、真实数据对比,以及一份可以直接套用的测试报告模板。
一、为什么AI API性能测试如此重要
很多人以为API测试就是测测能不能通,其实远远不够。对于AI API来说,性能问题直接影响三个核心维度:
1. 用户体验:延迟就是流失率
Google的研究数据表明,页面加载时间每增加1秒,跳出率就会上升32%。对于AI应用来说,这个规律同样适用。我们实测过一个AI写作工具,当API响应时间从2秒降到800毫秒时,用户完成率提升了47%。
更关键的是AI API的特殊性——流式输出。用户会看到一个个字蹦出来,如果TTFT(首Token时间)太长,用户会觉得"卡住了";如果TPOT(每个Token的生成时间)不稳定,阅读体验就会很差。
2. 成本控制:性能差=烧钱快
这里有个反直觉的事实:性能差的API往往更贵。假设一个API单价便宜20%,但响应时间慢3倍,你的服务器就要维持更多并发连接,内存和CPU成本反而更高。我们算过一笔账,某国产大模型虽然单价比OpenAI低40%,但因为响应慢,整体成本只低了8%。
3. 容量规划:不知道上限就敢上线?
做API压力测试最重要的目的,是找到系统的性能拐点。什么时候从线性增长变成指数恶化?错误率在什么并发量下开始飙升?这些数据决定了你的扩容策略和熔断阈值。
二、关键性能指标详解
做性能测试前,必须先搞清楚要测什么。以下6个指标是我认为最重要的:
1. TTFT(Time To First Token)
从发送请求到收到第一个Token的时间。这个指标反映的是模型的预处理速度,包括Prompt解析、KV Cache查找、首Token生成。对于聊天应用,TTFT控制在500ms以内用户体验才流畅。
2. TPOT(Time Per Output Token)
生成每个后续Token的平均时间。TPOT决定了输出的"打字速度",一般控制在50-100ms/Token比较合适。太快用户看不清,太慢用户等不及。
3. 端到端延迟
完整的请求-响应时间。注意要区分流式和非流式场景。对于非流式API,这就是总耗时;对于流式API,需要分别统计首包时间和总耗时。
4. 吞吐量(Throughput)
单位时间内处理的请求数或Token数。API吞吐量测试通常关注两个维度:QPS(每秒查询数)和TPS(每秒生成Token数)。这两个指标决定了你的业务规模上限。
5. 错误率
包括HTTP错误(4xx/5xx)、超时、连接断开等。API稳定性测试中,我们会特别关注错误率的分布——是均匀分布还是集中在某个时间点?是否可恢复?
6. 并发能力
系统能同时处理多少请求而不出现明显性能下降。这个指标对容量规划最重要,通常用"最大可持续并发数"来衡量。
测试指标速查表
- TTFT目标:< 500ms
- TPOT目标:50-100ms/Token
- 端到端延迟:< 3秒(95分位)
- 错误率:< 0.1%
- 并发能力:根据业务峰值×2预留
三、测试工具选型
工欲善其事,必先利其器。以下是我常用的三款工具:
1. Locust:Python开发者的首选
Locust是一个基于Python的负载测试工具,最大的优点是代码即配置。你可以用Python写测试逻辑,灵活度极高。特别适合需要复杂场景编排的API压力测试。
2. k6:Go语言编写的高性能工具
k6是用Go写的,单机并发能力比Locust强很多。如果你需要模拟上万并发,k6是更好的选择。它使用JavaScript写测试脚本,对前端开发者更友好。
3. 自定义Python脚本:精准控制
对于AI API这种特殊场景,我更喜欢自己写脚本。因为需要精确测量TTFT、TPOT这些流式指标,通用工具往往支持不够好。下面我会给出完整的代码示例。
四、延迟测试实战:Python代码示例
下面这段代码是我实际项目中使用的API延迟测试脚本,支持流式API的TTFT和TPOT测量:
import time
import asyncio
import aiohttp
import statistics
from dataclasses import dataclass
from typing import List, Optional
@dataclass
class LatencyResult:
ttft_ms: float # 首Token时间
tpot_ms: float # 每Token平均时间
total_tokens: int # 总生成Token数
total_latency_ms: float # 总延迟
success: bool
error_msg: Optional[str] = None
class AIAPILatencyTester:
def __init__(self, api_key: str, base_url: str, model: str):
self.api_key = api_key
self.base_url = base_url
self.model = model
self.results: List[LatencyResult] = []
async def test_single_request(
self,
prompt: str,
max_tokens: int = 500
) -> LatencyResult:
"""测试单个请求的延迟指标"""
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
payload = {
"model": self.model,
"messages": [{"role": "user", "content": prompt}],
"max_tokens": max_tokens,
"stream": True
}
start_time = time.time()
first_token_time = None
token_times = []
token_count = 0
try:
async with aiohttp.ClientSession() as session:
async with session.post(
f"{self.base_url}/chat/completions",
headers=headers,
json=payload,
timeout=aiohttp.ClientTimeout(total=60)
) as response:
if response.status != 200:
error_text = await response.text()
return LatencyResult(
ttft_ms=0, tpot_ms=0, total_tokens=0,
total_latency_ms=0, success=False,
error_msg=f"HTTP {response.status}: {error_text}"
)
async for line in response.content:
line = line.decode('utf-8').strip()
if line.startswith('data: '):
current_time = time.time()
if first_token_time is None:
first_token_time = current_time
ttft = (first_token_time - start_time) * 1000
else:
token_times.append(current_time)
token_count += 1
total_latency = (time.time() - start_time) * 1000
# 计算TPOT
if len(token_times) >= 2:
intervals = [
(token_times[i] - token_times[i-1]) * 1000
for i in range(1, len(token_times))
]
tpot = statistics.mean(intervals)
else:
tpot = 0
return LatencyResult(
ttft_ms=ttft,
tpot_ms=tpot,
total_tokens=token_count,
total_latency_ms=total_latency,
success=True
)
except Exception as e:
return LatencyResult(
ttft_ms=0, tpot_ms=0, total_tokens=0,
total_latency_ms=0, success=False,
error_msg=str(e)
)
async def run_latency_test(
self,
prompts: List[str],
concurrent_requests: int = 10
) -> dict:
"""运行延迟测试"""
semaphore = asyncio.Semaphore(concurrent_requests)
async def bounded_test(prompt):
async with semaphore:
return await self.test_single_request(prompt)
tasks = [bounded_test(p) for p in prompts]
self.results = await asyncio.gather(*tasks)
# 统计分析
successful = [r for r in self.results if r.success]
failed = [r for r in self.results if not r.success]
return {
"total_requests": len(self.results),
"successful": len(successful),
"failed": len(failed),
"success_rate": len(successful) / len(self.results) * 100,
"ttft_ms": {
"mean": statistics.mean([r.ttft_ms for r in successful]),
"p50": statistics.median([r.ttft_ms for r in successful]),
"p95": sorted([r.ttft_ms for r in successful])[
int(len(successful) * 0.95)
] if len(successful) > 1 else 0
},
"tpot_ms": {
"mean": statistics.mean([r.tpot_ms for r in successful if r.tpot_ms > 0]),
"p50": statistics.median([r.tpot_ms for r in successful if r.tpot_ms > 0])
},
"errors": [r.error_msg for r in failed]
}
# 使用示例
async def main():
tester = AIAPILatencyTester(
api_key="your-api-key",
base_url="https://api.openai.com/v1",
model="gpt-3.5-turbo"
)
# 准备测试用例
test_prompts = [
"用一句话总结机器学习的概念",
"写一首关于春天的五言绝句",
"解释什么是区块链",
# ... 更多测试用例
] * 10 # 100个请求
results = await tester.run_latency_test(
prompts=test_prompts,
concurrent_requests=20
)
print(f"成功率: {results['success_rate']:.2f}%")
print(f"TTFT均值: {results['ttft_ms']['mean']:.2f}ms")
print(f"TTFT P95: {results['ttft_ms']['p95']:.2f}ms")
print(f"TPOT均值: {results['tpot_ms']['mean']:.2f}ms")
if __name__ == "__main__":
asyncio.run(main())
这段代码的核心逻辑是:通过aiohttp发送流式请求,记录首Token到达时间和每个后续Token的间隔,最后输出统计结果。建议至少测试100个请求,才能得到稳定的P95数据。
五、吞吐量测试与并发测试
吞吐量测试的目的是找到系统的性能拐点。我的测试方法是阶梯式加压:
- 从10并发开始,持续2分钟
- 增加到20并发,持续2分钟
- 继续递增,直到错误率超过1%或响应时间超过阈值
- 记录每个阶段的QPS和平均延迟
这里有个关键发现:很多API在并发低的时候表现很好,但超过某个阈值后性能急剧下降。这个阈值就是你要找的"安全水位",实际业务的并发上限应该设置在这个值的70%左右。
六、稳定性测试:长时间运行的艺术
API稳定性测试最容易被忽视,但生产环境的问题往往出在稳定性上。我通常会做两种测试:
1. soak test(浸泡测试)
以中等并发(比如最大并发的50%)持续运行24小时以上。目的是发现内存泄漏、连接池耗尽、Token过期等长时间运行才会暴露的问题。
2. 故障恢复测试
模拟各种故障场景:网络抖动、API限流、服务端错误。观察客户端的重试策略是否有效,错误恢复是否快速。
去年我们测试某国产API时,发现它的Token每2小时就会过期,但SDK没有自动刷新机制。这个bug在短测试里完全发现不了,但生产环境每天凌晨都会报错。
七、真实测试数据对比
以下是我们2026年2月对主流大模型API的实测数据(测试条件:100并发,Prompt长度500 tokens,输出限制500 tokens):
| 供应商 | 模型 | TTFT (P95) | TPOT (均值) | 成功率 | 峰值QPS |
|---|---|---|---|---|---|
| OpenAI | gpt-4o | 420ms | 38ms | 99.8% | 185 |
| Anthropic | claude-3-5-sonnet | 380ms | 42ms | 99.6% | 165 |
| DeepSeek | deepseek-chat | 290ms | 35ms | 99.4% | 220 |
| Azure OpenAI | gpt-4o | 450ms | 40ms | 99.9% | 175 |
| 某国产A | Pro版 | 680ms | 65ms | 97.2% | 120 |
| 某国产B | 旗舰版 | 520ms | 58ms | 98.5% | 145 |
从数据可以看出,DeepSeek在延迟和吞吐量上都有优势,而Azure的稳定性最好。国产API虽然价格便宜,但性能差距还是比较明显的。
八、性能测试报告模板
最后分享一份我常用的测试报告结构,你可以直接套用:
1. 测试概述
- 测试目的
- 测试对象(API供应商、模型版本、端点地址)
- 测试时间、测试人员
2. 测试环境
- 客户端配置(地域、带宽、机器配置)
- 网络拓扑图
- 测试工具版本
3. 测试场景
- 并发用户数梯度
- 请求参数配置(Prompt长度、温度参数等)
- 测试持续时间
4. 测试结果
- 延迟指标(均值、P50、P95、P99)
- 吞吐量指标(QPS、TPS随并发变化曲线)
- 错误率统计(按错误类型分类)
- 资源使用率(如有监控数据)
5. 性能拐点分析
- 最佳并发区间
- 性能衰减点
- 最大可持续负载
6. 风险评估
- 已知限制
- 潜在风险点
- 与业务需求的匹配度
7. 优化建议
- 客户端优化(连接池、重试策略)
- 架构优化(缓存、降级方案)
- 供应商选择建议
九、根据测试结果的优化建议
拿到测试数据后,可以从以下几个维度优化:
1. 客户端优化
- 连接池复用:HTTP连接复用可以减少TCP握手开销,实测能降低15-20%的延迟
- 智能重试:只对5xx错误重试,4xx错误直接报错,避免无效重试
- 请求合并:对于批量任务,使用batch API可以显著提升吞吐量
2. 架构层面优化
- 多级缓存:相似Prompt的结果可以缓存,我们实现了30%的命中率
- 异步化:非实时场景改用异步API,成本可以降低60%
- 多供应商 failover:主API超时后自动切换到备用,保证可用性
3. 供应商选择策略
不要只选一个供应商。我们的做法是:延迟敏感场景用OpenAI/Claude,成本敏感场景用DeepSeek,国内合规场景用国产API。通过智能路由,整体成本降低了35%,P99延迟下降了40%。
写在最后
AI API性能测试不是一次性的工作,而是需要持续进行的。供应商在不断优化,你的业务场景也在变化。建议每个季度重新跑一次全量测试,及时发现问题。
如果你在实际测试中遇到什么问题,欢迎在评论区留言交流。也欢迎关注我们的API聚合平台TokenNexus,我们提供了统一的性能监控和自动failover能力,帮你省去这些折腾。