Qwen3-Coder 评测实践:ExecRepoBench 仓库级代码补全基准的环境搭建、多级掩码机制与端到端评估指南
【免费下载链接】Qwen3-CoderQwen3-Coder is the code version of Qwen3, the large language model series developed by Qwen team.项目地址: https://gitcode.com/GitHub_Trending/co/Qwen3-Coder
ExecRepoBench 是面向真实软件开发场景的仓库级(repository-level)代码补全基准,它从活跃的 Python 仓库中抽取 1.2K 个样本,并要求模型在跨文件依赖上下文中完成被掩码的代码片段,同时配套 Repo-Instruct 指令语料以提升开源大语言模型的代码补全能力。本文以 ExecRepoBench README 为骨架,结合 qwencoder-eval 评测目录下的真实实现(create_test_repo.py、eval.py、utils.py 等),完整讲解数据下载、评测环境准备、四类补全任务的构造原理,以及基于 vLLM 的生成与 pass@1/编辑相似度评估全流程。读完本文,你将能够在本仓库中复现 ExecRepoBench 的完整评测链路,并理解其 AST 多级掩码与跨文件上下文组织的底层设计。
ExecRepoBench 是什么:仓库级代码补全评测的核心思想
传统代码补全基准(如 HumanEval 的 FIM 变体)通常在单文件内构造任务,而真实开发中,一个函数往往依赖同仓库其他模块中定义的类、工具函数与配置。ExecRepoBench 的核心目标正是填补这一空白:它以多文件协作、存在复杂跨文件依赖的活跃 Python 仓库为素材,构建"前缀 + 被掩码片段 + 后缀"的补全样本,让模型在参考了仓库其他文件内容后才能正确恢复被掩码的代码。
根据 README 的 Dataset Summary,该工作同时贡献了两样东西:
- ExecRepoBench 基准:包含 1.2K 条来自活跃 Python 仓库的样本,用于评测仓库级代码补全;
- Repo-Instruct 指令语料:用于改善开源 LLM 在真实编程场景中的补全能力,与基准配套使用。
与基准配套的是基于抽象语法树(AST)的多级语法掩码方法:不是随机挖掉几行,而是按代码的逻辑单元(语句 statement、表达式 expression、函数 function/block)有策略地掩码,从而让评测任务覆盖不同粒度的补全难度。从源码看,这一思想在 prepare_multi_level_completion 中得到了完整实现(下文详述)。
数据下载与目录结构
获取 ExecRepoBench 数据集
README 给出了基于 Hugging Face + Git LFS 的下载方式:
git lfs install git clone https://huggingface.co/datasets/CSJianYang/ExecRepoBench数据集通过 Hugging Face 分发,因此需要先安装并启用 Git LFS。下载完成后,建议将数据目录与本仓库的评测脚本配合使用:本仓库的评测脚本默认从./ExecRepoBench、./repos/、./test_set/等相对路径读取仓库与测试集(见下文各脚本的默认参数),实际操作时请按需将数据集软链或拷贝到对应位置,或通过命令行参数显式指定路径。
评测脚本目录速览
本仓库中 ExecRepoBench 的完整评测实现位于qwencoder-eval/base/benchmarks/ExecRepoBench/,核心文件与职责如下:
| 文件 | 职责 |
|---|---|
| create_test_repo.py | 测试仓库环境创建、测试数据构造、正确性验证(四种 action 入口) |
| eval.sh | 一键评测入口:先生成再评估 |
| eval_models.sh | 带默认参数的评测调用示例 |
| eval.py | vLLM 推理生成 + 正确性/编辑相似度评估 |
| utils/prompt_template.py | 各模型家族的 FIM 提示模板 |
| utils/utils.py | 语言符号表、BM25、相关度排序、编辑相似度等工具 |
| create_environment.sh / test_repo_correctness.sh | 单仓库环境创建与正确性验证示例 |
环境准备:Miniconda 与测试仓库依赖隔离
ExecRepoBench 的正确性评估需要在隔离的 Python 环境中运行每个被测仓库的测试套件,因此环境准备是整个流程的第一步。README 给出的步骤为:
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh安装 Miniconda 后,通过 create_test_repo.py 的prepare_environmentsaction 批量创建环境:
export PATH=./miniconda3/envs/vllm/bin/:$PATH python create_test_repo.py --action "prepare_environments"从 prepare_test_repo_environment 的实现可以看到它实际做了四件事:
- 遍历
repo_root_path(默认./repos/)下每个仓库,跳过build目录与点开头目录; - 为每个仓库创建独立的 conda 环境:
conda create -p {env_dir}/envs/repo_{repo_name} python=3.9 -y; - 依次执行
pip install -r requirements.txt(若存在)与pip install -e .安装仓库本身及其依赖; - 统一安装评测依赖:
pytest pytest-cov pytest-benchmark coincidence cssutils docutils openpyxl hypothesis pycodestyle。
create_environment.sh 给出了单仓库(markdown-it-py)的等价手动流程,方便调试单个环境。每个仓库一个独立环境的隔离设计,保证了某个仓库的依赖不会污染其他仓库的测试结果,也避免了"评测环境错误"被误判为"模型补全错误"。
验证评测环境
环境创建完成后,用以下命令验证所有仓库能否真正跑通测试(即环境本身是否完整可用):
export PATH=./miniconda3/envs/vllm/bin/:$PATH python create_test_repo.py --action "verify_all_repo_correctness"该 action 对应 evaluate_all_correctness:读取test_set/test.jsonl(ground truth 掩码样本),逐条执行"还原中间代码 + 运行仓库测试",统计 pass@1。如果某仓库环境缺失依赖或仓库本身无法安装,此步会提前暴露问题。此外还提供了output_environmentsaction,可将每个仓库环境的pip freeze导出到{env_dir}/repo_requirements/,用于复现评测环境。
端到端评估:eval.sh 一键评测的完整拆解
标准评估命令
README 给出的核心评估命令如下:
ROOT_DIR="./ExecRepoBench" MODEL_NAME="qwen2.5-coder-base-7B" MODEL_DIR="./qwen/Qwen2.5-Coder-7B" INPUT_PATH="./test_set/exec_repo_bench.jsonl" OUTPUT_PATH="./results/${MODEL_NAME}/generation.jsonl" WORKERS=64 TP=1 EXTRA_ARGS="-max_context_tokens 8192 -max_tokens 32768 -max_generation_tokens 1024" bash ${ROOT_DIR}/eval.sh ${MODEL_NAME} ${MODEL_DIR} ${INPUT_PATH} ${OUTPUT_PATH} ${WORKERS} ${TP} "${EXTRA_ARGS}"其中eval.sh接受 7 个位置参数,逐一说明:
| 参数 | 含义 | 默认值 |
|---|---|---|
MODEL_NAME($1) | 模型名称标识,用于生成结果目录与提示模板匹配(如qwen2.5-coder-base-) | 空 |
MODEL_DIR($2) | 本地模型权重路径(Hugging Face 格式,含config.json) | 空 |
INPUT_PATH($3) | 测试集 JSONL 路径 | 空 |
OUTPUT_PATH($4) | 生成结果输出路径 | 空 |
WORKERS($5) | 评估阶段并行进程数 | 64 |
TP($6) | vLLM 张量并行 GPU 数 | 1 |
EXTRA_ARGS($7) | 透传给 eval.py 的额外生成参数 | 空 |
eval.sh 内部的两个阶段
查看 eval.sh 源码,它本质上是"生成 + 评估"两个阶段的封装,并用结果文件是否存在作为断点续跑的判据:
- 生成阶段:若
${OUTPUT_PATH}不存在,则执行:
python eval.py \ -model_name ${MODEL_NAME} -model_dir ${MODEL_DIR} \ -input_path ${INPUT_PATH} -output_path ${OUTPUT_PATH} \ -workers ${WORKERS} -generation_only \ -tensor_parallel_size ${TP} ${EXTRA_ARGS}- 评估阶段:若
${OUTPUT_PATH}.metric不存在,则执行:
python eval.py \ -model_name ${MODEL_NAME} -model_dir ${MODEL_DIR} \ -input_path ${INPUT_PATH} -output_path ${OUTPUT_PATH} \ -workers ${WORKERS} -evaluation_only这种设计意味着:生成结果一旦落盘,重复运行脚本不会重复推理;评测结果一旦产出,也不会重复执行测试,非常适合长时评测任务的中断恢复。eval_models.sh 则是把 README 中的示例命令固化为可直接运行的脚本。
eval.py 的关键参数
parse_args 定义了完整的参数集,除eval.sh透传的参数外,值得关注的有:
-context_order {far, close}:上下文文件的排列顺序,默认close(将相关度最高的文件放在最靠近被掩码文件的位置);-env_path(默认./repo/envs/)与-repo_dir(默认./repos):指向评测环境与仓库源码根目录;-max_context_tokens(默认 4096):跨文件上下文预算;-max_generation_tokens(默认 512):单条补全生成的最大 token 数;-max_tokens(默认 8192):总序列长度上限;-verbose:打印每条样本的执行细节。
generate_samples会读取模型config.json中的max_position_embeddings/n_positions作为模型支持的最大长度;若模型上限小于-max_tokens,会自动将max_tokens收敛到模型上限并把max_context_tokens设为其一半,避免越界(eval.py)。
四种补全任务与 AST 多级掩码机制(源码级解析)
任务类型分布
ExecRepoBench 的每个仓库样本按固定比例混合四类补全任务,这一逻辑在 create_samples 中体现(每仓库 100 个样本,i从 0 到 99):
| 样本序号 | 任务类型 | 掩码方式 | 说明 |
|---|---|---|---|
| i < 3 | Random Single-line Completion | 随机掩 1 行 | 单行级补全,最简单 |
| 3 ≤ i < 6 | Random Multi-line Completion | 随机掩 ≤10 行 | 连续多行补全 |
| 6 ≤ i < 9 | Random Span Completion | 随机掩 ≤30 字符 | 片段级补全,粒度最细 |
| i ≥ 9 | Grammar-based Completion | 基于 AST 掩码 | 按逻辑单元(block/statement/expression)掩码 |
create_prefix_suffix_code(create_test_repo.py)实现了前三种随机掩码:随机行掩码与随机字符掩码都会做边界处理,保证 prefix/suffix 的换行格式正确。生成的样本字段结构为:repo_name、file_name、prefix_code、middle_code、suffix_code、context_code(仓库内其他文件的完整内容)以及fill_type。
多级语法掩码:从 AST 到补全任务
第四类"Grammar-based Completion"是 ExecRepoBench 的方法核心,实现在 prepare_multi_level_completion:
- 用 tree-sitter 解析被掩码文件,得到 AST;
- 遍历 AST 收集 10 类节点:import、comment、return 语句、if 语句、for 语句、while 语句、class 定义、function 定义、赋值语句、表达式语句;
- 按配置的采样比例随机选择一类节点(如
IF_STATEMENT_TYPE: 0.15、FOR_STATEMENT_TYPE: 0.15、IMPORT_TYPE: 0.1、FUNCTION_TYPE: 0.1、COMMENT_TYPE: 0.05等),再从该类节点中随机挑一个作为掩码候选; - 80% 概率直接掩整个节点;若节点跨行过多(>10 行)或位于文件边界,则退化为掩节点内部的某个子节点;
- 依据节点的字节区间(
start_byte/end_byte)切出 prefix/middle/suffix 三段。
随后 create_samples 通过NODE_TYPE2LEVEL映射将 tree-sitter 节点类型归为三个逻辑层级:class_definition/function_definition/if_statement/for_statement/while_statement归为block,import_statement/return_statement归为statement,assignment/expression_statement/identifier/attribute等归为expression,最终形成grammar-based: block、grammar-based: statement、grammar-based: expression三类带难度的任务标签。
树节点遍历依赖 tree-sitter 的游标接口,实现见 traverse_tree。各语言的 AST 节点类型映射集中在 utils.py 的 language_symbols,覆盖 Python、Java、C++、C#、TypeScript、JavaScript、PHP,例如 Python 的if_statement对应IF_STATEMENT_TYPE,C++ 的preproc_include对应IMPORT_TYPE。这意味着多级掩码方法在原理上可推广到多语言,而当前基准版本落地在 Python 仓库上。
样本过滤规则
为保证任务质量,create_samples 会对生成的中间代码做过滤:长度需大于 10 字符且小于 512 字符、不能与已有样本重复、不能包含#(避免掩码注释行)。同时在 prepare_test_repo_data 中通过is_valid_file排除tests/、docs/、build/目录以及setup.py、evaluate_repo.py等文件,确保只有业务源码参与任务构造。
跨文件上下文组织:相关度排序与长度预算
仓库级补全的关键在于如何把"仓库上下文"塞进有限的模型窗口。ExecRepoBench 的上下文组织分三步:
相关度打分:utils.get_relevance 结合两类信号为每个仓库文件打分——(a)import 依赖:解析被掩码代码的 import 语句(见 extract_imports),凡是定义了这些模块的文件获得加分(过滤掉已安装的第三方包,用 is_installed_package 判断);(b)BM25 词法相似度:用 BM25 实现(k1=1.5,b=0.75)计算每个上下文文件与被掩码代码的相似度并归一化。两者相加得到最终相关度。
按相关度排序:
create_samples中对context_code按相关度降序排列(create_test_repo.py),并在推理时以-context_order close将相关度最高的文件放在最靠近被掩码文件的位置(eval.py),模仿人类开发者"参考最相关文件"的习惯。token 预算分配:在 get_prompt 中,先按
-max_context_tokens预算依次填入上下文文件(超预算的文件从右侧截断);随后在max_tokens - max_generation_tokens - max_context_tokens的剩余预算内,把被掩码文件的前缀从左侧截断、后缀从右侧截断(各占一半)。截断工具 truncate_prompt 基于 tokenizer 实现。
提示模板:面向不同模型家族的 FIM 格式适配
由于 ExecRepoBench 用于评测多种开源代码模型,get_prompt 根据model_name自动选择对应家族的 FIM 模板,模板定义集中在 prompt_template.py:
| 模型家族 | 匹配关键字 | 模板形式 |
|---|---|---|
| Qwen2.5-Coder(base) | qwen2.5-coder-base- | <\|repo_name\|>+<\|file_sep\|>上下文 +{context}<\|fim_prefix\|>{prefix}<\|fim_suffix\|>{suffix}<\|fim_middle\|> |
| CodeQwen1.5 / StarCoder | codeqwen1.5-base/starcoder- | <repo_name>/<file_sep>上下文 +<fim_prefix>/<fim_suffix>/<fim_middle> |
| StarCoder2 | starcoder2- | ## 文件标题式上下文 + suffix 逐行加#注释前缀 |
| DeepSeek-Coder | deepseek-coder | #文件标题式上下文 +<|tool▁calls▁begin|>/<|tool▁call▁begin|>/<|tool▁call▁end|>模板 |
指令模型(如qwen2.5-coder-C) | qwen2.5-coder-C | chat 模板:REPO_COMPLETE_TEMPLATE+SYSTEM_PROMPT,经apply_chat_template格式化 |
| CodeLLaMA / Granite / OpenCoder / Codestral / CodeGeeX / Yi-Coder / CodeGemma | 各自前缀 | ## 文件标题式上下文 + suffix 注释化的续写式 prompt |
其中 Qwen2.5-Coder 的仓库级 FIM 格式会在上下文部分注入<|repo_name|>与<|file_sep|>标记,让模型显式感知仓库边界;codeqwen-base还会为生成阶段设置<file_sep>作为停止符(eval.py),防止模型继续生成到下一个文件。
生成与评估:vLLM 推理、pass@1 与编辑相似度
推理生成
generate_samples 使用 vLLM 批量推理:SamplingParams(temperature=0.0, top_p=0.95, max_tokens=args.max_generation_tokens),即贪心解码(temperature=0 保证确定性)。vLLM 引擎通过worker_use_ray=True启动,tensor_parallel_size支持多 GPU 张量并行。每条样本生成结果以generated_middle_code字段写回并落盘为 JSONL。
正确性评估
评估阶段 的核心是evaluate_correctness:
- 将
prefix_code + generated_middle_code + suffix_code重组为完整文件; - 把仓库源码拷贝到临时目录(copy_src_to_dest),用生成的代码覆盖被掩码文件,避免污染原始仓库;
- 切换到该仓库的隔离 conda 环境(
{env_path}/repo_{repo_name}/bin),运行python evaluate_repo.py,超时上限 240 秒; - 返回码为 0 记 1.0 分,否则记 0.0 分并记录 stderr;超时同样记 0 分并注明原因。
并行评估通过multi_tasks_from_objs(utils.py)以-workers个进程分块执行,-chunk_size控制每个 worker 处理的样本数。
指标输出
最终在{OUTPUT_PATH}.metrics中输出:
- pass@1:正确还原(通过仓库测试)的样本占比;
- es(edit similarity):基于 fuzzywuzzy 的
fuzz.ratio(cal_edit_sim),对去除注释与空行的生成代码与 ground truth 计算编辑相似度,衡量"即使测试未通过,补全内容与参考答案的贴近程度"; - 按
fill_type分组的细分指标(Random Single-line/Multi-line/Span与grammar-based: expression/statement/block各自的 pass@1 与 es),并汇总生成 LaTeX 表格字符串,便于论文结果呈现(eval.py)。
评测流程小结与注意事项
将整条链路串起来,一次完整的 ExecRepoBench 评测分为四步:
- 下载数据:
git lfs install && git clone拉取数据集; - 安装 Miniconda 并准备环境:
create_test_repo.py --action "prepare_environments"为每个仓库创建隔离 conda 环境; - 验证环境:
create_test_repo.py --action "verify_all_repo_correctness"确认所有仓库测试可运行; - 评测模型:按 README 的 7 参数命令调用
eval.sh,得到generation.jsonl、generation.jsonl.results与generation.jsonl.metrics。
实操中需要注意:eval.sh默认从./ExecRepoBench、./qwen/、./test_set/等相对路径取数,运行前请将数据布局对齐脚本预期;prepare_environments要求先激活 vLLM 所在环境(export PATH=./miniconda3/envs/vllm/bin/:$PATH);评估阶段依赖每个仓库可安装且测试可运行,verify_all_repo_correctness是降低误判率的必要前置步骤;生成阶段使用贪心解码,若要复现论文中的多次采样请自行调整SamplingParams。
引用
若在你的研究或工程中使用了 ExecRepoBench 数据与方法,请按 README 的 Citation 引用原始论文:
@article{yang2024execrepobench, title={ExecRepoBench: Multi-level Executable Code Completion Evaluation}, author={Yang, Jian and Zhang, Jiajun and Yang, Jiaxi and Jin, Ke and Zhang, Lei and Peng, Qiyao and Deng, Ken and Miao, Yibo and Liu, Tianyu and Cui, Zeyu and others}, journal={arXiv preprint arXiv:2412.11990}, year={2024} }本篇指南对应的可运行脚本与全部源码证据均位于本仓库 qwencoder-eval/base/benchmarks/ExecRepoBench/ 目录,可直接对照阅读与复现。
【免费下载链接】Qwen3-CoderQwen3-Coder is the code version of Qwen3, the large language model series developed by Qwen team.项目地址: https://gitcode.com/GitHub_Trending/co/Qwen3-Coder
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考