AI Agent 工程实践|让 AI Agent 自动修 Bug:定位、修改、测试与人工审核

AI Agent 工程实践|让 AI Agent 自动修 Bug:定位、修改、测试与人工审核

让 Agent 修 Bug,最容易做出的版本是:把报错贴进去,让它直接改代码。小项目里偶尔很惊艳,放进真实仓库后问题马上出现:它可能理解错需求、改了无关文件、删掉失败测试,甚至为了让 CI 变绿把异常吞掉。

我更愿意把自动修复拆成一条受控流水线:

text 复制代码
缺陷输入 → 复现 → 定位 → 提交最小补丁 → 验证 → 生成变更说明 → 人工审核

Agent 可以完成大部分机械工作,但不能自己宣布"修复完成",更不能直接向主分支推送。

先把 Bug 描述变成任务单

一句"登录偶尔失败"不足以驱动自动修复。进入 Agent 前先整理成结构化任务:

json 复制代码
{
  "issue_id": "BUG-1024",
  "title": "用户名包含空格时登录失败",
  "expected": "首尾空格应被忽略并正常登录",
  "actual": "接口返回用户不存在",
  "reproduce": [
    "创建用户 demo",
    "使用用户名 ` demo ` 登录"
  ],
  "allowed_paths": ["src/auth/**", "tests/auth/**"],
  "forbidden_paths": [".github/**", "deploy/**", "secrets/**"],
  "success_command": "pytest tests/auth -q"
}

allowed_paths 很重要。它把 Agent 的修改范围从整个仓库缩小到相关模块,防止一次小修复顺手重构半个项目。

第一步不是改代码,而是复现

Agent 收到任务后,先执行现有测试并尝试添加一个失败用例:

python 复制代码
def test_login_trims_username(client, user_factory):
    user_factory(username="demo", password="secret123")

    response = client.post(
        "/api/login",
        json={"username": " demo ", "password": "secret123"},
    )

    assert response.status_code == 200

如果测试一开始就通过,说明复现条件不完整,Agent 应停止并报告缺少信息,而不是凭感觉改代码。

我会把状态机设计成:

python 复制代码
from enum import StrEnum


class FixState(StrEnum):
    RECEIVED = "received"
    REPRODUCED = "reproduced"
    PATCHED = "patched"
    VERIFIED = "verified"
    NEEDS_REVIEW = "needs_review"
    BLOCKED = "blocked"

只有新增测试能够稳定失败,任务才进入 REPRODUCED

定位时先建立证据链

Agent 可以使用搜索、调用关系和日志定位,但输出必须说明为什么怀疑某段代码:

text 复制代码
请求体中的 username 原样进入 AuthService.login
UserRepository.find_by_username 使用精确匹配
注册流程已经执行 strip,登录流程没有
失败测试在 Repository 查询前可以复现

这比"我发现这里可能有问题"更方便人工判断。

定位阶段只允许读文件、搜索和运行测试,不允许修改。把读和写分开,可以避免 Agent 在证据不足时边看边改。

补丁必须尽量小

针对示例 Bug,补丁可能只有一行:

python 复制代码
def login(self, username: str, password: str):
    normalized_username = username.strip()
    user = self.users.find_by_username(normalized_username)
    ...

Agent 常见的坏习惯是顺便改名、格式化整个文件、抽取新基类。自动修复阶段可以设置硬限制:

python 复制代码
@dataclass(frozen=True)
class PatchPolicy:
    max_files: int = 5
    max_changed_lines: int = 200
    allow_dependency_change: bool = False
    allow_test_deletion: bool = False

超过限制不一定代表补丁错误,但应该转人工处理,不能静默放宽范围。

不允许通过修改测试"证明自己正确"

Agent 可以新增测试,但对已有测试的修改要单独审核。下面这些行为直接阻断:

text 复制代码
删除失败测试
把精确断言改成宽松断言
给测试加 skip
降低覆盖率阈值
修改 CI 配置跳过测试
捕获异常后不做断言

可以在补丁检查器里加入简单规则:

python 复制代码
FORBIDDEN_DIFF_PATTERNS = [
    "@pytest.mark.skip",
    "pytest.skip(",
    "|| true",
    "continue-on-error: true",
]


def check_diff(diff: str) -> list[str]:
    return [
        pattern
        for pattern in FORBIDDEN_DIFF_PATTERNS
        if pattern in diff
    ]

规则不负责判断所有语义问题,只负责拦住确定不能接受的行为。

验证分成四层

修复后不要只运行新增测试。

text 复制代码
第一层:原始复现测试
第二层:当前模块测试
第三层:静态检查和类型检查
第四层:仓库完整回归测试

