AI API 测试技巧:从单元测试到压测

把大模型 API 接进生产系统之后,大多数人会立刻撞上一堵墙:这东西怎么测?传统 API 的测试套路——固定输入、固定输出、断言相等——在 LLM 面前全部失效。同一个 prompt 问两遍,答案几乎不会一字不差;跑一次测试还要花钱、还要等几秒钟;更麻烦的是,模型一升级,行为就悄悄变了。于是很多团队干脆不测了,把「模型会不会乱说」完全交给运气。

这篇文章给你一套能直接落地的分层测试方案:从毫秒级、零成本的 Mock 单元测试,到花钱但必要的真实集成测试,再到上线前的压测与容量规划。每层解决什么问题、用什么工具、成本多少,一次讲清楚。这也是我们对内部接入 40+ 模型的AI API 网关做质量保障时实际在用的方法。

一、为什么 AI API 的测试和传统 API 不一样

核心差异有三个。第一是非确定性:同样的输入,输出在合法范围内浮动,断言不能等于「字符串完全一致」,而必须是「结构正确 + 语义达标」。第二是成本与延迟:一次真实调用要花钱、要等秒级响应,测试不可能像普通单元测试那样跑几千次。第三是退化风险:模型版本、提示词、参数任何一处变动,都可能让输出质量整体下滑,而且往往是悄悄下滑。

应对方法不是「不测」,而是分层:上层用 Mock 保证逻辑正确且快,下层用真实调用保证行为正确且可信。每一层只负责自己的问题,成本可控,速度可控。

二、第一层:Mock 单元测试——毫秒级、零成本

凡是「我的代码怎么处理模型返回」的逻辑,都应该用 Mock 测。解析、分支、重试、超时、错误处理——这些逻辑与模型无关,完全可以确定性地测。用 responseshttpx.MockTransport 拦截 HTTP 请求,返回固定的假响应:

# test_chat.py
import pytest, responses

@responses.activate
def test_parse_reply():
    responses.add(
        responses.POST, "https://api.dr-ai.top/v1/chat/completions",
        json={"choices": [{"message": {"content": "{\"city\": \"北京\"}"}}]},
    )
    result = chat_and_parse("北京今天天气怎么样?")
    assert result["city"] == "北京"

@responses.activate
def test_retry_on_429():
    responses.add(responses.POST, URL, status=429, json={"error": {"message": "rate limited"}})
    responses.add(responses.POST, URL, status=200, json=ok_body())
    # 断言第一次失败后自动退避重试,最终成功
    assert chat("hi") == "ok"
    assert responses.calls[0].request.url == URL

这一层要覆盖的典型场景:响应解析成功、JSON 损坏、空内容、HTTP 429/5xx、超时、网络错误。把这些写全,你的「AI 应用」里 80% 的代码就和「AI」无关了,可以放心快速迭代。Mock 是结构化输出解析测试的主力,强烈建议配套使用。

三、第二层:契约测试与 Schema 校验

Mock 测的是「我的代码」,契约测的是「模型答应给我什么」。约定好输出格式之后,把它写成 JSON Schema,每次真实调用都校验一遍:

import jsonschema

SCHEMA = {
    "type": "object",
    "properties": {
        "city": {"type": "string"},
        "temperature": {"type": "number"},
        "units": {"enum": ["celsius", "fahrenheit"]},
    },
    "required": ["city", "temperature", "units"],
    "additionalProperties": False,
}

def validate(output: str) -> dict:
    data = json.loads(output)          # 1. 可解析
    jsonschema.validate(data, SCHEMA)  # 2. 结构正确
    return data

把 Schema 校验挂在所有真实调用的出口上,任何一次「模型不守规矩」都会立刻暴露。更高级的做法是「契约测试」:把固定输入 + 期望结构固化成用例集,模型升级或换模型时批量跑一遍,谁破坏了契约一目了然。这比肉眼抽查可靠得多,也是模型评估的简化版——想系统化地评估模型质量,可以看我们的结构化输出指南和英文站的模型评估方法论。

