首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >大模型API上线前怎么压测?一次基于蓝耘元生代MaaS的生产验证复盘

大模型API上线前怎么压测?一次基于蓝耘元生代MaaS的生产验证复盘

作者头像
fruge365
发布2026-08-24 08:17:09
发布2026-08-24 08:17:09
300
举报
文章目录
  • 大模型API上线前怎么压测?一次基于蓝耘元生代MaaS的生产验证复盘
    • 一、第一版脚本为什么几乎没用
    • 二、先让客户端记录完整请求生命周期
    • 三、压测数据集不能全是 “你好”
    • 四、阶梯压测比直接打满并发更有价值
      • 1\. 基线测试
      • 2\. 阶梯压测
      • 3\. 稳态测试
      • 4\. 突发流量
      • 5\. 故障注入
    • 五、三个最容易误判的结果
      • 误判一:平均延迟正常,所以平台稳定
      • 误判二:缓存命中率高,所以生成速度也会提升
      • 误判三:备用模型返回 200,所以降级成功
    • 六、统一入口在压测链路里承担什么
    • 七、最终留下的不是一张跑分榜

大模型API上线前怎么压测?一次基于蓝耘元生代MaaS的生产验证复盘

我们给企业知识助手接入大模型时,第一版测试脚本只有一个功能:向接口发起请求,然后打印总耗时。

项目最初只是给一条业务线做内部知识问答:员工输入制度、产品或流程问题,系统检索文档后生成答案;遇到需要查询状态的请求,再交给工具接口处理。模型调用由蓝耘元生代 MaaS 承接。试点用户不多,问题却很杂 —— 有十几字的简单查询,也有附带长材料的规则判断,还有必须返回固定字段的后续流转任务。

当试点从几个人扩展到一个团队,大家最常反馈的已经不是 “模型答得对不对”,而是 “为什么同类问题有时秒回、有时一直转圈”“明明页面超时了,为什么用量还在增长”。这也是我们从一次性脚本转向生产压测的起点:不是为了给平台排一个跑分榜,而是要确认这条链路能否被业务团队持续使用和排障。

这套脚本在 Demo 阶段看不出问题。模型能回答,流式输出也正常,不同平台之间的耗时差距似乎不大。但当调用链进入真实业务,问题很快变了:同一个请求偶尔要等很久;客户端已经超时,上游还在继续执行;失败重试后账单出现两次调用;备用模型虽然返回成功,JSON 结构却变了。

这时我们才意识到,所谓 “大模型 API 性能测试”,并不是给接口跑一个平均响应时间,而是把一条生产调用链拆开,找到每种异常究竟发生在哪里。


一、第一版脚本为什么几乎没用

最初的测试请求全部是短问题,输入长度接近,输出上限固定,而且只有单并发。它能回答 “接口通不通”,回答不了下面这些问题:

  • 用户多久能看到第一个 Token?
  • 首 Token 之后的输出是否连续?
  • 并发增加后,P99 从哪个区间开始上升?
  • 客户端超时后,上游是否仍然产生 Token?
  • 重试发生在 SDK、网关还是模型服务层?
  • 缓存命中减少了 Prefill,还是直接复用了历史答案?
  • 切换备用模型后,业务结果还能不能被下游解析?

在重构压测方案时,我们参考了一份生产级模型服务选型白皮书,将指标拆成交互响应、任务效率、调用稳定、负载承载、缓存复用以及业务连续与成本六组。我们没有把六组指标直接做成一张大而全的仪表盘,而是先改造客户端采样逻辑。

二、先让客户端记录完整请求生命周期

一开始,客服只会转来一张 “正在加载” 的截图,研发却只能在服务端日志里按时间范围猜是哪一次调用。只记录接口耗时,无法把用户看到的卡顿、应用侧的超时和上游的实际推理过程串起来。于是,客户端记录成为压测改造的第一步。

