Author SHA1 Message Date
xiaoheiandClaude Opus 4.8 435f720cfb fix(agent): 注入目标文件完整原文 + 强令最小改动(修整文件重写噪声)
CI / guardrails (push) Successful in 5s
CI / guardrails (pull_request) Successful in 7s
CI / tests (push) Successful in 48s
CI / tests (pull_request) Successful in 41s
根因:agent 无本地读文件工具,只拿到 workspace 的截断采样,被要求"输出完整文件"
时只能凭残缺采样+自身知识重构整个文件 → 丢行/重排/改注释/版权头 → 巨噪声 diff,
要么 apply 不上,要么 apply 了破坏无关测试(SWE-bench Pro 两臂 0/10 的主因)。

修法:
- _load_exact_target_files():从子任务描述正则提取目标文件路径,注入其**完整未截断**
  的磁盘原文(上限6文件/各120KB,超限留给采样)。
- 执行 prompt 新增 "EXACT CURRENT CONTENT" 段 + 强令:返回内容必须是该原文逐字节保留、
  仅施加任务所需最小编辑,禁止 reformat/reorder/rename/clean-up 无关行。
- 强化原 full-file 输出说明。

目的:让 diff 只含必要行,救回 NodeBB 296/300、ansible 171/175 这类"差几个测试"的近失题。
契约测试全绿(runtime-contract/merge-smoke/workflow-e2e/contract-freeze)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 09:47:35 +00:00
xiaohei 85e6ea6dfb Merge pull request 'fix: 关 qwen 思考模式(消除 reasoning 回退) + 修 bounce 死锁 (BUG-A/BUG-B)' (#26) from fix/qwen-thinking-and-bounce-deadlock into main
CI / guardrails (push) Successful in 5s
CI / tests (push) Successful in 48s
Reviewed-on: #26
2026-06-22 05:55:05 +00:00
+57 -1
View File
@@ -11,6 +11,7 @@ import asyncio
import json
import logging
import os
import re
import time
from pathlib import Path
from typing import Callable, Optional
@@ -377,6 +378,7 @@ Return ONLY the JSON array, no other text."""
task_id, context.get("specialist_role", "general"), description)
workspace_files = self._summarize_workspace()
workspace_context = self._collect_workspace_context()
exact_target_files = self._load_exact_target_files(description)
user_prompt = context.get("user_prompt") or context.get("run_goal") or context.get("root_task_description") or ""
specialist_role = context.get("specialist_role", "general")
dependency_artifacts = context.get("dependency_artifacts") or []
@@ -447,6 +449,16 @@ Current workspace files:
{json.dumps(workspace_files, indent=2)}
Relevant workspace file contents:
{json.dumps(workspace_context, indent=2)}
EXACT CURRENT CONTENT of the file(s) this task edits (verbatim from disk — authoritative):
{json.dumps(exact_target_files, indent=2)}
^ When a file appears above, your returned `content` for it MUST be that exact text with ONLY the
minimal edits the task requires. Copy every other line byte-for-byte: imports, comments, copyright
headers, blank lines, whitespace, and unrelated code. Do NOT reformat, reorder, rename, restyle,
"clean up", or re-wrap anything the task does not explicitly require. The resulting diff against the
base commit must contain ONLY the lines the task needs — unrelated changes break tests and fail the
patch. If a file you must edit is NOT shown above, read it conceptually from the listed workspace
contents and still change as little as possible.
Peer specialist input:
{json.dumps(peer_context, indent=2)}
@@ -457,7 +469,9 @@ Workspace & tool boundaries (IMPORTANT):
it is not listed above: if the task references e.g. `qutebrowser/utils/log.py`, that file EXISTS
on disk at {self.workspace_dir}/qutebrowser/utils/log.py — assume it is present and edit it.
- Treat the local files as the single source of truth. Return the complete modified content of each
file you change in `files` (full file, not a diff).
file you change in `files` (full file, not a diff) — but it MUST be the file's exact current content
with only the minimal task-required edits applied (see "EXACT CURRENT CONTENT" above). Preserve all
unchanged lines verbatim; never regenerate a file from memory or reformat it.
- The web search / read_url tools are for EXTERNAL knowledge ONLY (library/framework docs, language
features, error-message lookups). NEVER use them to fetch THIS repository's own source — the web
copy is a DIFFERENT version than your local base commit and will corrupt your patch.
@@ -852,6 +866,48 @@ Return ONLY JSON:
logger.warning(f"Failed to read workspace file {relative}: {e}")
return context
#: Path-like tokens in a subtask description, e.g. `lib/ansible/module_utils/urls.py`.
_PATH_RE = re.compile(
r"[A-Za-z0-9_][A-Za-z0-9_./\-]*\.(?:py|js|jsx|ts|tsx|go|rb|java|c|h|cc|cpp|hpp|"
r"rs|php|cs|kt|swift|scala|vue|json|ya?ml|toml|cfg|ini|sh)\b"
)
def _load_exact_target_files(self, description: str, max_files: int = 6,
max_bytes: int = 120000) -> dict[str, str]:
"""Load the FULL, UNTRUNCATED on-disk content of the file(s) a subtask names.
Root cause of noisy/non-applying patches: the agent was only shown a truncated SAMPLE of the
repo, so it reconstructed whole files from partial input → dropped/reordered/reformatted lines
→ huge diffs that break tests. Here we read the exact current content of the files referenced
in the subtask description so the model can preserve every unchanged line verbatim and edit
only what the task requires. Paths that don't resolve inside the workspace are skipped."""
if not description:
return {}
out: dict[str, str] = {}
# Longest paths first: prefer `a/b/c.py` over a bare `c.py` mention of the same file.
cands = sorted(set(m.group(0) for m in self._PATH_RE.finditer(description)),
key=len, reverse=True)
for cand in cands:
if len(out) >= max_files:
break
try:
p = self._resolve_workspace_path(cand)
except ValueError:
continue
if not p.is_file():
continue
try:
data = p.read_bytes()
except Exception:
continue
if len(data) > max_bytes:
continue # too large to inject in full; leave to the sampled context
try:
out[cand] = data.decode("utf-8")
except UnicodeDecodeError:
continue
return out
def _resolve_workspace_path(self, file_path: str) -> Path:
if not file_path or os.path.isabs(file_path):
raise ValueError(f"Invalid relative path: {file_path}")