四、第三层:集成测试——花小钱买真话

Mock 再全,也测不出「模型真实返回长什么样」。集成测试用真实 API 跑小样本,验证端到端行为。要点是控制成本:

集成测试最好挂在 CI 的定时任务或发布流水线里,每天跑一次。花几块钱,换来「今天模型没抽风」的安心,非常划算。

五、第四层:回归测试与黄金样本集

模型和提示词是会「漂移」的。建立一份黄金样本集(golden set):50~200 条覆盖典型场景的输入,配上「什么算通过」的标准——可以是结构化字段全对、可以是答案包含关键信息、也可以是人工打过分数的参考答案。每次改提示词、换模型、升级 SDK 之后,全量跑一遍:

变化必须跑的测试重点观察
改提示词黄金样本全量通过率、输出格式
换模型 / 升级版本黄金样本 + 契约测试成本、延迟、质量分
改解析代码单元测试 + 契约测试解析成功率
SDK / 网关升级全量回归兼容性、错误码

黄金样本集是 AI 应用最值得投入的资产之一——它把「模型变笨了」这种模糊的抱怨,变成了「通过率从 98% 掉到 91%」这样可行动的信号。

六、第五层:压测与容量规划

上线前还要回答一个问题:扛得住多少并发?AI API 的压测和普通接口不同——单请求耗时秒级、成本随请求数线性增长、上游还有限流。用 Locust 模拟真实负载,重点看三个指标:

# locustfile.py
from locust import HttpUser, task, between

class ChatUser(HttpUser):
    wait_time = between(0.5, 2.0)

    @task
    def chat(self):
        self.client.post("/v1/chat/completions", json={
            "model": "gpt-5-mini",
            "messages": [{"role": "user", "content": "用一句话介绍量子计算"}],
            "max_tokens": 200,
            "stream": True,   # 流式更接近真实场景
        })

压测时要同时盯:TTFT 与 TPOT(感知延迟)、P95/P99 延迟错误率与限流触发次数。得到容量上限后反推:峰值流量 × 安全系数 = 需要预留的配额。特别提醒:上游模型商的限流(RPM/TPM)往往是瓶颈,网关侧要做好排队和退避,否则压测会变成「雪崩演练」。延迟优化和限流的细节,我们分别在《LLM API 延迟优化》API 接入安全里展开过。

七、成本与测试预算管理

测试要花钱,但可以控制:给每类测试设月度预算上限(比如集成测试每月 $50),超过自动告警;能用 mini 模型跑的就别用旗舰;Mock 层能覆盖的绝不上真实调用。测试成本本质上是保险金——一个月几十美元,换的是「上线不被模型坑」。

八、常见坑与建议

测试不是 AI 应用的奢侈品,是及格线。把这五层搭起来,模型升级、提示词迭代、流量增长就都不再是「开盲盒」。想快速搭好测试环境?注册 DrAI,一个 Key 就能在测试环境里切换 40+ 模型,黄金样本集的模型对比跑起来很方便。

🚀 用真实模型跑你的测试集

注册 DrAI,一个 API Key 调用 GPT-5 系列、Claude 4、DeepSeek R1、Gemini 2.5 Pro 等 40+ 模型,Pro 套餐仅 $9.99/月。

免费注册 →   查看定价

📚 延伸阅读

LLM 结构化输出指南:JSON Schema 与可靠解析用 JSON Schema 约束 LLM 输出,配合 Function Calling 与 Pydantic,把解析成功率从 95% 提到 99.9%。 LLM API 延迟优化:从 3 秒到 300 毫秒TTFT 与 TPOT 拆解、流式输出、连接复用、模型路由与缓存,一份完整的延迟优化账单。 AI API 成本优化:12 个立省 60% 的方法模型路由、上下文压缩、语义缓存与包月订阅,实测可将 AI API 调用成本降低 60%。
🌐 中文