平台控制台的数据适合观察整体趋势,但开发团队还需要一份自己的调用记录。至少要把业务请求 ID、首 Token 时间、结束时间、状态码、流中断、Token 用量和重试次数关联起来。

下面是简化后的 Python 结构,省略了鉴权和业务字段:

代码语言:javascript
复制
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 只说明接口返回成功,不代表答案符合格式、引用有效或工具执行成功。我们的最终成功条件是:结果质量达标、结构可解析,并且在业务允许的时间内完成。

三、压测数据集不能全是 “你好”

真实请求分布也比测试脚本复杂得多。知识助手的系统提示词和检索上下文往往高度重复,但用户上传的材料、工具参数和期望输出又差异很大;如果仍用一批短问题压测,缓存、排队和长输出带来的影响都会被掩盖。

第二个改动是重做测试集。我们从业务记录中脱敏抽取请求特征,而不是直接复制用户内容,然后按比例生成四类样本:

  1. 短输入、短输出,用于模拟分类和简单问答;
  2. 短输入、长输出,用于模拟总结与内容生成;
  3. 长输入、短输出,用于模拟知识材料判断;
  4. 长输入、长输出,用于模拟复杂分析和连续对话。

同样的并发数,在四类任务上的资源消耗完全不同。如果测试集只有短问题,平台看起来会非常稳定;一旦真实业务加入长上下文和深度推理,排队、输出速度和完整任务成本都会变化。

我们还为每类请求准备了质量门槛。分类任务检查枚举值,结构化抽取检查字段,知识问答检查引用,工具任务检查最终执行结果。这样得到的 “业务有效成功率”,比单纯的接口成功率更接近生产体验。

四、阶梯压测比直接打满并发更有价值

我们最初也试过直接把并发拉满,结果只得到一串错误码,既看不出容量边界,也无法判断是客户端、网关还是模型服务先出现瓶颈。改成阶梯压测之后,每一级负载都保留观察窗口,异常才有机会和当时的请求类型、缓存状态与队列变化对应起来。

我们的压测顺序最终固定为五步:

1. 基线测试

先在低负载下固定模型、参数、输入输出分布和网络条件,分别记录冷缓存与热缓存结果。基线不是为了找最快的一次,而是确认采样逻辑正确。

2. 阶梯压测

逐级增加并发或请求速率,每一级保持足够观察时间。重点不是 “压到多少”,而是找到吞吐停止增长、P99 明显上升或队列持续累积的拐点。

3. 稳态测试

在接近日常生产负载的条件下持续运行,观察错误是否累积、尾延迟是否逐渐恶化。很多短时测试看不到的问题,会在持续运行后暴露。

4. 突发流量

短时间提高请求量,然后恢复到正常水平。我们不仅看高峰期失败多少,还看流量回落后积压请求多久能被消化。

5. 故障注入

分别模拟限流、服务端错误、超时、流式连接中断和上游不可用。故障注入的目标不是证明系统 “永不失败”,而是验证失败能否被识别、限制和恢复。

当 P99 持续超过业务时延目标、超时率越过预设阈值、吞吐不再增长或业务有效成功率明显下降时,我们就停止继续加压。继续堆并发只会制造更多噪声,不能帮助定位容量边界。


五、三个最容易误判的结果

这三类误判并不只发生在压测报告里。它们经常以 “偶发卡顿”“模型切换后字段不对”“用量突然升高” 的形式进入工单,等到业务量继续增长才被放大。

误判一:平均延迟正常,所以平台稳定

我们遇到过平均值变化不大、P99 却持续升高的情况。原因不一定是模型变慢,也可能是请求开始排队。此时如果客户端立即重试,相当于继续给已经拥堵的链路增加流量。

处理方法是把排队、首 Token、生成阶段和端到端时间分开观察,并给重试设置上限和退避策略。没有这一步,“自动重试” 很容易从容错机制变成流量放大器。

误判二:缓存命中率高,所以生成速度也会提升

