首页
/ screenshot-to-code 评测体系实战:模型与 Prompt 对比评测的数据集、批量生成与人工评分全流程

screenshot-to-code 评测体系实战:模型与 Prompt 对比评测的数据集、批量生成与人工评分全流程

2026-09-04 16:36:34作者:宣海椒Queenly

Evaluation.md 是 screenshot-to-code 项目中专门说明“如何系统性地对比不同模型与 Prompt”的文档:它定义了一套基于 16 张截图输入数据集的批量评测流程——用 Python 脚本并行地对每个输入跑一遍 screenshot-to-code,再借助前端 /evals 页面对生成结果进行 1~4 分的人工评分。读完本篇,你可以掌握该评测体系完整的目录配置、批量运行链路(含重试、计时与失败记录机制)、多模型对比与 Best-of-N 页面用法,以及作者采用的“每组配置跑三次取平均分”的评测方法论。

评测体系总览

根据 Evaluation.md,整套评测由两部分组成:

  1. 评测数据集:由 16 张截图组成。文档说明输入截图应放在 backend/evals_data/inputs,输出写到 backend/evals_data/outputs(注意:当前仓库代码中结果实际写入 backend/evals_data/results/{日期}_{模型}_{技术栈} 子目录,见下文);
  2. 评测工具链:一个运行 screenshot-to-code 的 Python 脚本(backend/run_evals.py)+ 一个用于查看并给输出评分的前端 UI(路由 /evals)。

这套组合的目的是回答一个实际问题:换不同的 LLM、换不同的 Prompt、换不同的输出技术栈(HTML/Tailwind/React/Vue 等),生成质量到底差多少? 评测流程把“生成”自动化、把“评判”标准化,使不同配置之间可以公平地打分对比。

准备工作:数据集与目录配置

输入数据集的放置位置

文档给出的准备步骤:

  • 输入截图放在 backend/evals_data/inputs,输出位于 backend/evals_data/outputs
  • 如果要修改数据根目录,修改 backend/evals/config.py 中的 EVALS_DIR

EVALS_DIR 的当前实现就一行,支持用环境变量覆盖默认值:

import os

EVALS_DIR = os.environ.get("EVALS_DIR", "./evals_data")

因此也可以通过 EVALS_DIR=/path/to/evals_data 的方式在不改代码的情况下切换数据目录(相对于启动 run_evals.py 的工作目录解释)。

关于数据集下载,文档原文是“download the input screenshot dataset here: TODO”,即数据集下载地址尚未在文档中补全——这是当前文档的一个待办项,使用时需要准备自己的截图集放入 inputs 目录即可。评测脚本对输入的约束很简单:backend/evals/runner.py_resolve_eval_filenames 会扫描输入目录下所有 .png 文件(或指定的文件列表,只保留 .png 结尾的条目),所以数据集命名只需满足扩展名为 .png

运行批量评测

文档定义的运行方式

Evaluation.md 给出的命令行流程:

  1. backend/run_evals.py 中设置技术栈与模型(STACK 变量、MODEL 变量);
  2. 运行:
OPENAI_API_KEY=sk-... python run_evals.py

该命令会并行地对输入数据集跑 screenshot-to-code,文档提示即便如此也“仍需几分钟完成”;

  1. 脚本结束后,在 backend/evals_data/outputs 查看输出。

需要说明适用前提:run_evals.py 只负责加载环境变量(load_dotenv())并异步调用 run_image_evals(),而 backend/evals/runner.py 中的 run_image_evals()stackmodel 都是必填项——缺任何一个都会直接抛出 ValueError。从当前仓库结构看,主路径已经演进为通过前端 /evals/run 页面调用后端 POST /run_evals(或流式版本 /run_evals_stream)来传入模型列表与技术栈,详见 backend/routes/evals.py;而命令行方式更适合作为一次性批处理入口,按文档把 STACK/MODEL 传给 run_image_evals() 即可。

执行链路:并行调度、重试与计时

