Some checks failed
Build and Deploy Log Center / build-and-deploy (push) Failing after 1m55s
12 KiB
12 KiB
Bug 自动修复流程分析与优化建议
分析日期: 2026-02-25 当前版本存在的问题和改进方案
1. 当前流程概览
主流程 (fix_project)
1. 初始化
├─ 获取项目信息(本地路径、仓库地址)
├─ 获取待修复 Bug 列表(NEW/PENDING_FIX)
├─ 初始化 Git(pull + 创建 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,无论测试通过与否都标记为
FIXED并break - 测试失败不会触发下一轮重试
问题代码:
# 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)
# 继续下一轮
🟡 严重问题 2:Git 分支管理混乱
问题描述:
- git_manager.py:55-75
pull()会切回 main/master - core.py:152-154 然后又从 main 创建新分支
- 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:代码重复
问题:
- core.py:95-290
fix_project - core.py:467-562
fix_single_bug - 两个方法的核心逻辑完全一样,只是处理单个 vs 批量
建议:
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 的核心逻辑
🟢 问题 6:retry_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(必须修复)
- ✅ 修复测试验证逻辑 - 测试失败应触发重试
- ✅ 移除自动合并到 main - 改为创建 PR
P1(强烈建议)
- ✅ 优化测试通过判断 - 更严格的检测逻辑
- ✅ 重构代码消除重复 - 提取
_fix_bugs_batch
P2(建议优化)
- ✅ 改进安全检查 - 改为软性警告
- ✅ 添加分诊次数限制 - 防止重复分诊
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. 测试建议
需要覆盖的场景
-
测试失败重试
- Bug 修复后测试失败 → 第 2 轮修复 → 测试通过
- 3 轮都失败 → 标记为 FIX_FAILED
-
Git 分支管理
- 修复后创建 PR 而不是直接合并
- 多个 Bug 修复复用同一个 fix 分支
-
安全检查
- 修改超限文件 → 警告但继续
- 核心文件修改 → 警告并记录
-
分诊流程
- FIX_FAILED → 分诊 → CANNOT_REPRODUCE
- 分诊失败达到上限 → 标记为 CANNOT_REPRODUCE
7. 总结
关键改进点
| 问题 | 现状 | 改进后 |
|---|---|---|
| 测试验证 | 无论通过与否都标记 FIXED | 测试失败触发重试,最终失败标记 FIX_FAILED |
| Git 工作流 | 直接合并到 main | 创建 PR 供人工审核 |
| 安全检查 | reset_hard 丢失修改 | 软性警告,继续测试 |
| 测试判断 | 简单字符串匹配 | 严格检测通过/失败标记 |
| 代码质量 | 逻辑重复 | 提取公共方法 |
| 分诊流程 | 可能重复分诊 | 添加次数限制 |
预期收益
- ✅ 多轮重试机制真正发挥作用,提高修复成功率
- ✅ Git 工作流更安全,避免直接污染主分支
- ✅ 测试验证更严格,减少假阳性
- ✅ 代码更简洁,维护成本降低
建议:优先实施 P0 和 P1 的改进,P2 可以逐步优化。