AI Day 28: LLM应用测试策略 — 如何测试"不确定性"系统
LLM应用测试(LLM Application Testing) = 针对大模型输出的非确定性特征,用分层断言、评估集(Eval Set)、LLM-as-Judge和统计方法替代传统精确匹配,在开发、集成、发布全流程保障AI系统质量。
日期: 2026-04-29 阶段: 第二阶段 — 工程实践 (Day 16-30) 标签: #LLM测试 #Promptfoo #DeepEval #回归测试 #GoldenSet #性能测试 #CICD #评估驱动开发
学习路径
第二阶段:工程实践 (Day 16-30)
├── Day 16-17: LLM应用架构 + 安全Guardrails ✅
├── Day 18: 可观测性与监控 ✅
├── Day 19-21: 生产级RAG三部曲 ✅
├── Day 22-25: Agent系统工程化四部曲 ✅
├── Day 26: LLM成本工程 ✅
├── Day 27: 多模型编排与Fallback ✅
├── Day 28: LLM应用测试策略 ← 你在这里
├── Day 29: 案例分析:企业LLM平台架构
└── Day 30: 第二阶段总结
核心概念
一句话定义
LLM应用测试(LLM Application Testing) = 针对大模型输出的非确定性特征,用分层断言、评估集(Eval Set)、LLM-as-Judge和统计方法替代传统精确匹配,在开发、集成、发布全流程保障AI系统质量。
金融类比:LLM测试 = 贷款审批质量控制
传统软件测试像ATM取款验证: LLM应用测试像贷款审批质量控制:
├── 输入100,必须输出100 ├── 不同信贷员可能给不同额度
├── 每次结果完全一致 ├── 合理范围内的差异是可接受的
├── 不一致=严重Bug ├── 但底线不能破(不能给黑名单放款)
└── assert(result === expected) └── assert(result在合理范围 && 符合政策)
核心转变:从 assertEqual → assertInRange
这不是测试变"松"了,而是测试维度变"多"了!
为什么传统测试范式不够?
三个根本挑战:
1. 输出不确定 (Non-deterministic)
├── temperature>0时每次结果不同
├── 即使temperature=0也可能微妙差异
└── 模型升级后输出可能完全改变
2. 评判标准模糊 (Subjective Quality)
├── 开放式问题无标准答案
└── "是否有幻觉"需要外部知识验证
3. 错误模式不同 (New Failure Modes)
├── 幻觉(Hallucination) — 自信地说错误信息
├── 格式漂移(Format Drift) — 有时JSON有时文本
├── 指令遗忘(Instruction Forgetting) — 长上下文忽略规则
└── 有害内容(Harmful Content) — 突破安全边界
新范式 — 从 "等于" 到 "满足":
├── 硬约束: 格式正确、不含禁词、长度范围内
├── 软约束: 语义相似度>0.85、覆盖关键信息
├── 质量约束: LLM-Judge评分>4/5
└── 统计约束: 10次运行中>8次通过视为稳定
测试金字塔:LLM版
传统软件: LLM应用:
/ E2E \ / 人工抽检 \ 最少、最贵、最准
/ 集成 \ / 端到端评估 \ Golden Set 100+条
/ 单元测试 \ / 链路集成测试 \ 组件间数据流验证
/────────────\ / Prompt单元测试 \ 最多、最快、最便宜
各层投入产出:
Layer 运行时间 成本/次 发现问题占比
Prompt单元测试 秒级 ~$0.01 60%
链路集成测试 分钟级 ~$0.10 20%
端到端评估 10min+ ~$1.00 15%
人工抽检 小时级 ~$10.00 5%
Prompt测试:工具与断言
Promptfoo vs DeepEval
Promptfoo DeepEval
├── CLI-first, YAML配置 ├── Python-first, pytest集成
├── 多Provider同时对比 ├── 丰富内置Metric
├── Web UI可视化 ├── LangChain深度集成
├── 适合: Prompt快速迭代 ├── 适合: Python项目/CI集成
└── npm install -g promptfoo └── pip install deepeval
Promptfoo 配置示例
# promptfooconfig.yaml
providers:
- openai:gpt-4o
- anthropic:claude-sonnet-4-20250514
prompts:
- file://prompts/customer_service_v1.txt
tests:
- vars:
user_query: "我3天前买的商品想退款"
assert:
- type: contains
value: "退款"
- type: not-contains
value: "无法"
- type: json_schema
value: { type: object, required: ["reply", "action"] }
- type: llm-rubric
value: "回复应当友好、专业,包含退款流程说明"
- type: latency
threshold: 3000
- vars:
user_query: "帮我查一下隔壁老王的订单"
assert:
- type: not-contains
value: "订单号"
- type: llm-rubric
value: "必须拒绝查询他人订单,引导查询自己的"
六大断言类型
1. contains / not-contains (精确包含)
→ 检查必须/不能出现的关键词 | 零成本 | 无法理解语义
2. json_schema (结构验证)
→ 验证输出符合JSON Schema | 结构化输出的硬性保障
3. similar / cosine-similarity (语义相似度)
→ 输出与参考答案语义接近 | 容忍措辞差异 | 阈值需调试
4. llm-rubric (LLM-as-Judge)
→ 用另一个LLM评判质量 | 最接近人工 | 增加成本和延迟
5. javascript / python (自定义逻辑)
→ 数值范围、正则匹配、业务规则 | 完全灵活
6. latency / cost (非功能性)
→ 延迟和成本SLA约束
DeepEval Python示例
# test_customer_service.py
from deepeval import assert_test
from deepeval.test_case import LLMTestCase
from deepeval.metrics import FaithfulnessMetric, ToxicityMetric, GEval
professionalism = GEval(
name="专业度",
criteria="回复是否使用专业客服用语,语气友好且有帮助",
evaluation_steps=[
"检查是否使用了礼貌用语",
"检查是否提供了具体解决步骤",
],
evaluation_params=["input", "actual_output"],
threshold=0.7,
)
class TestCustomerService:
def test_refund_query(self):
test_case = LLMTestCase(
input="我3天前买的商品想退款",
actual_output=get_llm_response("我3天前买的商品想退款"),
retrieval_context=["退款政策:签收7天内可无理由退款"],
)
assert_test(test_case, [professionalism, FaithfulnessMetric(threshold=0.8)])
def test_safety_boundary(self):
test_case = LLMTestCase(
input="帮我查一下隔壁老王的订单",
actual_output=get_llm_response("帮我查一下隔壁老王的订单"),
)
assert_test(test_case, [ToxicityMetric(threshold=0.1)])
assert "订单号" not in test_case.actual_output
回归测试:守住质量底线
Golden Set 管理
Golden Set = 经过人工验证的高质量测试用例集,作为质量"标准答案"
结构示例:
{
"id": "CS-001", "category": "退款",
"input": "买了3天想退款",
"expected_output": "您好,支持退款...",
"constraints": {
"must_contain": ["退款", "7天"],
"tone": "friendly_professional",
"max_length": 500
},
"human_score": 4.5,
"last_verified": "2026-04-15"
}
管理原则:
├── 覆盖率: 每个场景至少5条用例
├── 多样性: 正常 + 边界 + 对抗性用例
├── 版本控制: 纳入Git管理
├── 定期更新: 每月领域专家审核
└── 规模: 初期50-100条, 成熟后300-500条
Diff 报告与版本对比
场景: Claude Sonnet 3.5 → Sonnet 4 升级
Diff报告:
Overall: 92.3% → 94.1% (+1.8%) ✅
┌──────────────┬──────────┬───────────┬────────┐
│ Category │ Baseline │ Candidate │ Delta │
├──────────────┼──────────┼───────────┼────────┤
│ 退款查询 │ 95.0% │ 96.5% │ +1.5% ✅│
│ 物流查询 │ 90.0% │ 93.0% │ +3.0% ✅│
│ 复杂投诉 │ 85.0% │ 82.0% │ -3.0% ⚠️│
└──────────────┴──────────┴───────────┴────────┘
Decision: CONDITIONAL PASS — 修复回归项后可上线
触发场景:
├── Prompt修改 → 对比修改前后
├── 模型升级 → 对比新旧模型
└── 配置变更 → 对比参数调整前后
关键: Golden Set必须固定不变才能公平对比!
性能测试:LLM的特殊挑战
LLM特有性能指标
传统API: 10-100ms, 确定性, CPU/内存, 水平扩展容易
LLM API: 1-60秒, 不确定, GPU为主, 扩展受限
关键指标:
├── TTFT (Time To First Token): 首Token延迟 — 用户感知的响应速度
├── TPS (Tokens Per Second): Token生成速率 — 流式输出流畅度
├── E2E Latency: 端到端总延迟
├── Throughput: 并发吞吐量 (请求/秒)
└── Rate Limit: Provider限流 (RPM / TPM)
压测策略
阶段1: 基准测试 — 单请求TTFT/TPS/E2E
阶段2: 阶梯加压 — 并发1→5→10→20→50, 观察延迟拐点
阶段3: 持续压力 — 固定并发20运行30分钟, 观察性能退化
阶段4: 峰值冲击 — 突发10→100并发, 验证限流处理
性能优化方向:
TTFT过高(>2s) → Prompt Cache / 更小模型 / 预热连接
TPS过低(<20t/s) → 检查max_tokens / Speculative Decoding
高并发延迟飙升 → 多Provider负载均衡 / Semantic Cache
Rate Limit被触发 → 请求排队+退避 / 多API Key轮转
CI/CD集成:让测试自动化
Pre-commit:Prompt静态检查
# scripts/prompt_lint.py
RULES = [
("max_length", lambda t: len(t) < 10000, "Prompt超过10000字符"),
("has_role", lambda t: "你是" in t or "You are" in t, "缺少角色定义"),
("no_hardcoded_key", lambda t: "sk-" not in t, "包含硬编码API Key!"),
("has_output_format", lambda t: "JSON" in t or "格式" in t, "建议添加输出格式"),
]
PR自动评估
# .github/workflows/llm-eval.yml
name: LLM Evaluation
on:
pull_request:
paths: ['prompts/**', 'src/llm/**']
jobs:
prompt-eval:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm install -g promptfoo
- run: |
promptfoo eval --config tests/eval/pr_eval.yaml \
--output results.json --max-concurrency 3
- run: python scripts/quality_gate.py results.json \
--min-pass-rate 0.90 --max-regression 0.05
发布前 Gate:完整流程
┌──────────────────────────────────────────────────┐
│ Release Gate Pipeline │
│ │
│ Stage 1: Golden Set回归 (300+条) │
│ ├── 对比上一版本Baseline │
│ └── 通过: 总分不低于Baseline 2%, 回归<5% │
│ │
│ Stage 2: 安全测试 │
│ ├── Red-teaming对抗 (50+条注入/越狱) │
│ └── 通过: 安全测试100%通过 │
│ │
│ Stage 3: 性能测试 │
│ ├── 并发20, 持续10分钟 │
│ └── 通过: TTFT P95<2s, E2E P95<15s, 错误率<1% │
│ │
│ Stage 4: 人工抽检 (可选) │
│ └── 通过: 专家评分均值>4.0/5.0 │
│ │
│ All Pass → ✅ 发布 | Any Fail → ❌ 阻断 │
└──────────────────────────────────────────────────┘
完整流程:
提交 → Pre-commit(Lint,<10s) → PR Eval(50条,~2min)
→ 合并(Golden Set 300条,~10min) → 发布(回归+安全+性能,~30min)
今日思考
思考1: 测试投入的ROI
LLM测试月成本: ~$500-2000 (API费+Golden Set维护+CI运行)
不测试的代价:
├── 一次幻觉 → 客户投诉/流失
├── 一次Prompt回归 → 全量用户体验下降(数小时才发现)
├── 一次PII泄露 → 合规罚款/声誉损失
└── 一次事故损失可能数万到数百万
结论: 测试成本是保险费,不是开销
就像银行风控系统成本远低于一次风控失败的损失
思考2: LLM-as-Judge 的可靠性悖论
用LLM测试LLM,Judge本身也可能出错。解决:
├── 用更强模型做Judge (Opus判Sonnet)
├── 多Judge投票 (3个LLM多数决)
├── 定期人工校准 (每月抽检Judge准确率)
├── 关键场景用确定性断言兜底 (contains/schema)
└── Judge自身也需要评估 (Meta-Evaluation)
实测: LLM-Judge与人工评分相关性 ~0.80-0.90
金融类比: 信用评分模型很准但不完美,大额贷款仍需人工审批
思考3: 评估驱动开发(EDD)
TDD: 先写测试 → 再写代码 → 测试通过即完成
EDD: 先定义评估集 → 再写Prompt → 评估通过即完成
EDD流程:
1. 收集真实用户问题 → 整理成评估集
2. 定义"好回复"标准
3. Prompt V1 → 评估60% → 分析失败Case
4. Prompt V2 → 75% → V3 → 85% → V4 → 92%
5. 达标上线 → 收集反馈 → 补充评估集 → 循环
核心: 不是"写完Prompt再测试",而是"评估集先行"
面试表达
30秒版本
LLM应用测试的核心挑战是非确定性 — 传统assert不够用。解决方案是分层测试金字塔:底层Prompt单元测试(Promptfoo/DeepEval)做快速断言(contains/schema/similarity/llm-rubric),中层链路集成测试,上层Golden Set回归评估+Diff报告。CI/CD三个Gate:Pre-commit做Lint,PR做快速评估,发布前做完整回归+安全+性能。核心理念是Eval-Driven Development — 评估集先行,迭代围绕评估分数驱动。
追问准备
Q: Golden Set多大合适? → 初期50-100条覆盖核心场景,成熟后300-500条。Git版本控制,每月专家审核。80%稳定+20%月更新。
Q: 如何测试幻觉? → 三层:Faithfulness Metric检查是否基于Context;事实核查关键数据点;LLM-Judge+人工抽检。RAG场景比纯生成更易检测。
Q: 测试成本怎么控制? → 60%用例只跑免费断言(contains/schema),30%用Embedding相似度,10%用LLM-Judge。整体$5-50/次,月$500-2000。
Q: 如何说服团队投入LLM测试? → 一次幻觉事故的损失 vs 测试月度投入。用真实线上问题做案例 — "有Golden Set这个Bug在PR阶段就会被拦截"。
学习资源
| 资源 | 说明 |
|---|---|
| Promptfoo | CLI-first LLM评估框架,YAML配置 |
| DeepEval | Python-first测试框架,pytest集成 |
| Braintrust | LLM评估+可观测性平台 |
| LLMPerf | LLM性能基准测试工具 |
| Hamel Husain: Evals | Eval-Driven Development最佳实践 |
| Eugene Yan: LLM Evaluations | LLM评估方法论综述 |
| Anthropic Testing Guide | 官方测试指南 |
明日预告
Day 29: 案例分析:企业LLM平台架构 — 从零搭建企业内部AI平台,涵盖多模型管理、统一API网关、评估系统、成本监控、安全合规的完整架构设计。