首页
/ Zed 编辑预测评测样例解析:flask--add-test-function.md 如何定义 Zeta 模型的输入与期望输出

Zed 编辑预测评测样例解析:flask--add-test-function.md 如何定义 Zeta 模型的输入与期望输出

2026-09-06 14:09:42作者:何将鹤

本文以 Zed 仓库中编辑预测(Edit Prediction,即 Zeta 功能)的评测样例 flask--add-test-function.md 为剖析对象,完整解读这类 Markdown 评测样例的 front matter 与三个核心小节(Edit History、Cursor Position、Expected Patch)的语义,并结合 example_spec.rsload_project.rs 等源码说明评测 CLI 如何把这份静态文本还原成真实的"仓库 + 缓冲 + 光标"现场、再对模型预测进行打分。读完你可以独立读懂 crates/edit_prediction_cli/evals/ 目录下的任意样例,并掌握编写新评测样例的格式约束。

1. 评测样例在 edit_prediction_cli 中的角色

Zed 的编辑预测模型需要通过大量"给定历史编辑与光标位置,补全下一段文本"的评测样例(example)来验证效果。crates/edit_prediction_cli/evals/ 目录就是这类样例的存放地,其中包含多组按"仓库--场景"命名规范组织的文件:

example.rs 中的 read_example_files 可以看到,CLI 支持三种输入格式:.md(走 parse_markdown_exampleExampleSpec::from_markdown)、.json(单个 Example 反序列化)、.jsonl(每行一个样例);样例的 name 默认取文件名主干(如 flask--add-test-function),这也解释了 evals 目录的命名约定。

每个样例最终被解析为 ExampleSpec 结构体,核心字段包括:namerepository_urlrevisiontagsreasoninguncommitted_diffrecently_opened_files/recently_viewed_filescursor_pathcursor_positionedit_historyexpected_patchesrejected_patch 等。

2. 逐段拆解 flask--add-test-function.md

2.1 Front matter:锁定上游仓库与提交版本

+++
repository_url = "https://github.com/pallets/flask"
revision = "2fec0b206c6e83ea813ab26597e15c96fab08be7"
+++

front matter 用 +++ 包裹的 TOML 块描述评测现场所在的外部仓库(这里是 Flask 上游仓库)与精确的 git 修订from_markdown 解析的 FrontMatter 结构还支持可选字段 tags(标签列表)和 uncommitted_diff_requires_edit_history_rollback(布尔值)。锁定 revision 的意义在于:后续 CLI 会 clone 该仓库并 checkout 到这一提交,保证所有评测者在完全一致的代码快照上复现同一评测——这是结果可重复的基础。

2.2 Edit History:用户刚刚敲下的编辑

--- 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")
     assert rv.status_code == 200
     assert rv.data.strip() == b"<h1>Hello World!</h1>"
     with app.test_request_context():
         assert flask.url_for("static", filename="index.html") == "/static/index.html"
     rv.close()


+de
+
+
 def test_static_url_path():

## Edit History 小节下的 diff 代码块记录了触发预测前用户在 tests/test_basic.py 中完成的编辑:在 test_static_filestest_static_url_path 两个测试函数之间插入了一段新代码,且只输入了两个字符 de(一个新测试函数名的开头)。这一小节会被 run_load_project 中的 apply_edit_history 原样应用到 worktree 缓冲上(底层调用 edit_prediction::udiff::apply_diff,见 load_project.rs 末尾),从而在内存中重建"用户刚编辑完"的现场。此外解析器还支持在 Edit History 小节中用 // User accepted prediction: 标记行夹带"被用户接受的预测"片段,用于复现多轮连续编辑场景(见 example_spec.rs 中的 ACCEPTED_PREDICTION_MARKER 常量及对应测试)。

2.3 Cursor Position:光标位置与上下文摘录

```tests/test_basic.py
def test_static_files(app, client):
    rv = client.get("/static/index.html")
    ...
de
# ^[CURSOR_POSITION]


def test_static_url_path():

`## Cursor Position` 小节有两个关键要素,均被解析器(`from_markdown` 中的 `Section::CursorPosition` 分支)提取:

