news 2026/9/7 3:57:58

Zed 编辑预测单元测试样例格式详解:flask--add-import-statement 案例精读

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Zed 编辑预测单元测试样例格式详解:flask--add-import-statement 案例精读

Zed 编辑预测单元测试样例格式详解:flask--add-import-statement 案例精读

【免费下载链接】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

本文以 flask--add-import-statement.md 这个真实评测样例为切入点,逐段拆解 Zed 编辑预测(Edit Prediction)单元测试样例的完整结构——前置元数据、编辑历史、光标位置标注与多个候选期望补丁——并结合 example_spec.rs、example.rs 等源码说明这些 Markdown 字段如何被解析为ExampleSpec结构体、又如何参与预测与打分流程,帮助读者读懂乃至独立编写合规的评测样例。

1. 什么是编辑预测单元测试样例

Zed 的编辑预测功能(即行内代码续写)依赖一套完整的评测基础设施:给定"用户在某个仓库的某个修订版本上、光标处于某处、之前刚做过一串编辑"这一上下文,让模型产出一个补丁(patch)与新的光标位置,再与预先记录的"期望补丁"对比打分。crates/edit_prediction_cli/evals/目录存放的就是这一流程的单元测试样例,目录中按仓库名--行为描述的命名规则组织了 19 个样例,例如flask--add-import-statementtree-sitter--if-let-to-matchvscode--add-async-and-await等。

从源码看,样例支持三种文件扩展名。example.rs 中的read_example_files函数按扩展名分发:.json.jsonl直接反序列化为Example(后者每行一条),而.md文件则走parse_markdown_example,最终委托给ExampleSpec::from_markdown完成解析。本案例所属的.md格式正是人工撰写、代码评审和版本管理最友好的形态。

2. 样例文件逐段精读

下面完整列出 flask--add-import-statement.md 的全部内容,然后分段解释。该样例取材于 Flask 仓库的src/flask/logging.py:用户把一行合法的from werkzeug.local import LocalProxy误敲成了imfrom werkzeug.local import LocalProxy,光标停在坏行行首,期望模型把这行重写为正确的 import 语句。

2.1 前置元数据:锁定源仓库与修订版本

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

文件以+++分隔的 TOML 前置块开头。example_spec.rs 中对应的FrontMatter结构体定义了两个必填字段与两个可选字段:

字段必填说明
repository_url评测上下文所属的 git 仓库地址,评测时会据此拉取 worktree 加载真实项目
revision精确的 commit 哈希,保证上下文可复现
tags样例标签列表,用于筛选
uncommitted_diff_requires_edit_history_rollback标记未提交 diff 是否包含需要回滚的编辑历史

2.2 Edit History:用户此前的编辑序列

