客服模型准备换推理引擎,团队拿来两张跑分图:一边每秒吐出的 token 更多,另一边首字出来更快。负责接入的人仍然回答不了一个问题:同样六位用户进来,到底有几位能及时拿到可用答案?

先看一个贯穿全文的教学例子。两套候选配置 A、B 处理相同的六条请求,在相同的 10 秒观察窗内,客户端分别收到了 1300 和 1060 个输出 token。只看这个数字,A 更快;把失败、等待和答案是否可用放回结果,A 只有两条达标,B 有四条。

这些数值是作者构造的手算数据,不是 SGLang 或 vLLM 的 GPU 跑分;A、B 不对应任何一个框架。 我们要走完的是一次选择过程:怎样准备这六条请求,怎样控制冷热状态,怎样从逐请求记录算出结果,以及结果为什么仍可能支持“先不迁移”。

公平比较的条件

先删掉一条过时的分工

vLLM 官方 APC 原文

“普通聊天用 vLLM,Agent 用 SGLang”无法替我们作决定。vLLM 官方的 Automatic Prefix Caching 文档也把同一份长文档上的多次提问和多轮对话列为适用场景。前缀缓存会复用已处理输入的中间状态;业务用了 Agent,不代表只有其中一个引擎可以复用这些输入。

在客服例子里,四位用户都要读同一份售后规则,另一位只问一句短问题,还有一位需要较长的物流说明。共享规则可能减少重复处理输入的工作,但长答案依然要逐步生成。同一官方文档的限制说明明确区分了输入处理和新 token 生成。因此,即使共享规则命中缓存,也不能推导整段长回复按相同比例变快。

第一轮应该先验证两边能否正确完成这些请求。假设一条任务要求返回含有 eligible 和 reason 的 JSON;某个候选只输出“可以退货”,HTTP 请求虽然正常结束,应用仍无法按约定解析。先在低负载下修好模板、结构化输出配置或解析器,再去压并发。不能把另一边为保证格式付出的工作,当成它无缘无故变慢。

同样也不应只验证 JSON 能解析。eligible 的值是否符合售后规则,理由是否引用了正确条件,仍需要按样本的预期答案核对。否则速度测得越认真,越可能只是精确比较两个不同的任务。

同一份权重,还不够让两次测试公平

把这次比较限制在一个容易检查的场景:同一台专用测试机器,两套引擎顺序运行;使用同一模型 revision、权重精度、GPU 数量和并行方式。A、B 各自的引擎版本、启动命令、注意力后端、缓存开关和显存相关配置分别记录。某个配置无法支持该模型或接口,就先作为兼容性问题处理,不能悄悄换一个更小模型继续对比。

同样的 messages 也可能经过不同聊天模板,变成不同 token 序列。实际测试时保存分词器版本、模板文件摘要,以及模板展开后的输入长度;先抽样对照 token ID。若输入不同,排查系统提示、角色分隔符和生成提示是否一致。只在报告里写“都是同一份权重”,无法解释这些差异。

我们给六条请求使用相同的采样配置和 384 token 输出上限,允许按正常结束条件提前停止。384 是约束,不是每条必须生成的长度。比较输出 token 速度时,要保留实际长度;比较业务有效完成量时,则用同一套答案要求判断完整性,不要求两边逐字相同。

例如,A 的物流回答生成 360 token,B 生成 310 token,两者都包含承运方、当前节点和后续动作,可以共同进入质量合格集合。不能因为 B 字数少就判它偷工减料,也不能仅凭 A 多写了 50 token 就判它更好。若把输出强行填满到 384 token,得到的会是固定生成长度实验,需要单独报告,不能冒充用户实际等待。

调优也要分轮。先记录两边能够正确工作的基线,再给两边同等的调优时间和资源,保存每次配置及结果。允许引擎专属参数不同,但最后比较的是哪两个具体配置,必须说清楚。

让测试像你的流量,而不是像演示

请求形态与到达节奏示意

下面是六条教学请求的最小清单。到达时间相对本轮统计起点,输入长度是示意值,不是这些中文短描述的实际分词结果。

请求计划到达秒模板后输入 token共享前缀要完成的工作
R010.02800P:同一版售后规则判断拆封商品能否退货,给出条件
R020.12840P判断退货运费由谁承担
R030.22810P判断优惠券是否随退款恢复
R041.02830P输出含 eligible、reason 的 JSON
R051.1220无业务共享前缀回答人工客服营业时间
R062.01200独立物流资料给出当前节点、承运方和后续动作