run_image_evals() 是整个评测的执行核心,值得拆开看:

  • 并行调度:为每张输入图 × 每个尝试序号(n 参数,默认 1)生成一个协程,全部通过 asyncio.as_completed 并发执行,先完成的先落盘。这就是文档所说“并行运行”的实现来源;
  • 图片预处理:每张 PNG 经 backend/evals/utils.pyimage_to_data_url() 读为字节流并转成 data:image/png;base64,... 数据 URL,供多模态模型直接消费。同一张输入图在多次尝试(n > 1)间只编码一次;
  • 重试策略MAX_EVAL_RETRIES = 2。生成失败会自动重试,最多 2 次,重试次数会计入任务日志;但有一类错误永不重试——BudgetExceededError(预算超限),因为“每次重试都会再花掉一整份预算上限”(见 backend/evals/runner.py 的注释);
  • 计时落盘:每个成功任务记录耗时,全部完成后追加写入输出目录下的 generation_times.txt(首行含 Model: <模型名>),失败任务则写入 failed_tasks.txt,记录输入、尝试序号、重试次数与错误信息,便于定位问题样本。

输出文件的命名规则

输出目录由 get_eval_output_subfolder() 生成:

{EVALS_DIR}/results/{Mon_dd_YYYY}_{model}_{stack}/
├── {输入文件名}_{尝试序号}.html   # 如 001.png -> 001_0.html
├── generation_times.txt
└── failed_tasks.txt

也就是说,每次(模型 + 技术栈 + 日期)组合对应一个独立的结果文件夹,文件名的“输入名_尝试序号”命名法是后续 /evals 页面把输出和输入截图配对展示的基础(backend/routes/evals.pyget_evals 正是按 base_name 从输出文件名反推、再去 inputs 目录找同名 .png)。

此外还有一个容易忽略的细节:normalize_local_asset_urls() 在写文件前会重写 HTML 中的本地资产引用。由于评测环境下没有 5173 端口的开发服务器,生成代码里的 /local-assets/...localhost:5173 资源引用会被统一替换为 LOCAL_ASSET_BASE_URL 前缀,保证评测产出的 HTML 单独打开时仍能渲染出真实提取的图片资产。

生成核心:复用主链路的 Agent 运行器

单张图的代码生成由 backend/evals/core.pygenerate_code_for_image() 完成,它并非独立实现一套生成逻辑,而是直接复用产品主链路的 Agent 运行器,这一点保证了“评测结果 = 线上真实表现”:

  1. build_image_prompt_messages()backend/prompts/create/image.py)基于截图数据 URL + 技术栈构造 Prompt 消息;text_prompt 传空串,因为评测只对比“同一张图”在不同模型/Prompt 下的表现;
  2. 校验对应厂商的 API Key——OPENAI_API_KEYANTHROPIC_API_KEYGEMINI_API_KEY 按所选模型分组检查,缺失则直接抛异常;
  3. 创建 AgentRunRecorderentry_point="eval",可附带 eval_sessioneval_setinput_file 元数据)并构造 Agentbackend/agent/runner.py),should_generate_images=True 表示允许走图片生成能力,asset_base_url 显式传入 LOCAL_ASSET_BASE_URL(注释说明:评测没有 websocket 可推断 host,否则提取资产会拿到无 host 的 /local-assets/ URL);
  4. await runner.run(model, prompt_messages) 返回最终代码文本。

从这套结构看,评测中更换 Prompt 的效果,本质上就是改变 build_image_prompt_messages 产物或系统 Prompt 后,同一 Agent 管线在不同模型上的输出差异——这正是文档所说“compare and evaluate various models and prompts”的落点。相关行为还有 backend/tests/test_eval_runner.pybackend/tests/test_eval_sessions.pybackend/tests/test_eval_sets.py 等测试用例可供交叉验证。

查看输出与评分(前端 /evals 页面)

