feat: v1.6.2 初始测试失败注入Generator + Wink润滑增强

- Generator: 首次生成时注入initial_test_failure到prompt(LLM第一次就看到具体报错)
- Wink: 润滑规则增强
- Orchestrator: 重试逻辑优化
- pyproject.toml: 1.6.2

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Val-sss
2026-05-07 12:32:44 +08:00
parent 44e1155032
commit 797549450e
5 changed files with 84 additions and 31 deletions

View File

@@ -4,6 +4,30 @@ All notable changes to KWCode are documented here.
---
## [1.6.2] - 2026-05-07
### Retry优化 + 润滑/免疫机制
**核心理念**让LLM在擅长的地方发挥最大效能在不擅长的地方不让它发力。给最好的原材料而不是更多的retry机制。
### Changed
- **Generator首次生成注入initial_test_failure**LLM第一次就能看到具体哪些测试在失败、期望什么输出不再盲目生成
- **retry_hint携带具体失败测试名**从verifier_output提取failed_tests列表注入hintLLM重试时精确知道哪些测试还没过
- **Reviewer在tests_total==0时跳过**无测试证据时Reviewer会幻觉reject现在直接放行
- **think_escalate改为条件触发**只在assertion错误逻辑错误时升级深度推理syntax/patch_apply等错误想更久没用
- **MISSING_TOOLCHAIN快速熔断**工具链缺失时直接返回失败告知用户不浪费retry
### Removed
- **scope narrowing**删除第2次失败时缩小到第一个文件+函数的逻辑。第一次定位错了缩小只会更错让Locator自然重新搜索
### Added
- **WinkMonitor免疫机制**:新增`tests_no_progress` pattern检测连续retry后通过率未提升注入"换方向"纠正hint
---
## [1.5.1] - 2026-05-06
### 三飞轮 + 遥测 + 模型自适应 + 前沿算法

View File

@@ -310,6 +310,18 @@ class PipelineOrchestrator:
# Gate 3: AB测试
ab_candidate_name, ab_used_new, gate_result = self._setup_ab_test(gate_result, expert_type, on_status)
# MISSING_TOOLCHAIN快速熔断工具链缺失时不进retry循环直接告知用户
if ctx.gap and ctx.gap.gap_type == GapType.MISSING_TOOLCHAIN:
self._emit(on_status, "circuit_break",
f"工具链缺失:{ctx.gap.error_msg[:100]},请手动安装后重试")
self.bus.emit("circuit_break", {"msg": "missing_toolchain"})
elapsed = time.time() - start_time
_watchdog.cancel()
checkpoint = Checkpoint(project_root)
return self._record_failure_result(ctx, project_root, gate_result,
None, False, 0,
elapsed, checkpoint, False, on_status)
# 优先使用专家注册表的自定义pipeline否则用默认
if gate_result.get("route_type") == "expert_registry" and "pipeline" in gate_result:
sequence = gate_result["pipeline"]
@@ -441,24 +453,6 @@ class PipelineOrchestrator:
if wink_hint:
ctx.retry_hint = (ctx.retry_hint + "\n" + wink_hint).strip() if ctx.retry_hint else wink_hint
# Scope narrowing: on 2nd failure, reduce to first file+function
if ctx.retry_count == 2 and ctx.locator_output:
files = ctx.locator_output.get("relevant_files", [])
funcs = ctx.locator_output.get("relevant_functions", [])
if len(files) > 1 and funcs:
ctx.locator_output = {
"relevant_files": [files[0]],
"relevant_functions": [funcs[0]],
"edit_locations": ctx.locator_output.get("edit_locations", [])[:1],
"method": "scope_narrowed",
}
if ctx.relevant_code_snippets:
ctx.relevant_code_snippets = {
files[0]: ctx.relevant_code_snippets.get(files[0], "")
}
self._emit(on_status, "scope_narrow", f"缩小范围:只修 {funcs[0]}()")
self.bus.emit("scope_narrow", {"msg": f"只修 {funcs[0]}()"})
self._emit(on_status, "retry", f"{ctx.retry_count}次尝试失败:{error_detail[:100]}")
self.bus.emit("retry", {"count": ctx.retry_count, "error": error_detail[:100]})
@@ -473,15 +467,14 @@ class PipelineOrchestrator:
# 设置重试策略:每次重试用不同方法
ctx.retry_strategy = ctx.retry_count # 0→1→2
# Fast/Slow双阶段首次用fast(think=off)失败后升级slow(think=on+高预算)
if ctx.retry_count == 1 and not ctx.think_config.get("think"):
# 第一次失败从fast升级到slow think
ctx.think_config = {"think": True, "budget": 2048}
self._emit(on_status, "think_escalate", "升级到深度推理模式")
elif ctx.retry_count >= 2 and ctx.think_config.get("budget", 0) < 4096:
# 第二次失败最大think预算
ctx.think_config = {"think": True, "budget": 4096}
self._emit(on_status, "think_escalate", "最大推理预算")
# Think escalation只在assertion错误时升级逻辑错误需要深度推理其他错误想更久没用
if current_error_type == "assertion":
if ctx.retry_count == 1 and not ctx.think_config.get("think"):
ctx.think_config = {"think": True, "budget": 2048}
self._emit(on_status, "think_escalate", "逻辑错误,升级到深度推理")
elif ctx.retry_count >= 2 and ctx.think_config.get("budget", 0) < 4096:
ctx.think_config = {"think": True, "budget": 4096}
self._emit(on_status, "think_escalate", "最大推理预算")
# 错误驱动搜索:按失败类型决定是否搜索(网络保护:异常不阻塞)
if self._should_search(current_error_type, ctx.retry_count) and not ctx.search_triggered and not no_search:
@@ -674,13 +667,13 @@ class PipelineOrchestrator:
ab_candidate_name, ab_used_new: bool, elapsed: float,
checkpoint, on_status) -> dict:
"""Record success: memory, registry, trajectory, AB, value, milestone, reflection."""
# Reviewer: 需求对齐审查 — 测试全部通过时跳过(测试是最终裁判,Reviewer会幻觉
# Reviewer: 需求对齐审查 — 测试全部通过或无测试结果时跳过(无证据时Reviewer会幻觉
v = ctx.verifier_output or {}
tests_passed = v.get("tests_passed", 0)
tests_total = v.get("tests_total", 0)
all_tests_pass = tests_total > 0 and tests_passed == tests_total
skip_review = (tests_total > 0 and tests_passed == tests_total) or tests_total == 0
if not all_tests_pass:
if not skip_review:
# 测试没全通过才需要Reviewer审查
review_result = self._do_review(ctx, on_status)
if review_result and not review_result.get("aligned") and review_result.get("confidence", 0) >= 0.7:
@@ -1086,6 +1079,11 @@ class PipelineOrchestrator:
if last_code:
hint += f"\n\n上次生成的代码(有问题):\n{last_code}\n\n请不要重复同样的错误。"
# 携带具体失败的测试名让LLM精确定位
failed_tests = (ctx.verifier_output or {}).get("failed_tests", [])
if failed_tests:
hint += "\n\n仍然失败的测试:\n" + "\n".join(f" - {t}" for t in failed_tests[:5])
return hint
def _should_search(self, error_type: str, retry_count: int) -> bool:

