一、为什么 AI 写出来的代码不能直接合并
用 AI Coding 工具半年多,我最大的教训是:把 Agent 生成的代码直接 accept all,和把实习生第一版草稿直接上线,风险差不多。
区别只在于实习生会脸红,Agent 不会。
AI 写代码有三个必然会出现的坑,和水平高低无关:
第一,类型错误。模型对某个库的版本记忆是训练时的快照,你项目里装的是新版本,它照着旧签名写,编译就挂。
第二,幻觉 API。它特别爱调用"看起来应该存在"的函数,比如 os.get_username() 这种根本不存在的东西,跑起来才报错。
第三,测试不过。它能让代码"看起来对",但边界条件、空值、并发这些,往往要真实跑一遍才暴露。
所以一句话:AI 的输出是草稿,不是成品。 缺的不是它写得好不好,而是有没有一个"写完立刻验证、验证不过就打回去"的闭环。
二、闭环验证到底是什么
闭环验证(closed-loop verification)指的是一个循环:
提议 → 验证 → 反馈 → 修复 → 再验证,直到全绿。
把它拆开看,核心是第三步"反馈"。人类做 code review 也是这个逻辑,但机器反馈有三个 human review 比不了的优势:
确定性。同样的输入跑十次,结果一样,不靠 reviewer 当天心情。
可重复。每次提交都能跑同一套检查,不会"这次忘了看类型"。
低成本。一条 lint 命令几秒钟,比等同事 review 快几个数量级。
把这三件事结合起来,结论就清楚了:让验证结果自动成为下一轮 Agent 的输入上下文。 Agent 上一轮犯的错,这一轮直接摆在它眼前,它自己改。这就是为什么现在主流 AI Coding 工具都强调 agent loop——循环本身比单次生成重要得多。
三、一个最小可用的验证闭环
我给自己定的最小闭环是三层,顺序很重要:
先 lint,再 typecheck,最后 test。
lint 最轻,能挡掉一半低级问题(未使用变量、格式、明显笔误),先跑它性价比最高。typecheck 抓类型错误和幻觉 API,这是 AI 代码的重灾区。test 最后跑,因为最慢,前面两关先过滤掉大部分噪音。
把这三步封装成一个脚本,让 Agent 每一轮都能自己调、自己读结果:
# verify_loop.py — 最小可用的 AI Coding 验证闭环
# 用法: python verify_loop.py
# 依赖: 项目自身的 lint/type/test 工具,无需额外 pip 包
import subprocess, sys, re
# 按你的项目改这三步即可
CHECKS = [
    {"name": "lint", "cmd": ["ruff", "check", "."]},
    {"name": "type", "cmd": ["mypy", "src/"]},
    {"name": "test", "cmd": ["pytest", "-q"]},
]
def run_check(step):
    try:
        r = subprocess.run(step["cmd"], capture_output=True, text=True, timeout=120)
    except FileNotFoundError:
        return True, f"[skip] {step['name']} 未安装,跳过"
    ok = r.returncode == 0
    # 只留错误行,裁掉噪声,避免把整份日志塞给 agent 撑爆上下文
    out = (r.stdout + r.stderr).strip()
    lines = [l for l in out.splitlines() if re.search(r"error|Error|FAIL|assert", l)]
    report = "\n".join(lines[:40])  # 最多 40 行
    return ok, report
def verify():
    report = []
    for step in CHECKS:
        ok, msg = run_check(step)
        report.append(f"### {step['name']}: {'PASS' if ok else 'FAIL'}\n{msg}")
        if not ok:
            return False, "\n\n".join(report)
    return True, "ALL GREEN"
if __name__ == "__main__":
    ok, msg = verify()
    print(msg)
    sys.exit(0 if ok else 1)

这个脚本的关键是那句 lines[:40]。AI 最容易犯的错不是不会修,而是你丢给它 2000 行报错日志,它的上下文被噪声淹没,反而修不好。只给错误行、带 file:line,它修得又快又准。
四、把验证结果喂回 Agent 的关键细节
光有脚本不够,喂法决定成败:
只给这一轮的报错,不要让它顺手大改。经验是让 Agent 的指令里写死"只修复验证报告里列出的错误,不要重构无关代码"。否则它会借机把整个文件重写一遍,引入新坑。
报错要带精确位置。上面脚本输出里的 file:line 就是为这个服务的,Agent 能直接定位,不用全文猜。
设定最大重试轮数。这是保命参数。我一般设 5 轮,到第 5 轮还红,就停,把当前报告扔回给我人工看。不然 Agent 会陷入"改 A 引出 B,改 B 引出 A"的死循环,token 烧得飞起。
五、踩坑记录
这几个坑都是真踩过的:
无限循环。最常见。Agent 修一个测试,测试过了,但另一个之前绿的挂了,它又去修,来回拉锯。解决办法就是上面说的 max iterations,到数就停,别跟它较劲。
测试 flaky。偶发失败的测试最坑人,Agent 会追着一个随机挂的用例改半天,越改越乱。正确做法是先把 flaky 用例标记 skip 或隔离,别让闭环追着噪声跑。
绿了也不能全信。测试全过,不代表行为对。AI 特别擅长写出"恰好让测试通过但逻辑是错的"代码。所以闭环之外,至少要有一个真实场景的冒烟验证,或者一个真测行为的用例,不能只看绿不绿。
成本别忽略。每一轮验证都要把报告喂回模型,长循环累积的 token 成本很可观,尤其是用 Pro 档模型跑大项目时。把 lint/type 这种便宜的检查放前面,能在烧钱之前先挡掉大部分问题。
六、怎么判断闭环跑得好不好
闭环跑了一阵子,可以用三个指标自检:
平均几轮到绿。新接手的项目或新写的模块,如果动辄要 5 轮以上,说明模型对这个技术栈不熟悉,或者你的检查太严。
错误类型分布。如果绝大多数是类型错误,说明该把类型定义喂给 Agent 当上下文;如果是测试失败居多,可能是用例本身没写好。
哪些文件反复出问题。反复红的文件,往往是最该补类型注解、补注释、或者干脆重构的地方。这些信号本身,又能用 AI 来汇总分析,形成一个二阶闭环。
七、小结
把 AI Coding 从"玩具"变成"生产工具",分水岭不是模型多强,而是有没有闭环验证。
三句话收一下:
AI 输出是草稿,验证才是成品把关人。
三层检查(lint → type → test)按成本从低到高排,前面先挡噪音。
反馈要精准、要限轮数,绿了也别盲信,真实场景冒烟验证不能省。
闭环这条线跑顺了,你会发现 Agent 不是替代你写代码,而是把"写、验、改"的脏活自动化了,你腾出来做它做不了的判断。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