真实样本文件应保存脱敏后的完整请求体,而不是只有这张表。每条记录同时带上请求 ID、计划到达偏移、请求文件摘要、前缀组、输出上限、质量规则和超时值。前缀组 P 只是我们组织样本的标记,不是发给引擎就会保证命中的缓存指令。要复用,实际输入还需要具备相同的可复用前缀;模板或资料版本变化都可能破坏这个条件。

这也决定了脱敏方式。替换手机号可以保留格式与位置;把四份售后资料分别改写一遍,却会把原来的共享关系打散。相反,把不同资料都替换成同一段“你好”,又会制造业务中不存在的共享。样本处理后,要重新检查长度分布和前缀关系,而不能只检查有没有个人信息。

本轮按表里的时刻发送,不等上一条返回才发下一条,也不自动重试。这样 A、B 承受的是同一个到达计划;如果使用“完成一条再补一条”的方式,较快的一侧会在相同时间里收到更多请求,已经不是这轮实验。客户端另存实际发出时刻和晚发量,若本地并发上限或连接池把请求扣住了,这部分等待不能伪装成服务端队列。

六条只适合演示判读,不能代表真实业务比例。正式采样时,保留高峰中的突发、长短答案及规则版本分布;低负载、中负载、高负载分别跑。不要为了覆盖四种形态就各取四分之一,把线上本来少见的长生成放大成主要压力。

冷缓存和热缓存各做一轮

SGLang 官方压测指南

冷缓存与热缓存分开记录

“冷”需要说清楚冷的是什么。这里比较业务前缀尚未缓存与已经缓存的差异,不比较进程启动时间。两侧先完成模型加载和运行时准备;准备请求不使用售后前缀 P。即使如此,也要检查实际缓存状态,不能仅凭“已经发过一条请求”就宣布完成了等价预热。

把执行过程写成可以照着核对的时间线:

阶段两边都执行的动作是否计入六条结果
建立起点在专用实例确认 P 不在可复用状态;必要时按各自版本的操作重建起点否
冷起点轮 C从空闲时刻开始,按表中偏移发 R01–R06,记录每条结果是,单独一轮
重新建立起点等待前轮工作结束,再建立同样的 P 未缓存起点否
热状态准备发不计分请求 W-P,使用与 R01–R04 相同的 P,等待完成否
热状态轮 H不夹入无关流量,重新从零点按同一偏移发 R01–R06是,单独一轮

冷起点轮并不意味着六条始终全部未命中。R01 处理以后,后续请求可能利用已经留下的状态;突发请求也可能在前缀尚未可用时一起进入。这正是流量顺序的一部分。如果想测“每条都强制冷”,需要为每条重新建立起点,得到的是另一种实验,不能与这个共享客服场景混在一起。

热状态准备也不等于保证命中。若 W-P 结束后有大量其他流量进入,P 可能已经被淘汰。出现“一侧前几条显著快,另一侧没有变化”时,先核对 W-P 的请求体、完成记录、缓存相关观测和间隔,再解释为引擎性能差异。两侧指标定义未核实一致时,不直接比较两个命中率百分数,更不能只靠 TTFT 下降反推命中已经发生。

SGLang 压测指南提供不同后端、到达速率、并发和详细结果选项,但不是这张自定义请求清单的通用执行器。该页说明,有限 --request-rate 使用泊松到达,inf 表示同时发出;因此填入平均速率不能保证复现表中的六个时刻。需要复放固定轨迹时,应使用能够保留请求体与时间偏移的客户端,并核对实际发送记录。

先查安装版本的 python -m sglang.bench_serving --help,再决定用现有数据集做补充压测,还是用业务回放器执行这组样本。不要把指南中面向 SGLang 的清缓存选项假定成两侧通用操作,也不要对在线实例清缓存。本文没有执行这些 GPU 对照。

吞吐更高,但用户等得更久,怎么办

回到开头的教学结果。以下是热状态轮 H 的全部六条记录,假设双方都已按相同过程建立起点,客户端能按计划发出。TTFT 表示从实际发出到收到第一个有效内容片段的时间,不把只有角色信息的空流式消息当成回答开始;完整响应时间从同一起点计到正常结束。超时固定为 8 秒。

本例的业务规则在测试前确定:正常结束、答案质量合格、TTFT 不超过 0.8 秒、完整响应不超过 3 秒,四项同时成立才算达标。 这些阈值是教学假设,不是两个框架的推荐标准。

请求A:TTFT / 完整响应秒A:收到的 token / 结果B:TTFT / 完整响应秒B:收到的 token / 结果
R010.35 / 2.1180 / 质量合格0.50 / 2.3180 / 质量合格
R020.55 / 2.6220 / 质量合格0.60 / 2.8220 / 质量合格
R030.95 / 3.4260 / 质量合格0.70 / 2.9240 / 质量合格
R040.25 / 1.5100 / 缺少 reason 字段0.45 / 1.8110 / 质量合格
R050.30 / 未完成,8 秒超时180 / 不完整无 / 0.1 秒错误结束0 / 请求失败
R060.40 / 3.8360 / 质量合格0.65 / 3.6310 / 质量合格

