log-center/repair_agent/REPAIR_FLOW_ANALYSIS.md
zyc 5611839fd8
Some checks failed
Build and Deploy Log Center / build-and-deploy (push) Failing after 1m55s
fix git pr
2026-02-25 10:55:26 +08:00

12 KiB
Raw Blame History

Bug 自动修复流程分析与优化建议

分析日期: 2026-02-25 当前版本存在的问题和改进方案


1. 当前流程概览

主流程 (fix_project)

1. 初始化
   ├─ 获取项目信息(本地路径、仓库地址)
   ├─ 获取待修复 Bug 列表NEW/PENDING_FIX
   ├─ 初始化 Gitpull + 创建 fix 分支)
   └─ 更新所有 Bug 状态 → FIXING

2. 多轮修复循环max 3 轮)
   └─ 每轮:
      ├─ 调用 Claude CLI 修复代码
      ├─ 获取 diff 和修改文件列表
      ├─ 安全检查(文件数/行数/核心文件)
      ├─ 运行 Claude 生成的测试文件
      ├─ 上传修复报告
      └─ 标记为 FIXED无论测试是否通过

3. 自动提交(可选)
   ├─ commit → push fix 分支
   └─ **直接合并到 main 并推送**

重试失败流程 (retry_failed_project)

1. 获取所有 FIX_FAILED Bug
2. 逐个分诊triage
   ├─ VERDICT:CANNOT_REPRODUCE → 标记为 CANNOT_REPRODUCE
   └─ VERDICT:FIX → 重置为 PENDING_FIX
3. 批量调用 fix_project 修复

2. 主要缺陷分析

🔴 严重问题 1测试验证与重试逻辑脱节

问题描述:

  • 代码中有 max_rounds=3 的多轮重试机制
  • 但在 core.py:241-254,无论测试通过与否都标记为 FIXEDbreak
  • 测试失败不会触发下一轮重试

问题代码:

# core.py Line 230-254
test_passed = bool(test_output) and "FAILED" not in test_output

# 无论 test_passed 是 True 还是 False都标记为 FIXED
for bug in bugs:
    self.task_manager.update_status(bug.id, BugStatus.FIXED)
    # ...
    results.append(FixResult(bug_id=bug.id, success=True, ...))  # ❌

# 然后直接 break不会进入第 2、3 轮
break  # Line 271

影响:

  • 多轮重试机制形同虚设
  • 测试失败的 Bug 被错误标记为已修复
  • 浪费了 Claude 重试优化的能力

建议修复:

# 第 1 步:运行测试
test_output = run_repair_test_file(project_path, test_file)
test_passed = self._check_test_passed(test_output)
cleanup_repair_test_file(project_path, test_file)

# 第 2 步:根据测试结果决定下一步
if test_passed:
    # 测试通过,标记为 FIXED
    for bug in bugs:
        self.task_manager.update_status(bug.id, BugStatus.FIXED)
        self._upload_round_report(...)
    break  # 成功,退出循环
else:
    # 测试失败,记录上下文供下一轮使用
    last_diff = diff
    last_test_output = test_output
    logger.warning(f"第 {round_num} 轮测试失败,尝试下一轮")

    if round_num == max_rounds:
        # 最后一轮仍失败,标记为 FIX_FAILED
        for bug in bugs:
            self.task_manager.update_status(bug.id, BugStatus.FIX_FAILED)
            self._upload_round_report(..., status=BugStatus.FIX_FAILED)
    # 继续下一轮

🟡 严重问题 2Git 分支管理混乱

问题描述:

  1. git_manager.py:55-75 pull() 会切回 main/master
  2. core.py:152-154 然后又从 main 创建新分支
  3. core.py:264 auto_commit直接合并到 main 并推送

问题:

  • 没有 PR 审核流程,变更直接进入主分支
  • 每次修复都创建新分支,但合并后删除,无法回溯
  • 不符合现代 Git 工作流(应该创建 PR 供人工审核)

建议修复方案:

方案 A保留 fix 分支,推送后创建 PR

