Zed 编辑预测评测样例解析:以 tree-sitter 的 if-let 改 match 重构为例理解 Zeta 评测格式
【免费下载链接】zedCode at the speed of thought – Zed is a high-performance, multiplayer code editor from the creators of Atom and Tree-sitter.项目地址: https://gitcode.com/GitHub_Trending/ze/zed
本文围绕 Zed 仓库中的编辑预测(edit prediction / Zeta)评测样例 tree-sitter--if-let-to-match.md 展开。你将看到这条"把 Rust 的if let重写为match"的真实重构是如何被拆解为编辑历史、光标摘录与多个可接受补丁的,以及ep评测 CLI 如何解析、重放并给模型输出打分——读完后你可以独立读懂并书写 Zed 编辑预测的 Markdown 评测样例,并将其纳入评测流水线运行。
一、这个文件在 Zed 评测体系中是什么
crates/edit_prediction_cli/evals/目录存放的是编辑预测模型的手写评测样例(eval examples),当前目录中共有 19 个.md样例,覆盖 tree-sitter、zed、flask、vscode 等多个上游仓库的典型编辑场景。文件名约定为<仓库>--<任务>.md,本文件即"在 tree-sitter 仓库上完成 if-let to match 重构"这一评测项。
这些 Markdown 文件不是给人读的技术文档,而是可直接被 CLI 消费的评测输入。在 Cargo.toml 中,edit_prediction_clicrate 声明了二进制ep([[bin]] name = "ep"),其入口 main.rs 定义了全局参数与子命令;当输入是.md文件时,读取链路如下:
read_example_files()(example.rs)按扩展名分派:json/jsonl/md,其中md走parse_markdown_example();parse_markdown_example()调用ExampleSpec::from_markdown()(example_spec.rs),用 pulldown_cmark 遍历 Markdown 事件流,按二级标题切分章节;- 识别的章节标题常量与本文档完全对应:
Edit History(EDIT_HISTORY_HEADING)、Cursor Position(CURSOR_POSITION_HEADING)、Expected Patch(EXPECTED_PATCH_HEADING),另支持Uncommitted Diff、Recently Opened Files、Rejected Patch等可选章节(见 example_spec.rs 的常量定义); Expected Patch下的每个```diff代码块都会追加为expected_patches中的一项——这正是本样例能给出三种可接受补丁的机制来源。
二、Front Matter:用 repository_url + revision 钉死外部仓库状态
文件开头是一个 TOML 前置块:
+++ repository_url = "git@github.com:tree-sitter/tree-sitter" revision = "17e3c7a5c56527a179fa6e37ce7ee934493e5047" +++ExampleSpec::from_markdown()会以+++为界剥离前置块,反序列化为FrontMatter结构(example_spec.rs):
| 字段 | 本样例取值 | 作用 |
|---|---|---|
repository_url | git@github.com:tree-sitter/tree-sitter | 评测代码来自哪个外部仓库(SSH 或 HTTP 形式均可,Example::repo_name()两种都能解析) |
revision | 17e3c7a5… | 精确到该仓库的某次提交,保证评测可复现 |
tags(可选) | 无 | 分类标签 |
uncommitted_diff_requires_edit_history_rollback(可选) | 无 | 控制 uncommitted diff 与编辑历史的回滚语义 |
评测运行时会依据这两个字段重建现场:load_project.rs 的setup_worktree()会把该仓库克隆到本地(或复用已有克隆并fetch指定 revision),再为样例创建一个独立的 git worktree 并checkout到钉死的 revision——这意味着本样例重放的是 tree-sitter 仓库在17e3c7a这个提交时的crates/loader/src/loader.rs,而非当前 Zed 仓库中的任何文件。
三、Edit History:重放"用户手动重构到一半"的现场
## Edit History章节是一个 unified diff,描述模型被调用之前用户在编辑器里已完成的编辑。本样例的编辑历史包含两个 hunk,内容是:
// 原始形态(删除行) if let Ok(entries) = fs::read_dir(parser_container_dir) { for entry in entries { ... } } // 目标形态(新增行,重构进行到一半) match fs::read_dir(parser_container_dir) { Ok(entries) => { for entry in entries { ... } // Ok 分支已展开 // ← 此处尚未补充 Err 分支 }即:用户在loader.rs里把if let Ok(entries) = fs::read_dir(...)逐行改写为match fs::read_dir(...) { Ok(entries) => { ... },Ok分支的语句体已经整体展开(可以看到内部find_language_configurations_at_path(...)调用的缩进也随 match 臂加深了一级),但Err分支还缺失——这正是等待模型续写的位置。
这个历史会被真实"重放"进缓冲区:run_load_project()中apply_edit_history()直接调用edit_prediction::udiff::apply_diff()(load_project.rs),把 diff 逐 hunk 应用到一个临时打开的 buffer 上;应用完成后,EditPredictionStore还会收集出本次会话的编辑事件序列(edit_history_for_project()),最终组装进Zeta2PromptInput作为模型的上下文事件流——也就是说,模型看到的不是静态 diff 文件,而是"用户刚刚做过的这些编辑"。
四、Cursor Position:光标摘录与^[CURSOR_POSITION]标记行
## Cursor Position章节的代码块语言标注为文件路径crates/loader/src/loader.rs(解析器会把它存入spec.cursor_path),块内是一段带光标标记的代码摘录:
if let Some(parser_dir_name) = entry.file_name().to_str() { if parser_dir_name.starts_with("tree-sitter-") { self.find_language_configurations_at_path( &parser_container_dir.join(parser_dir_name), false, ) .ok(); } // ^[CURSOR_POSITION] } } } } }标记约定由 example_spec.rs 的cursor_excerpt()精确定义:
- 标记行是紧随光标行之后的一行,包含字符串
[CURSOR_POSITION](CURSOR_POSITION_MARKER); - 行内第一个
^字符所在的列,就是光标在"标记行上一行"中的列偏移;若光标列早于注释前缀长度,则退化为<[CURSOR_POSITION]形式,取该行第一个非空白字符位置; - 另有一种内联标记
<|user_cursor|>(INLINE_CURSOR_MARKER,见 udiff.rs),把光标直接嵌在文本中间,解析时直接按内联偏移计算。
在本样例中,^指向.ok();所在行的行尾(即整个Ok(entries) => { ... }块结束、}闭合match之前),精确刻画了"用户敲完 Ok 臂、光标悬在等待补 Err 臂"的瞬间。
解析后load_project::cursor_position()(load_project.rs)还要做一件关键校验:用纯摘录文本在 buffer 全文中做match_indices,要求恰好匹配一次("More than one cursor position match found"会直接报错),然后把摘录偏移加上摘录内偏移,得到最终的Anchor光标锚点。这一"摘录必须唯一"的设计,保证了人工编写的 Cursor Position 摘录不能太短而撞车。
五、Expected Patch:一个样例,三种可接受答案
## Expected Patch下本样例给出了三个独立 diff 块,对应三种都算对的模型输出:
+ Err(error) => {}—— 用命名绑定error但空作用体丢弃;+ Err(_) => {}—— 用_显式忽略;- 多行未闭合形态:
+ Err(e) => { + + }光标落在空作用体内部——模拟"用户刚开括号、准备往里敲东西"的中间编辑态。三个补丁块中都出现了形如# ^[CURSOR_POSITION]的标记行,它标注的是补丁应用完之后光标应停在的位置(^的列对应新增文本中的列,例如第三个变体里光标落在空行上,即新Err臂作用体的开头)。ExampleSpec::expected_patches_with_cursor_positions()(example_spec.rs)会对每个补丁调用 udiff.rs 的extract_cursor_from_patch()恢复出(干净补丁, 期望光标偏移)二元组:逐行解析 diff,把内联光标标记从+行中剔除、并按上下文/新增行的累计字节数换算出光标在新文本中的绝对偏移。从源码结构看,#开头的标记行不属于标准 diff 内容行,评测工具链(如同文件的strip_diff_metadata())会将其与index/diff --git等元数据一并剥离,只保留可用于应用与比对的 hunk 内容行。
"多个 expected patch"的意义在于:真实编辑预测的正确答案天然不唯一。if let改match时Err臂的写法因人而异,若评测只认一种拼法,会把大量行为正确的预测误判为失败。打分时 score.rs 的run_scoring()会把每个 expected patch 都经edit_prediction_metrics::prepare_expected_patches()预演(在光标摘录原文上试应用)后,逐条调用score_prediction()与模型的actual_patch比对,多个期望答案共同构成该样例的"及格线"。
六、跑这条样例:ep CLI 的最小工作流
结合 main.rs 的Command枚举,针对单条 Markdown 样例的典型流程为:
# 1. 读取并校验样例(输出规范化 JSONL) ep read crates/edit_prediction_cli/evals/tree-sitter--if-let-to-match.md -o out.jsonl # 2. 为样例建 git worktree、重放编辑历史、解析光标、生成 prompt_inputs ep load-project out.jsonl -o out.jsonl # 3. 调模型生成预测(provider 字符串见 main.rs 的 PredictionProvider::from_str) ep predict out.jsonl --provider=zeta2 # 4. 或直接一步到位:预测 + 对 actual/expected 补丁打分 ep eval out.jsonl --provider=zeta2几个可复用的筛选与调参开关(均为EpArgs全局参数):
--name tree-sitter--if-let-to-match:按样例名子串过滤,只处理这一条;--repo tree-sitter:按repository_url子串过滤;--max-parallelism N(默认 10)、--limit N、--offset N:控制并发与取样窗口;--markdown(-m):输出改为每样例一个.md文件(命名取自样例名),方便回看评测结果;--failed=keep|skip|skip-no-files:失败样例在输出中的处理方式(失败样例无论如何都会落到 run 的failed/目录)。
predict阶段的 provider 字符串支持zeta2(默认)、zeta1、teacher:<backend>、teacher-jumps:<backend>等形态(解析逻辑见 main.rs,含sonnet45/sonnet46/gpt52/gpt54/gpt55等 teacher 后端别名)。
load-project这一步产出的prompt_inputs是评分的前置条件:run_load_project()在解析出光标后,会基于 buffer 快照计算光标摘录与语法范围(compute_cursor_excerpt/compute_syntax_ranges),连同EditPredictionStore中还原出的编辑事件,填入Zeta2PromptInput;score.rs 中明确提示prompt_inputs is required for scoring - run prediction first,即跳步运行eval/score会直接报错。
七、这条样例的设计意图小结
- 单一仓库 + 钉死 revision:Front Matter 把评测锚定在 tree-sitter 的
17e3c7a提交,编辑历史与光标摘录都出自该版本的crates/loader/src/loader.rs,ep通过 git worktree 机制现场重建,保证任何人任何时间跑出的输入状态一致; - 重构进行到一半的"半态":Edit History 刻意停在
match缺Err臂的位置,考察模型能否依据上下文(fs::read_dir返回Result<ReadDir, _>、既有Ok臂结构)补全惯用的错误分支,而非补全任何"看起来像"的语句; - 多可接受答案 + 期望光标:三个 expected patch 覆盖了
Err(_)、命名绑定与未闭合臂三种形态,且每处都标注了补丁后光标落点,使打分不仅看"改得对不对",还能看"改完后光标在不在合理位置"(对应ActualCursor与editable_region_offset的评分输入,见 example.rs)。
八、关键文件索引
| 文件 | 角色 |
|---|---|
| crates/edit_prediction_cli/evals/tree-sitter--if-let-to-match.md | 本文剖析的评测样例本体 |
| crates/edit_prediction/src/example_spec.rs | Markdown 样例的序列化/解析规范(from_markdown、cursor_excerpt、期望补丁光标提取) |
| crates/zeta_prompt/src/udiff.rs | 光标标记常量与补丁光标解码(extract_cursor_from_patch、strip_diff_metadata) |
| crates/edit_prediction_cli/src/example.rs | 样例读取(.md/.json/.jsonl)与Example数据模型 |
| crates/edit_prediction_cli/src/load_project.rs | worktree 重建、编辑历史重放、光标解析与 prompt 输入组装 |
| crates/edit_prediction_cli/src/score.rs | 期望补丁预演与打分入口 |
| crates/edit_prediction_cli/src/main.rs | epCLI 参数、子命令与 provider 解析 |
适用前提:以上命令与格式均基于当前仓库的edit_prediction_cli实现;ep是仓库内二进制,需在工作区中通过 Cargo 构建运行,且涉及外部仓库克隆(依赖可访问的 git 远端)与模型 provider 的相应凭证/配置。
【免费下载链接】zedCode at the speed of thought – Zed is a high-performance, multiplayer code editor from the creators of Atom and Tree-sitter.项目地址: https://gitcode.com/GitHub_Trending/ze/zed
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考