Zed 编辑预测评测样例(evals)格式深度解析:从 hello-world 改名样例看epCLI 的数据规范
【免费下载链接】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
本文以 hello-world--rename-accepted-group-by.md 这一评测样例文件为主线,完整拆解 Zed 编辑预测(Edit Prediction / Zeta)评测数据的 Markdown 格式:TOML front matter、Edit History 中的"用户接受预测"标记、[CURSOR_POSITION]光标注释的列定位语义,以及 Expected Patch 的游标编码方式;并结合 example_spec.rs 与 example.rs 的解析实现,说明如何用 edit_prediction_cli 的ep命令驱动这条评测链路。读完后你可以独立编写、阅读和运行 Zed 的编辑预测评测样例。
1. 这个样例文件在仓库中的位置与作用
该文件位于 crates/edit_prediction_cli/evals/ 目录下,是 Zed 编辑预测模型的评测样例之一。同一目录中还有来自 flask、tree-sitter、vscode、zed 等仓库的样例(如 flask--rename-accepted-prediction.md、tree-sitter--tuple-to-struct-definition.md),命名遵循仓库名--场景描述.md的约定。
文件名本身就是样例名:解析入口read_example_files(example.rs)在读入.md文件后,若spec.name为空,就直接取文件主名(去掉扩展名的部分)作为样例名:
"md" => { let mut example = parse_markdown_example(&content).unwrap(); if example.spec.name.is_empty() { example.spec.name = filename; } examples.push(example); }也就是说,hello-world--rename-accepted-group-by这个文件名精确表达了该样例测试的语义:在 hello-world 仓库中,用户先接受了一次自动补全,随后对补全出的函数做改名,模型应当能预测出"连带修改函数体内引用"的完整重命名。
2. 样例全文结构:front matter、编辑历史、光标与期望补丁
下面逐段解析该样例文件的真实内容(与仓库文件完全一致)。
2.1 TOML front matter:锚定到确定性的仓库状态
+++ repository_url = "https://github.com/octocat/hello-world" revision = "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d" +++front matter 用+++定界,内部是 TOML。解析器ExampleSpec::from_markdown(example_spec.rs)会先strip_prefix("+++\n")取出 front matter 并用toml::from_str::<FrontMatter>反序列化,可识别的字段包括:
| 字段 | 含义 |
|---|---|
repository_url | 样例所锚定的仓库地址。从源码结构看,repo_name()(example.rs)会同时解析git@github.com:owner/repo.git与https://github.com/owner/repo.git两种形式,并据此推导 worktree 缓存路径(owner/repo) |
revision | 固定到某个提交哈希,保证评测可复现 |
tags | 可选标签数组(可选字段,省略时为空) |
uncommitted_diff_requires_edit_history_rollback | 可选布尔值,控制加载Uncommitted Diff章节时是否需要回滚编辑历史(可选字段,省略时为 false) |
该样例只使用了repository_url与revision两个必填字段。
2.2 Edit History:三段 diff 讲述"手写 → 接受预测 → 改名"的完整故事
样例的## Edit History章节包含三段```diff代码块,其中第二段前有一行特殊注释:
--- a/README +++ b/README @@ -1,1 +1,6 @@ -Hello World! +function filterByStatus(items, status) { + return items.filter(item => item.status === status); +} + +function groupBy +// User accepted prediction:
--- a/README +++ b/README @@ -4,3 +4,9 @@ -function groupBy +function groupByStatus(items) { + return items.reduce((groups, item) => { + const key = item.status; + (groups[key] = groups[key] || []).push(item); + return groups; + }, {}); +}--- a/README +++ b/README @@ -4,4 +4,4 @@ -function groupByStatus(items) { +function groupByCat(items) { return items.reduce((groups, item) => {三段 diff 的语义分别是:
- 手写编辑:用户把 README 的
Hello World!替换为 JavaScript 代码,并手敲了function groupBy(未写完); - 被接受的自动补全:
// User accepted prediction:注释标记的 diff,表示模型补全了groupByStatus的完整实现,且用户接受了这次预测——这正是样例名中rename-accepted的来源; - 后续改名:用户把接受的补全改名为
groupByCat(一个被截断的名字),触发本次评测:模型此时应预测出把名字补全为groupByCategory、并同步修改函数体内item.status→item.category的编辑。
// User accepted prediction:这一行不是给人看的普通注释,而是解析协议的一部分。from_markdown中定义了常量:
const ACCEPTED_PREDICTION_MARKER: &str = "// User accepted prediction:";(example_spec.rs)。解析时,如果某个 diff 代码块之前紧跟该行文本,解析器会把该行标记重新拼回edit_history字符串中(example_spec.rs),保证序列化/反序列化往返一致。同文件内的测试test_from_markdown_accepted_prediction_marker(example_spec.rs)验证了:三段 diff 均被保留、标记恰好出现一次、且只出现在第二段之前。
2.3 Cursor Position:#...^[CURSOR_POSITION]注释标记光标
```README function filterByStatus(items, status) { return items.filter(item => item.status === status); } function groupByCat(items) { # ^[CURSOR_POSITION] return items.reduce((groups, item) => { const key = item.status; (groups[key] = groups[key] || []).push(item); return groups; }, {}); }`Cursor Position` 章节的代码块**信息串(info string)就是光标所在文件路径**(这里是 `README`),块内容是光标所在文件的节选。解析逻辑见 [example_spec.rs](https://link.gitcode.com/i/fc6b88b3a6aef362cd369f3179c433fd#L393-L396):`cursor_path` 取代码块 info string,`cursor_position` 取块内全文。 光标位置用一条注释行表达,其解析规则([example_spec.rs](https://link.gitcode.com/i/fc6b88b3a6aef362cd369f3179c433fd#L417-L473) 的 `cursor_excerpt` 文档注释与实现)为: - 注释行必须包含 `[CURSOR_POSITION]`(常量 `CURSOR_POSITION_MARKER`,定义于 [udiff.rs](https://link.gitcode.com/i/e59b52bed422861287f61c0e75c650c6)),位于光标所在行的**下一行**; - `^`:光标列 = `^` 字符在该行的列位置(指向上方的光标); - `<`:光标列 = 该行第一个非空白字符所在列(用于注释前缀较长、`^` 会越界到注释符内部的情况); - 此外还支持内联标记 `<|user_cursor|>`(常量 `INLINE_CURSOR_MARKER`,同样定义于 [udiff.rs](https://link.gitcode.com/i/e59b52bed422861287f61c0e75c650c6)),直接写在光标字节偏移处,例如 `let x = <|user_cursor|>42;`,解析时直接剥掉标记并返回其偏移量。 在本样例中,`#` 后的 `^` 指向 `function groupByCat(items) {` 中 `Cat` 之后的位置——即用户刚敲完 `Cat`、等待模型把名字补全为 `Category` 的时刻。测试 `test_cursor_excerpt_with_caret`([example_spec.rs](https://link.gitcode.com/i/fc6b88b3a6aef362cd369f3179c433fd#L521-L657))覆盖了 `^` 与 `<` 两种形式在行首、行中、行尾及文件末尾(无换行)的往返一致性。 注意解析器强制要求该章节存在,缺失会直接报错: ```rust if spec.cursor_path.as_ref() == Path::new("") || spec.cursor_position.is_empty() { anyhow::bail!("Missing cursor position codeblock"); }(example_spec.rs)
2.4 Expected Patch:期望补丁里也编码了光标
--- a/README +++ b/README @@ -5,7 +5,7 @@ -function groupByCat(items) { +function groupByCategory(items) { # ^[CURSOR_POSITION] return items.reduce((groups, item) => { - const key = item.status; + const key = item.category; (groups[key] = groups[key] || []).push(item); return groups; }, {});这是评测的"标准答案":模型应当输出的 unified diff。两个要点:
- 期望补丁包含连带修改(
item.status→item.category),这正是"改名的正确预测"与"只改名不改引用"的分界线,也是该样例的评测价值所在; - 补丁中同样有一行
#...^[CURSOR_POSITION]标记,说明编辑完成后光标应落在哪里(groupByCategory的Category末尾)。ExampleSpec提供expected_patches_with_cursor_positions/set_expected_patches_with_cursor_positions(example_spec.rs)成对地提取/写回光标偏移,底层由extract_cursor_from_patch/encode_cursor_in_patch实现。源码注释说明:在样例的序列化表示中,编辑后的光标位置用"新增 diff 行内的内联标记"表达,偏移量是相对 hunk 起点、在新文本中的字节偏移。
此外ExampleSpec还支持一个可选的## Rejected Patch章节(example_spec.rs),用于保存"用户拒绝的预测",对应训练中的 DPO 负样本(rejected_patch字段)。本样例未使用该章节——它考察的是正向预测,而非拒绝场景。
3. 解析链路:从.md文件到内存中的Example
把样例格式与解析实现对照起来,整条链路如下:
- CLI 读入:
read_example_files(example.rs)按扩展名分派——.md走parse_markdown_example,.json走 serde 直接反序列化Example,.jsonl逐行反序列化;输入为-时从 stdin 读取。 - Markdown 解析:
ExampleSpec::from_markdown(example_spec.rs)基于pulldown_cmark事件流,识别 H1(样例名)与七种 H2 章节标题(大小写不敏感):Uncommitted Diff、Recently Opened Files、Recently Viewed Files、Edit History、Cursor Position、Expected Patch、Rejected Patch,并处理 accepted-prediction 标记的回写。本样例使用了其中Edit History、Cursor Position、Expected Patch三个章节。 - 内存结构:
Example(example.rs)=ExampleSpec(打平内嵌)+prompt_inputs(Zeta2 提示输入)+prompt(模型输入/期望输出,含 DPO 用的rejected_output)+predictions(实际预测列表,含actual_patch、actual_cursor、logprob 等)+score+qa结果。前四个运行时字段在磁盘样例文件中不存在,由预测/评分流程填充。
ExampleSpec的完整字段列表(example_spec.rs)还包括tags、reasoning、uncommitted_diff、recently_opened_files、recently_viewed_files、telemetry(来自生产遥测的拒绝预测元数据:request_id、rejection_reason等)、human_feedback与rating,覆盖了遥测捕获、用户评分等多种样例来源。
4. 如何运行这个样例
评测样例由 edit_prediction_cli 提供的ep命令驱动(二进制名在 Cargo.toml 中定义为ep)。
4.1 命令与全局参数
ep使用 clap 定义,全局参数(main.rs)包括:
--max-parallelism(默认 10)、--group-by-repo(同一仓库的样例集中处理)、--limit/--offset;--name:按样例名过滤,正好可以单独选中hello-world--rename-accepted-group-by;--repo:按仓库 URL 过滤;- 输入
inputs:支持普通文件路径(.md/.json/.jsonl)以及captured-after:/rejected-after:/settled-after:/rated-after:等从 Snowflake 拉取遥测样例的特殊说明符(见 main.rs 的INPUTS_HELP); -o/--output、--in-place、--failfast、--failed(keep/skip/skip-no-files);--markdown/-m:输出为 Markdown 而非 JSONL,每个样例写入一个.md文件——即evals/目录中这类文件的读写是同构的。
子命令包含predict、score、qa、distill、pull-examples、synthesize、split-commit等;--provider可选 Mercury、Zeta1、Zeta2、Baseten、Teacher 系列及 Repair(main.rs)。
4.2 针对本样例的最小用法
对应当前仓库结构,运行编辑预测评测的典型形式是把样例文件作为输入传给ep的子命令,例如:
# 对该样例跑一次编辑预测 cargo run -p edit_prediction_cli -- predict \ crates/edit_prediction_cli/evals/hello-world--rename-accepted-group-by.md # 用 --name 在整批样例中按名过滤 cargo run -p edit_prediction_cli -- score --name hello-world--rename-accepted-group-by \ crates/edit_prediction_cli/evals/ # 结果以 Markdown 形式写回(-o 指定目录) cargo run -p edit_prediction_cli -- predict --markdown \ -o /tmp/ep-out \ crates/edit_prediction_cli/evals/hello-world--rename-accepted-group-by.md其中score子命令复用PredictArgs(main.rs),用于把模型输出与Expected Patch对照打分;synthesize子命令的默认输出目录即crates/edit_prediction_cli/evals-generated(main.rs),用于从真实仓库提交中批量合成同类样例。另外,仓库还提供了一个独立的单元测试评测脚本 script/run-unit-evals,通过cargo nextest run --workspace --features unit-eval -E 'test(::eval_)'运行带unit-evalfeature 的eval_前缀单元测试(run-unit-evals),它面向的是各 crate 内嵌的单元级评测,与evals/目录这批 Markdown 样例分属两条评测通道。
5. 小结:这个样例教给模型的到底是什么
把各要素串起来,hello-world--rename-accepted-group-by构造了一条最小但完整的"上下文 → 行为"评测链:
- 上下文由 Edit History 给出:先有手写编辑,再有一次已被接受的预测(
// User accepted prediction:标记),说明模型此前给出的groupByStatus实现已成为当前文件状态的一部分; - 触发点由 Cursor Position 精确定位到
groupByCat这个名字的中间——用户改名的意图刚刚暴露; - 标准答案由 Expected Patch 给出:不仅把名字补全为
groupByCategory,还连带把函数体内item.status改为item.category,并在补丁中标注编辑完成后的光标位置。
从源码结构看,epCLI 的评分与 QA 流程(score、qa子命令及 score.rs、qa.rs)正是围绕"实际补丁 vsexpected_patches"这类字段展开的,而rejected_patch字段则为负样本(DPO)预留了同一格式通道。掌握本文介绍的 front matter、四个 H2 章节、光标注释(^/</<|user_cursor|>三种形式)与 accepted-prediction 标记,即可在该目录下编写新的评测样例,并用 example_spec.rs 中的往返测试方法验证其可解析性。
关键文件索引
| 文件 | 作用 |
|---|---|
| crates/edit_prediction_cli/evals/hello-world--rename-accepted-group-by.md | 本文剖析的评测样例 |
| crates/edit_prediction/src/example_spec.rs | ExampleSpec定义、Markdown 解析、光标标记语义与往返测试 |
| crates/edit_prediction_cli/src/example.rs | Example结构、多格式文件读入、仓库名解析 |
| crates/edit_prediction_cli/src/main.rs | epCLI 参数、子命令与 provider 定义 |
| crates/zeta_prompt/src/udiff.rs | [CURSOR_POSITION]与<\|user_cursor\|>常量及补丁光标编解码 |
| script/run-unit-evals | 单元级评测的 nextest 运行脚本 |
【免费下载链接】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),仅供参考