观察窗固定为从首条计划到达开始的 10 秒。最后一条在第 2 秒发送,而 R05 的超时在第 9.1 秒发生;本例到第 10 秒时六条均已正常、错误或超时终结,没有悬而未决的请求。实际实验如果窗结束时还有请求在跑,必须标出未决数,事先约定继续排空还是按截止结果计,不能临时把它们从分母里删掉。

先逐行判断:A 的 R01、R02 达标;R03 等待超标,R04 格式不合格,R05 超时,R06 完整响应太慢。B 的 R01–R04 达标,R05 失败,R06 仍然太慢。因此 A 是 2/6,B 是 4/6;按全部尝试计算,达标比例分别为约 33.3% 和 66.7%。失败请求保留在六条分母内,不能改写成“成功请求中有多少达标”。

再把时间口径带回计算:

  • A 的达标完成量为 2 ÷ 10 = 0.2 请求/秒。
  • B 的达标完成量为 4 ÷ 10 = 0.4 请求/秒。
  • A 收到的输出 token 为 180+220+260+100+180+360=1300;B 为 180+220+240+110+0+310=1060。

所以按本例自定义的“客户端收到的输出 token / 固定观察窗”计数,A 是 130 token/秒,B 是 106 token/秒,A 反而领先。这里包含 A 超时前收到的 180 token 和格式不合格的 100 token;它们确实到达过客户端,却没完成客服任务。这个计数用于暴露判断冲突,不冒充任何压测工具默认的吞吐口径。

R04 还提示我们一个回查点:前面的低负载校验若合格,这次负载下为什么少了字段?应当保留原始分片、结束原因和解析结果,判断是生成内容、截断还是客户端拼接出了问题。在原因未明前,它就是一条失败样本,不应删掉再发布漂亮的延迟表。

只有六条时也没有必要据此宣称稳定的 p99,更不能把热状态四条达标外推为整天容量。正式比较要增加样本、重复轮次,并单列冷起点结果;交替安排 A/B 的执行顺序,减少机器温度或邻近负载变化造成的偏差。若修复了 R04,整轮重测并保留旧记录,不能只把新结果补回旧表。

最后把迁移成本也放回决定里

选型记录:达标与迁移成本

假设 B 是现有服务。这组教学数据给出的局部结论很明确:在这个共享售后规则、混合输出长度的热状态突发中,B 完成了更多满足等待和质量要求的请求。A 的输出 token 更多,不构成迁移动机。但 B 也有一条请求失败,长物流回答仍超过 3 秒,不能把“比 A 好”写成“已经满足上线要求”。

下一步由业务目标决定。如果六条都应及时完成,就先处理错误和长回答,再用更多样本验证;如果产品允许较长物流说明采用不同等待目标,须先由产品确定分类型标准,再用同一标准重新评价两边。不能看见 B 的结果以后,专门放宽一项阈值让它过线。

迁移成本也需要具体到本次接入。假设换到 A 要修改 JSON 解析配置、适配流式结束事件、补齐超时取消观测并验证回滚到 B;这四项都应有负责人和可验证结果。仅改服务地址成功,不证明原来的取消、错误处理和告警还能正常工作。反过来,如果 B 的接口已经难以维护,A 能消除大量接入问题,这些收益也应该进入决定,但需要记录,不能靠“新框架更先进”代替。

这次可以留下这样一份结论:“教学轨迹 H 中,B 在 10 秒内得到 4 条达标结果,A 得到 2 条;两侧各有一条错误或超时,长回复均超标。保留 B 作为当前基线,不据此发起迁移。先查失败及长回复,再补冷起点和更大样本;只有候选在相同验收标准下稳定改善结果,且接入与回滚改动有证据,才进入迁移评估。”

真正部署时,把这里的 A、B 换成明确的引擎版本、模型、机器和配置,把六条手算表换成可回查的业务记录。你得到的才是一份能被下一轮实验修正的选型结论,而不是另一个脱离条件的排行榜。

资料核对于 2026-09-12。vLLM 链接为 latest 文档,SGLang 链接为在线压测指南;文中不主张具体安装版本默认启用哪些选项。两份官方资料支撑能力和工具边界,六请求、阈值、结果及迁移情境均为作者教学构造,没有实际 GPU 对照。

配图中的流程与记录项是方法示意;教学结果见正文表格。两张官方原文图保留 2026-09-06 的来源检查标识,本文于 2026-09-12 复核相关能力与参数说明。