Zed 编辑预测评测样本详解:以 Flask 测试函数补全样本为例掌握 edit prediction example 格式
这篇技术指南以 Zed 仓库中一个真实的编辑预测(Edit Prediction,即 Zeta)评测样本 flask--add-and-rename-test-function.md 为主体,逐字段拆解 Zed ep 评测工具使用的 example 格式:Front Matter、Edit History、Cursor Position 与 Expected Patch 四个部分如何组合成一道"代码续写题"。读完本文,你能看懂并手工编写 Zed 编辑预测的评测/训练样本,理解样本如何被解析、送入预测与评分流水线。
样本整体结构:一个 example 文件由什么组成
目标样本位于 crates/edit_prediction_cli/evals/flask--add-and-rename-test-function.md,它描述了一个真实编辑场景:在 Flask 仓库的 tests/test_basic.py 中,用户先输入一个 de 片段、补全为 def test_(): pass,再把函数名改为 test_static_file_not_found,模型需要预测下一步——把这个空测试函数补全为完整的"静态文件 404"测试。
整个文件格式由 ExampleSpec 结构体定义,其 Markdown 序列化/反序列化实现在 crates/edit_prediction/src/example_spec.rs。样本文件的四个组成部分与源码中的段落标题常量一一对应:
const UNCOMMITTED_DIFF_HEADING: &str = "Uncommitted Diff";
const EDIT_HISTORY_HEADING: &str = "Edit History";
const CURSOR_POSITION_HEADING: &str = "Cursor Position";
const EXPECTED_PATCH_HEADING: &str = "Expected Patch";
(见 example_spec.rs#L71-L78)。本样本包含了其中三个必需/可选段落:Front Matter(仓库元数据)、Edit History(编辑历史)、Cursor Position(光标位置)和 Expected Patch(期望补丁)。
Front Matter:锁定仓库版本以保证可复现
样本文件开头是 TOML 格式的 Front Matter:
+++
repository_url = "https://github.com/pallets/flask"
revision = "2fec0b206c6e83ea813ab26597e15c96fab08be7"
+++
这两个字段对应 ExampleSpec 中的 repository_url 与 revision 字段(example_spec.rs#L25-L54)。其作用是把样本锚定到 Flask 仓库的一个精确 commit 上:ep 工具在加载项目时会基于该仓库地址和 revision 检出对应版本的代码(工作目录见 RepoName::worktree_path 的实现),从而让评测环境与被预测时的代码状态完全一致。
从 example_spec.rs 的 FrontMatter 结构体还能看到两个可选字段:tags(标签列表)和 uncommitted_diff_requires_edit_history_rollback,本样本未使用。此外 to_markdown 方法(example_spec.rs#L145-L255)表明该格式是可逆的:任何 ExampleSpec 都能序列化成这种 Markdown,再用 from_markdown 解析回来。
Edit History:三段 diff 记录"用户是如何一步步编辑的"
## Edit History 段落是模型的"输入语境"。本样本包含三段连续的 unified diff:
--- a/tests/test_basic.py
+++ b/tests/test_basic.py
@@ -1376,5 +1376,8 @@
def test_static_files(app, client):
rv = client.get("/static/index.html")
...
+de
第一段:用户在 test_static_files 之后敲下了 de(diff 中新增的 +de 行)。
@@ -1372,15 +1372,15 @@
-de
+def test_():
+ pass
第二段:de 被替换为 def test_():\n pass——模拟了一次编辑预测被接受后的结果(一个空的测试函数骨架)。
@@ -1372,15 +1372,15 @@
-def test_():
+def test_static_file_not_found():
pass
第三段:用户把无参函数名改成了 test_static_file_not_found,并把光标停留在函数名之后的位置。
Edit History 的价值在于它把"上下文"还原成时序化的编辑动作,而不只是一张文件快照。从 from_markdown 的解析逻辑看(example_spec.rs#L258-L415),Edit History 段落下的每一个 fenced code block 会被依次拼接进 spec.edit_history;如果某个代码块前出现了 // User accepted prediction: 标记(ACCEPTED_PREDICTION_MARKER,example_spec.rs#L79),该标记也会被保留进编辑历史——本样本的第二段 diff 正是这种"预测被接受"的典型形态。
Cursor Position:用注释行标记光标列的编码约定
## Cursor Position 段落的 fenced code block 的 info string 是光标所在文件路径,块内文本是光标附近的代码摘录,外加一行特殊注释标记光标位置:
```tests/test_basic.py
def test_static_file_not_found():
# ^[CURSOR_POSITION]
pass
def test_static_url_path():
```
这个约定的精确语义在 cursor_excerpt 方法的文档注释中写明(example_spec.rs#L417-L473):
- 标记行出现在光标所在行的下方,注释前缀(本例是
#,因为 Python 的行注释)之后用箭头指明列号; ^:光标列就是^字符所在列,即指向其上方行的某一列——本例中光标停在test_static_file_not_found函数名末尾;<:光标列是该行第一个非空白字符所在列(用于光标列小于注释前缀长度的场景);- 还支持内联标记
<|user_cursor|>(INLINE_CURSOR_MARKER),直接内嵌在文本中。
解析时 cursor_excerpt 会剥掉标记行、把 ^ 的列偏移换算成该摘录内的字节偏移,供后续构造 prompt 使用;测试用例 test_cursor_excerpt_with_caret(example_spec.rs#L520-L599)验证了 ^ 格式在不同列下的往返一致性。如果 Cursor Position 代码块缺失,from_markdown 会直接报错 Missing cursor position codeblock(example_spec.rs#L410-L412),可见该段落是样本的必填项。
Expected Patch:多个可接受答案与评分方式
## Expected Patch 段落是本样本的核心"答案区",共给出三个 diff 代码块——在 ExampleSpec 中对应 expected_patches: Vec<String>(example_spec.rs#L45),解析时 Expected Patch 段下的每一个代码块都推入该向量(example_spec.rs#L397-L399)。也就是说,一个样本可以有多条可接受的标准答案:
答案 1(最小补全)——补上 app, client 参数,发起一次对不存在文件的请求并断言 404:
--- a/tests/test_basic.py
+++ b/tests/test_basic.py
@@ -1372,15 +1372,15 @@
-def test_static_file_not_found():
- pass
+def test_static_file_not_found(app, client):
+ rv = client.get("/static/non_existent.html")
+ assert rv.status_code == 404
+ rv.close()
答案 2——结构与答案 1 相同,只是测试用的路径换成了 /static/not_found.html。这说明评分容忍等价变体,而非要求逐字匹配。
答案 3(加强版)——额外断言响应体内容,并用 pytest.raises(BuildError, ...) 验证 flask.url_for 在缺失静态文件时抛错:
@@ -1376,8 +1376,13 @@
-def test_static_file_not_found():
- pass
+def test_static_file_not_found(app, client):
+ rv = client.get("/static/nonexistent.html")
+ assert rv.status_code == 404
+ assert rv.data.strip() == b"<h1>Not Found</h1>"
+ with app.test_request_context():
+ pytest.raises(BuildError, flask.url_for, "static", filename="nonexistent.html")
+ rv.close()
值得注意的是,这些期望补丁都可以携带光标位置信息:expected_patches_with_cursor_positions 方法(example_spec.rs#L489-L502)会调用 extract_cursor_from_patch,从补丁新增行中的 <|user_cursor|> 内联标记提取"补丁应用后光标应停在哪里",用于同时评测模型对光标移动的预测。本样本的三个补丁未携带该标记,表示不考核光标落点。
此外格式还保留了 rejected_patch(Rejected Patch 段落)字段,用于 DPO(Direct Preference Optimization)负样本,本样本未使用。
样本如何被 ep 工具消费:从 .md 到预测与评分
这个 .md 文件最终由 Zed 的 ep 命令行工具(edit_prediction_cli crate,二进制名 ep,见 crates/edit_prediction_cli/Cargo.toml 的 [[bin]] 定义)读取和评测。
输入加载逻辑在 read_example_files:按扩展名分派——.json 解析为单个 Example,.jsonl 逐行解析,.md 则走 ExampleSpec::from_markdown 路径;文件名(去掉扩展名)会成为样本的 name,本例即 flask--add-and-rename-test-function。
在预测流水线中(crates/edit_prediction_cli/src/predict.rs 的 run_prediction),一个 example 会依次经历:
- 加载项目(
run_load_project):按 Front Matter 中的仓库/revision 检出代码并构造 Zed 的Project; - 上下文检索(
run_context_retrieval,默认ContextRetrievalType::Lsp,见 predict.rs#L119-L128):用 LSP 收集光标所在文件的上下文; - 执行预测:根据
--provider指定的后端(Teacher / Zeta2 / Baseten 等)生成actual_patch,写入ExamplePrediction; - 评分:由
edit_prediction_metricscrate(ExampleScore,example.rs#L161)将实际预测与expected_patches中的各候选答案比对打分。
ep 的通用参数(main.rs#L71-L115)对运行本类样本同样适用,例如 --name 可按样本名过滤、--repo 可按仓库过滤(本样本的仓库为 pallets/flask)、--limit/--offset 控制处理数量、-o 指定输出、--markdown 把输出写回"每个样本一个 .md 文件"的形式——这正是 evals 目录里这批文件的形态。--failed 参数(keep/skip/skip-no-files)控制失败样本是否保留在主输出中;失败的样本无论如何都会落盘到该次运行的 failed/ 目录。
总结:从一道样本看 Zed 编辑预测的评测设计
回看 flask--add-and-rename-test-function.md 这个样本,它浓缩了 Zed 编辑预测评测体系的几个关键设计:
- 可复现性:
repository_url+revision把评测锚定在精确 commit 上; - 过程化上下文:Edit History 用多段 diff 记录编辑时序,而非静态快照;
- 精确定位:Cursor Position 用注释行 +
^箭头的约定编码光标行列,解析失败会显式报错; - 宽容的评分标准:Expected Patch 允许多个等价答案,并可附带光标落点考核。
如果你想继续深入,可以从 ExampleSpec 的完整字段定义、from_markdown 解析器、ep 的输入规范说明(含 captured-after: / rejected-after: 等 Snowflake 数据源 specifier),以及同目录下其他样本(如 flask--add-test-function.md、tree-sitter--if-let-to-match.md)对照阅读,它们覆盖了增补 import、重命名、注释补全等多种预测场景。
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 StartedRust0624
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