## Edit History ```diff --- a/src/flask/logging.py +++ b/src/flask/logging.py @@ -4,7 +4,7 @@ import sys import typing as t -from werkzeug.local import LocalProxy +imfrom werkzeug.local import LocalProxy from .globals import request
`## Edit History` 段落用一段(或多段)统一 diff 记录"预测发生之前用户刚刚做过的编辑"。这是编辑预测与静态补全的本质区别:模型看到的不仅是文件内容,还有用户行为的"最近趋势"。本样例中,用户把正确的 import 行改成了 `imfrom ...` 这种残缺写法,模型需要判断这是输入中的中间态并给出整行重写。 该段落还支持一个可选约定:在某段 diff 之前单独写一行 `// User accepted prediction:`。同目录的 [flask--rename-accepted-prediction.md](https://link.gitcode.com/i/68d3abf83b140e35774155a1a9f9084b) 就用了它,表示紧随其后的那段 diff 并非用户手打、而是"用户接受了一条模型预测"产生的编辑——这对训练与蒸馏是有价值的信号。解析逻辑在 [example_spec.rs](https://link.gitcode.com/i/c8731f66782f34f30026fc785d190fbd#L79) 中以常量 `ACCEPTED_PREDICTION_MARKER` 实现,`from_markdown` 状态机识别到该标记后会在合并编辑历史时重新插入这行注释,保证序列化/反序列化往返一致(同文件测试 `test_from_markdown_accepted_prediction_marker` 验证了这一点)。 ### 2.3 Cursor Position:文件节选与光标标记 ```text ## Cursor Position ```src/flask/logging.py from __future__ import annotations import logging import sys import typing as t imfrom werkzeug.local import LocalProxy # ^[CURSOR_POSITION] from .globals import request if t.TYPE_CHECKING: # pragma: no cover from .sansio.app import App
`## Cursor Position` 段落的代码围栏由两部分构成: 1. **围栏 info string 是光标所在文件路径**(本例为 `src/flask/logging.py`)。解析器把围栏 info 直接存入 `cursor_path`,见 [example_spec.rs](https://link.gitcode.com/i/c8731f66782f34f30026fc785d190fbd#L393-L396) 中 `Section::CursorPosition` 分支:`spec.cursor_path = Path::new(block_info)`。 2. **围栏内容是该文件的光标周边节选(excerpt),并内嵌一行光标标记**。标记行以语言注释形式写在光标行的下一行,包含 `[CURSOR_POSITION]` 字符串和一个指向光标列的箭头。 箭头有两种语法,定义在 [cursor_excerpt()](https://link.gitcode.com/i/c8731f66782f34f30026fc785d190fbd#L417-L473) 的文档注释与实现中: | 箭头 | 光标列语义 | | --- | --- | | `^` | 光标列 = `^` 字符在标记行中的位置(本例 `# ^[CURSOR_POSITION]` 中 `^` 位于第 0 列,即 `imfrom` 行行首) | | `<` | 光标列 = 光标行上第一个非空白字符的位置(用于列位置小于注释前缀长度的场景) | 解析时,`cursor_excerpt()` 会定位 `[CURSOR_POSITION]`(常量 `CURSOR_POSITION_MARKER`,定义于 [udiff.rs](https://link.gitcode.com/i/4c8837e9856d65da1788b34bbdfec3cc)),算出标记行范围,按 `^` 或 `<` 规则得出光标列,把光标定位在**标记行的上一行**,然后删除标记行、修剪尾部空行,返回"纯文本节选 + 节选内光标字节偏移"。若文本中直接内嵌了 `<|user_cursor|>`(常量 `INLINE_CURSOR_MARKER`),则走更简单的内联分支:删除标记、其位置即光标偏移。[example_spec.rs](https://link.gitcode.com/i/c8731f66782f34f30026fc785d190fbd#L520-L657) 的 `test_cursor_excerpt_with_caret` 测试覆盖了行首、行内、行尾、文件末尾等边界列位,`test_cursor_excerpt_with_inline_marker` 则验证内联标记。 ### 2.4 Expected Patch:多个可接受的期望补丁 ```diff ## Expected Patch ```diff --- a/src/flask/logging.py +++ b/src/flask/logging.py @@ -1,21 +1,21 @@ from __future__ import annotations import logging import sys import typing as t -imfrom werkzeug.local import LocalProxy +import # ^[CURSOR_POSITION] +from werkzeug.local import LocalProxy from .globals import request
--- a/src/flask/logging.py +++ b/src/flask/logging.py @@ -1,21 +1,21 @@ from __future__ import annotations import logging import sys import typing as t - -imfrom werkzeug.local import LocalProxy +import werkzeug +# ^[CURSOR_POSITION] +from werkzeug.local import LocalProxy from .globals import request
本样例的 `## Expected Patch` 段包含**两个** diff,这是该格式的关键能力之一:同一个 prompt 允许存在多个都被判为正确的期望输出。两个候选补丁都完成"把 `imfrom` 坏行修成正确 import"这一意图,但终态与光标落点不同: - 候选一:该行变为 `import`,光标停在 `import` 之后(第 6 列,`# ^[CURSOR_POSITION]` 中 `^` 的位置); - 候选二:该行变为 `import werkzeug`,光标停在 `werkzeug` 之后(第 15 列)。 注意期望补丁里的光标标注复用了与 Cursor Position 段完全相同的"标记注释行"语法,只是出现在 diff 的 `+` 新增行之后。`ExampleSpec` 中 `expected_patches` 的类型是 `Vec<String>`([example_spec.rs](https://link.gitcode.com/i/c8731f66782f34f30026fc785d190fbd#L45)),解析时每个独立的 diff 代码块都会 push 进这个向量。`expected_patches_with_cursor_positions()`([example_spec.rs](https://link.gitcode.com/i/c8731f66782f34f30026fc785d190fbd#L489-L502))则把每个补丁展开为 `(补丁, Option<光标偏移>)` 二元组,文档注释明确:光标偏移是"应用补丁后的新文本中、相对于 hunk 起点的偏移"。与之配对的 `encode_cursor_in_patch` / `extract_cursor_from_patch` 实现在 [zeta_prompt 的 udiff 模块](https://link.gitcode.com/i/8cf86432955fccb741be205f3fa44e30),负责"含光标标注的补丁文本"与"干净补丁 + 光标偏移"之间的双向转换;同文件测试 `test_expected_patches_with_cursor_positions` 还验证了编码的幂等性。 ## 3. 源码级解析:Markdown 如何变成 ExampleSpec `ExampleSpec` 结构体([example_spec.rs](https://link.gitcode.com/i/c8731f66782f34f30026fc785d190fbd#L25-L54))是样例的核心数据模型,除本节已讲到的字段外,还包括: - `name`:样例名,缺省时取文件名(不含扩展名),见 [example.rs](https://link.gitcode.com/i/2432e3998eb78e151c970bd15799da1c#L270-L276) 中 `"md"` 分支对 `example.spec.name` 的兜底填充; - `reasoning` / `uncommitted_diff`:可选的"推理说明"与"未提交 diff"段落,分别对应 `## Reasoning`、`## Uncommitted Diff` 标题; - `recently_opened_files` / `recently_viewed_files`:对应 `## Recently Opened Files`、`## Recently Viewed Files` 段落,每行一个路径(可附 tab 分隔的光标偏移); - `rejected_patch`:对应 `## Rejected Patch` 段落,供 DPO(拒绝采样偏好训练)使用——[example.rs](https://link.gitcode.com/i/2432e3998eb78e151c970bd15799da1c#L64-L73) 的 `ExamplePrompt.rejected_output` 字段注释即标明 "For DPO"。 全部合法的二级标题常量集中在 [example_spec.rs](https://link.gitcode.com/i/c8731f66782f34f30026fc785d190fbd#L71-L79),解析器 `from_markdown`([L258](https://link.gitcode.com/i/c8731f66782f34f30026fc785d190fbd#L258) 起)用 `pulldown_cmark` 遍历 Markdown 事件流,维护一个 `Section` 状态机(`Start / UncommittedDiff / RecentlyOpenedFiles / RecentlyViewedFiles / EditHistory / CursorPosition / ExpectedPatch / RejectedPatch / Other`),按当前标题把代码块内容归位到对应字段。约束方面: - 标题层级只允许 H1(作为样例名)与 H2(作为段落),出现 H5 及更深层级会直接报错;缩进代码块(非围栏)也会报错; - **Cursor Position 是唯一硬性必填的段落**:解析结束时若 `cursor_path` 或 `cursor_position` 为空,`anyhow::bail!("Missing cursor position codeblock")`([example_spec.rs](https://link.gitcode.com/i/c8731f66782f34f30026fc785d190fbd#L410-L412)); - Edit History 可以为空,`to_markdown` 序列化空历史时会输出 `(No edit history)` 占位文本。 `Example` 结构([example.rs](https://link.gitcode.com/i/2432e3998eb78e151c970bd15799da1c#L21-L54))在 `ExampleSpec` 之上还挂载了运行期产物:`prompt_inputs`(光标节选、光标偏移、关联文件等 Zeta prompt 输入)、`prompt`(格式化后的 prompt 与期望输出)、`predictions`(模型实际预测)、`score`(与期望补丁的匹配分数)以及 `qa` 结果。也就是说,`.md` 样例只是"输入规格",跑完预测与打分后整个 `Example` 可序列化为 JSON 留档复现。 ## 4. 样例如何被消费:从格式化到打分 评测流程由 `edit_prediction_cli`(CLI 命令名 `ep`,见 [main.rs](https://link.gitcode.com/i/7699f3e72c612f4de19562f23ce4eeb6))串联,与本样例直接相关的环节有两个: **(1)格式化 prompt。** [format_prompt.rs](https://link.gitcode.com/i/65df2e2cc29f0e32f6b3db846ff844d8) 不存在——正确路径是 [crates/edit_prediction_cli/src/format_prompt.rs](https://link.gitcode.com/i/65df2e2cc29f0e32f6b3db846ff844d8)。其中 `TeacherPrompt` 把 `ExampleSpec` 渲染为教师模型提示词:编辑历史最多保留最后 128 行(`MAX_HISTORY_LINES`),光标节选被 `<|editable_region_start|>` / `<|editable_region_end|>` 包围、光标位置注入 `<|user_cursor|>` 标记([format_prompt.rs](https://link.gitcode.com/i/65df2e2cc29f0e32f6b3db846ff844d8#L143-L152)),关联文件上下文按 1024 token 预算截断。教师模型的响应再由 `TeacherPrompt::parse` 还原为统一 diff 与实际光标位置。 **(2)预测与打分。** [score.rs](https://link.gitcode.com/i/d51ca66bc0c97155ccea0ecd4c529feb) 的 `run_scoring` 先执行预测(可复用 JSON 中已存的 `predictions`),随后在后台任务中调用 `expected_patches_with_cursor_positions()` 取出所有期望补丁,经 `edit_prediction_metrics::prepare_expected_patches` 归一化后逐条调用 `score_prediction` 比对。因此本样例中"两个候选补丁"的语义就是:模型预测的补丁与光标位置只要与其中任意一个匹配,即被视为正确。评测的上下文检索量默认受 `EVAL_RELATED_CONTEXT_TOKENS_LIMIT = 4000`([score.rs](https://link.gitcode.com/i/c9bf31fa812bf0fae11ea1a14100e7e7))约束。 ## 5. 如何运行这些单元测试 仓库提供了现成的 CI 入口 [script/run-unit-evals](https://link.gitcode.com/i/73ea51ca0a59388ccd6bad4ddb4146a9),其核心就是一行 nextest 调用: ```bash GPUI_TEST_TIMEOUT=1500 cargo nextest run --workspace --no-fail-fast \ --features unit-eval --no-capture -E 'test(::eval_)'

即:开启unit-evalfeature、用::eval_过滤器只跑评测相关测试、不捕获输出以便观察进度,并把测试超时放宽到 1500 秒。脚本还支持UNIT_EVAL_COMMIT环境变量,先git fetch并切换到指定提交再跑,用于对历史版本做回归评测。

若要针对单个样例做更细粒度的分析,可直接使用epCLI。main.rs 定义了全局参数:--name按样例名过滤、--repo按仓库过滤、--limit/--offset控制处理数量、--max-parallelism(默认 10)、--output/-o指定输出、--markdown把输出写成每样例一个.md文件、--failfast遇错即停、--in-place原地更新样例文件(把预测与分数写回)。输入文件即本样例所在目录下的.md/.json/.jsonl路径。

6. 编写合规样例的要点清单

综合以上解析逻辑,手写一个评测样例时需满足:

  1. +++包裹的 TOML 前置块开头,repository_urlrevision必填;
  2. ## Cursor Position必须存在:围栏 info string 写光标文件路径,围栏内是包含光标行的节选文本,下一行用语言注释加^[CURSOR_POSITION](或行首列场景的<[CURSOR_POSITION])标注光标列;
  3. ## Edit History用统一 diff 描述用户此前的编辑;若其中某段来自被接受的预测,前缀一行// User accepted prediction:
  4. ## Expected Patch可写一个或多个 diff,光标用同样的标记注释行标在+新增行上;多个 diff 表示多个可接受答案;
  5. 只使用 H1/H2 标题,代码块一律用围栏形式;
  6. 文件名遵循仓库名--行为描述命名(如flask--add-import-statement),样例名缺省即取文件名。

掌握这套格式后,无论是要读懂 Zed 编辑预测评测如何复现真实编辑场景,还是要为新场景补充单元测试样例、定位某次预测质量回退,都可以直接以evals/目录中的这些 Markdown 文件为蓝本进行扩展。

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 3:57:58

游戏战败CG渲染全流程:从资源规范到性能优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 3:56:56

ArcGIS 10.8基础实验100例:从坐标系到字段计算的实战指南

我第一次认真翻“ArcGIS 10.8 地理信息系统基础实验操作100例”这个系列时&#xff0c;心里是带着怀疑的。原因很简单&#xff1a;ArcGIS 10.8 并不是新版本&#xff0c;网上讲这个版本的教程一抓一大把&#xff0c;很多还是十几年前的课程资料。你让我一个已经不只一次被 ArcG…

作者头像 李华
网站建设 2026/9/7 3:56:31

谁说高价才酷?中端智能电动摩托车的长续航与智能体验

当你把“酷”等同于“贵”的时候&#xff0c;可能已经错过了智能电动摩托车最值得购买的区间。这不是一句营销口号&#xff0c;而是一个正在发生的行业变化。过去几年&#xff0c;两轮车智能化往往跟着价格走&#xff1a;高端旗舰先用上大屏仪表、无钥匙解锁、牵引力控制、远程…

作者头像 李华
网站建设 2026/9/7 3:56:13

Unity动画重定向实战:跨角色舞蹈动作移植与IRIS渲染输出

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华