AI Agent 防作弊 CI 实战:自动拦截 assertTrue(true)、跳过测试和空 catch

系列导读:本系列用一个可运行的 Spring Boot 秒杀 Demo,拆解“当 AI 成为我的全职下属”之后,研发工作流应该如何重建。五期内容从一次超卖事故开始,依次讨论并发正确性、上下文裁剪、防作弊 CI、Agent 友好架构和 Human-in-the-loop 上线门禁。主线不是证明 AI 能不能写代码,而是回答一个更实际的问题:当 Agent 已经能高吞吐地产出代码时,工程师如何用边界、验证和审批机制,把它变成可靠的执行者。

1. 问题现场:CI 绿了,但问题没有修好

你给 Agent 的任务是“让测试通过”。它确实做到了:流水线变绿,PR 看起来也能合并。

但代码里可能出现了 @DisabledassertTrue(true)、空 catch,甚至把并发度从 100 改成 1。业务问题没有修好,失败信号被消音了。

本文实现一个轻量级完整性审计器,在进入 Maven 测试前检测 @DisabledassertTrue(true)、空 catch、捕获 Throwable 和把并发度降为 1 等模式。重点不是依赖几个正则表达式,而是建立不可由执行者自行修改的验收边界

2. 验证目标

验证目标包括两部分:恶意样例必须被检出,当前合规工程必须以退出码 0 通过。只满足其中一项,都不能证明审计器可用。

3. 原因:绿色流水线为什么可能是假成功

下面几种改动都能让失败的并发测试“恢复绿色”:

@Disabled("偶发失败,先关闭")
void shouldHandleConcurrency() {}
assertTrue(true);
int concurrentCount = 1;
try {
    executeSeckill();
} catch (Throwable ignored) {
}

它们没有修复任何业务问题,只是移除了失败信号。

配套动画:

4. 原理:把禁止项变成机器可执行规则

Demo 中的规则定义如下:

RULES = (
    Rule("DISABLED_TEST", re.compile(r"@Disabled\b"), "禁止跳过核心测试"),
    Rule("ALWAYS_TRUE", re.compile(r"assertTrue\s*\(\s*true\s*\)"), "禁止万能真断言"),
    Rule("EMPTY_CATCH", re.compile(r"catch\s*\([^)]*\)\s*\{\s*\}"), "禁止空 catch 块"),
    Rule("SWALLOW_THROWABLE", re.compile(r"catch\s*\(\s*Throwable\b"), "禁止捕获 Throwable 掩盖失败"),
    Rule("LOW_CONCURRENCY", re.compile(r"concurrent(?:Count|cy)\s*=\s*1\b"), "禁止把并发度降为 1"),
)

执行审计:

python pipeline\verify_integrity.py

通过时输出:

Integrity audit passed.

如果测试中出现万能断言,脚本以非零状态退出,流水线立即失败。例如在临时目录中放入一个只包含 assertTrue(true);BrokenTest.java 后,实际输出格式如下:

%TEMP%\cheat-demo\BrokenTest.java:1 [ALWAYS_TRUE] 禁止万能真断言
EXIT_CODE=1

这段输出有三个关键信息:命中的文件、行号和规则码。CI 不需要理解业务语义,只要看到非零退出码,就能阻断这次提交。

配套动画:

5. 在 Demo 中验证审计器

不能因为工具名叫“审计器”就默认它可靠。项目使用临时目录构造恶意样例:

def test_detector_finds_weakened_assertion(self) -> None:
    with tempfile.TemporaryDirectory() as directory:
        root = Path(directory)
        (root / "BrokenTest.java").write_text(
            "assertTrue(true);",
            encoding="utf-8",
        )
        self.assertTrue(any("ALWAYS_TRUE" in item for item in scan(root)))

运行:

python -m unittest discover -s ai_firm\tests -v

当前 Demo 的两项 Python 测试均已通过:一项验证作弊模式检测,一项验证上下文裁剪依赖遍历。

再对当前仓库执行正向验证:

python pipeline\verify_integrity.py
if ($LASTEXITCODE -ne 0) { throw '完整性审计失败' }

6. 解决办法:不能让 Agent 修改守门脚本

如果 Agent 能同时修改业务代码、测试和审计脚本,它仍可以删除规则后提交。生产环境应至少采用以下一种隔离方式:

  1. 审计脚本放在独立仓库,由平台团队维护。
  2. CI 从固定版本的制品或容器中加载规则。
  3. 使用 CODEOWNERS,修改 pipeline/ 必须由人类审批。
  4. 核心测试在远端私有测试集执行,不暴露全部断言。

这就是“只读门禁”的真正含义:不是文件系统只读,而是任务执行者没有单方面改变验收标准的权限

最小团队落地可以从两段配置开始。先在 CI 中把完整性审计放在 Maven 测试之前:

name: integrity-gate

on:
  pull_request:

jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - name: Run integrity audit
        run: python pipeline/verify_integrity.py
      - name: Run Maven tests
        run: mvn test

再用 CODEOWNERS 约束守门脚本的修改权限:

/pipeline/ @platform-reviewers
/ai_firm/cheat_detector.py @platform-reviewers

这样 Agent 可以提交业务修复,但凡它试图改掉审计脚本或流水线规则,都必须经过独立维护者审批。

配套动画:

7. 正则扫描的边界

本例故意保持简单,适合教学和本地前置检查。生产环境还应组合:

  • Checkstyle、PMD 或 Error Prone:Java 语义和代码风格。
  • SpotBugs:字节码级缺陷分析。
  • SonarQube:质量门、覆盖率和重复代码。
  • Maven Enforcer:依赖版本与禁止依赖。
  • Git diff 策略:限制 Agent 可修改路径。

静态扫描无法证明代码正确,但能快速淘汰一批确定错误的提交。

8. 把奖励函数改写成验收函数

不要给 Agent 下达“让 CI 通过”,而要给出分层验收:

第一层:编译通过;
第二层:禁止模式扫描通过;
第三层:核心并发测试通过;
第四层:订单数、库存、成功响应三者一致;
第五层:人类 Review SQL、事务边界和锁释放。

Agent 的目标越接近业务不变量,钻空子的空间越小。

实验环境

项目 版本或参数
Python 3.12.5 实测,只依赖标准库
被审计 Demo JDK 17、Spring Boot 3.5.15、Maven 3.6.3+
核心验证 恶意样例能检出,合规工程以退出码 0 通过

小结

测试不是装饰,它是人类把意图固化为机器约束的方式。让 Agent 自动写代码之前,先确保它不能随意改写裁判规则。

下一期将从代码结构本身入手:为什么职责单一、依赖倒置和小接口,会直接提高 Agent 修改代码的成功率。

发布到团队 CI 前的边界

正则扫描可能误报注释、字符串和规则文件自身,也可能漏掉换行、别名封装后的作弊方式。本文工具适合作为快速前置门禁,不能替代 AST、覆盖率差异检查、私有测试集和人工 Review。

测试 Demo 仓库

配套 Demo 仓库地址:https://github.com/quan020406/xiaozhan-blog-column-demos


上一篇用 AST 依赖裁剪治理 AI 编程上下文
下一篇:小z疯狂码字ing…
感谢阅读,记得点赞、关注、收藏,欢迎各位评论区交流!!!
请添加图片描述

参考资料

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