核心摘要
AI 生成的代码不会因为作者是模型就天然低质,人写的代码也不会因为作者是人就天然可靠。真正有用的问题是:这次变更是否满足版本化契约,并产生了足够的安全合并证据。
把生成视为提案,把验证视为验收。可靠工作流由明确任务边界、代码库上下文、确定性检查、结构断言、独立审查和分阶段生产证据组成。单元测试通过是一条信号,不是可维护性或安全性的证书。
把质量定义为可观察证据
“干净代码”过于主观,无法直接成为发布门禁。团队应先定义变更必须保持的属性,以及证明每项属性的证据。
| 质量维度 | 核心问题 | 更强证据 |
|---|---|---|
| 功能正确性 | 需求行为是否成立? | 回归、属性、集成和负向测试 |
| 兼容性 | 公共契约或持久化数据是否变化? | API/Schema Diff、Migration 演练、Consumer Test |
| 可维护性 | 变更是否符合既有 Ownership 与抽象? | 变更局部性与结构断言 |
| 安全性 | 不可信调用方能否跨越边界? | 授权测试、SAST、依赖与 Secret 检查 |
| 可靠性 | Timeout、Retry、Cancel、Restart 时怎样? | 故障注入、幂等和对账测试 |
| 性能 | 延迟、内存或计算是否退化? | 固定环境下的工作负载基准 |
| 可运营性 | 团队能否观测并撤销变更? | Metric、Log、Alert、灰度与回滚证明 |
这些契约应在实现前写清楚。规范驱动开发只有在 Spec 明确行为、不变量、失败语义和验收证据时才有价值;把含糊需求改写成更长的含糊文档并没有帮助。
当前证据究竟说明了什么
现有研究不支持“AI 代码一定更差”这种普遍结论。证据说明质量会随任务、语言、工作流和测量方式变化,而且功能成功会漏掉重要失败类别。
- Needle in the Repo(NITR) 是 2026 年预印本,用功能测试与结构 Oracle 评估 21 个经过设计的 C++ Repository Probe。在该诊断环境中,一些变更通过了行为测试,却破坏了目标可维护结构;论文数字不能外推到全部语言和生产仓库。
- 2026 年生产 C++ 研究在一家大型企业中沿 Authoring-time Provenance 跟踪代码经过 Review、Static Finding 和运营信号的过程,报告了特定问题分布与针对性反馈效果。组织、语言、工具链和观察性研究设计限制了可迁移性。
- NIST SSDF 给出稳定的软件开发控制:保护软件、产出更安全的 Release、响应漏洞并治理根因。它没有为 AI 代码提供例外,也没有定义单一综合分。
工程结论应更窄也更实用:所有变更承担相同发布责任,再针对 Coding Agent 带来的生成规模、不确定性、Provenance 和 Tool 权限增加控制。
从变更契约开始
Coding Agent 需要的是有边界的变更契约,而不是一个 Persona。
Goal:
为 invoice retry 增加幂等能力,不改变定价行为。
Authoritative sources:
api/invoice.yaml
internal/billing/repository.go
internal/billing/retry_test.go
Invariants:
保留 Tenant 与 Principal 校验。
相同 Operation Key 不能创建两笔扣款。
Dispatch 后 Timeout 必须进入 outcome_unknown。
Allowed scope:
internal/billing/**
api/invoice.yaml
Evidence:
相关测试、Race Test、Schema Diff、已审 Migration Plan。
Escalate:
任何授权、数据库、依赖或外部 API 变化。
“像资深工程师一样工作”不是可验证不变量,“不得改变公共 API”才是。AI 编程助手定制进一步解释如何把可复用 Repository Rule 与任务级验收条件分开。
建立分层验证门禁
没有任何单一 Checker 能证明软件质量。按成本从低到高排列门禁,让便宜的确定性失败在进入高成本 Review 前终止变更。
- 范围与来源:识别生成文件、Ownership 边界和意外文件。
- 构建与静态检查:Compile、Type Check、Format、Lint、Secret Scan 和 Dependency Policy。
- 行为测试:覆盖目标路径、非法输入、授权、并发和历史回归。
- 结构检查:断言 Patch 复用目标抽象、隔离副作用且没有复制 Policy。
- 独立审查:检查 Diff 与证据。第二个模型可以发现候选问题,但可追责的人或服务拥有决策权。
- 发布控制:Canary、Feature Flag、Rollback 与 Monitoring 把源码置信度连接到生产行为。
用 Go 实现可执行证据门禁
下面的标准库程序验证 Evidence Record,而不是相信模型描述。必需门禁缺失或未通过时,它会 Fail Closed。
package main
import (
"errors"
"fmt"
)
type Check struct {
Name string
Required bool
Passed bool
Evidence string
}
func Evaluate(checks []Check) error {
seen := map[string]bool{}
for _, check := range checks {
if check.Name == "" || seen[check.Name] {
return errors.New("invalid or duplicate check")
}
seen[check.Name] = true
if check.Required && (!check.Passed || check.Evidence == "") {
return fmt.Errorf("required check failed: %s", check.Name)
}
}
for _, required := range []string{
"scope", "build", "tests", "security", "review", "rollback",
} {
if !seen[required] {
return fmt.Errorf("required check missing: %s", required)
}
}
return nil
}
func main() {
checks := []Check{
{Name: "scope", Required: true, Passed: true, Evidence: "diff.json"},
{Name: "build", Required: true, Passed: true, Evidence: "build.log"},
{Name: "tests", Required: true, Passed: true, Evidence: "tests.xml"},
{Name: "security", Required: true, Passed: true, Evidence: "sast.sarif"},
{Name: "review", Required: true, Passed: true, Evidence: "review-42"},
{Name: "rollback", Required: true, Passed: true, Evidence: "runbook.md"},
}
fmt.Println(Evaluate(checks))
// Output: <nil>
}
生产系统应按不可变 Revision 签名或保存证据,并绑定到被 Review 的精确 Commit。“测试已通过”这行文本不等于目标 Revision 的测试输出。
增加结构 Oracle
行为测试询问当前案例是否工作,结构 Oracle 询问实现是否保留了未来变更依赖的工程属性。
实用结构断言包括:
- 只有目标 Package 拥有某条业务规则;
- 新类型扩展既有 Interface,而不是复制 Dispatch Logic;
- 生产代码没有导入 Test-only 或未经批准的 Dependency;
- Authorization 仍位于 Service Boundary;
- Generated Client 由生成器重建,而不是手工修改;
- 时间、随机数和网络等不确定依赖可注入测试;
- 外部副作用统一经过一个幂等 Adapter;
- Schema 变化包含兼容 Reader、Migration 和 Rollback。
不要把任意 Style Preference 都设成硬门禁。每条 Oracle 都应保护一个具名风险,并返回可行动诊断。Linting适合可重复的局部规则,架构与领域不变量通常需要 Repository 专用检查。
独立验证安全与供应链
Agent 可以完成 Feature,同时扩大攻击面。重点检查:
- Caller、Tenant、Object 与 Operation Authorization;
- Secret 访问、日志、Telemetry 与 Prompt/Tool Payload 留存;
- 新 Package 的名称、版本、License、Registry 和 Install Script;
- 生成期间使用的命令、文件系统、网络与 Credential Scope;
- 不安全反序列化、Injection、Path Traversal、SSRF 与 Shell 拼接;
- Timeout、Retry、重复请求、Cancel 与 Partial Commit 行为。
“请写安全代码”无法强制这些属性。固定依赖、使用可信 Registry、运行安全工具、隔离执行并测试应用边界。Repository 文本和 Issue 描述都应视为不可信输入,因为它们可能携带提示注入。
让人工审查围绕证据展开
Reviewer 在拿到小而连贯的 Diff 和具体证明时最有效:
- 目标行为和 Non-goal;
- 变化的公共契约与数据 Migration;
- 修复前真实失败的测试;
- 最终 Revision 上通过的测试;
- Security 与 Dependency Finding;
- 未解决假设和跳过检查;
- Rollout、Monitoring 与 Rollback Plan。
不要要求 Reviewer 从数千行生成代码中猜测意图。拆分独立变更,限制无关格式 Churn,拒绝无法复现的生成产物。Reviewer 应挑战契约与证据,而不是只看语法。
衡量完整生命周期,而不是建议采纳率
Acceptance Rate 奖励“看起来合理”的输出,真正需要测量的是被接受变更能否存活。
| 信号 | 能揭示什么 |
|---|---|
| 按风险切片的任务成功率 | 工作流能否解决目标任务 |
| Merge 前返工 | 隐藏的 Steering 与 Verification 成本 |
| 逃逸缺陷与回滚 | 哪些失败穿过了发布前门禁 |
| 变更局部性 | 小需求是否跨越了过多 Ownership |
| Dependency 与 API 增长 | 长期兼容性和供应链负担 |
| Review Latency 与 Reviewer Confidence | 人工瓶颈与证据质量 |
| 生产延迟、成本和事故 | Merge 后的运营后果 |
| 理解与修复时间 | Comprehension 与维护负担 |
记录 Model、Agent、Instruction、Tool、Repository 和 Evaluator Revision。没有 Provenance 的分数无法解释究竟是哪一部分系统发生了变化。
常见失败模式
- 用“资深工程师”Persona 替代明确不变量;
- 因为新增 Happy-path Test 通过就批准 Patch;
- 允许模型绕过 Registry 和 License 检查添加依赖;
- 把模型 Confidence 或 Self-review 当作校准证据;
- 在功能变更中混入无关清理;
- 不检查语义就接受 Snapshot Churn;
- 因为任务看似普通就授予宽泛 Credential;
- 没有 Idempotency 或 Reconciliation 契约就重试外部写入;
- 只统计生成代码行,而不统计长期可维护结果;
- 没有可追责 Reviewer 和 Rollback Path 就发布。
常见问题
为什么 AI 生成代码通过测试后仍可能质量很差?
测试只覆盖已经编码的观察,可能遗漏重复 Policy、错误 Ownership、绕开的抽象、意外公共 API 和未来扩展路径。除了当前示例,还要使用结构断言,并按 Repository Architecture 审阅变更。
更好的 Prompt 能否保证代码可维护?
不能。更好的上下文与指令可以改善提案分布,但无法强制结果。确定性工具、Repository Policy、独立 Review 和生产控制必须决定是否接受提案。
AI 生成代码的最小验证流水线是什么?
审阅 Diff,执行 Compile 或 Type Check,运行相关与负向测试,检查 Dependency 与 Security 变化,验证 Authorization 和 Side Effect,并要求可追责审批。Migration、外部写入、隐私敏感路径和大型变更需要更高门槛。
团队是否应该拒绝所有大型 AI 生成变更?
不应,但规模会提高举证责任。优先拆成独立、可审查的 Commit。无法拆分的 Migration 应提供生成与手写边界、确定性再生成、Consumer Test、灰度发布和经过演练的回滚。
团队应该如何衡量 AI 代码质量?
同时跟踪任务成功和接受后的下游成本:返工、缺陷、回滚、Dependency 增长、Review 时间、生产性能与修复时间。按任务和风险切片,避免用一个全局“AI 采纳率”掩盖关键失败。
总结
提升 AI 生成代码质量的持久方法,是让验收证据强于生成置信度。定义变更契约、约束范围、运行分层确定性与结构门禁、审阅精确 Diff,并用生产证据约束发布。模型可以写出 Patch,工程系统负责判断它是否值得长期保留。