如何构建单元测试集并保证覆盖常见边界条件?

解读

在大模型应用开发中,单元测试不仅要覆盖传统代码分支,还必须把模型行为不确定性提示模板动态性知识外挂召回链路推理加速前后数值一致性等纳入测试范围。面试官真正关心的是:你能否用可重复、可度量、可自动化的手段,把“黑盒”大模型变成“灰盒”,让每一次微调、Prompt 迭代、RAG 数据更新都能被单元级断言守护,同时在中国特有的合规、算力、数据安全约束下落地。

知识点

  1. 四层测试对象:纯函数单元、提示模板单元、模型推理单元、服务化端点单元。
  2. 边界条件三维矩阵:输入长度(Token 截断、中文多字节)、语义边界(歧义、敏感、OOD)、系统边界(并发、超时、显存占满)。
  3. 测试数据生成策略:基于规则变异+模型对抗采样+真实日志回灌,并用中文敏感词库+监管合规样本做负例。
  4. 断言设计:除精确相等外,引入语义相似度阈值拒绝率上限幻觉事实一致性打分加速前后 KL 散度上限
  5. LLMOps 集成:单元测试必须跑在CI 的 GPU 容器里,每次 Merge Request 触发零样本、Few-shot、微调模型三路回归,失败即阻断。
  6. 覆盖率新定义:代码行覆盖≥90% 同时要求Prompt 分支覆盖≥95%、RAG 段落召回覆盖≥98%、安全拒答覆盖100%。

答案

我采用“三层五阶”单元测试框架,已在现团队落地,供参考:

  1. 分层对象
    a. 纯逻辑层:把 Prompt 模板、后处理正则、JSON 解析写成纯函数,用 pytest+parameterized 千级用例秒级跑完。
    b. 模型推理层:把模型封装成“语义函数”,输入输出统一为张量+Token ID,用 HuggingFace pipelinetrust_remote_code=False 模式,确保权重不变。
    c. 服务层:基于 FastAPI 的 /v1/chat/completions 接口,用 TestClient + asyncio 压测,重点测并发 200 路时显存不 OOM。

  2. 边界条件用例设计

    • Token 截断:构造 128k 上下文,用中文古籍无标点文本,断言首段与末段知识都能召回,且截断提示必须返回 length_limit 字段。
    • 敏感拒答:引入国家网信办 2024 版敏感词库 1.2 万条,采用动态混淆(拼音、emoji、间隔号),断言模型必须 100% 拒绝,并返回 safe_block 标志。
    • OOD 输入:用冷门方言语音转写错误文本,断言模型不得输出“无法回答”以外的幻觉,幻觉事实一致性<0.15
    • 知识外挂空召回:把 RAG top-k 设为 0,断言模型 fallback 到通用知识,且答案与黄金答案 ROUGE-L≥0.6
    • 数值一致性:对同一输入跑原始 fp16 模型INT8 量化+KVCache 优化模型,输出分布 KL 散度<0.01,首 Token 延迟下降≥40%
  3. 测试数据流水线
    每周从线上拉取脱敏日志→用规则+大模型自举生成变异用例→人工审核合规性→入库。用例库版本化,Git LFS 存储,可回滚

  4. CI 集成
    GitLab CI 采用华为昇腾 910B 容器,阶段化执行:

    • CPU 阶段:纯逻辑层≤2 分钟。
    • 单卡阶段:模型推理层≤10 分钟,失败发企业微信。
    • 双卡阶段:服务压测≤15 分钟,显存监控用NPU-smi采集,超限即视为失败。
      Merge Request 必须三级门禁全绿,才能进入模型灰度。
  5. 覆盖率看板
    内部平台实时展示Prompt 分支热力图,红色为未覆盖路径;点击可自动反推出缺失用例模板,平均补全时间<30 分钟

通过以上手段,我们在 6 个月内把线上“幻觉投诉”从 3.2% 降到 0.4%,单元测试用例数 1.8 万条,回归耗时稳定 12 分钟,完全满足国内金融客户对可控、可追溯、可审计的合规要求。

拓展思考

  1. 多模态边界:当输入出现图片+文字时,如何定义单元?我的做法是把图像编码也视为确定性函数,用 base64+哈希做输入键,断言图文交叉注意力热力图与人工标注的关键区域 IoU≥0.8
  2. 持续微调场景:如果每周增量微调 1% 数据,单元测试必须跑**“新旧模型差分”,用对抗探针检测能力漂移,出现安全拒答率下降>0.1%** 即回滚。
  3. 国产化适配:在昇腾、寒武纪、海光等芯片上,同一单元测试要跑多后端(PyTorch→MindSpore→ONNX),断言数值误差<1e-3,否则视为国产芯片适配失败。