{SITE.title} logo 云萱笔记
AI 工程 · · 4 分钟阅读

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 自己判断自己有没有安全问题。

这是一条红线。安全评估必须基于:

  • 确定性的漏洞扫描规则
  • 注入攻击检测模式
  • 策略合规检查
  • 已知漏洞库匹配

为什么?因为:

  1. 自我服务偏差:LLM 倾向于认为自己没问题
  2. 越狱攻击:如果攻击本身影响了 LLM 的判断,安全评估就失效了
  3. 合规要求:安全审计需要确定性的、可复现的证据链

三、功能评估器(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}",
        ))

        # ...

注意几个关键点:

  1. 每个评估项都有 evidence:记录判定的原始数据,保证可追溯
  2. score 是连续值:不是非黑即白,而是 0~1 之间的连续分数(覆盖率 70% 得 0.7 分)
  3. 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 才靠谱?

  1. 给出明确的评分 rubric(评分标准)

    ❌ “请评价这段代码的质量” ✅ “请从以下 5 个维度评分,每个维度 0-2 分:

    1. 命名清晰度(变量名、函数名是否表意明确)
    2. 结构合理性(函数长度、模块划分)
    3. 注释充分性(关键逻辑是否有说明)
    4. 错误处理(边界情况是否考虑)
    5. 可测试性(是否容易写单元测试)”
  2. 少用绝对判断,多用相对比较 LLM 做”哪个更好”的判断,比做”打多少分”的判断更准确。

  3. 多模型交叉验证 重要的评估,可以用两个不同的模型分别打分,取平均或投票。

  4. 人工校准 定期抽取一部分 LLM 评分的样本,人工复核,校准评分偏差。


五、安全评估器(Security Evaluator)

5.1 评估项

安全评估是质量保障的红线,100% 确定性,绝不依赖 LLM:

评估项判定标准严重程度
no_critical_vulnerabilities无严重漏洞致命
no_high_vulnerabilities无高危漏洞高危
injection_tests_pass注入测试通过高危
policy_compliance策略合规中危
medium_vulns_acceptable中危漏洞在可接受范围中危

5.2 安全评估的输入来源

安全评估的数据来自哪里?

  1. 输入安全守卫:Prompt Injection 检测结果
  2. 输出安全守卫:敏感数据泄露检测
  3. 策略引擎检查:工具权限合规性
  4. 漏洞扫描结果:已知 CVE 匹配
  5. 审计日志分析:异常行为检测
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?

这是一个原则性问题,不是技术问题。

  1. 攻击者可以影响评估结果:如果输入包含注入攻击,LLM 在评估安全性时可能已经被”洗脑”了

  2. 自我服务偏差:让 LLM 评估自己的安全性,就像让考生自己判卷——倾向于给自己打高分

  3. 合规要求:很多行业的安全合规(如金融、医疗)要求审计证据是确定性的、可复现的

  4. 可解释性:安全评估的结论必须能清晰地解释”为什么说有漏洞”,而不是”模型觉得有风险”


六、黄金用例评估器(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 怎么判断”质量回退”?

黄金用例的难点在于——怎么定义”输出变差了”?

对于有精确答案的场景(如数学计算、代码生成可运行测试),直接对比结果就行。

对于开放式场景,我们用这些信号综合判断:

  1. 确定性指标:输出长度、结构完整性、是否包含必要元素
  2. 语义相似度:用 Embedding 相似度衡量(相似度低于阈值就算回退)
  3. LLM-as-Judge 对比:让 LLM 判断”新版本的输出比基线好还是差”
  4. 人工抽检:对有争议的 case 人工复核

核心原则:黄金用例是”回退检测”,不是”质量评估”。

  • 它的目标是发现”明显变差了”的情况
  • 不是为了给质量打一个精确的分数

七、评估引擎:统一编排与报告

7.1 Evaluation Engine 的职责

四类评估器是独立的,Evaluation Engine 负责:

  1. 统一调度:按顺序运行所有评估器
  2. 综合打分:加权计算总体得分
  3. 通过/失败判定:和阈值比较,给出最终结论
  4. 报告生成:JSON + HTML 双格式报告
  5. 证据汇总:所有评估项的 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 工程的质量保障是一个大题目,我们的实践才刚刚开始。

回顾一下核心设计原则:

  1. 确定性优先:能靠代码算出来的,绝不依赖 LLM
  2. 安全独立:安全评估 100% 确定性,绝不靠 LLM 自己判断
  3. 证据可追溯:每个结论都有数据支撑,能解释”为什么”
  4. 分层演进:从基础到高级,一步步来,不要一口吃成胖子
  5. 融入流程:只有接入 CI Pipeline,评测才不会沦为摆设

AI 质量保障不是一个”做好了就完成了”的项目,而是一个持续迭代的过程。随着模型能力的提升、应用场景的扩展,评测体系也需要不断进化。

希望这篇文章的实践经验,能给你带来一些启发。


相关阅读: