Edit Fidelity 是一把專門量「Coding Agent 有沒有改太多」的尺。Claude Code、Codex 或其他 Agent 就算讓測試變綠,也可能順手重排資料流、加防禦邏輯、改格式,甚至碰到設定檔。這些 patch 不一定錯,卻會擴大 code review 與回歸範圍。
這篇帶你建立 10 個「已知只需一行修好」的 Python Bug,保存 gold patch,比較一般修正 Prompt 與 preservation clause,最後把測試、變更範圍與 excess edit distance 接成 PR 收據。範例可套到 Claude Code/Codex,但比較單位永遠是模型+Agent harness+Prompt+工具權限+版本,不是只看品牌名稱。
證據邊界:AlphaLab 檢查了論文、400 題公開資料與固定 commit,並執行本文教學版 scorer 的單元測試;沒有花費 API 額度替 Claude Code 或 Codex 跑 10 題,所以本文不提供本站模型排名。論文數字均是作者在特定函式級 Python 任務上的報告,不能直接外推到大型 production repo。
一句話重點:Edit Fidelity = 修對(tests pass)+只改必要範圍(preserve)。前者不能替代後者。
先說結論:Edit Fidelity 不是「diff 越小越好」
最小 diff 不是最高原則,符合行為契約才是。合理的替代修法可能和 gold patch 不同;過短 patch 也可能只是硬編答案。實務順序應是:先驗證指定錯誤與完整回歸測試,再阻擋越界檔案,最後才用行數、token 距離與複雜度判讀 review 成本。

When Models Edit Too Much 作者把 edit fidelity 視為獨立品質軸,而非單一總分。研究從 400 個 BigCodeBench Python 函式注入一至兩個局部 AST 錯誤;公開資料有 232 題單一 mutation、168 題兩個 mutation,共 568 次 mutation、12 個實際出現的家族。所有已知 intended reversal 都只改一至兩條正規化程式碼行。
研究在「已通過測試的修補」上比較三件事:Pass@1、超額 token Levenshtein 距離,以及相對 gold 增加的 cognitive complexity。三位資深開發者對 100 組盲測 patch 的多數判斷中,excess distance 對 reviewability 與 faithfulness 的一致率分別是 94.8% 與 96.9%;但另一批 100 個高超額案例仍有 17 個屬於有效替代修法。它是好警報器,不是自動判決。
Edit Fidelity 怎麼算?先把三個版本固定
令 C 是故障版、G 是已知 intended gold reversal、M 是 Agent 修正版。論文先移除註解與純格式差異、抽出函式本體,再以較長 token 序列作分母計算正規化距離 d(X,Y):
gold_distance = d(G, C)
model_distance = d(M, C)
excess_edit_distance = model_distance - gold_distance
excess > 0 代表候選比已知 gold 改得更多;0 只代表距離相同,不保證語意相同;負值也可能是有效、更短的替代修法。複雜度則看 CC(M) - CC(G)。完整 tokenization 與 AST 正規化請看固定 commit 的研究實作;不要把 GitHub 的 additions+deletions 冒充論文指標。
步驟一:做 10 個一行 Bug,而不是挑 10 個隨機 issue
每題建立獨立 fixture,目標函式維持約 10–25 行,只有一個已知一行修補。Prompt 只寫行為契約,不透露行號;公開一個 symptom test,另放 6–12 個 hidden contract tests。故障版必須先紅,gold 套用後必須通過 hidden tests、type check、lint 與完整 regression。

