From 2162a9585e6a16a1daec3de1e5f176f5ce34c08d Mon Sep 17 00:00:00 2001 From: Val-sss <154882199@qq.com> Date: Thu, 14 May 2026 08:09:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=89=E9=A1=B9=E6=A1=86=E6=9E=B6?= =?UTF-8?q?=E4=BC=98=E5=8C=96=20=E2=80=94=20=E7=B2=BE=E5=87=86retry=20+=20?= =?UTF-8?q?ReAct=E9=A6=96=E6=AC=A1=E5=90=AF=E7=94=A8=20+=20=E5=A4=9A?= =?UTF-8?q?=E6=96=87=E4=BB=B6batch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 精准Retry: 高通过率(差1-2个测试)时保留完整诊断不截断, 加聚焦指令"只修这个问题不要改其他代码"(原300字符截断→800) 2. ReAct首次启用: 多文件/rename/refactor任务首次attempt就用 ReAct循环探索,不再等retry才触发(_is_complex_task门控) 3. 多文件batch: ReactLoop增加rename专用策略指引(grep找引用→ 逐文件修改→验证), 扩展rename任务文件数限制3→8 Co-Authored-By: Claude Opus 4.6 (1M context) --- kaiwu/agent/react_loop.py | 21 +++++++++++++++ kaiwu/core/orchestrator.py | 10 ++++++++ kaiwu/experts/generator.py | 52 ++++++++++++++++++++++++++++++-------- 3 files changed, 72 insertions(+), 11 deletions(-) diff --git a/kaiwu/agent/react_loop.py b/kaiwu/agent/react_loop.py index 0d49c59..0bc03a1 100644 --- a/kaiwu/agent/react_loop.py +++ b/kaiwu/agent/react_loop.py @@ -247,12 +247,33 @@ class ReactLoop: f"## 项目根目录: {ctx.project_root}", f"## 目标文件: {', '.join(target_files)}", ] + # rename/refactor任务追加规则 + user_lower = ctx.user_input.lower() + if any(kw in user_lower for kw in ["rename", "重命名", "refactor", "重构"]): + parts.append("") + parts.append("## 重命名/重构规则") + parts.append("- 必须用grep找到所有引用点,不能只改目标文件") + parts.append("- 修改顺序:先改定义,再改所有调用点") + parts.append("- 全部改完再run_test") return "\n".join(parts) def _build_initial_message(self, ctx, target_files: list[str]) -> str: """构建初始消息,包含任务描述和当前失败信息。""" parts = [f"## 任务\n{ctx.user_input}"] + # 检测rename/refactor任务,注入多文件策略 + user_lower = ctx.user_input.lower() + rename_kw = ["rename", "重命名", "refactor", "重构", "split", "拆分"] + if any(kw in user_lower for kw in rename_kw): + parts.append( + "\n## 多文件重命名策略\n" + "1. 先用 grep 搜索旧名称,找到所有包含它的文件\n" + "2. 逐个 read_file 读取每个相关文件\n" + "3. 逐个 write_file 修改每个文件中的所有引用\n" + "4. 最后 run_test 验证\n" + "不要遗漏任何文件中的引用!" + ) + # 当前测试失败信息 if ctx.verifier_output: error_detail = ctx.verifier_output.get("error_detail", "") diff --git a/kaiwu/core/orchestrator.py b/kaiwu/core/orchestrator.py index f01a6e4..df7c4ab 100644 --- a/kaiwu/core/orchestrator.py +++ b/kaiwu/core/orchestrator.py @@ -1279,6 +1279,16 @@ class PipelineOrchestrator: ctx._last_ver_passed = current_passed + # 高通过率聚焦: 差1-2个测试时prepend精准指令 + v = ctx.verifier_output or {} + tp = v.get("tests_passed", 0) + tt = v.get("tests_total", 0) + if tt > 0 and tp >= tt - 2 and tp > 0: + hint = ( + f"你已通过 {tp}/{tt} 个测试,只有 {tt - tp} 个失败。\n" + f"只修复下面指出的问题,不要改动其他代码。\n\n" + ) + hint + return hint def _should_search(self, error_type: str, retry_count: int) -> bool: diff --git a/kaiwu/experts/generator.py b/kaiwu/experts/generator.py index 9fcb84f..f38b71e 100644 --- a/kaiwu/experts/generator.py +++ b/kaiwu/experts/generator.py @@ -228,16 +228,17 @@ class GeneratorExpert: # 需要整文件scope的任务:重构(提取函数/拆分类)或综合任务(bug+refactor) # 判断条件:任务需要新增类/函数,或测试要求代码行数缩短/拆分 if self._needs_whole_file_scope(ctx, files, funcs): - # Retry时:优先尝试ReAct循环(多轮交互),fallback到targeted_fix - if ctx.retry_count >= 1: + # 复杂任务(多文件/rename)首次就用ReAct; 简单任务retry时才用 + use_react_first = self._is_complex_task(ctx, files, funcs) + if use_react_first or ctx.retry_count >= 1: react_result = self._try_react_loop(ctx, files) if react_result: return react_result - # ReAct失败,如果有best_state用targeted_fix,否则再试whole_file + # ReAct失败,fallback if ctx.best_tests_passed > 0: return self._run_targeted_fix(ctx, files) return self._run_whole_file_refactor(ctx, files) - # 首次attempt始终用whole_file_refactor(更稳定,t04靠这个PASS) + # 非复杂任务首次: whole_file_refactor(t04等靠这个PASS) return self._run_whole_file_refactor(ctx, files) # For each file+function pair, extract original and generate modified @@ -462,13 +463,23 @@ class GeneratorExpert: elif test_failure: prompt += f"\n\n## 当前测试失败详情\n{test_failure[-2000:]}\n" - # Inject retry_hint: 只传一句话总结,不传完整历史 + # Inject retry_hint: 高通过率时保留完整诊断,聚焦修复 if ctx.retry_hint: - # 截断 retry_hint,避免注入过多历史 hint = ctx.retry_hint - if len(hint) > 300: - hint = hint[:300] + "..." - prompt += f"\n\n## 重试提示\n{hint}" + v = ctx.verifier_output or {} + tests_passed = v.get("tests_passed", 0) + tests_total = v.get("tests_total", 0) + if tests_total > 0 and tests_passed >= tests_total - 2 and tests_passed > 0: + # 差1-2个测试: 保留完整诊断,聚焦指令 + prompt += ( + f"\n\n## 精准修复(已通过 {tests_passed}/{tests_total})\n" + f"只有 {tests_total - tests_passed} 个测试失败,只修这个问题,不要改其他代码。\n\n" + f"{hint}\n" + ) + elif len(hint) > 800: + prompt += f"\n\n## 重试提示\n{hint[:800]}..." + else: + prompt += f"\n\n## 重试提示\n{hint}" system = self._build_system(ctx) @@ -1206,6 +1217,22 @@ class GeneratorExpert: return False + def _is_complex_task(self, ctx, files: list[str], funcs: list[str]) -> bool: + """检测需要首次就用ReAct的复杂任务(多文件/rename/refactor跨文件)""" + user_input = ctx.user_input.lower() + non_test_files = [f for f in files if "test" not in f.lower()] + + # 多个非测试目标文件 + if len(non_test_files) >= 2: + return True + + # rename/refactor关键词 + 有目标文件 + rename_kw = ["rename", "重命名", "split", "拆分", "move", "迁移"] + if any(kw in user_input for kw in rename_kw) and len(non_test_files) >= 1: + return True + + return False + def _run_whole_file_refactor(self, ctx: TaskContext, files: list[str]) -> Optional[dict]: """整文件scope重构:LLM看到完整文件+测试失败,输出完整修改后的文件。 与_run_whole_file不同:prompt针对重构/bug修复而非存根实现。""" @@ -2606,8 +2633,11 @@ class GeneratorExpert: # 所有tier都允许ReAct(小模型步数少一些) # 注意:retry_count检查已移到调用方(generator.run()中 ctx.retry_count>=1 才进入此分支) - # 过滤测试文件 - target_files = [f for f in files[:3] if "test" not in f.lower()] + # 过滤测试文件,rename任务扩展文件数 + user_lower = ctx.user_input.lower() + is_rename = any(kw in user_lower for kw in ["rename", "重命名", "refactor", "重构"]) + file_limit = 8 if is_rename else 3 + target_files = [f for f in files[:file_limit] if "test" not in f.lower()] if not target_files: return None