1. **代码块 info string 即文件路径**:解析器把代码块围栏后的信息字符串直接当作 `cursor_path`(`spec.cursor_path = Path::new(block_info).into()`),所以这里写的是 `tests/test_basic.py`。
2. **`^[CURSOR_POSITION]` 标记行**:光标所在行的下一行是一条注释,`^` 字符的水平位置指向上方光标列。从 `cursor_excerpt` 的文档注释可知两种写法:
   - `^`:光标列就是 `^` 字符所在列(本例中 `de` 行下、`d` 之后);
   - `<`:当光标列小于注释前缀长度时使用,表示光标在该行第一个非空白字符处;
   - 还有一种内联标记 `<|user_cursor|>`,直接内嵌在文本流中标记光标字节偏移(`cursor_excerpt` 会优先检查它)。

这段摘录(excerpt)不是摆设:`load_project` 会用**整段摘录文本**在打开的缓冲中做子串匹配来定位光标偏移,并且**要求匹配必须恰好唯一**(`"More than one cursor position match found"` 会直接报错,见 [load_project.rs](https://gitcode.com/GitHub_Trending/ze/zed/blob/b1a7ef0cf66dfbf9d7661170c96d97c7df916c68/crates/edit_prediction_cli/src/load_project.rs?utm_source=gitcode_repo_files) 的 `cursor_position` 函数)。因此编写样例时,Cursor Position 摘录必须包含足够的上下文使其在整个文件中只出现一次——这也是摘录里带上前后相邻测试函数的原因。

### 2.4 Expected Patch:多候选期望补全 + 期望光标位置

```diff
--- a/tests/test_basic.py
+++ b/tests/test_basic.py
@@ -1372,15 +1372,15 @@
-de
+def test_():
#         ^[CURSOR_POSITION]
+

## Expected Patch 小节允许包含多个 diff 代码块,解析器会把它们全部收进 expected_patches: Vec<String>——即"以下任何一种补全都算模型答对"。本样例列出了 10 个候选,构成一条清晰的能力阶梯:

候选 考察点
def test_(): / def test_(): + pass 最小合法补全:补全关键字与函数头
def test_(app, client): / 带 pass 识别相邻测试函数使用的 app, client fixture 并补入参数
def test_static_(): / def test_static_(app, client): 从文件名 test_basic.py 的上下文(test_static_files 等)推断函数名以 test_static_ 开头
def test_static_folder(): + pass 更具体的函数名猜测
def test_static_route_with_host_matching(app, client): 完整函数名 + 参数,且光标落在 d 之前

注意每个期望补丁中都带有一条 # ^[CURSOR_POSITION] 标记行,它编码了编辑完成后光标应该移动到的位置。源码中 expected_patches_with_cursor_positions 通过 extract_cursor_from_patch 从补丁文本中提取出(补丁本体, 期望光标偏移)二元组;encode_cursor_in_patch 则负责反向编码且保证幂等(example_spec.rs 末尾的测试 test_encode_cursor_in_patch_is_idempotent 验证了重复编码不会叠加标记)。这意味着评测不仅比"补全内容对不对",还比"补全后光标落点准不准",完整覆盖 Zeta 的双输出(文本补丁 + 光标跳转)。

对照 flask--add-import-statement.md 可以看出同一套机制的另一种用法:Edit History 记录了用户在 src/flask/logging.py 中把 from werkzeug.local import LocalProxy 误删成 imfrom ... 的错误编辑,Cursor Position 停在 imfrom 行,期望补丁则是把它还原为 import + from ... 两行——说明这套样例同样覆盖"修复误删"类场景,而 hello-world--rename-accepted-group-by.mdflask--rename-accepted-prediction.md 等文件名则暗示还存在"接受预测后再重命名"的多轮场景。

3. CLI 如何消费这份样例:从 Markdown 到现场还原

edit_prediction_cli 的命令行工具在 main.rs 中通过 clap 声明为 ep#[command(name = "ep")]),子命令覆盖完整评测流水线:read(读取/拉取样例)、load-project(建 worktree、加载文件)、context(检索相关上下文)、format-prompt(按 provider 生成 prompt)、predict(跑预测)、parse-output(把模型输出解析回 unified diff)、score(打分)、distill(蒸馏数据集)、eval(聚合评分)、synthesize(从 git 提交自动生成样例)、split-committruncate-patchsplitfilter-languagesqa(LLM-as-a-judge 质量评估)、repair 等。全局参数包括 --name/--repo 过滤、--limit/--offset 分页、--markdown(按样例输出 md 文件)、--max-parallelism(默认 10)等,因此可以直接用 ep eval <evals 目录中的 md 文件> 这类方式针对单个样例跑通全流程。

以本 Flask 样例为例,load-project 阶段(run_load_project)的执行链条是:

  1. 建 worktreesetup_worktreerepository_url 计算本地仓库目录,必要时 git init + remote add originfetch_if_needed 拉取 revision,然后 git worktree add 出一个以样例文件名为分支名的独立工作树(分支名来自 example.spec.filename(),即对 name 做字符清洗)。
  2. 应用 Edit Historyapply_diff(&example.spec.edit_history, ...)+de 这段编辑写入缓冲,此时缓冲中 tests/test_basic.py 已包含半成品的 de
  3. 定位光标:先按 cursor_path(含去前缀回退,兼容旧样例的 zed/crates/... 写法)打开缓冲,再用 Cursor Position 摘录做唯一子串匹配换算出 Anchor 光标。
  4. 构造 prompt 输入compute_cursor_excerpt / compute_syntax_ranges 计算光标摘录与语法范围,连同项目编辑历史事件、最近打开文件列表一起装入 Zeta2PromptInput(见 load_project.rsprompt_inputs 的组装代码),这就是喂给模型的最终输入结构。

4. 打分:expected_patches 如何参与评分

score/eval 阶段的入口是 score.rsrun_scoring,关键步骤:

  1. 先跑一次 run_prediction 拿到模型的 actual_output(或由 parse_prediction_output 解析出 actual_patchactual_cursor);
  2. spec.expected_patches_with_cursor_positions() 取出全部 10 个候选补丁及其期望光标偏移,再由 edit_prediction_metrics::prepare_expected_patches 把补丁应用回原文本,得到"期望编辑后的完整文本";
  3. 对每条 prediction 调 score_prediction,输入包括原文本 original_text、期望补丁集合、实际补丁、实际光标、编辑历史事件(用于判定"预测是否反转了用户刚做的编辑")、以及检索到的相关上下文;
  4. 相关上下文参与评分时有 token 上限,常量 EVAL_RELATED_CONTEXT_TOKENS_LIMIT 为 4000(可在 eval 子命令的 --related-context-limit 覆盖)。

由于本样例的 10 个候选覆盖从"只补关键字"到"补全函数名+fixture 参数"的不同粒度,评分可以区分模型"能给出语法合法的最低限度补全"与"能像人类开发者一样选择 test_static_* 命名并带上 app, client fixture"两种质量层次——这正是编辑预测评测要度量的核心能力。

5. 编写新评测样例的格式要点

结合解析源码(ExampleSpec::from_markdown)可以总结出编写 .md 样例必须满足的约束:

  1. front matter 必填 repository_urlrevision;revision 必须是可 checkout 的完整提交,Cursor Position 摘录必须能在该提交对应文件(应用 Edit History 后)中唯一匹配;
  2. 小节标题固定## Edit History## Cursor Position## Expected Patch 为常用三节,另支持 ReasoningUncommitted DiffRecently Opened FilesRecently Viewed FilesRejected Patch(后者提供 DPO 负样本);未知小节会被当作 Section::Other 忽略,但 H5/H6 级别标题会直接 bail!
  3. Cursor Position 代码块:info string 写相对文件路径;正文中必须含 ^[CURSOR_POSITION](或 <[CURSOR_POSITION]、内联 <|user_cursor|>)标记行;若缺少 cursor 代码块,解析会报 Missing cursor position codeblock
  4. Expected Patch 代码块diff 块内用注释形式的 ^[CURSOR_POSITION] 标记行编码期望光标落点,一个样例可写任意多个候选补丁;补丁必须能应用在"应用了 Edit History 后的缓冲"上,否则评分阶段会以 Expected patch did not apply 报错;
  5. 命名:文件主干即样例名,建议沿用 仓库名--场景描述.md 的既有约定,便于 --name/--repo 过滤与失败日志检索。

这套"Markdown 声明评测现场 + git worktree 精确复现 + 光标双通道评分"的机制,使得 crates/edit_prediction_cli/evals/ 中每个文件都是一个可独立复现、可回归的编辑预测测试用例;flask--add-test-function.md 作为其中覆盖面较广的一条(多候选期望、fixture 参数推断、光标落点校验),是理解整个评测体系的理想入口。

登录后查看全文
热门项目推荐
相关项目推荐