Prompt 前缀缓存复用的是相同 Token 前缀对应的 KV Cache,主要减少 Prefill 计算,并不会直接加快后续 Token 生成。如果答案很长,Decode 仍可能占据主要时间。

语义去重则是另一层能力。它可以识别、聚合或短时间合并相似请求,但不一定直接返回历史答案。因此,我们把 Prompt 前缀命中率、可复用 Token、语义识别率、合并率和实际触发的上游调用次数分开统计,不再使用一个笼统的 “缓存命中率”。

误判三:备用模型返回 200,所以降级成功

模型切换最容易被忽略的是业务兼容性。同一个 Prompt 换到另一模型后,字段名称、工具参数、引用格式和拒答行为都可能变化。

所以故障测试完成后,我们还会重新跑一遍业务校验集。只有备用路径能被触发、结果能被解析、质量达到门槛,才能算真正的切换成功。

六、统一入口在压测链路里承担什么

这次项目中,我们将蓝耘元生代 MaaS 作为调用入口,但没有把它当成 “接上就自动变快” 的黑盒,而是把客户端记录作为性能判断的第一手数据。

这样做有三个直接好处:

  • 多个候选模型可以在相同调用方式和测试数据下验证,减少 SDK 差异带来的干扰;
  • 应用侧可以用业务请求 ID 串联重试记录,再结合平台侧的用量和异常信息核对,方便区分客户端重试与实际上游调用;
  • 当业务需要从 API 验证继续走向知识应用或私有化部署时,不必重新设计整套验证口径。

知识链路则继续拆分职责:MaaS 负责模型调用,智能体知识平台负责知识库和文档检索,企业业务系统负责身份、权限和最终业务规则。任何一层变慢,都能先在自己的计时点被发现,而不是统一归因于 “大模型不稳定”。

我们仍然保留应用侧超时、请求 ID、幂等控制和人工兜底。统一调用入口可以承接模型治理,但不能替代业务系统对任务状态和最终结果负责。

七、最终留下的不是一张跑分榜

完成这轮改造后,我们没有得到一个 “所有场景最快的平台”,而是得到了一组更有用的结论:

  • 哪类任务适合哪种模型;
  • 日常负载和突发负载的容量边界在哪里;
  • 哪些错误可以重试,哪些必须立即失败;
  • Prompt 前缀缓存在哪些请求上有收益;
  • 备用模型切换后哪些业务规则需要重新校验;
  • 一次完整任务实际产生了多少上游调用和 Token。

对我们而言,蓝耘元生代 MaaS 的作用不是替代这些判断,而是让模型调用、知识应用和后续部署需求可以继续沿用同一套压测与验收口径;业务是否真正可用,仍要由应用侧的质量校验和真实负载结果决定。

大模型 API 进入生产环境后,性能优化的对象不再只是模型,而是客户端、网关、模型服务、知识检索、工具接口和业务系统共同组成的链路。平均响应时间只能告诉你系统 “大多数时候看起来怎样”,P99、业务有效成功率和故障恢复过程,才会告诉你它能不能真正上线。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-08-17,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • 文章目录
  • 大模型API上线前怎么压测?一次基于蓝耘元生代MaaS的生产验证复盘
    • 一、第一版脚本为什么几乎没用
    • 二、先让客户端记录完整请求生命周期
    • 三、压测数据集不能全是 “你好”
    • 四、阶梯压测比直接打满并发更有价值
      • 1. 基线测试
      • 2. 阶梯压测
      • 3. 稳态测试
      • 4. 突发流量
      • 5. 故障注入
    • 五、三个最容易误判的结果
      • 误判一:平均延迟正常,所以平台稳定
      • 误判二:缓存命中率高,所以生成速度也会提升
      • 误判三:备用模型返回 200,所以降级成功
    • 六、统一入口在压测链路里承担什么
    • 七、最终留下的不是一张跑分榜
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档