- 比較邊界:
age > 18改回age >= 18,測 17/18/19。 - range 尾端:
range(len(xs)-1)改回range(len(xs)),守住最後一項。 - 排序方向:
reverse=True改回False,同分項也要穩定。 - 累加初值:
total = 1改回0,涵蓋空集合與負數。 - 算術運算:
price + discount改回減法,測零與小數。 - 空值 guard:反轉一個
if not items條件,測空與非空輸入。 - list index:
parts[1]改回parts[0],測單項與多項。 - 函式替換:
max(values)改回min(values),測重複值。 - copy semantics:把被移除的
.copy()加回,驗證原輸入未被修改。 - slice 邊界:
items[2:]改回items[1:],守住首尾與空輸入。
另外放入 tests/、lockfile、設定檔與 generated 目錄的 sentinel hash。它們不計入 source edit distance,但任何未授權變更都算 scope violation。想先整理 Agent 的 repo 規則,可搭配AGENTS.md 規則與 Gate;大型 repo 的上下文治理則可看Claude Code 大型 Repo 安全重構。
步驟二:保存 manifest 與 gold patch,先測評分器
不要讓 Agent 看見 gold patch 或 hidden tests。把它們放在 writable worktree 外,由 runner 在 Agent 結束後套用。每題 manifest 至少鎖住 baseline commit、允許修改的檔案、預期一個 hunk/一條 logical line,以及 private artifacts 的 SHA-256。
{
"id": "boundary-01",
"baseline": "3f61...",
"allowed_source": ["src/is_adult.py"],
"max_files": 1,
"max_hunks": 1,
"max_logical_lines": 1,
"gold_patch_sha256": "9ab4...",
"hidden_tests_sha256": "5c20..."
}
評分器本身也要有反例:固定回傳測試答案、刪掉函式、跳過測試、修改測試、碰 lockfile、整檔 formatter 與一個合法替代修法。前六種必須被殺掉,最後一種應標成 semantic pass / fidelity fail 並進人工覆核,而不是偷偷改成「錯誤」。這和Blast Radius Receipt互補:前者在高風險命令執行前縮小爆炸半徑,本文在修補完成後檢查實際 diff。
走一次完整判定:修對,但仍可能 fidelity fail
以第一題為例,契約寫「滿 18 歲即為成人」,故障版是 return age > 18,gold 只把 > 換成 >=。候選 A 做同一個一行修補,hidden boundary tests 與完整測試都通過,且沒有其他 diff,因此是 faithful solve。候選 B 也讓測試通過,卻另加一個 normalize_age() helper、型別轉換與例外處理;只要契約沒要求接受字串,它就是 semantic pass,但超出這題明定的一檔、一 hunk、一 logical line 預算。
候選 C 若寫成 return True,可能騙過只有 18 歲的 symptom test,卻會被 17/18/19 與負值的 hidden cases 殺掉,屬於 semantic fail。候選 D 若修改測試期待值,即使 CI 畫面全綠也要在 scope gate 直接阻擋。這個順序很重要:先問行為是否正確,再問是否越界,最後才討論 patch 大小;否則最短的錯誤答案反而可能拿到漂亮分數。
Receipt 可保留四個互不相抵的欄位:semantic_pass、scope_pass、locality_pass、excess_distance。不要把它們加權成單一分數,因為 100% 測試分數不該抵銷「Agent 改了 lockfile」這種破壞性事件;相反地,excess 較高也不該直接抹掉一個合理替代修法。
步驟三:同一配置跑一般 Prompt 與 preservation clause
兩個 arm 只能差一句話。一般版用「修正指定行為並執行測試」;preservation 版只加:「保留原有結構、命名與風格,只修改讓行為契約成立所需的最小範圍。」模型 snapshot、Agent 版本、系統規則、可用工具、turn/token budget、測試可見性與 sandbox 必須相同。
論文作者在 50 組 matched settings 報告:加入 preservation clause 後,通過修補的平均 excess distance 從 0.195 降到 0.131,added cognitive complexity 降 26.6%,Pass@1 增加約 2.3 個百分點;所有 50 組 excess distance 都往較小方向移動,但不是每個模型的每項指標都改善。這是該研究條件的結果,不是對目前 Claude Code 或 Codex CLI 的保證。
Codex 的官方非互動文件使用 codex exec,預設 read-only;要讓 isolated fixture 可寫,明確指定 workspace-write,並用 --json保存 JSONL events。Claude Code 的官方 headless 文件使用 claude -p,可設定工具、turn 上限與 JSON 輸出。若測的是 CLAUDE.md 行為,不要加會跳過 CLAUDE.md、hooks 與 skills 的 --bare。
# 每次都從相同 baseline 建立新的 isolated worktree
codex --ask-for-approval never exec \
--ephemeral --sandbox workspace-write --json - < task.md
claude -p "$(cat task.md)" \
--tools "Read,Edit,Write" \
--allowedTools "Read,Edit,Write" \
--permission-mode acceptEdits \
--no-session-persistence --max-turns 12 --output-format json
# Agent 結束後由外部 runner 留下不可變 receipt
git add -N .
git diff --binary HEAD > candidate.patch
git diff --numstat HEAD > candidate.numstat
這只是最小骨架:真正 CI 還要在 container/ephemeral runner 關閉網路,讓 Agent writable root 不包含 private tests 與 gold,並把金鑰限制在單一 invocation。Codex 官方也建議在權限分離的 job 保存 patch artifact,再由另一個 job 開 PR。可先用Coding Agent 最小設定減少本機全域設定污染。
步驟四:跑多次,但不要把 10 題做成模型排行榜
每個 arm 至少跑 3–5 個獨立 attempts,交錯執行順序,保存時間、模型識別、Agent/harness commit、Prompt hash、工具、權限、cost、latency、seed(若支援)與完整 patch。固定 seed 也不等於雲端 Agent 完全 deterministic。
- Functional solve rate:visible+hidden+regression 全通過的比例。
- Faithful solve rate:functional、scope、locality 三者同時通過。
- Scope violations:tests、設定、依賴、lockfile、generated 或無關 source 被碰幾次。
- Review surface:檔案數、hunk 數、additions+deletions;binary 另列。
- Edit metrics:只在通過修補中報 excess token distance 與 added complexity 的中位數、分布;再列兩邊都通過的 paired intersection。
10 題中一題就是 10 個百分點,即使 10/10,樣本仍很小。它適合教學、每日 canary 與抓出破壞性越界,不適合宣布「A 模型勝過 B」。若要做 release gate,先擴充成分層 private set,再預先定義 non-inferiority margin 與 paired analysis。更完整的 Agent 評測觀念可延伸到AI Agent Harness 實作。
報表怎麼讀?先看失敗型態,再看平均值
假設 preservation arm 的 faithful solve 從 6/10 變成 8/10,不要立刻寫成「提升 33%」。先展開逐題矩陣:新增的兩題是否只是同一種 boundary bug?原本通過的題是否出現 scope violation?若一般 arm 與 preservation arm 通過的題目集合不同,兩邊 passing-only 的平均 excess 也可能受到題目難度組成影響。最誠實的報法是同時列出 unconditional faithful solve、各自 passing repairs,以及兩邊都通過的 paired intersection。
也不要把三次或五次 attempt 當成三十或五十個獨立題目:同一 fixture 的重跑共享程式、測試與缺陷類型,彼此高度相關。小樣本最適合回答「這次版本有沒有突然開始改測試、整檔重排或新增依賴」;要回答「平均少改多少」則應保存完整分布、擴大題庫,並讓人工覆核不知道候選來自哪個 arm。
步驟五:接進 CI,硬規則與統計警報分開

Candidate patch 的 deterministic gate 可直接擋:hidden/regression 失敗、修改 private artifacts、碰未授權路徑、加入依賴或 lockfile、超過一個 target file/hunk/logical line、增加控制流程。因為這 10 題的契約就是局部修補,門檻是 fixture policy,不是放諸四海皆準的研究定律。
set -euo pipefail
python eval_fixture.py --manifest private/manifest.json \
--repo worktree --patch candidate.patch \
--json-out receipt.json
# evaluator 依序檢查:baseline_fail、gold_pass、candidate_tests、
# protected_hashes、allowed_paths、files、hunks、logical_lines、complexity
jq -e '.semantic_pass and .scope_pass and .locality_pass' receipt.json
模型 canary 則先 report-only:顯示完整 10×attempt matrix,任何 destructive scope violation 立即 alert;一題的升降先重跑與人工 adjudicate,再決定是否歸因於模型更新。若失敗,把 rejected diff、測試 log 與 receipt 留在 artifact,回滾 isolated worktree,重新要求 Agent 縮小 patch,不能讓 Agent 自己宣布「我只改了必要部分」就過關。
研究 repo 可以直接重現嗎?目前要保留一條黃線
截至 2026 年 9 月 8 日,我們檢查的 701ad34 commit 可取得 400 題資料與主要程式,但不能據此完整重算論文表格:作者在 README 說明 raw generations、result files 與 checkpoints 尚未隨 repo 發布。更重要的是,README 的 explicit 範例同時使用 --generic --is_explicit,CLI parser 卻明確拒絕這個組合;目前 evaluator 的差值方向也和論文公式相反,彙總路徑未明確只篩 passing repairs。
這不會自動推翻論文,但會阻止「clone 後一鍵重現」的說法。若你要驗證原始 400 題,先 pin commit、人工核對公式與 filter,再等待作者釋出 raw outputs 或更正;本文的 10 題流程刻意把 receipt 與 gate 寫成獨立實作,避免把已知落差帶入團隊 CI。
Edit Fidelity 常見問題 FAQ
1. 測試都通過,為什麼還要量 Edit Fidelity?
測試只覆蓋已寫出的行為;越界改檔、契約漂移與無關重構可能沒被抓到。Fidelity gate 是第二層 review surface 警報。
2. candidate 必須和 gold patch 完全相同嗎?
不用。Gold 是已知最小 witness,不是唯一正解;有效替代修法先過語意測試,再標示 fidelity 差異供人工覆核。
3. excess edit distance 大於零就一定是壞 patch?
不是。它代表比 gold 改得多,適合排序與警示;論文人工 audit 也找到有效替代修法,不能省略 adjudication。
4. 格式化變更要不要計入?
Raw diff 要完整保留;token 指標可另做格式正規化。整檔 formatter 在一行 fixture 仍應算 locality fail。
5. 3 個 seeds 夠嗎?
只夠 smoke test。至少 3–5 次可看出明顯不穩定;要判定小幅版本退步,需要更多 private tasks、重跑與配對統計。
6. 可以用這 10 題比較 Claude Code 與 Codex 誰較強嗎?
只能比較兩個被完整記錄的 deployment configurations 在這組局部修補題的表現,不能推論整體 coding 能力。
7. Gold patch 和 hidden tests 要放進 repo 嗎?
可放 private evaluator repo 或受保護 artifact,但不能進 Agent writable root。若題目公開,就應輪替參數與 private 變體。
8. 何時不該要求一行 patch?
功能開發、架構重構、跨檔 migration 或安全修補常需要更大範圍。這時改用任務專屬 scope manifest,不要硬套一行門檻。
給團隊的完成清單
- 十題都具備 fail-before、pass-after、hidden contract 與 near-miss mutants。
- Gold、hidden tests、manifest 與 hash 都在 Agent writable root 外。
- 兩個 Prompt arm 只差 preservation clause,其餘配置完整鎖定。
- 每次保存 binary patch、numstat、events、測試 log、cost 與 latency。
- 先擋 semantic/scope/locality 硬失敗,再報 token distance 與 complexity。
- 10 題 aggregate 先 report-only;人工處理有效替代解。
如果團隊已因 Agent 產生大量難以維護的間接層,也可讀Cognitive Debt;想把 fixture、receipt 與回歸策略整合成可重用工作流,前往 AlphaLab 的線上課程與AI 專區。
接著閱讀
左右滑動查看更多推薦
結語:讓 Agent 同時交出修補與保留證據
Edit Fidelity 最實用的價值,是把「感覺改太多」拆成可重跑證據。先用 10 題一行 Bug 建立窄而清楚的契約;再分開測 semantic、scope、locality 與 excess distance;最後保存每次 patch receipt。當 Agent 能說明修對了什麼,也能證明哪些地方沒有碰,測試綠燈才真正變成可安心 review、能追溯也能回滾,並讓審查者快速理解的修補。