示例执行器:

python 复制代码
import subprocess


def run_check(name: str, command: list[str], timeout: int) -> dict:
    result = subprocess.run(
        command,
        capture_output=True,
        text=True,
        timeout=timeout,
        check=False,
    )
    return {
        "name": name,
        "exit_code": result.returncode,
        "passed": result.returncode == 0,
        "stdout": result.stdout[-8000:],
        "stderr": result.stderr[-8000:],
    }

每个命令要有超时,输出也要截断,避免测试死循环或海量日志撑满上下文。

失败后不能无限自我修改

Agent 第一次补丁失败后可以根据错误再尝试一次,但要设置上限:

python 复制代码
MAX_PATCH_ATTEMPTS = 3

for attempt in range(1, MAX_PATCH_ATTEMPTS + 1):
    patch = propose_patch(context)
    apply_patch_in_sandbox(patch)
    report = run_validation()
    if report.passed:
        break
    restore_worktree()
else:
    mark_blocked("三次补丁均未通过验证")

每次失败都应恢复工作区,避免第二次方案叠在第一份错误补丁上。超过次数后交给人,而不是继续消耗 Token 和计算资源。

Agent 最终交付什么

不要只返回"已修复"。交付物至少包括:

markdown 复制代码
问题原因:登录流程未规范化 username,注册流程和登录流程行为不一致。

修改文件:
- src/auth/service.py
- tests/auth/test_login.py

验证结果:
- 新增复现测试:通过
- auth 模块测试:128 passed
- 类型检查:通过
- 完整测试:未运行,预计耗时较长

剩余风险:未验证包含全角空格的用户名。

"完整测试未运行"必须明确写出,不能因为部分测试通过就包装成全部通过。

人工审核看什么

审核人不需要重新做一遍定位,重点确认:

  1. 失败测试是否真实复现问题;
  2. 修改是否只覆盖必要范围;
  3. 有没有掩盖异常或放宽断言;
  4. 是否改变兼容性、权限或数据结构;
  5. 测试证据是否对应最新补丁;
  6. 是否存在 Agent 没有运行的验证项。

高风险文件可以要求 Code Owner 审批,例如认证、支付、数据库迁移和部署配置。

不让 Agent 直接推主分支

推荐权限链路:

text 复制代码
Agent 在临时分支修改
        ↓
自动测试和安全扫描
        ↓
创建 Draft PR
        ↓
人工审核
        ↓
必需检查全部通过
        ↓
人工合并

GitHub 的 Required Status Checks 可以把构建、测试和扫描设为合并前置条件;检查失败、超时或需要人工处理时,PR 不能进入受保护分支。GitHub Status Checks

最后

让 Agent 自动修 Bug,核心不是让它改代码,而是让它提供一条可以复核的证据链:问题能够复现,修改范围受控,新增测试先失败后通过,相关回归没有被破坏。

Agent 最适合承担搜索、生成小补丁和运行验证这些重复工作。是否接受行为变化、是否允许扩大修改范围、是否合并到主分支,仍然应该由人决定。

真正可用的自动修复不是"一句话改好",而是失败时会停、越界时会拒绝、完成后能把自己做过什么讲清楚。

相关推荐
幻灵尔依1 小时前
LLM 推理核心链路&缓存命中讲解
llm·agent·ai编程
katttt_1 小时前
实体经营,优先选卡特加特垂域专用AI
人工智能·卡特加特
阿里云大数据AI技术1 小时前
EMR Daft × Qoder:一句话生成并运行智驾数据处理 Pipeline
人工智能
JaydenAI1 小时前
[基于AgentEvals的自动化评估-06]以LLM-as-a-Judge评估LangGraph执行轨迹
ai·langchain·agent·evaluation·openevals·agentevals
ClouGence1 小时前
当 AI 开始直接操作数据库,传统数据库管理工具还有必要吗?
数据库·后端·agent
OceanBase数据库官方博客1 小时前
OceanBase Harness Engineering?AI 产品真正的瓶(技术解析与实践)
大数据·人工智能·oceanbase
兰亭妙微UI设计公司1 小时前
兰亭妙微 UX 实战:新手引导全类型设计入门指南
人工智能·ux
手写码匠2 小时前
华为云Flexus+DeepSeek征文|Dify 多 Agent 故障演练实战:用混沌工程主动“搞破坏“,让智能体系统越炸越稳
人工智能·深度学习·算法·aigc
Vuji2 小时前
Pi 插件解剖|todo.ts:297 行,拼出工具+命令+状态的完整插件
前端·agent