文档“Rating evals”一节的操作要点:

  • 打开前端的 /evals 路由查看并评分;
  • 每个输出按 1~4 分制评分
  • 可以把页面打印为 PDF,方便把评测结果分享给他人。

对应到当前仓库,/evals 并不是单一页面,而是一组评测工作台(见 frontend/src/main.tsx 路由定义):

路由 页面 作用
/evals AllEvalsPage 评测导航首页
/evals/run RunEvalsPage 选择多模型 + 技术栈 + 输入文件,发起批量评测并实时显示进度
/evals/best-of-n BestOfNEvalsPage 横向对比多个结果文件夹中同一输入的输出
/evals/sessions EvalSessionsPage 管理评测会话(见下文)

后端为这些页面提供的 API(backend/routes/evals.py):

  • GET /evals?folder=<结果目录>:读取某结果文件夹下所有 .html,与 inputs 中同名截图配对,返回 [{input: <数据URL>, outputs: [<HTML内容>]}],供页面并排展示“原图 vs 生成代码渲染效果”;
  • POST /run_evals:接收 models(模型名列表)、stack、可选 filesdiff_mode,对每个模型依次调用 run_image_evals()
  • POST /run_evals_stream:同样执行批量评测,但以 NDJSON 流式下发 start / model_start / task_complete / complete 事件,前端据此渲染进度条与失败任务数;
  • GET /models:返回当前代码库中可用的全部 Llm 枚举值与全部 Stack 取值,前端下拉框即来源于此;
  • GET /best-of-n-evals?folder1=...&folder2=...:取多个结果文件夹的公共输入基名,把每张输入图在各文件夹里的输出拼成一组返回,实现“N 个输出并排比较”;某文件夹缺输出时会用 <html><body>Output not found</body></html> 占位;
  • GET /output_folders:列出 evals_data/results 下全部结果文件夹,按修改时间倒序,供页面选择要查看哪一轮评测。

diff_mode 值得单独说明:开启后,若某(输入图 × 尝试序号)的输出 HTML 已存在于目标结果文件夹,该任务会被跳过(count_pending_eval_tasks() 会先统计“待跑/已跳过”数量并在进度事件里报出 total_skipped_existing),从而支持“只补跑缺失项”的增量评测。

评测方法论:跑三次取平均

文档最后给出了作者的评测惯例:

通常对每组「模型/Prompt + 技术栈」组合跑 3 次测试,取这 3 次测试的平均分作为评估依据。

即同一配置重复 n=3 次(对应输出文件 _0.html_1.html_2.html),人工评分后取平均,以抵消单次生成中的随机波动。这是这套评测体系最重要的统计学约定,评分时应严格遵循,否则单次高分容易把噪声当结论。

进阶能力:评测集(Sets)与评测会话(Sessions)

当前仓库在文档所述基础上又演进出了两套配套机制,用于管理“多轮、多数据集”的评测工作流,理解它们有助于把评测从一次性脚本升级为可持续的对比工程。

评测集:sets/{名称}/inputs/

backend/evals/sets.py 定义了“具名评测集”:把 PNG 放进 {EVALS_DIR}/sets/{set_name}/inputs/ 即完成创建(无创建 API)。目录名受正则 ^[A-Za-z0-9][A-Za-z0-9._ -]*$ 约束(允许 jun-21-evalsLanding Pages v2 这类人类友好命名,同时阻断路径穿越)。每个集合附带一个 manifest.json 元数据旁挂文件:

  • 存放 display_namecreated_atnotes 等展示信息;
  • 缓存每张图的 sha256,以 (文件大小, mtime) 为新鲜度键——文件没变就直接命中缓存,变了才重新哈希;
  • 注释中说明了它的用途:图像的 sha256 是 UI 生成结果与“矩阵行”(图片 × 模型)对账的键。

评测会话:一次工作期锁定一个评测集