# core.py Line 256-267
if git_enabled and auto_commit and modified_files and git_manager:
    bug_ids = ", ".join([f"#{b.id}" for b in bugs])
    git_manager.commit(f"fix: auto repair bugs {bug_ids}")
    git_manager.push()
    logger.info("fix 分支已推送,请手动审核并合并")

    # 可选:调用 gh CLI 创建 PR
    # subprocess.run(["gh", "pr", "create", "--title", f"Auto fix {bug_ids}", ...])

方案 B如果确实需要直接合并慎用

# 添加配置项控制
if settings.auto_merge_to_main:  # 默认 False
    if git_manager.merge_to_main_and_push():
        logger.info("已合并到 main 并推送")
else:
    logger.info("fix 分支已推送,需人工审核")

🟠 问题 3安全检查位置不当

问题代码: core.py:209-225

# Step 3: 安全检查(在修改后)
if git_manager and not self._safety_check(modified_files, diff):
    failure_reason = "安全检查未通过"
    git_manager.reset_hard()  # ❌ 丢失所有修改

    # 标记为 FIX_FAILED但不会重试
    for bug in bugs:
        self.task_manager.update_status(bug.id, BugStatus.FIX_FAILED)
    break

问题:

  • Claude 已经修改了代码,安全检查失败后 reset_hard 丢失所有修改
  • 状态已经更新为 FIXING,造成不一致
  • 不会重试,浪费了 Claude 的工作

建议:

方案 A软性警告 + 继续测试

# 安全检查改为警告,记录但不阻断
if git_manager:
    safety_passed, warnings = self._safety_check(modified_files, diff)
    if not safety_passed:
        logger.warning(f"安全检查警告: {warnings}")
        # 将警告加入报告,但继续执行测试

# 让测试结果决定是否成功

方案 B预检查机制

# 在调用 Claude 前,先用只读工具做预分析
success, analysis = self.claude_service.analyze_bug(bug, project_path)
if "core file" in analysis.lower():
    logger.warning("分析发现可能涉及核心文件,跳过自动修复")
    # 标记为需要人工介入

🟡 问题 4测试通过判断过于简单

问题代码: core.py:231

test_passed = bool(test_output) and "FAILED" not in test_output and "Error" not in test_output.split("\n")[-5:].__repr__()