View File

@@ -5,6 +5,7 @@ Wink 自修复监控:轨迹监控 + 偏离检测 + 课程纠正。
- Specification Drift偏离用户原始意图scope creep
- Reasoning Problems同类错误反复原地打转
- Tool Call Failurespatch 持续失败
- Progress Stall重试未提升通过率免疫机制
理论来源:
- Wink: Recovering from Misbehaviors in Coding AgentsarXiv:2602.17037
@@ -65,6 +66,17 @@ class WinkMonitor:
),
"hint": "Generator 未产出有效 patch尝试简化任务描述或缩小修改范围",
},
# 免疫机制:重试未提升通过率(结疤,不改变结构)
{
"name": "tests_no_progress",
"detect": lambda ctx: (
ctx.retry_count >= 2 and
ctx.verifier_output and
hasattr(ctx, '_prev_tests_passed') and
ctx.verifier_output.get("tests_passed", 0) <= getattr(ctx, '_prev_tests_passed', 0)
),
"hint": "连续重试未提升通过率,尝试完全不同的实现方式,不要在同一个方向上继续",
},
]
def check(self, ctx, bus: Optional[EventBus] = None) -> Optional[str]:
@@ -72,6 +84,10 @@ class WinkMonitor:
检查当前 context 是否有偏离,返回纠正 hint 或 None。
非阻塞,任何异常静默忽略。
"""
# 记录本次tests_passed供下次比较免疫机制的记忆
if ctx.verifier_output:
ctx._prev_tests_passed = ctx.verifier_output.get("tests_passed", 0)
for pattern in self.DRIFT_PATTERNS:
try:
if pattern["detect"](ctx):

View File

@@ -391,6 +391,11 @@ class GeneratorExpert:
if ctx.doc_context:
prompt += f"\n\n## 相关文档参考\n{ctx.doc_context[:800]}"
# 注入初始测试失败信息让LLM第一次就看到具体报错
initial_failure = getattr(ctx, 'initial_test_failure', '')
if initial_failure and ctx.retry_count == 0:
prompt += f"\n\n## 当前测试失败\n{initial_failure[:500]}"
# Inject retry_hint if available
if ctx.retry_hint:
prompt += f"\n\n## 重试提示\n{ctx.retry_hint}"
@@ -435,6 +440,11 @@ class GeneratorExpert:
while "\n\n\n" in prompt:
prompt = prompt.replace("\n\n\n", "\n\n")
# 注入初始测试失败信息
initial_failure = getattr(ctx, 'initial_test_failure', '')
if initial_failure and ctx.retry_count == 0:
prompt += f"\n\n## 当前测试失败\n{initial_failure[:500]}"
if ctx.retry_hint:
prompt += f"\n\n## 重试提示\n{ctx.retry_hint}"
@@ -825,6 +835,11 @@ class GeneratorExpert:
f"实现所有pass存根函数保持已有实现不变。"
)
# 注入初始测试失败信息
initial_failure = getattr(ctx, 'initial_test_failure', '')
if initial_failure and ctx.retry_count == 0:
prompt += f"\n\n## 当前测试失败\n{initial_failure[:500]}"
if ctx.retry_hint:
prompt += f"\n\n## 重试提示\n{ctx.retry_hint}"

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "kwcode"
version = "1.6.1"
version = "1.6.2"
description = "KwCode - Local-model coding agent with MoE expert pipeline"
requires-python = ">=3.10"
readme = "README.md"