backend/evals/sessions.py 把“一段时间内对某个评测集的评测”建模为 session,持久化在 agent-runs 的 SQLite 索引库(schema 见 backend/fs_logging/agent_runs.py):

  • 同一时刻只有一个 active session,新建会话会使旧会话失活(会话永不“结束”,只是被取代);
  • 会话创建时固定(pin)到某个评测集:运行评测时若 active 会话属于别的集合,会抛 SessionSetMismatchError,要求先显式开新会话;若根本没有会话,则自动创建一个,保证“带 set 直接跑”的体验;
  • 评测运行时,core.py 里的 AgentRunRecorder 会把 eval_sessioneval_setinput_file 写进运行记录。由此,“会话矩阵”(图片 × 模型)就是对 runs 索引的一次普通 SQL 查询——completed_eval_inputs() 据此统计某会话中(模型, 技术栈)已完成哪些输入,供 diff 模式跳过;
  • 注释还特别强调:会话永不拦截 UI 的生成路径,它只是纯元数据,普通产品生成不受影响。

对评测者的实际意义是:以“session + set”为单位组织实验,之后任何一次补跑(diff_mode)都只补该会话里真正缺的格子,而不是凭共享日期文件夹猜文件是否存在——源码注释明确指出,按日期命名的输出文件夹是跨集合共享的,靠文件存在性判断会在集合间产生假阳性,所以集合运行一律以会话历史为准。

关键配置与产物速查

位置/取值 说明
EVALS_DIR backend/evals/config.py,默认 ./evals_data 评测数据根目录,可被环境变量覆盖
输入截图 {EVALS_DIR}/inputs/*.png 默认数据集 16 张截图;仅识别 .png
具名评测集 {EVALS_DIR}/sets/{名称}/inputs/*.png + manifest.json sha256 缓存、显示名与备注
结果文件夹 {EVALS_DIR}/results/{Mon_dd_YYYY}_{model}_{stack}/ 按 日期+模型+技术栈 隔离
输出文件 {输入名}_{尝试序号}.html 与输入截图同名前缀配对
generation_times.txt 结果文件夹内 每个成功任务的耗时(秒),首行标注模型
failed_tasks.txt 结果文件夹内 失败任务的输入/重试次数/错误信息
重试上限 MAX_EVAL_RETRIES = 2backend/evals/runner.py BudgetExceededError 不重试
评分标准 1~4 分制;每组配置跑 3 次取平均 Evaluation.md
可用模型/技术栈 GET /models 动态返回 LlmStack 枚举 以后端代码为准,勿写死

适用前提与注意事项

  • API Key:所选模型属于 OpenAI/Anthropic/Gemini 哪一族,就必须配置对应的 OPENAI_API_KEY / ANTHROPIC_API_KEY / GEMINI_API_KEY(文档示例命令只演示了 OpenAI 一族);
  • 成本与时长:批量评测会真实调用付费模型接口,16 张图 × 多模型 × 多次数叠加后开销不小,BudgetExceededError 不重试的机制就是为控住这类开销;
  • 输出目录以代码为准:文档写的是 evals_data/outputs,当前代码实际写到 evals_data/results/{日期}_{模型}_{技术栈},查看结果时以 results 下最新文件夹(/evals 页面按修改时间倒序列出)为准;
  • 数据集下载链接待补:文档中的数据集下载地址仍是 TODO,需要自备 16 张(或任意数量).png 截图作为输入;
  • 评测与产品链路同源:由于评测直接复用 build_image_prompt_messages + Agent 运行器,评测结论对线上生成质量有直接参考价值;反之,修改主链路 Prompt 后重跑同一评测集,就是最直接的回归验证手段。

综合来看,screenshot-to-code 的这套评测体系把“选数据 → 批量生成(并行 + 重试 + 计时 + 失败留痕)→ 多输出对比(Best-of-N)→ 1~4 分人工评分 ×3 次取均值”串成闭环,并以评测集、评测会话和 runs 索引把多轮实验组织成可查询的矩阵,是研究开源模型对比时相当完整、可复刻的一套方法论。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
docsdocs
暂无描述
Markdown
889
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341