编码智能体的上下文工程:ContextBench 到底测了什么,以及为何「过度检索」必然失败
💡 工具推荐:AI Token 计数器、AI 代码解释、AI 提示词模板
ContextBench 由南京大学与伦敦大学学院的研究者于 2026 年 2 月 11 日提交,它问了一个多数智能体团队靠「碰运气」回答的问题:智能体到底用没用上它检索到的上下文?该基准覆盖 66 个仓库上的 1,136 个任务,结论并不好看:顶尖模型追逐召回率而牺牲精度,检索得越多,噪声越多;智能体常常检视了正确的代码,却没能把它用进最终修改,因为「检索到」不等于「用上了」;而更均衡的检索策略反而以更低成本取得更强的 Pass@1。如果你在做编码智能体,这是今年最实用的一篇论文。
注意力是有限预算
一、ContextBench 究竟测了什么
多数编码基准只评结果:测试过没过。ContextBench 评的是流水线中段。它把「智能体是否检索到相关代码」与「它是否在最终修改中用到这些代码」分开评估,而这正是在一次失败运行里最容易丢失的区分。基准覆盖 66 个仓库、1,136 个任务,因此结论不是某个代码库或某种语言的偶然产物。
# retrieve.py - over-retrieve wide, then buy precision with a re-ranker
RETRIEVE_K = 40 # cheap recall
FINAL_K = 8 # expensive precision
def build_context(query, repo_index, reranker):
candidates = repo_index.search(query, k=RETRIEVE_K) # recall stage
ranked = reranker.score(query, candidates) # precision stage
return ranked[:FINAL_K]二、召回便宜,精度昂贵
第一个发现是行为层面的:模型偏爱召回而非精度。面对不确定性,智能体会检索更多文件、更多符号、更多历史。多出来的每一段内容单看都很安全,累积起来却有腐蚀性——模型的注意力预算有限,每一个无关 token 都在与相关 token 竞争。检索得越多,引入的噪声就越多,而正是这些噪声把一次自信的修改变成一次看起来合理的错误。
# budget.py - the context window is a budget with a ceiling
TOKEN_BUDGET = {
"system": 800,
"task": 600,
"code": 6000, # the part that actually fixes the bug
"examples": 1200,
"reserve": 800, # room for the model to reason
}
def fits(section_tokens):
return sum(section_tokens.values()) <= 9000
def enforce(chunks, cap):
kept, used = [], 0
for c in chunks: # already sorted by relevance
if used + c.tokens > cap:
break
kept.append(c); used += c.tokens
return kept三、检索到 ≠ 用得上
第二个发现最容易被团队低估:智能体常常检视了正确的代码,却没能把它用上。一个文件可以读进上下文窗口,却依然被最终 diff 忽略,因为模型从未把它和正在做的修改关联起来。这也是为什么通过率基准看起来还行、底层检索却已悄悄坏掉:检索失败与利用失败这两类不同的失败,被扔进了同一个桶里。
# filter.py - drop chunks that fail a relevance floor
MIN_RELEVANCE = 0.35
def relevance_filter(chunks, floor=MIN_RELEVANCE):
return [c for c in chunks if c.score >= floor]
# ContextBench lesson: retrieved is not utilised.
# A chunk that scores below the floor is not context, it is noise,
# and it will compete for attention with the chunk that matters.检索到,不等于用得上
四、上下文预算是设计约束
实用结论是:把上下文窗口当成有硬顶的预算,而不是随便填的桶。一个常见的生产模式照着论文的教训来:先用较宽的 k 过度检索,再重排收敛到一个很小的最终集合。代码示例 1 用 k=40 生成候选,再重排到前 8 条——把廉价的召回收益收下,同时让重排器而不是模型来支付精度的代价。
# rerank.py - a small model that decides what the big model sees
def rerank(query, candidates, cross_encoder, top_k=8):
pairs = [(query, c.text) for c in candidates]
scores = cross_encoder.predict(pairs) # one forward pass per pair
ordered = sorted(zip(candidates, scores),
key=lambda pair: pair[1], reverse=True)
return [c for c, s in ordered[:top_k]]五、一条可落地的上下文流水线
把它拆成五个显式阶段:先规划任务需要哪些信息,再宽口径检索,然后按与本次修改的相关性重排,接着在 token 预算内组装,最后校验你留下的内容是否真的被改动引用。代码示例 2 让预算保持诚实;代码示例 3 丢弃未过相关性门槛的片段;代码示例 4 展示重排步骤;代码示例 5 把整条链路变成可度量的评估,而不是凭感觉。
# eval.py - does the context pipeline actually help?
def context_eval(runs):
retrieved, used, cost = 0, 0, 0
for r in runs:
retrieved += r["chunks_retrieved"]
used += r["chunks_in_final_diff"]
cost += r["prompt_tokens"]
return {
"utilisation_rate": used / max(retrieved, 1), # the number to track
"avg_prompt_tokens": cost / max(len(runs), 1),
"pass_at_1": sum(r["tests_pass"] for r in runs) / max(len(runs), 1),
}六、度量你自己的上下文
别迷信基准,复制它的方法。为每次智能体运行记录:检索了多少片段、过了重排还剩多少、最终 diff 里出现了多少、花了多少 token。被使用的片段与检索到的片段之比,才是预测质量的数字,也几乎没人跟踪它。一旦看见它,上下文工程就不再是提示词小把戏,而变成普通的系统工程。
七、窗口再大也修不好
百万 token 的上下文窗口会诱出这门手艺里最贵的错误:把整个仓库倒进去,指望注意力自己理出头绪。它不会。注意力是有限且近似零和的资源,你加进去的每个 token 都在和真正要紧的那个 token 抢位置;而模型「找到相关文本」的能力,明显强于「忽略无关文本」的能力。正是这种不对称,让均衡的检索策略在容量充裕时依然在基准中取胜。窗口是天花板,不是策略。把容量当成「可以检索得更好」的许可,而不是「可以全都检索」的借口。
八、上线前清单
编码智能体上线前有五项检查。一:每一段被检索的内容都有存在的理由,还是只是习惯?二:检索窗口是按度量定的,还是按模型上限定的?三:你有没有记录多少片段过了重排、多少出现在最终 diff 里?四:你的评估量的是利用率,还是只看通过率?五:当一次运行失败时,你能分辨是检索失败还是利用失败吗?这五问答上了,你拥有的是一条上下文流水线,而不是一段偶尔管用的提示词;答不上,你拥有的是一个连失败都解释不清的系统。
先重排,再为 token 买单
📌 常见问题 FAQ
ContextBench 是什么?
一个面向编码智能体上下文检索的基准,由南京大学与伦敦大学学院于 2026 年 2 月 11 日提交,覆盖 66 个仓库上的 1,136 个任务。
它的核心结论是什么?
模型重召回轻精度;检索到的上下文常常没被用上;更均衡的检索策略以更低成本取得更强的 Pass@1。
「检索到 ≠ 用得上」是什么意思?
智能体常常在检索时找到了正确代码,却没把它并入最终修改,因此单看检索准确率并不能预测成功。
检索窗口该开多大?
常见生产做法是先用 20 到 40 的 k 过度检索,再重排收敛到前 8 条左右,收下召回收益、控制精度代价。
上下文工程就是提示词工程吗?
不是。提示词工程雕琢指令;上下文工程决定模型每次调用看到哪些信息,这是系统问题,而不是措辞问题。