AI 工程质量保障体系:确定性评估优先的设计哲学
前言
在 AI 工程化落地的过程中,质量保障是一个被严重低估但又至关重要的话题。
传统软件的质量保障体系已经非常成熟——单元测试、集成测试、E2E 测试、静态分析、性能测试、安全扫描……每一环都有标准的方法论和工具链。
但 AI 系统的质量保障面临全新的挑战:
- LLM 的输出是概率性的,“100% 通过测试”几乎不可能
- 代码质量、文档完整性这些”软指标”怎么量化?
- 安全评估能不能让 LLM 自己判断自己有没有漏洞?
- 每次模型升级、Prompt 变更,怎么保证没有质量回退?
本文将基于云萱 AI 原生工程平台的实践,详细拆解一套 AI 工程质量保障体系,核心原则是:
确定性评估优先,LLM-as-Judge 只用于语义质量,安全评估 100% 不依赖 LLM。
一、为什么 AI 评测这么难?
1.1 传统软件 vs AI 系统的质量差异
| 维度 | 传统软件 | AI 系统 |
|---|---|---|
| 输出确定性 | 100% 确定 | 概率性输出 |
| 测试通过标准 | 断言相等 | 语义相似/质量达标 |
| 质量定义 | 功能正确 | 功能正确 + 质量高 + 安全 |
| 回归测试 | 精确对比 | 近似对比 + 人工抽检 |
| 安全评估 | 漏洞扫描 + 渗透测试 | 注入检测 + 输出守卫 + 策略合规 |
| 评估可解释性 | 断言明确 | 需要证据链支撑 |
1.2 AI 评测的三大陷阱
在实践中,我们见过很多团队踩过这些坑:
陷阱一:全靠 LLM-as-Judge
“让 GPT-4 来评判输出质量好不好就行。”
问题:
- LLM 评判本身有偏见(偏好长答案、偏好自己风格的答案)
- 成本高(每次评测都要调用大模型)
- 不可复现(同样的输入,LLM 的评判可能不一致)
- 安全评估不能让 LLM 自己当法官(自己的问题自己发现不了)
陷阱二:只看自动评估分数
“我们的 BLEU/ROUGE 分数达到了 0.85,质量很好。”
问题:
- 自动指标和人类感知的相关性有限
- 很多场景下没有标准答案(如开放式对话、创意写作)
- 分数优化不代表真实体验提升
陷阱三:没有回归测试
“每次发版人工抽几个 case 看看就行。”
问题:
- 人工抽检覆盖度低,容易漏
- 没有量化的趋势数据,不知道质量在变好还是变差
- 模型升级、Prompt 变更的影响无法评估
我们的质量保障体系,就是为了避开这些陷阱而设计的。
二、整体架构:四类评估器
2.1 四层评估架构
我们的 Evaluation Engine 包含四类评估器,每类负责不同的质量维度:
┌──────────────────────────────────────────────────┐
│ Evaluation Engine │
│ (统一编排、综合打分、报告生成、可追溯) │
└──────────────────────┬───────────────────────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌───────▼──────┐ ┌────▼─────┐ ┌──────▼──────┐
│ Functional │ │ Quality │ │ Security │
│ Evaluator │ │Evaluator │ │ Evaluator │
│ (功能评估) │ │ (质量评估)│ │ (安全评估) │
│ 100% 确定性 │ │ 确定性+LLM│ │ 100% 确定性 │
└──────────────┘ └──────────┘ └─────────────┘
│ │ │
└──────────────┼──────────────┘
│
┌──────▼──────┐
│ Golden │
│ Evaluator │
│ (黄金用例) │
│ 回归测试 │
└─────────────┘
2.2 设计原则
原则一:确定性优先
能靠代码计算出来的指标,绝不依赖 LLM。
确定性评估的优势:
- 可复现:同样的输入,永远得到同样的结果
- 成本低:不需要调用大模型
- 速度快:毫秒级完成
- 客观:没有主观偏见
原则二:LLM-as-Judge 只用于语义质量
只有那些”人类也会有不同意见”的质量判断,才用 LLM 辅助。
比如:
- 文档写得好不好?(有结构差异,但都是”好”的)
- 回答是否清晰易懂?(主观性较强)
- 代码注释是否充分?(量的问题)
原则三:安全评估 100% 不依赖 LLM
绝对不能让 LLM 自己判断自己有没有安全问题。
这是一条红线。安全评估必须基于:
- 确定性的漏洞扫描规则
- 注入攻击检测模式
- 策略合规检查
- 已知漏洞库匹配
为什么?因为:
- 自我服务偏差:LLM 倾向于认为自己没问题
- 越狱攻击:如果攻击本身影响了 LLM 的判断,安全评估就失效了
- 合规要求:安全审计需要确定性的、可复现的证据链
三、功能评估器(Functional Evaluator)
3.1 评估项
功能评估是最基础的一层,完全基于测试结果:
| 评估项 | 判定标准 | 满分条件 |
|---|---|---|
all_tests_pass | 所有测试通过 | failed == 0 且 total > 0 |
test_coverage | 测试覆盖率 | coverage >= 80% |
sufficient_tests | 足够的测试用例 | total >= 5 |
3.2 实现逻辑
class FunctionalEvaluator(BaseEvaluator):
"""功能评估器(基于测试结果,100% 确定性)。"""
name = "functional"
def evaluate(self, target: dict[str, Any]) -> list[EvaluationResultItem]:
results = []
total = target.get("test_total", 0)
passed = target.get("test_passed", 0)
failed = target.get("test_failed", 0)
# 检查项 1:测试全部通过
all_passed = failed == 0 and total > 0
pass_rate = passed / total if total > 0 else 0.0
results.append(EvaluationResultItem(
name="all_tests_pass",
passed=all_passed,
score=pass_rate,
details=f"{passed}/{total} 测试通过",
evidence=f"total={total}, passed={passed}, failed={failed}",
))
# 检查项 2:测试覆盖率
coverage = target.get("coverage", 0.0)
results.append(EvaluationResultItem(
name="test_coverage",
passed=coverage >= 80.0,
score=min(1.0, coverage / 100.0),
details=f"测试覆盖率: {coverage}%",
evidence=f"coverage={coverage}",
))
# ...
注意几个关键点:
- 每个评估项都有 evidence:记录判定的原始数据,保证可追溯
- score 是连续值:不是非黑即白,而是 0~1 之间的连续分数(覆盖率 70% 得 0.7 分)
- passed 是布尔值:是否通过阈值(用于”绿灯/红灯”判定)
3.3 为什么这很重要?
功能评估是质量底线——连功能测试都通不过的东西,根本不用谈质量和安全。
在 CI Pipeline 中,功能评估是第一道闸门。
四、质量评估器(Quality Evaluator)
4.1 评估项
质量评估关注”好不好”,而不仅仅是”能不能用”:
| 评估项 | 判定标准 | 类型 |
|---|---|---|
has_design_doc | 有设计文档 | 确定性 |
code_structure | 文件结构合理 | 确定性 |
architecture_doc | 有架构说明 | 确定性 |
4.2 确定性 + LLM 辅助
当前我们的质量评估器主要还是确定性检查(有没有文档、有没有结构)。
更深入的质量评估(如”文档写得好不好”、“代码是否优雅”)需要 LLM-as-Judge 辅助,但这部分要注意几个问题:
怎么用 LLM-as-Judge 才靠谱?
-
给出明确的评分 rubric(评分标准)
❌ “请评价这段代码的质量” ✅ “请从以下 5 个维度评分,每个维度 0-2 分:
- 命名清晰度(变量名、函数名是否表意明确)
- 结构合理性(函数长度、模块划分)
- 注释充分性(关键逻辑是否有说明)
- 错误处理(边界情况是否考虑)
- 可测试性(是否容易写单元测试)”
-
少用绝对判断,多用相对比较 LLM 做”哪个更好”的判断,比做”打多少分”的判断更准确。
-
多模型交叉验证 重要的评估,可以用两个不同的模型分别打分,取平均或投票。
-
人工校准 定期抽取一部分 LLM 评分的样本,人工复核,校准评分偏差。
五、安全评估器(Security Evaluator)
5.1 评估项
安全评估是质量保障的红线,100% 确定性,绝不依赖 LLM:
| 评估项 | 判定标准 | 严重程度 |
|---|---|---|
no_critical_vulnerabilities | 无严重漏洞 | 致命 |
no_high_vulnerabilities | 无高危漏洞 | 高危 |
injection_tests_pass | 注入测试通过 | 高危 |
policy_compliance | 策略合规 | 中危 |
medium_vulns_acceptable | 中危漏洞在可接受范围 | 中危 |
5.2 安全评估的输入来源
安全评估的数据来自哪里?
- 输入安全守卫:Prompt Injection 检测结果
- 输出安全守卫:敏感数据泄露检测
- 策略引擎检查:工具权限合规性
- 漏洞扫描结果:已知 CVE 匹配
- 审计日志分析:异常行为检测
class SecurityEvaluator(BaseEvaluator):
"""安全评估器(完全确定性)。
安全评估绝不依赖 LLM 判断。
基于:漏洞扫描结果、策略合规性、注入测试结果。
"""
name = "security"
def evaluate(self, target: dict[str, Any]) -> list[EvaluationResultItem]:
results = []
# 检查项 1:无严重漏洞
critical = target.get("critical_count", 0)
no_critical = critical == 0
results.append(EvaluationResultItem(
name="no_critical_vulnerabilities",
passed=no_critical,
score=0.0 if critical > 0 else 1.0, # 有严重漏洞直接 0 分
details=f"严重漏洞数: {critical}",
evidence=f"critical_vulns={critical}",
))
# 检查项 2:无高危漏洞
high = target.get("high_count", 0)
no_high = high == 0
results.append(EvaluationResultItem(
name="no_high_vulnerabilities",
passed=no_high,
score=0.0 if high > 0 else 1.0,
details=f"高危漏洞数: {high}",
evidence=f"high_vulns={high}",
))
# ...
注意:严重和高危漏洞的得分是 0 或 1,没有中间值——有就是有,没有就是没有,不能”差不多”。
5.3 为什么安全评估不能用 LLM?
这是一个原则性问题,不是技术问题。
-
攻击者可以影响评估结果:如果输入包含注入攻击,LLM 在评估安全性时可能已经被”洗脑”了
-
自我服务偏差:让 LLM 评估自己的安全性,就像让考生自己判卷——倾向于给自己打高分
-
合规要求:很多行业的安全合规(如金融、医疗)要求审计证据是确定性的、可复现的
-
可解释性:安全评估的结论必须能清晰地解释”为什么说有漏洞”,而不是”模型觉得有风险”
六、黄金用例评估器(Golden Evaluator)
6.1 什么是黄金用例?
黄金用例(Golden Cases)是一组精选的、有标准答案的测试用例,用来做回归测试。
每次模型升级、Prompt 变更、代码修改后,跑一遍黄金用例,对比基线版本的输出,检测是否有质量回退。
6.2 评估项
| 评估项 | 判定标准 | 说明 |
|---|---|---|
golden_cases_pass | 黄金用例通过率 | >= 90% 才算通过 |
no_regression | 无回归 | 输出质量没有明显下降 |
quality_within_tolerance | 质量指标在容差内 | 偏差 <= 容差阈值 |
6.3 怎么构建黄金用例集?
黄金用例不是越多越好,而是要精选 + 分层:
L1 - 冒烟级(10-20 个):
- 最核心、最高频的场景
- 每次提交都跑(快速验证)
- 必须 100% 通过
L2 - 完整级(50-100 个):
- 覆盖所有主要功能和边界情况
- 每天跑一次(夜间构建)
- 通过率 >= 95%
L3 - 全面级(200+ 个):
- 覆盖长尾场景、极端情况
- 每周跑一次
- 通过率 >= 90%
这样分层的好处是:
- 日常开发反馈快(L1 几分钟跑完)
- 全面质量有保障(L3 定期跑)
- 成本可控(不用每次都跑全量)
6.4 怎么判断”质量回退”?
黄金用例的难点在于——怎么定义”输出变差了”?
对于有精确答案的场景(如数学计算、代码生成可运行测试),直接对比结果就行。
对于开放式场景,我们用这些信号综合判断:
- 确定性指标:输出长度、结构完整性、是否包含必要元素
- 语义相似度:用 Embedding 相似度衡量(相似度低于阈值就算回退)
- LLM-as-Judge 对比:让 LLM 判断”新版本的输出比基线好还是差”
- 人工抽检:对有争议的 case 人工复核
核心原则:黄金用例是”回退检测”,不是”质量评估”。
- 它的目标是发现”明显变差了”的情况
- 不是为了给质量打一个精确的分数
七、评估引擎:统一编排与报告
7.1 Evaluation Engine 的职责
四类评估器是独立的,Evaluation Engine 负责:
- 统一调度:按顺序运行所有评估器
- 综合打分:加权计算总体得分
- 通过/失败判定:和阈值比较,给出最终结论
- 报告生成:JSON + HTML 双格式报告
- 证据汇总:所有评估项的 evidence 整理归档
7.2 综合得分怎么算?
# 综合分数 = 所有评估项的平均分
overall = sum(item.score for item in all_items) / total_items
# 是否通过 = 综合得分 >= 阈值
passed = overall >= pass_threshold # 默认 0.7
这个设计很朴素,但够用。更复杂的加权模型需要根据实际场景调试——不同项目、不同阶段,各维度的权重应该不一样。
比如:
- 安全敏感场景:安全评估权重加倍
- 原型阶段:功能评估权重高,质量评估可以松一点
- 生产发布:所有维度都要达标
7.3 可追溯性设计
每个评估项都有 evidence 字段,记录”为什么打这个分”。
这非常重要——没有证据的评估结论是没有价值的。当有人问”为什么质量评分只有 0.6?“时,你需要能给出具体的数据。
报告中的证据汇总示例:
{
"functional": [
{"name": "all_tests_pass", "evidence": "total=100, passed=95, failed=5"},
{"name": "test_coverage", "evidence": "coverage=78.5"}
],
"security": [
{"name": "no_critical_vulnerabilities", "evidence": "critical_vulns=0"},
{"name": "injection_tests_pass", "evidence": "injection_passed=true"}
]
}
八、CI Pipeline 集成
评测只有融入开发流程,才能真正发挥作用。我们的 GitLab CI Pipeline 包含 4 个阶段:
lint → test → evaluate → report
lint(代码检查):
- Ruff 代码风格检查
- 类型检查(可选)
test(单元测试):
- pytest 单元测试
- 覆盖率统计
- 生成 pytest 报告
evaluate(AI 评测):
- 运行完整评测脚本(7 维度 30+ 检查项)
- 综合得分 < 0.7 则 Pipeline 失败
- 生成 JSON + HTML 评测报告
report(报告汇总):
- 汇总所有报告
- 生成简洁的摘要
- 归档 artifacts
这样,每次代码提交都有完整的质量数据——开发者提交代码后 5 分钟内就能看到质量评估结果。
九、实践经验与思考
9.1 评测体系的演进路线
很多团队一开始就想做”大而全”的评测体系,结果迟迟用不起来。我们建议的演进路线:
第一阶段:功能评测 + 黄金用例(L1)
- 先把基础打好:单元测试 + 覆盖率
- 建 10 个核心黄金用例,做冒烟回归
- 目标:确保核心功能不回退
第二阶段:加入质量评估 + 安全评估
- 补充代码质量、文档完整性等确定性检查
- 加入注入检测、权限检查等安全评估
- 扩展黄金用例到 L2 级别
- 目标:质量可视化,安全有保障
第三阶段:LLM-as-Judge + 人工校准
- 在质量评估中加入 LLM 辅助的语义质量评分
- 建立人工校准流程,定期复核
- 建立 L3 级全面黄金用例集
- 目标:精细化质量把控
第四阶段:智能分析 + 趋势预测
- 基于历史数据分析质量趋势
- 自动发现异常波动
- 根因分析辅助
- 目标:质量保障从”事后检测”走向”事前预警”
9.2 常见误区
误区 1:评测分数越高越好
“我们的评测得分 0.95,质量非常好!”
分数只是一个参考值,关键是分数的趋势和分布:
- 分数在上升还是下降?
- 哪个维度拉低了分数?
- 失败的评估项具体是什么问题?
分数是手段,不是目的。
误区 2:自动化评测可以替代人工
“有了自动评测,就不用人工测试了。”
AI 评测再完善,也不能完全替代人工。自动化评测的价值在于:
- 快速发现明显的回退
- 规模化覆盖(几千个用例自动跑)
- 提供量化数据辅助决策
但最终的质量判断,尤其是用户体验层面的,还是需要人来做。
误区 3:一次建好就不用管了
“评测体系建好了,以后就自动运行了。”
评测体系需要持续迭代:
- 黄金用例需要持续补充新场景
- 评分标准需要根据人工反馈校准
- 评估维度需要随产品演进而调整
- 新的攻击手段出现后,安全评估要更新
十、总结
AI 工程的质量保障是一个大题目,我们的实践才刚刚开始。
回顾一下核心设计原则:
- 确定性优先:能靠代码算出来的,绝不依赖 LLM
- 安全独立:安全评估 100% 确定性,绝不靠 LLM 自己判断
- 证据可追溯:每个结论都有数据支撑,能解释”为什么”
- 分层演进:从基础到高级,一步步来,不要一口吃成胖子
- 融入流程:只有接入 CI Pipeline,评测才不会沦为摆设
AI 质量保障不是一个”做好了就完成了”的项目,而是一个持续迭代的过程。随着模型能力的提升、应用场景的扩展,评测体系也需要不断进化。
希望这篇文章的实践经验,能给你带来一些启发。
相关阅读: