公开页面上写着一个 resolved 率,你在自己机器上按同样的模型跑一遍 DeepSWE,结果差出一截,或者干脆跑不起来——这类事情在编码智能体评测里发生频率不低。下面按"先复现、再验真"的顺序走一遍:固定环境、对齐 harness、核对分母、排查污染,最后给出兜底与预防。文中不绑定具体版本号,命令里的镜像、仓库、字段名一律以官方文档当前版本为准。
报错现象
典型现象有这么几类,往往是成组出现的:
1. 分数对不上。同一模型,本地跑完整套 DeepSWE 的 resolved 率比公开报告低若干个百分点,而且不是零星几题挂,是整片仓库类型一起挂。
2. 环境构建阶段直接失败,日志里出现:
```
docker: Error response from daemon: pull access denied for <image>
ERROR: failed to solve: no matching manifest for linux/arm64/v8 in the manifest list entries
```
3. 补丁应用失败,但流程没中断,只是把该题记成未解决:
```
error: patch failed: src/foo.py:120
error: src/foo.py: patch does not apply
```
4. 测试阶段报依赖缺失:
```
E ModuleNotFoundError: No module named 'xxx'
FAILED tests/test_x.py::test_y - AssertionError
```
5. 分母缩水。结果文件里 resolved: 0 / 0,或者 skipped、error 的行数比 resolved 还多,但汇总脚本照样算出一个百分比。
6. 同模型两次跑分数不一样,波动明显超出预期。
7. 污染检查报警:
```
[n-gram] instance_id=... overlap=0.87 (threshold 0.50)
```
8. 超时:
```
TimeoutError: instance exceeded the configured limit
```
影响范围不只是这一轮评测:对外引用分数、模型选型、采购决策,都会跟着一起偏。
可能原因
按出现概率从高到低:
1. 环境不一致:Python 与依赖版本不同、镜像用 tag 拉导致内容漂移、CPU 架构不同(arm64 跑 x86 镜像)、系统库与编译工具链缺失。
2. Harness 配置不一致:prompt 模板、温度与采样次数、超时阈值、重试策略、并发数、上下文截断长度、允许使用的工具集(能否读测试文件、能否联网)、resolved 的判定口径(是否要求 FAIL_TO_PASS 与 PASS_TO_PASS 全部通过)。
3. 数据集版本与分母不一致:dataset 的 commit 不同、instance 列表被过滤、部分 instance 因环境构建失败被静默跳过、多次采样后择优却按一次计入。
4. 测试集污染:题面、gold patch、测试代码出现在训练语料里,模型在预训练阶段见过对应 PR。
5. 模型侧差异:同名模型不同日期快照、量化版本、system prompt 不同、工具调用策略不同。
6. 资源与稳定性:内存或磁盘不足被 OOM kill、并发过高导致超时、拉镜像失败后静默降级。
7. 统计口径混乱:分母是全部 instance 还是"成功构建的 instance";报的是 pass@1 还是 AI 词典:pass@k">pass@k;多次运行只报成绩好的那次。
8. 产物串味:不同 run 写进同一目录,结果文件是追加而不是覆盖。
逐条排查与解决
1. 对齐环境
```bash
uname -m
python -V
pip freeze > env-lock-$(date +%F).txt
docker version
docker image inspect <image> --format '{{index .RepoDigests 0}}'
```
判断方法:镜像用 digest 记录,不用 tag。若公开报告给了 digest 就逐位比对;在 arm64 机器上跑 amd64 镜像会走模拟层,速度和部分系统调用行为都会变,属于高嫌疑项。
解决:显式指定平台,并按 digest 拉取。
```bash
docker pull --platform linux/amd64 <image>@sha256:<digest>
```
把 env-lock.txt 与分数一起归档,下次复现直接对照。
2. 对齐 harness
开跑前把配置打到日志头部,敏感字段过滤掉:
```bash
grep -vE 'api_key|token|secret' config.yaml | tee -a run.log
```
判断方法:温度是否为 0、是否允许多次采样、超时多少秒、并发多少、给模型的上下文里是否出现了测试文件路径。这些项逐条与官方报告对齐,一次只改一项,改完跑一小批对照。
做法建议:先原封不动用官方给的 config 跑 20 个 instance,能稳定跑通再谈改参数。
3. 核对分母
```bash
jq -r '.status' results.jsonl | sort | uniq -c | sort -rn
jq -r '.instance_id' results.jsonl | sort -u > got.txt
sort -u all_instances.txt > want.txt
comm -3 want.txt got.txt | head -50
```
判断方法:got.txt 比 want.txt 少,说明有 instance 没跑或没落盘;skipped 与 error 合计占比超过一个小比例(比如 5%)就应当停下来查,而不是直接报 resolved 率。
解决:把失败 instance 单独重跑;确实构建不起来的,在报告里明确写"分母 = N,其中 M 个因环境构建失败被排除",别让分母悄悄变小。
4. 污染排查
两条腿走路:静态重叠 + 行为探针。
静态检查用 n-gram 重叠,13-gram 是个常用起点:
```python
ngram_overlap.py
import re, sys, json
def norm(s):
return re.sub(r'\s+', ' ', s.lower())
def grams(s, n=13):
s = norm(s)
return {s[i:i+n] for i in range(max(0, len(s) - n + 1))}
def load(path):
with open(path, encoding='utf-8', errors='ignore') as f:
return grams(f.read())
if __name__ == '__main__':
corpus = load(sys.argv[1])
for line in open(sys.argv[2], encoding='utf-8'):
inst = json.loads(line)
text = inst.get('problem_statement', '') + '\n' + inst.get('patch', '')
g = grams(text)
if not g:
continue
print(f"{inst.get('instance_id')}\t{len(g & corpus) / len(g):.3f}")
```
```bash
python ngram_overlap.py train_corpus.txt instances.jsonl | sort -k2 -nr | head -20
```
判断方法:重叠率明显高于整体分布的那批 instance 需要人工看一眼;逐字命中的基本可以判定为泄漏。阈值不是标准答案,取决于语料采样方式,别把某个数字当铁律。
行为探针更直接:
- 把 instance_id、仓库名、PR 号从题面里剥掉重跑,看分数是否大幅下滑;
- 把变量名、函数名改掉但逻辑不变,看模型是否还能一把过;
- 直接问模型某个文件的测试函数叫什么,答得和仓库一字不差时要提高警惕。
时间切分也很有用:挑训练截止日期之后合并的 PR 子集单独算一个分数,这个分数比总分更能说明真实能力。
5. 核对模型侧
```bash
curl -s <api-endpoint>/models | jq '.data[] | {id, created}'
```
判断方法:确认模型 id 与快照日期,本地部署的核对权重文件哈希。system prompt 若有,原样复制进 manifest。
6. 排除资源问题
```bash
dmesg -T | tail -50 | grep -iE 'oom|killed'
df -h
free -g
```
判断方法:超时集中在少数几个依赖多、构建慢的仓库,或并发升高时失败率同步上升,基本就是资源问题。把并发降到 1 重跑一小批做对照。
7. 统一统计口径
```bash
jq -r '[.instance_id, .resolved] | @tsv' results.jsonl | sort | uniq -c | awk '$1>1' | head
```
判断方法:同一个 instance 出现多行,说明多次采样被追加进同一个文件,按"有成功即算解决"来统计会虚高。清理后按 pass@1 与 pass@k 分别算清楚,报告里写明白报的是哪个。
8. 避免产物串味
每次跑写到新目录:
```bash
OUT=runs/$(date +%F-%H%M%S)
mkdir -p "$OUT"
```
跑之前确认目录为空,汇总脚本只读这一个目录。
都不管用时的兜底方案
1. 缩小战场。固定 30–50 个 instance 的子集,只比不同模型、不同配置之间的相对差距,不比绝对分数。相对结论受环境噪声影响小得多。
2. 双 harness 交叉验证。同一批 instance 换另一套开源 harness 再跑,两边结论一致才写进报告;不一致就说明问题在 harness 而不在模型。
3. 人工看轨迹。抽 10 条完整对话与工具调用记录,看模型是真的在定位代码、改代码、跑测试,还是照着题面猜测试断言。
4. 请对方复跑。把 manifest、完整日志、镜像 digest 整理成一个包,发给榜单维护方,请对方按你的 manifest 复跑一次,这样能把"环境差异"这个变量彻底排除。
5. 改报告写法。给区间不给单点:写清分母、失败数量、采样次数、温度、超时、排除规则,以及污染检查的结论。
如何预防再次发生
- 冻结 manifest。dataset commit、harness commit、镜像 digest、模型 id 与日期、温度、超时、并发、seed,全部写进一个文件,随结果一起提交。
- 先跑 smoke test。5 个左右的 instance 全绿再放开全量,能挡掉大部分环境问题。
- 把分母做成 CI 卡点。失败率超过阈值直接 fail,不产出分数,避免"跑完了但数字不可用"。
- 保留 trajectory 与原始日志。出问题时能回放,比事后猜强。
- 留 holdout。定期换一批新题做验证集,不参与调参和 prompt 迭代,这是发现污染的主要手段。
- 统一报告模板。pass@k、分母、排除规则、污染检查结论四栏必填。
- 版本别抄博客。镜像 tag、依赖版本、命令参数一律以官方文档当前版本为准,别人文章里的旧 tag 很容易把你带到坑里。
把上面这套走完,你得到的就不只是一个数字,而是一个"能被别人按同样步骤复现出来"的数字——后者才是体检报告,前者只是体温计读数。