问题:

  • 只检查 "FAILED" 字符串,不严谨
  • 没有测试文件也会被判定为通过(test_output 为空时返回 False
  • 但代码逻辑:没有测试文件 → test_output = "Claude 未生成测试文件"bool(test_output) = True → 可能误判

建议:

def _check_test_passed(self, test_output: str) -> bool:
    """检查测试是否通过"""
    if not test_output or "Claude 未生成测试文件" in test_output:
        logger.warning("没有测试输出,无法验证修复")
        return False  # 没有测试视为未通过

    # 检查常见失败标记
    fail_markers = ["FAILED", "ERROR", "AssertionError", "Exception"]
    for marker in fail_markers:
        if marker in test_output:
            return False

    # 检查是否有通过标记(更严格)
    if "OK" in test_output or "passed" in test_output.lower():
        return True

    # 无明确通过标记,保守判断
    return False

🟠 问题 5代码重复

问题:

建议:

def fix_project(self, project_id: str, ...) -> BatchFixResult:
    bugs = self.task_manager.fetch_pending_bugs(project_id)
    return self._fix_bugs_batch(bugs, project_id, ...)

def fix_single_bug(self, bug_id: int, ...) -> FixResult:
    bug = self.task_manager.get_bug_detail(bug_id)
    result = self._fix_bugs_batch([bug], bug.project_id, ...)
    return result.results[0] if result.results else FixResult(...)

def _fix_bugs_batch(self, bugs: list[Bug], project_id: str, ...) -> BatchFixResult:
    """通用的批量修复逻辑"""
    # 原 fix_project 的核心逻辑

🟢 问题 6retry_failed_project 分诊逻辑可优化

当前流程: core.py:292-398

FIX_FAILED Bug → 分诊 → CANNOT_REPRODUCE / 重置为 PENDING_FIX → fix_project

问题:

  • 分诊失败会保留 FIX_FAILED 状态,下次仍会重复分诊
  • 没有分诊次数限制,可能死循环

建议:

# 在 Bug 模型中增加 triage_count 字段
if bug.retry_count >= settings.max_triage_count:
    logger.info(f"Bug #{bug.id} 已分诊 {bug.retry_count} 次,标记为 CANNOT_REPRODUCE")
    self.task_manager.update_status(bug.id, BugStatus.CANNOT_REPRODUCE)
    continue

# 分诊
success, output = self.claude_service.triage_bug(bug, project_path)
if not success:
    # 分诊执行失败,增加计数但保留状态
    self.task_manager.increment_retry_count(bug.id)
    continue

3. 优化后的理想流程

新流程设计

1. 初始化
   ├─ 获取项目信息和 Bug 列表
   ├─ Git pull + 创建 fix 分支
   └─ 更新状态 → FIXING

2. 多轮修复循环(最多 3 轮)
   └─ 每轮:
      ├─ 调用 Claude CLI 修复
      ├─ 获取 diff 和修改文件
      ├─ 【新增】软性安全检查(警告不阻断)
      ├─ 运行测试文件
      ├─ 【关键】严格判断测试是否通过
      │  ├─ 通过 → 标记 FIXED + break
      │  └─ 失败 → 记录上下文,继续下一轮
      └─ 最后一轮仍失败 → 标记 FIX_FAILED

3. 提交代码
   ├─ commit + push fix 分支
   ├─ 【推荐】创建 PR 供人工审核
   └─ 【慎用】auto_merge_to_main需配置开关

4. 实施建议

优先级排序

P0必须修复

  1. 修复测试验证逻辑 - 测试失败应触发重试
  2. 移除自动合并到 main - 改为创建 PR

P1强烈建议

  1. 优化测试通过判断 - 更严格的检测逻辑
  2. 重构代码消除重复 - 提取 _fix_bugs_batch

P2建议优化

  1. 改进安全检查 - 改为软性警告
  2. 添加分诊次数限制 - 防止重复分诊

5. 配置建议

新增配置项

# config/settings.py

# Git 工作流
auto_merge_to_main: bool = False  # 默认不自动合并
create_pr_after_fix: bool = True   # 自动创建 PR

# 测试验证
require_test_pass: bool = True     # 必须测试通过才标记 FIXED
test_timeout: int = 300            # 测试超时时间(秒)

# 重试策略
max_retry_count: int = 3           # 最大重试轮数
max_triage_count: int = 2          # 最大分诊次数

# 安全检查
safety_check_mode: str = "warn"    # warn / block
max_modified_files: int = 10
max_modified_lines: int = 500

6. 测试建议

需要覆盖的场景

  1. 测试失败重试

    • Bug 修复后测试失败 → 第 2 轮修复 → 测试通过
    • 3 轮都失败 → 标记为 FIX_FAILED
  2. Git 分支管理

    • 修复后创建 PR 而不是直接合并
    • 多个 Bug 修复复用同一个 fix 分支
  3. 安全检查

    • 修改超限文件 → 警告但继续
    • 核心文件修改 → 警告并记录
  4. 分诊流程

    • FIX_FAILED → 分诊 → CANNOT_REPRODUCE
    • 分诊失败达到上限 → 标记为 CANNOT_REPRODUCE

7. 总结

关键改进点

问题 现状 改进后
测试验证 无论通过与否都标记 FIXED 测试失败触发重试,最终失败标记 FIX_FAILED
Git 工作流 直接合并到 main 创建 PR 供人工审核
安全检查 reset_hard 丢失修改 软性警告,继续测试
测试判断 简单字符串匹配 严格检测通过/失败标记
代码质量 逻辑重复 提取公共方法
分诊流程 可能重复分诊 添加次数限制

预期收益

  • 多轮重试机制真正发挥作用,提高修复成功率
  • Git 工作流更安全,避免直接污染主分支
  • 测试验证更严格,减少假阳性
  • 代码更简洁,维护成本降低

建议:优先实施 P0 和 P1 的改进P2 可以逐步优化。