写一份提示词契约测试,在同一把 key 上跑遍六个后端
介绍如何用一份提示词契约测试,在聚合 API key 可触达的多个模型后端上验证输出结构、关键字段与边界行为的一致性,把"换模型"从试错变成可重复的回归检查。
为什么需要提示词契约测试
用一把聚合 key 调多个模型(Claude、GPT、DeepSeek、Qwen、GLM、Kimi)时,最先坏掉的往往不是模型能力,而是输出契约:同样是"返回 JSON",有的模型包了 ```json 代码块,有的在前后加了说明文字,有的把字段名写成 camelCase。上游模型版本一变,你的解析器就悄悄挂了。
这类问题用肉眼看几条输出验证不了,适合写成断言集合——也就是一份提示词契约测试:对同一个提示词,在多个后端上重复跑同一组检查。
第一步:把"能用"翻译成断言
先明确这份提示词对外承诺了什么,再逐条变成机器可判定的检查。常见四类:
- 结构断言:输出能否被
json.loads直接解析(不经清洗);顶层是对象还是数组;必填字段是否齐全。 - 关键字段断言:字段类型正确(布尔就是布尔,不是字符串
"true");枚举值落在允许集合内;数值在合理区间。 - 拒答断言:对越界请求,模型是返回明确的拒绝语义,还是编造答案。这条最难写,通常检查的是"不出现某类内容"加上"出现拒绝意图标记",而不是匹配固定措辞。
- 长度边界断言:输出 token 数不超过上限;截断风险在可接受范围;超长输入时是否优雅降级而非报错。
断言要少而稳。一开始就写二十条,维护成本会超过收益。
第二步:让提示词本身可测
模型间差异有一部分来自提示词写得松。写测试时顺手收紧:
- 明确要求"只输出 JSON,不要代码块、不要前后说明"。
- 给出字段名、类型、允许取值,而不是用自然语言描述期望。
- 给一个极小的示例输出,而不是长示例——长示例容易诱导某些模型照抄示例内容。
- 把温度等采样参数固定下来;不确定的采样会让断言随机失败。
这一步做完,往往一半的"模型不稳定"其实是提示词不稳定。
第三步:在多个后端上跑同一套用例
聚合 key 的价值在这里体现:你不需要为每个厂商分别申请账号、分别改 SDK 配置,只需要换模型标识。测试脚本的设计要点:
- 用表驱动:用例列表 × 后端列表做笛卡尔积,每个组合单独一条结果。
- 同一份提示词、同一组参数,只变模型名。参数不兼容时(不同后端对某些参数的接受度不同),显式记录"该后端跳过该参数",而不是静默忽略。
- 重试与判定分离:网络/限流错误单独归类,不要让它变成断言失败,否则你会去改本来没问题的提示词。
- 结果落盘:每条记录后端、用例、通过与否、失败原因、原始输出片段。失败时保留原文,这是修提示词的唯一线索。
第四步:区分"契约失败"和"模型差异"
跑完一定会出现不一致。先分两类:
- 契约失败:违反了你明确写下的规则,比如 JSON 解析不出来、字段缺失。这类必须修——要么改提示词,要么在调用侧加一层归一化。
- 风格差异:措辞、详略、语气不同,但契约没破。这类不该进断言,否则测试会变成"我更喜欢哪个模型"的主观投票。
一个实用的判断标准:如果这条断言失败时你不知道该改提示词还是改模型,它就不该是断言。
第五步:让它成为回归检查
契约测试真正的价值在重复。把脚本接进 CI 或定期任务,每次改提示词、每次上游模型更新后重跑,对比前后结果表。
注意成本:多后端 × 多用例 = 成倍的调用量。用短提示词、少用例、只跑关键断言来控制;在聚合站上按官方价 ×1.3 扣费,跑测试的消耗是可预期的,但仍应把用例集当成要精简的资产而不是随手加。
一个容易忽略的点:把归一化层也测进去
如果你的调用侧有清洗逻辑(剥代码块、补字段、兜底默认值),契约测试应该覆盖"清洗后"的输出,同时单独统计"清洗前"的通过率。前者告诉你系统是否可用,后者告诉你提示词本身的质量趋势。混在一起,你会长期看不到提示词的退化。
小结
提示词契约测试不是评测模型谁更强,而是回答一个工程问题:换后端之后,我的下游解析还活着吗。断言少、提示词紧、结果落盘、定期重跑,这四点做到,多后端就从一个不确定因素变成一个可管理的变量。