Zed 编辑预测评测样本解读:用 zed--add-eprintln 评测剖析 Zeta 预测模型的"保守预测"与光标定位能力
本文以仓库中的编辑预测(Edit Prediction)评测样本 zed--add-eprintln.md 为核心,逐段拆解该评测文件的 front matter、编辑历史、光标位置与期望补丁四个部分,并结合 ExampleSpec 的 Markdown 解析器 与 游标标记定义 等源码,说明 Zed 是如何构造、解析和验证一条"预测下一步编辑"评测数据的。读完本文,你可以理解 Zed 编辑预测评测集的文件格式规范、[CURSOR_POSITION] 标记的解析机制,以及 ep 命令行工具如何加载并执行这类评测。
评测样本在 Zed 编辑预测体系中的位置
Zed 内置了"编辑预测"(代码续写)功能,其模型与评测数据由独立的 crate 支撑:edit_prediction 负责预测存储与数据收集,edit_prediction_ui 负责预测展示 UI,而 edit_prediction_cli 则提供了一个名为 ep 的命令行工具(见 Cargo.toml 中 [[bin]] name = "ep"),用于对编辑预测数据集做读取、加载、上下文检索、提示词生成、预测、打分与评估。
crates/edit_prediction_cli/evals/ 目录下存放的 Markdown 文件就是一组手工精选的评测样本,本文的主角 zed--add-eprintln.md 就是其中之一。与目录中其他样本(如 tree-sitter--tuple-to-struct-definition.md、vscode--add-async-and-await.md)一样,它固定了被测仓库(Zed 自身)、固定了 git revision(b7090c9fae7390a82021b994994c0f587744d96c),从而保证评测结果可复现。
该样本开头的一句话说明它考察的能力:
This example shows the model's preference for making conservative predictions, and ability to place the cursor within the predicted output.
即:用户只敲了三个字母 epr,一个"保守"(conservative)的模型应当只预测出唯一确定的最短补全 eprintln!("");,而不会凭空编造要打印什么内容;同时模型还需要能把光标放在预测输出内部(引号内、分号前)的正确位置。
评测文件结构逐段解析
下面把 zed--add-eprintln.md 的完整内容逐段展开,并对照解析器源码说明每一段的语义。
1. Front matter:锁定仓库与版本
+++
repository_url = "git@github.com:zed-industries/zed"
revision = "b7090c9fae7390a82021b994994c0f587744d96c"
+++
评测文件以 +++ 包裹的 TOML front matter 开头,声明被测仓库地址与精确 revision。源码中 ExampleSpec::from_markdown 会先剥离该段,用 toml::from_str::<FrontMatter> 解析出 repository_url、revision、可选的 tags 以及 uncommitted_diff_requires_edit_history_rollback 标志。后续 ep load-project 阶段会依据这些信息创建 git worktree,把被测仓库检出到指定 revision,从而还原当时的文件内容。
ep 命令行也依赖 repository_url 的形式:Example::repo_name 能同时解析 git@github.com:owner/repo 与 https://github.com/owner/repo 两种写法,并把 worktree 组织到 WORKTREES_DIR/owner/repo 路径下。
2. Edit History:用户最近一次的编辑
--- a/crates/edit_prediction_ui/src/rate_prediction_modal.rs
+++ b/crates/edit_prediction_ui/src/rate_prediction_modal.rs
@@ -144,7 +144,7 @@
fn select_next_edit(&mut self, _: &NextEdit, _: &mut Window, cx: &mut Context<Self>) {
+ epr
let next_index = self
.ep_store
.read(cx)
"Edit History" 段给出触发预测前的编辑历史(unified diff 形式)。本例中用户刚刚在 select_next_edit 函数体内插入了一行 epr——这显然是一个 Rust 宏名的输入前缀。
解析器对该段的处理见 from_markdown 中的 Section::EditHistory 分支:代码块内容被原样拼入 edit_history 字段;若两个 diff 块之间出现 // User accepted prediction: 这样的注释行,解析器会将其标记到历史中(表示这一笔编辑本身是用户对先前预测的接受,供模型学习"预测被接受后用户如何继续编辑"的模式)。
值得一提的是,rate_prediction_modal.rs 正是当前仓库中存在的文件 rate_prediction_modal.rs。评测钉住的 revision 中该方法调用的是 shown_predictions(),而当前版本(L159-L166)已演进为 rateable_predictions()——这恰好说明为什么评测文件必须固定 revision,而不能直接引用"最新代码"。
3. Cursor Position:光标位置与 [CURSOR_POSITION] 标记
fn select_next_edit(&mut self, _: &NextEdit, _: &mut Window, cx: &mut Context<Self>) {
epr
// ^[CURSOR_POSITION]
let next_index = self
.ep_store
.read(cx)
.shown_predictions()
.skip(self.selected_index)
.enumerate()
.skip(1) // Skip straight to the next item
"Cursor Position" 段的 fenced code block 的 info string 是光标所在文件的路径,块内容是光标附近的代码摘录,光标本身用一行注释标记。
标记语法有严格的定义,见 ExampleSpec::cursor_excerpt 的文档注释:
- 标记行必须位于光标行的下一行,且包含
[CURSOR_POSITION]字样; - 行内必须有一个
^或<:^:光标列就是^字符所在的列(指向上一行光标);<:光标列是该行第一个非空白字符的列(用于光标位于行首、^会被注释前缀挤偏的情况)。
本例中 // ^[CURSOR_POSITION] 的 ^ 位于第 8 列,正好对准上一行 epr 末尾之后,即光标停在 epr 之后、尚未补全的位置。
解析实现上,cursor_excerpt() 会先查找内联标记 <|user_cursor|>,否则定位 [CURSOR_POSITION] 标记行,从 ^/< 计算出列号,最后剥掉标记行,返回"干净摘录 + 摘录内光标字节偏移"二元组(L436-L472)。这两个常量在 zeta_prompt/src/udiff.rs 中定义:
pub const CURSOR_POSITION_MARKER: &str = "[CURSOR_POSITION]";
pub const INLINE_CURSOR_MARKER: &str = "<|user_cursor|>";
该 crate 内的单元测试(test_cursor_excerpt_with_caret)覆盖了 ^ 标记在行中间、行首 < 标记、行尾、文件末尾无换行等多种边界,与评测文件中手写的标记必须完全一致。
4. Expected Patch:期望的预测输出(含补丁内光标)
--- a/crates/edit_prediction_ui/src/rate_prediction_modal.rs
+++ b/crates/edit_prediction_ui/src/rate_prediction_modal.rs
@@ -144,14 +144,14 @@
fn select_next_edit(&mut self, _: &NextEdit, _: &mut Window, cx: &mut Context<Self>) {
- epr
+ eprintln!("");
# ^[CURSOR_POSITION]
let next_index = self
.ep_store
.read(cx)
.shown_predictions()
.skip(self.selected_index)
.enumerate()
.skip(1) // Skip straight to the next item
"Expected Patch" 段是标准答案:模型应当输出一行替换,把 epr 补全为 eprintln!("");,并把光标放到空字符串引号内(^ 对准第一个引号后、第二个引号前)。这体现了两点:
- 保守性:不猜测要打印的内容,只补全到语法上自洽的最短形式
eprintln!("");; - 光标落在预测文本内部:续写完成后光标停在字符串内,用户可以直接键入要打印的内容——这正是文档开头所说 "ability to place the cursor within the predicted output"。
这里的光光标编码方式与 Cursor Position 段不同:在补丁里,光标被编码为新增行(+ 行)内的内联标记。序列化格式中该标记是 <|user_cursor|>,插入到期望补丁的新增行内;expected_patches_with_cursor_positions 会调用 extract_cursor_from_patch 将补丁还原为 (clean_patch, cursor_offset),其中 cursor_offset 是相对补丁 hunk 起始处、在新文本坐标系中的字节偏移。反向的 encode_cursor_in_patch 则保证幂等(见测试 test_encode_cursor_in_patch_is_idempotent)。
注意评测文件里期望补丁下方也使用了注释风格的 # ... ^[CURSOR_POSITION] 行——这是给人看的可视化表示;机器可读的光标位置最终以内联 <|user_cursor|> 标记形式存在于补丁文本中,打分时以 expected_patches_with_cursor_positions() 的解码结果为准。
5. 评分环节
ep eval / ep score 会对比"模型实际补丁"与"期望补丁",打分逻辑位于 edit_prediction_metrics crate(ep 的 Cargo.toml 依赖其 tree-sitter feature),并回填到 Example 结构的 score 字段;对光标位置,则使用 ActualCursor 记录模型预测落地后的 path/row/column/offset,与期望光标偏移做对齐比较。其中 ActualCursor::from_editable_region 还专门做了防御性钳位——异常光标偏移只会被收敛到合法字符边界,而不会 panic 中断整批昂贵的评测运行。
如何加载与运行这条评测
ep 的输入接受 .md、.json、.jsonl 三种格式,.md 文件走 read_example_files 中的 "md" 分支,调用 parse_markdown_example 解析为 Example;若文件内未指定 name,则以**文件名(去扩展名)**作为样本名——因此该评测样本在输出中被记录为 zed--add-eprintln。
典型用法(在仓库根目录执行,属于只读查看与运行评测,不修改仓库内容):
# 读取评测样本,输出规范化 JSONL
cargo run -p edit_prediction_cli -- read crates/edit_prediction_cli/evals/zed--add-eprintln.md -o /tmp/ep-example.jsonl
# 为该样本创建 git worktree 并加载文件内容(需要能访问被测仓库)
cargo run -p edit_prediction_cli -- load-project crates/edit_prediction_cli/evals/zed--add-eprintln.md -o /tmp/ep-example.jsonl
# 生成提示词 / 运行预测 / 打分(provider 可选 mercury、zeta1、zeta2:<format>、teacher:<backend> 等)
cargo run -p edit_prediction_cli -- format-prompt --provider=zeta2 /tmp/ep-example.jsonl -o /tmp/ep-prompt.jsonl
cargo run -p edit_prediction_cli -- predict /tmp/ep-prompt.jsonl -o /tmp/ep-predict.jsonl
cargo run -p edit_prediction_cli -- score /tmp/ep-predict.jsonl -o /tmp/ep-score.jsonl
# 一键评估(预测 + 聚合分数)
cargo run -p edit_prediction_cli -- eval crates/edit_prediction_cli/evals/zed--add-eprintln.md
全局参数(EpArgs)包括:--max-parallelism(默认 10)、--limit、--offset、--name/--repo(按名称/仓库过滤)、--max-duplicates(基于 MinHash 近似 Jaccard 相似度的光标位置去重)、--in-place、--failfast、--failed(失败样本在输出中保留或跳过)以及 --markdown(把结果写成每样本一个 .md 文件,格式即本文拆解的这套 Markdown 规范——这正是 to_markdown 的逆过程)。provider 字符串的解析规则与可选后端见 PredictionProvider::from_str。
小结
zed--add-eprintln.md 虽只有 50 余行,却是理解 Zed 编辑预测评测体系的理想入口:
- 格式规范:
+++TOML front matter(仓库 + revision)→ Edit History diff → Cursor Position(fenced 块 info string 为光标文件路径)→ Expected Patch(可多个),每一段都对应 ExampleSpec 中的一个字段; - 光标双编码:代码摘录用
[CURSOR_POSITION]标记行 +^/<列指示符,补丁内用<|user_cursor|>内联标记,两套编码在 zeta_prompt/src/udiff.rs 中统一定义; - 能力考察点:模型对不完整前缀(
epr)应给出保守且唯一确定的补全(eprintln!("");),并把光标精确放置到预测文本内部的语义位置; - 可复现性:评测钉死 git revision,
ep load-project据此建 worktree 还原上下文,ep predict/score/eval构成完整的预测—验证闭环。
如果你在扩展评测集,新增样本时只要遵循同样的四段式结构、保证标记行列对齐规则(可用 cargo test -p edit_prediction example_spec 中的光标摘录测试作为行为参照),ep 工具链即可无缝加载并参与后续预测与打分。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00