如果你最近在关注模型推理服务,应该会注意到一种新的“卷法”:同一个模型版本,会被多家服务商同时上架,甚至在标题里直接拿延迟数据互相比较。DeepSeek V4 Flash 0731 latency numbers from nine providers,这个标题翻译成开发语言就是一句话:这里有九个推理服务商对同一个模型的延迟数据,到底该怎么选?
先看这个模型名能读出什么。DeepSeek V4代表主版本;Flash通常意味着一个偏向低延迟、低成本、适合高频调用的子版本;0731则表示 checkpoint 或部署快照日期。对开发者来说,0731这类日期版本标识很重要,它防止你在不知情的情况下被服务商悄悄切换到另一个权重。更重要的是,模型权重可以一致,但部署它的服务商未必一致。同一个模型,交给九家 provider 之后,首字延迟、生成速度、稳定性、限流策略和成本都可能相差很大。
这篇文章不会给出“某家一定比某家好”的单一结论,因为抛开业务场景谈延迟排名没有意义。文章会讲清楚四件事:延迟评测到底该看哪些指标;如何搭建一套能复跑的九家评测脚本;如何看待 P50/P99、流式与非流式的差异;实际选型时有哪些坑。读完你可以照着自己跑一遍,得到自己的结果,而不是轻信一份没有注明测试条件的排行表。
1. 当同一个模型版本出现在九家 provider 时,真正要关注什么
这里先给一个明确判断:模型能力决定生成质量,provider 的推理架构和调度策略决定你看到的延迟。同一个模型名出现在九家 provider 上,不代表九家运行的是完全一样的二进制和配置。
许多人习惯把模型抽象成一个黑盒,认为“同一个模型,谁的 API 快就用谁”。但在生产中,模型权重只是结果的一部分,服务商如何把权重跑起来才是真正的变量。这些变量包括:
- 推理引擎不同:常见引擎有 vLLM、TensorRT-LLM、SGLang、TGI 等,不同引擎在连续批处理、算子优化、KV Cache 管理上的策略差异很大,直接表现为请求高峰期的吞吐和时延表现不同。
- 硬件规格不同:A100、H100、L40S 甚至国产加速卡,显存带宽和算力差异会直接影响 prefill 和 decode 速度。
- 量化与精度策略不同:有的服务商可能用 FP8 甚至 INT4 部署,换来更高吞吐,但极端情况下会影响生成质量。
- 实例调度和排队策略不同:有的 provider 为每个请求预留独立显存,延迟低但并发成本高;有的走高密度批处理路线,延迟中位数不高,但 P99 可能偏高。
- API Gateway 和网络链路不同:请求到达数据中心的路由、响应压缩、连接复用策略,都会反映在端到端延迟上。
- 缓存策略不同:如果服务商实现了 prompt 前缀缓存或语义缓存,重复或相似请求可能会大幅缩短首字延迟。
这也解释了为什么同一个模型会需要“来自九家 provider 的延迟数据”:在权重层面的差距已经缩小之后,部署层面的差异被放大了。如果你是做 C 端对话产品,可能更关心流式首字速度;如果你是做离线数据处理管道,可能更关心每百万 token 的成本和稳定吞吐。两种业务面对同一份延迟榜单,得出来的结论很可能不一样。
所以,看任何 provider 对比数据之前,先反问一句:这份数据是在什么硬件、什么并发、什么 prompt 长度、什么输出长度下测出来的?如果这份数据不能对应你的业务形态,那它只能作为参考,不能作为选型依据。
2. 延迟评测要先分清楚四类指标
延迟这个词在模型推理语境下经常被混用。实际上,一次 API 调用里的“耗时”可以拆成多个阶段,不同阶段对用户体验的影响完全不同。
为了便于后续写脚本,先把四类核心指标讲清楚。
| 指标 | 含义 | 主要决定因素 | 适合关注它的场景 |
|---|---|---|---|
| TTFT | Time To First Token,从请求发出到收到第一个输出 token 的耗时 | prefill 阶段性能、排队情况、网络链路 | 对话、客服、实时流式交互 |
| 端到端延迟 | 从请求发出到完整响应结束的耗时 | prefill + decode + 网络 + 排队 | 非流式调用、批量生成 |
| TPOT / ITL | 平均每个输出 token 的间隔,或相邻 token 间延迟 | decode 阶段速度、批大小、显存带宽 | 长文生成、流式阅读体验 |
| 吞吐量 | 每秒生成的 token 数 | 服务端整体并发处理能力 | 离线批处理、高并发内部管道 |
这四个指标不是孤立存在的。比如流式对话场景中,TTFT 直接决定用户“能不能很快看到回应”,但如果 TPOT 很慢,用户看到的前几个字很快,后面的内容却一个字一个字往外挤,体验依然很差。反过来,非流式批处理任务中,用户根本不看中间过程,只有端到端延迟和吞吐量有意义。
还有个容易混淆的点:端到端延迟低,不代表 TTFT 低。有的 provider 对短请求做了快速完成,但如果请求本身很短,你只能看到总耗时,看不到“第一个字等待了多久”。所以评测代码里必须把 TTFT 单独打点,不能让端到端延迟掩盖了首字延迟的问题。
除了上述指标,还应该统计失败率和重试率。一次请求如果因为超时或限流失败,即便延迟数据再好看,也不能用于生产环境。尤其是高并发场景,P99 延迟加上失败率,才是一份完整的延迟画像。
3. 评测前把数据口径固定下来
很多人跑 provider 对比,第一次测完发现结果和网上的榜单差很多,往往不是 provider 变了,而是数据口径不一致。以下七个口径必须在测试前固定。
3.1 请求文本一致且可复现
评测必须使用一组固定的 prompt,不能每天随便想几个问题来测。固定 prompt 的文本、长度和难度,才能保证不同 provider 之间的输入条件一致。更严谨的做法是给每个 prompt 一个固定编号,并把测试文本提交到仓库,方便后续追溯。
3.2 采样参数一致
temperature、top_p、max_tokens、presence_penalty等参数要固定。不同服务商对参数的默认值可能不同,比如没传max_tokens时,有的服务商默认很短,有的默认很长。参数不一致时,延迟差异可能来自服务商配置,而不是模型执行性能。
3.3 是否使用流式必须分开
流式和非流式的耗时结构不同。流式接口从 HTTP 响应头到达开始,就能逐步收到 token;非流式接口必须等全部 token 生成完,才把完整结果一次性返回。评测时不能混在一起,否则 TTFT 的定义会出现偏差。
3.4 输出长度上限一致
max_tokens的取值对端到端延迟影响巨大。生成 64 个 token 和生成 1024 个 token,时长完全不是一个量级。如果要观察长文本能力,必须把max_tokens固定在一个足够大的值,比如 1024;如果要模拟真实短问答,则固定为 128。
3.5 客户端和服务商区域固定
延迟数字包含网络传输时间。如果你的客户端和服务商数据中心在同一地域,延迟自然很低;如果跨地域访问,光网络往返就会增加几十甚至上百毫秒。评测时要固定客户端所在区域,或者分别记录不同区域的 P50/P99,不能拿一个区域的数字和另一个区域的数字直接比较。
3.6 测试时段固定
服务商在不同时间段的负载不同。白天业务高峰和凌晨空闲时段,同一个 provider 的延迟可能相差很大。做九家横向对比时,最好在同一时间段轮流或交错测试,避免某家正好赶上高峰期而其他家都在空闲。
3.7 统计方法统一
平均值容易被极端值拉高,只看平均值不可靠。更合理的做法是同时记录 P50、P90、P99 和失败率。如果某家平均值低但 P99 很高,说明它在突发流量下不稳定;如果平均值和 P99 都很低,才说明系统整体稳定。
4. 环境准备与最小探测代码
在写批量评测脚本之前,先准备一个最小可运行的探测脚本。下面以 Python 为例,通过 OpenAI 兼容接口访问服务商。绝大多数支持模型托管并提供 API 的服务商都兼容 OpenAI 的消息结构,即使不兼容,也通常会在文档里给出对应 SDK 的调用方式。
4.1 环境安装
建议使用 Python 3.10 及以上版本,并先创建虚拟环境隔离依赖。
python -m venv .venv source .venv/bin/activate # 如果你的系统是 Windows,激活命令是: # .venv\Scripts\activate pip install openai pyyamlopenai包提供客户端封装,pyyaml用于后续读取批量配置。版本以安装时的最新稳定版为准,这里不固定版本号。
4.2 配置 API Key
API Key 是敏感信息,不要写死在代码里,更不要提交到 Git 仓库。这里使用环境变量保存。
export PROVIDER_API_KEY="你的服务商API Key" export PROVIDER_BASE_URL="https://api.provider-a.example.com/v1"上面的provider-a.example.com只是占位符,实际操作时请替换为你所使用服务商文档给出的真实 API Base URL。每个服务商的 API Base URL 不同,要以管理后台或官方文档为准。
4.3 单次非流式探测
下面这段代码发起一次普通的非流式对话请求,并测量端到端延迟。
# latency_probe.py import os import time from openai import OpenAI client = OpenAI( api_key=os.environ.get("PROVIDER_API_KEY"), base_url=os.environ.get("PROVIDER_BASE_URL"), ) prompt = "用三句话解释什么是微积分。" start = time.perf_counter() response = client.chat.completions.create( model="deepseek-v4-flash-0731", messages=[ {"role": "user", "content": prompt} ], max_tokens=256, temperature=0.7, stream=False, ) end_to_end_ms = (time.perf_counter() - start) * 1000 content = response.choices[0].message.content print(content) print(f"end_to_end_ms={end_to_end_ms:.1f}")代码里的model="deepseek-v4-flash-0731"是本主题中用到的模型标识。实际请求时,请务必查阅服务商提供的“模型 ID 列表”,因为不同服务商对同一模型的命名可能不完全一致。如果服务商还没有上架这个版本,会返回model_not_found之类的错误。
运行命令:
python latency_probe.py这段代码只是最基础的连通性测试,验证了三件事:API Key 是否有效、Base URL 是否正确、模型 ID 是否可用。如果这一步都跑不通,后面批量评测就无从谈起。
5. 九家服务商统一评测:配置化与批量流程
单次探测通过后,就可以把九个 provider 的配置集中到一个文件中,写一个统一 runner。这样以后模型版本更新,或者服务商列表变化时,只需要改配置,不需要改代码。
5.1 provider 配置文件
下面是一个 YAML 配置示例,里面只列出了两个 provider,实际使用时可以扩展到九家甚至更多。注意文件里的地址是示例占位符,不能直接请求。
# providers.yaml providers: - name: provider_a base_url: "https://api.provider-a.example.com/v1" api_key_env: "PROVIDER_A_API_KEY" model_id: "deepseek-v4-flash-0731" - name: provider_b base_url: "https://api.provider-b.example.com/v1" api_key_env: "PROVIDER_B_API_KEY" model_id: "deepseek-v4-flash-0731"每个 provider 的api_key_env指向不同的环境变量名,这样可以把九家 Key 放在同一台机器的环境变量里,互不干扰,也不会写入仓库。
5.2 批量探测代码
下面这段代码会读取providers.yaml,对每个 provider 连续发起多次请求,并把端到端延迟保存到列表。
# benchmark_runner.py import os import time import yaml from openai import OpenAI def load_config(path="providers.yaml"): with open(path, "r", encoding="utf-8") as f: return yaml.safe_load(f) def create_client(provider): api_key = os.environ.get(provider["api_key_env"]) if not api_key: raise RuntimeError(f"缺少环境变量 {provider['api_key_env']}") return OpenAI( api_key=api_key, base_url=provider["base_url"], ) def measure_once(client, model_id, prompt, max_tokens=256): start = time.perf_counter() response = client.chat.completions.create( model=model_id, messages=[{"role": "user", "content": prompt}], max_tokens=max_tokens, temperature=0.7, stream=False, ) latency_ms = (time.perf_counter() - start) * 1000 content = response.choices[0].message.content return latency_ms, content def run(): cfg = load_config() prompt = "用一段话解释什么是Redis,并给出一个使用场景。" for provider in cfg["providers"]: client = create_client(provider) latencies = [] for i in range(10): latency_ms, _ = measure_once(client, provider["model_id"], prompt) latencies.append(latency_ms) print(f"{provider['name']} sample={i + 1} latency_ms={latency_ms:.1f}") avg_ms = sum(latencies) / len(latencies) print(f"{provider['name']} avg_ms={avg_ms:.1f}") if __name__ == "__main__": run()这段代码里做了几件关键事情:每个 provider 独立创建客户端,避免 Key 串号;连续请求 10 次,避免单次波动的偶然性;输出每次延迟和平均值,便于快速观察。循环次数可以根据精度要求调整,但生产级评测不建议少于 30 次。
5.3 增加分位数统计
平均值只能给出一个大概感觉。更严谨的评测需要计算 P50、P90、P99。把下面的百分位数函数加入脚本:
# percentile.py def percentile(data, p): """计算百分位数,data 为延迟列表,p 为 0~100 之间的数值。""" if not data: return 0.0 sorted_data = sorted(data) k = (len(sorted_data) - 1) * (p / 100) lower = int(k) upper = min(lower + 1, len(sorted_data) - 1) weight = k - lower return sorted_data[lower] * (1 - weight) + sorted_data[upper] * weight调用方式:
p50 = percentile(latencies, 50) p90 = percentile(latencies, 90) p99 = percentile(latencies, 99) print(f"provider={provider['name']} p50={p50:.1f} p90={p90:.1f} p99={p99:.1f}")如果样本量只有 10 次,P99 的统计意义有限,建议至少 30 到 50 次再算分位数。样本量越大,P99 越能反映真实的长尾情况。
6. 流式 TTFT 测试与并发测试
对 C 端产品来说,流式 TTFT 往往比非流式端到端延迟更接近真实感受。前端页面已经拿到响应状态,第一个字越快出现,用户就越觉得“系统在思考并回应了”。
6.1 流式 TTFT 探测
下面代码用流式接口发起请求,并记录第一个 content token 出现的时间。
# ttft_stream_probe.py import os import time from openai import OpenAI client = OpenAI( api_key=os.environ.get("PROVIDER_API_KEY"), base_url=os.environ.get("PROVIDER_BASE_URL"), ) prompt = "请写一篇500字左右的文章,介绍分布式系统的基本概念。" start = time.perf_counter() stream = client.chat.completions.create( model="deepseek-v4-flash-0731", messages=[{"role": "user", "content": prompt}], max_tokens=500, temperature=0.7, stream=True, ) first_token_time = None received_tokens = [] for chunk in stream: if not chunk.choices: continue delta = chunk.choices[0].delta if delta and delta.content: if first_token_time is None: first_token_time = time.perf_counter() ttft_ms = (first_token_time - start) * 1000 print(f"ttft_ms={ttft_ms:.1f}") received_tokens.append(delta.content) end_to_end_ms = (time.perf_counter() - start) * 1000 print(f"generated_tokens={len(received_tokens)}") print(f"end_to_end_ms={end_to_end_ms:.1f}")这段代码的关键点在于:必须在循环里区分“第一个 content chunk”和“后面的 chunk”。如果只是简单记录请求总耗时,你会错过 TTFT 这个对交互体验最重要的指标。
6.2 并发压力测试
单发单个请求只能反映服务商空闲状态下的表现。真实业务中,请求往往并发到来。可以先用一个试探性的并发测试,观察服务商在并发下的表现。
# concurrent_probe.py import os import time from concurrent.futures import ThreadPoolExecutor from openai import OpenAI BASE_URL = os.environ.get("PROVIDER_BASE_URL") API_KEY = os.environ.get("PROVIDER_API_KEY") MODEL_ID = "deepseek-v4-flash-0731" PROMPT = "用三句话解释数据库索引的优点。" MAX_TOKENS = 128 CONCURRENCY = 16 TOTAL_REQUESTS = 64 def one_request(_): client = OpenAI(api_key=API_KEY, base_url=BASE_URL) start = time.perf_counter() client.chat.completions.create( model=MODEL_ID, messages=[{"role": "user", "content": PROMPT}], max_tokens=MAX_TOKENS, temperature=0.7, stream=False, ) return (time.perf_counter() - start) * 1000 with ThreadPoolExecutor(max_workers=CONCURRENCY) as executor: latencies = list(executor.map(one_request, range(TOTAL_REQUESTS))) latencies.sort() p50 = latency_len = len(latencies) p50_val = latencies[int(p50_len * 0.50)] p90_val = latencies[int(p50_len * 0.90)] p99_val = latencies[int(p50_len * 0.99)] print(f"concurrency={CONCURRENCY}") print(f"requests={TOTAL_REQUESTS}") print(f"p50_ms={p50_val:.1f}") print(f"p90_ms={p90_val:.1f}") print(f"p99_ms={p99_val:.1f}") print(f"max_ms={latencies[-1]:.1f}")需要注意,这里每个线程都创建新的OpenAI客户端,避免共享连接池干扰测量结果。测试时不要一上来就拉 100 并发,那样很容易触发服务商的限流,导致数据中混入大量 429 错误,分位数失真。可以先从 4 并发、8 并发、16 并发逐级增加,观察延迟和错误率的变化趋势。
6.3 并发测试的正确姿势
并发压力测试不是并发越高越好。你的目标是找到“业务真实负载下的表现”,而不是把服务商打到限流。如果业务峰值只有 20 QPS,那么并发 64 意义不大;如果目标是双十一级别的峰值,那应该在测试环境逐步增量,并记录每个并发档位的 P50/P99 和失败率。评测得到的曲线可以用来反推:当延迟增长斜率明显变陡时,大概率已经接近服务商的性能拐点。
7. 数据怎么看:建立自己的选型矩阵
九个 provider 的延迟数据跑出来后,直接拿延迟排名做决定依然不够。假设你统计完发现 provider A 的 TTFT 短 20%,但 provider B 每天有 0.5% 的请求超时,provider A 价格是 B 的三倍,这种选择就不是一道延迟题,而是一道工程权衡题。
更推荐的做法是建一张自己的选型矩阵。权重可以按业务调整,但至少包含以下维度:
| 维度 | 建议权重 | 评估方式 |
|---|---|---|
| 响应质量 | 30% | 用固定业务 prompt 集,让人工或自动化指标给结果打分 |
| 延迟 | 25% | 用前文脚本得到 P50/P90/P99,分场景记录 |
| 稳定性 | 15% | 连续多天观察失败率、超时率、P99 波动 |
| 价格 | 15% | 按实际 token 用量估算月度成本 |
| 工程兼容性 | 10% | SDK 成熟度、是否兼容流式、是否有 JSON 模式 |
| 数据合规要求 | 5% | 是否满足公司内部对数据存储地域和权限的约束 |
权重不是固定的。如果你的业务是一个对响应速度极敏感的实时客服助手,延迟和稳定性权重应该更高;如果你的业务是跑离线数据清洗,那么价格和吞吐量权重会超过单次延迟。先列清楚业务约束,再去看数据,才不会把“延迟最快”误当成“最适合”。
在实际选型时,还有一个容易被忽略的原则:不要只看最优值,要看候选之间是否有显著性差异。如果 provider A 和 B 的 P50 只差 5%,而 B 的 P99 比 A 低很多,那么 B 可能更适合生产环境。因为 P50 的微小差距在实际体验中很难被用户察觉,P99 的稳定表现却能显著降低超时报警数量。
8. 容易被忽略的坑
九家 provider 的延迟测试看似简单,实际跑起来会遇到不少“数字很漂亮但不能复现”的情况。下面几个坑最容易让人误判。
8.1 短 prompt 和长 prompt 的结论可能反转
如果评测只用了 20 个 token 的短问题,一些 prefill 优化强的 provider 会表现非常好。换成长文档总结任务时,原来越快的 provider 未必仍然领先,因为长 prompt 的 prefill 时间更长,不同引擎在长序列下的显存管理策略差异也会拉大。因此评测集必须覆盖短、中、长三种长度。
8.2 重复请求触发缓存
如果你在循环里连续请求同一个 prompt,服务商可能命中了 prompt 前缀缓存或语义缓存,导致后面的请求延迟显著下降。缓存本身不是坏事,但如果评测目的是了解模型真实推理性能,就需要在测试集里混合多个不同 prompt,或者记录是否命中缓存。否则,你不会知道该服务商在没有缓存时到底有多快。
8.3 忽略了响应内容长度差异
某些服务商可能会提前截断、或者返回不同长度的内容。延迟对比必须同时检查usage.completion_tokens或实际输出 token 数是否接近。如果 provider A 平均只生成了 80 个 token,而 provider B 生成了 120 个 token,那么 A 的端到端延迟低很正常,并不能说明 A 更快。
8.4 只测高峰期或只测低谷期
要观察真实的稳定性,最好在一天内选择几个不同时段测试:早上、下午、晚上各测一轮。这样既能了解平均表现,也能看出服务商在高峰时段的延迟是否急剧恶化。
8.5 不关注单次超时
有时一家 provider 的平均延迟低,是因为个别请求超时被监控系统重试,服务端实际产生了更长耗时。代码里如果直接把超时请求丢弃,结果就会偏乐观。更稳妥的做法是设置明确的超时时间,例如 60 秒,并记录超时请求数量。超时率超过 0.1% 的服务商,即使延迟数据再好看,也应该谨慎选择。
9. 常见问题与排查方法
如果你的批量评测脚本运行不顺利,可以按下面的表格排查。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 401 Invalid API Key | 环境变量未设置或设置错误 | 检查终端中是否导入了正确的环境变量 | 重新导出 Key,或检查是否有空格 |
| 404 model not found | 模型 ID 写错或服务商未上架该版本 | 查看服务商模型列表文档 | 改为服务商文档中的正确模型 ID |
| 429 Too Many Requests | 并发过高,触发限流 | 查看返回头的限流字段或日志 | 降低并发,增加退避重试 |
| 请求一直超时 | 客户端与服务商区域网络延迟过高 | 测试不同区域节点,检查 DNS 解析 | 选择离业务更近的区域 |
| 流式响应中 TTFT 很远 | 没有正确解析 SSE 首包 | 确认代码在每个 chunk 上是否判断 content | 在第一个非空 content chunk 处打点 |
| 非流式响应很慢但流式很快 | provider 对非流式接口或流式接口做了不同调度 | 分别测试两类接口 | 根据业务选择更合适的接口类型 |
| 结果和网上榜单差异大 | prompt、参数、区域、时段不一致 | 对照评测方法逐项检查 | 统一数据口径后重跑 |
| 某一 provider 夜间稳定白天波动 | 高峰负载影响 | 白天、夜间各测一轮比较 P99 | 评估时按高峰时段数据为主 |
网络超时问题通常要先区分是“客户端发不出请求”还是“服务端响应慢”。最简单的方式是在代码里记录requests连接建立耗时、首字节耗时和服务端响应耗时。如果某个 provider 的连接建立耗时明显较长,问题往往在网络链路而不是推理性能。
10. 把评测沉淀成工程机制
一次性的评测只能解决眼前的选择问题,不能解决模型版本频繁更新时的持续对比问题。更推荐的思路是,把延迟评测做成一个可以定期运行的工程任务。
10.1 使用版本化测试集
将测试 prompt 集合作为固定文件保存,例如prompts.jsonl,记录每个 prompt 的唯一编号。
{"id": "short_001", "prompt": "用一句话解释什么是HTTP。"} {"id": "medium_001", "prompt": "请用200字介绍Python的GIL,并说明它对多线程的影响。"} {"id": "long_001", "prompt": "请完整介绍分布式系统中的CAP理论,并给出实际工程中的取舍案例。"}每次评测都使用这个文件,如果要增删 prompt,应通过版本管理提交变更,而不是覆盖原文件。这样可以追溯“为什么这次评测结果和上次不一样”背后到底是因为模型版本变了,还是测试集变了。
10.2 结果入库和监控
评测输出不要只打印到终端,可以按 JSON Lines 格式落盘。每一天或每一周的评测结果追加到同一个目录,最后导入数据库或用脚本做趋势分析。当某个 provider 的 P99 比基准值上升 30% 时,可以生产报警,提示负责选型的开发团队关注。
10.3 锁定模型版本
0731这类日期版本标识应该在请求参数里写死。如果服务商后续推出了新版本,并把旧版本的模型 ID 映射改为新版本,你的评测和生产请求都可能在不知情的情况下切换模型。选型确定后,要显式固定模型 ID 和 API 版本号,避免“模型悄悄变了”。
10.4 至少保留两个备选 provider
任何外部服务都可能出现故障。即使选定了主力 provider,也应该至少保留一个兼容的备选 provider,并定期用同样的测试集做连通性验证。当主力 provider 出现大面积延迟波动时,可以通过简单的路由切换把流量切到备选。
10.5 定义失败重试策略
生产环境接入时,要为 LLM 请求设计合理的重试策略。常见做法是:第一次 429 或 5xx 时等待 1 秒重试,第二次等待 2 秒,第三次等待 4 秒,最多重试 3 次。重试请求要记录日志,方便评估服务商实际可用性。不要把重试逻辑写在调用业务代码里散落各处,应该封装成一个统一的 LLM 调用客户端,集中处理超时、重试和降级。
11. 一点实用建议
如果你现在正准备做九个 provider 的延迟对比,建议从三个 provider 的小规模测试开始。
先把最小探测脚本跑通,确认 API Key、Base URL、模型 ID 都没有问题;然后用固定测试集跑 30 次非流式和流式请求,记录 TTFT、端到端延迟、P50/P99;再在有业务语义的 prompt 上做并发测试,观察不同并发档位的表现;最后把结果放进选型矩阵,和成本、稳定性、工程兼容性一起打分。
这种做法的核心不是让你得到一份漂亮的报告,而是把选型从“看别人标题里的 latency numbers”变成“看自己业务压力下的真实数据”。如果只带走一件事,我建议从今天起,对 provider 排序时不要只看平均值,把 P99 和流式 TTFT 一起打印出来。你的业务如果面向真实用户对话,这两个数字比任何榜单里的跑分都更接近你的体感。