
我们给企业知识助手接入大模型时,第一版测试脚本只有一个功能:向接口发起请求,然后打印总耗时。
项目最初只是给一条业务线做内部知识问答:员工输入制度、产品或流程问题,系统检索文档后生成答案;遇到需要查询状态的请求,再交给工具接口处理。模型调用由蓝耘元生代 MaaS 承接。试点用户不多,问题却很杂 —— 有十几字的简单查询,也有附带长材料的规则判断,还有必须返回固定字段的后续流转任务。
当试点从几个人扩展到一个团队,大家最常反馈的已经不是 “模型答得对不对”,而是 “为什么同类问题有时秒回、有时一直转圈”“明明页面超时了,为什么用量还在增长”。这也是我们从一次性脚本转向生产压测的起点:不是为了给平台排一个跑分榜,而是要确认这条链路能否被业务团队持续使用和排障。
这套脚本在 Demo 阶段看不出问题。模型能回答,流式输出也正常,不同平台之间的耗时差距似乎不大。但当调用链进入真实业务,问题很快变了:同一个请求偶尔要等很久;客户端已经超时,上游还在继续执行;失败重试后账单出现两次调用;备用模型虽然返回成功,JSON 结构却变了。
这时我们才意识到,所谓 “大模型 API 性能测试”,并不是给接口跑一个平均响应时间,而是把一条生产调用链拆开,找到每种异常究竟发生在哪里。
最初的测试请求全部是短问题,输入长度接近,输出上限固定,而且只有单并发。它能回答 “接口通不通”,回答不了下面这些问题:
在重构压测方案时,我们参考了一份生产级模型服务选型白皮书,将指标拆成交互响应、任务效率、调用稳定、负载承载、缓存复用以及业务连续与成本六组。我们没有把六组指标直接做成一张大而全的仪表盘,而是先改造客户端采样逻辑。
一开始,客服只会转来一张 “正在加载” 的截图,研发却只能在服务端日志里按时间范围猜是哪一次调用。只记录接口耗时,无法把用户看到的卡顿、应用侧的超时和上游的实际推理过程串起来。于是,客户端记录成为压测改造的第一步。
平台控制台的数据适合观察整体趋势,但开发团队还需要一份自己的调用记录。至少要把业务请求 ID、首 Token 时间、结束时间、状态码、流中断、Token 用量和重试次数关联起来。
下面是简化后的 Python 结构,省略了鉴权和业务字段:
import os
import time
import uuid
from openai import AsyncOpenAI
client = AsyncOpenAI(
api_key=os.environ["LLM_API_KEY"],
base_url=os.environ["LLM_BASE_URL"],
)
async def invoke(model: str, messages: list[dict]) -> dict:
request_id = str(uuid.uuid4())
started = time.perf_counter()
first_token_at = None
chunks = 0
content = []
try:
stream = await client.chat.completions.create(
model=model,
messages=messages,
stream=True,
extra_headers={"X-Business-Request-ID": request_id},
)
async for event in stream:
if not event.choices:
continue
text = event.choices[0].delta.content or ""
if text and first_token_at is None:
first_token_at = time.perf_counter()
if text:
chunks += 1
content.append(text)
finished = time.perf_counter()
return {
"request_id": request_id,
"ok": True,
"ttft_ms": None if first_token_at is None else (first_token_at - started) * 1000,
"e2e_ms": (finished - started) * 1000,
"chunks": chunks,
"output": "".join(content),
}
except Exception as exc:
return {
"request_id": request_id,
"ok": False,
"error_type": type(exc).__name__,
"elapsed_ms": (time.perf_counter() - started) * 1000,
}这段代码本身并不复杂,关键是不要只保留一个 elapsed。首 Token 延迟和完整响应时间必须分开;流式响应是否完整结束也要单独记录。正式版本中,我们还会保存相邻数据块间隔、输入输出 Token、模型标识、重试来源和业务校验结果。
业务校验结果尤其重要。HTTP 200 只说明接口返回成功,不代表答案符合格式、引用有效或工具执行成功。我们的最终成功条件是:结果质量达标、结构可解析,并且在业务允许的时间内完成。
真实请求分布也比测试脚本复杂得多。知识助手的系统提示词和检索上下文往往高度重复,但用户上传的材料、工具参数和期望输出又差异很大;如果仍用一批短问题压测,缓存、排队和长输出带来的影响都会被掩盖。
第二个改动是重做测试集。我们从业务记录中脱敏抽取请求特征,而不是直接复制用户内容,然后按比例生成四类样本:
同样的并发数,在四类任务上的资源消耗完全不同。如果测试集只有短问题,平台看起来会非常稳定;一旦真实业务加入长上下文和深度推理,排队、输出速度和完整任务成本都会变化。
我们还为每类请求准备了质量门槛。分类任务检查枚举值,结构化抽取检查字段,知识问答检查引用,工具任务检查最终执行结果。这样得到的 “业务有效成功率”,比单纯的接口成功率更接近生产体验。
我们最初也试过直接把并发拉满,结果只得到一串错误码,既看不出容量边界,也无法判断是客户端、网关还是模型服务先出现瓶颈。改成阶梯压测之后,每一级负载都保留观察窗口,异常才有机会和当时的请求类型、缓存状态与队列变化对应起来。
我们的压测顺序最终固定为五步:
先在低负载下固定模型、参数、输入输出分布和网络条件,分别记录冷缓存与热缓存结果。基线不是为了找最快的一次,而是确认采样逻辑正确。
逐级增加并发或请求速率,每一级保持足够观察时间。重点不是 “压到多少”,而是找到吞吐停止增长、P99 明显上升或队列持续累积的拐点。
在接近日常生产负载的条件下持续运行,观察错误是否累积、尾延迟是否逐渐恶化。很多短时测试看不到的问题,会在持续运行后暴露。
短时间提高请求量,然后恢复到正常水平。我们不仅看高峰期失败多少,还看流量回落后积压请求多久能被消化。
分别模拟限流、服务端错误、超时、流式连接中断和上游不可用。故障注入的目标不是证明系统 “永不失败”,而是验证失败能否被识别、限制和恢复。
当 P99 持续超过业务时延目标、超时率越过预设阈值、吞吐不再增长或业务有效成功率明显下降时,我们就停止继续加压。继续堆并发只会制造更多噪声,不能帮助定位容量边界。
这三类误判并不只发生在压测报告里。它们经常以 “偶发卡顿”“模型切换后字段不对”“用量突然升高” 的形式进入工单,等到业务量继续增长才被放大。
我们遇到过平均值变化不大、P99 却持续升高的情况。原因不一定是模型变慢,也可能是请求开始排队。此时如果客户端立即重试,相当于继续给已经拥堵的链路增加流量。
处理方法是把排队、首 Token、生成阶段和端到端时间分开观察,并给重试设置上限和退避策略。没有这一步,“自动重试” 很容易从容错机制变成流量放大器。
Prompt 前缀缓存复用的是相同 Token 前缀对应的 KV Cache,主要减少 Prefill 计算,并不会直接加快后续 Token 生成。如果答案很长,Decode 仍可能占据主要时间。
语义去重则是另一层能力。它可以识别、聚合或短时间合并相似请求,但不一定直接返回历史答案。因此,我们把 Prompt 前缀命中率、可复用 Token、语义识别率、合并率和实际触发的上游调用次数分开统计,不再使用一个笼统的 “缓存命中率”。
模型切换最容易被忽略的是业务兼容性。同一个 Prompt 换到另一模型后,字段名称、工具参数、引用格式和拒答行为都可能变化。
所以故障测试完成后,我们还会重新跑一遍业务校验集。只有备用路径能被触发、结果能被解析、质量达到门槛,才能算真正的切换成功。
这次项目中,我们将蓝耘元生代 MaaS 作为调用入口,但没有把它当成 “接上就自动变快” 的黑盒,而是把客户端记录作为性能判断的第一手数据。
这样做有三个直接好处:
知识链路则继续拆分职责:MaaS 负责模型调用,智能体知识平台负责知识库和文档检索,企业业务系统负责身份、权限和最终业务规则。任何一层变慢,都能先在自己的计时点被发现,而不是统一归因于 “大模型不稳定”。
我们仍然保留应用侧超时、请求 ID、幂等控制和人工兜底。统一调用入口可以承接模型治理,但不能替代业务系统对任务状态和最终结果负责。
完成这轮改造后,我们没有得到一个 “所有场景最快的平台”,而是得到了一组更有用的结论:
对我们而言,蓝耘元生代 MaaS 的作用不是替代这些判断,而是让模型调用、知识应用和后续部署需求可以继续沿用同一套压测与验收口径;业务是否真正可用,仍要由应用侧的质量校验和真实负载结果决定。
大模型 API 进入生产环境后,性能优化的对象不再只是模型,而是客户端、网关、模型服务、知识检索、工具接口和业务系统共同组成的链路。平均响应时间只能告诉你系统 “大多数时候看起来怎样”,P99、业务有效成功率和故障恢复过程,才会告诉你它能不能真正上线。