Expo 技能评估 是做什么的?
评估 plugins/expo/skills/ 中的技能在触发准确性、生成代码质量以及 Expo Go 中运行时渲染方面的表现。
要求:macOS 及 Xcode(iOS 模拟器)、安装了至少一个 AVD 的 Android SDK,以及 bun。不假设使用其他设备工具。
工作区根目录:/private/tmp/expo-skill-eval-<skill-name>/iteration-N/(例如 /private/tmp/expo-skill-eval-expo-ui/iteration-4/)。
开始前——明确范围
在开始任何管道工作之前,请先确认以下所有内容——不要跳过任何一项(如果请求中已明确说明某项选择,则跳过)。将它们分批放入 AskUserQuestion 调用中,每次不超过 4 个问题,按以下顺序:
- 要评估哪个技能(如果请求中未明确)。
- 提示词——驱动评估的提示词。内置提示词(来自技能的评估案例)默认全部选中;可以去掉一些,添加自定义文本提示词,或从上传的截图构建(技能必须重现的目标 UI)。详见提示词部分。
- 要验证的内容——三项多选:运行时 + 截图 / 触发准确性 / 代码检查(无设备)。详见要验证的内容部分。
- Expo SDK——最新版(默认,自动检测)或固定版本。
- 运行器——Expo Go(默认)或开发构建。
- 平台——iOS / Android / Web(始终提供所有三个选项)。
claude -p的权限标志——跳过权限(默认)或接受编辑。- 查看器交付——仅本地(默认)或发布可共享的 Artifact。
- 如果选择了触发准确性——确认已发布的
expo插件已禁用(或未安装)。
每个选项的详细信息如下。第 4-6 项(SDK、运行器、平台)可以自然地放在一个 AskUserQuestion 调用中。
如果请求中未明确要评估的技能,列出 plugins/expo/skills/ 中的可用技能,并询问要评估哪个。
被测试技能的加载方式——每个阶段两种机制(不要全局选择一种):执行器运行通过文件路径(SKILL_PATH = plugins/expo/skills/<skill>/SKILL.md,显式读取)引用该技能,而触发评估则将其作为插件加载(--plugin-dir plugins/expo,因此模型可以根据其描述自动选择)。两者都指向本地、仓库内的版本——这就是你正在评估的内容。你不需要任何特殊标志来启动测试框架会话本身(测试框架通过仓库路径找到技能);这些机制适用于它生成的 claude -p 子进程。请参阅步骤 1 和 3 以了解为什么每个阶段不同。一个必需的预运行检查(当触发评估在范围内时): 如果已发布的 expo 插件已安装/启用,请在启动测试框架之前禁用(通过 /plugin),并在之后重新启用。一次禁用是一个全局配置更改,此会话和生成的 claude -p 子进程都会继承。为什么触发评估需要它:该阶段通过 --plugin-dir 加载本地技能,并且第二个已安装的 expo 会与之冲突——模型可能会触发已发布的 expo:expo-ui,由于检测只看到工具调用名称,你会默默地给已发布的描述打分,而不是你本地编辑的描述(冲突也可能导致错误)。执行器/运行时/静态阶段不会受到影响——它们通过本地 SKILL_PATH 读取被测试的技能,且不带 --plugin-dir——因此运行时不包含触发评估可以跳过禁用。禁用 expo 不会禁用 expo-skill-eval(一个独立的项目技能,不属于 expo 插件的一部分),因此测试框架仍然可用。
将这一点以明确的预先确认形式展示给用户——就像确认要评估哪个技能一样。当触发评估在范围内时,请在开始步骤 1 之前*让用户确认已发布的 expo 插件已禁用(或未安装);如果它仍然启用,暂停并让他们通过 /plugin 禁用它。在用户确认之前不要运行触发评估——测试框架无法自行可靠地检测已安装的插件(读取全局插件配置或 claude plugin list 会提示),因此这是手动确认,而不是自动检查。
选择提示词——内置、自定义或目标截图。 提示词是驱动执行器的输入(带技能和不带技能);它们与你验证的内容是分开的。使用 AskUserQuestion 确认它们(如果请求中已指定提示词,则跳过):
- 内置提示词——通过阅读被测试的技能(其
SKILL.md+references/)和references/runtime-matrix.md生成的代表性提示词,涵盖技能的标准用例。(如果技能已经在其evals/evals.json中附带了评估案例,也将其prompt字段纳入——但大多数技能没有,因此你通常自己推导。)默认全部选中,因此默认运行会锻炼技能的标准案例;让用户取消选择任何预选。 - 自定义文本提示词——用户输入的一次性提示词。不要为此专门分配一个选项槽:
AskUserQuestion会自动添加一个 "输入内容" / 其他条目,并且在那里输入的任何内容都会成为自定义文本案例。 - 从上传的截图构建——用户提供目标截图的路径(要重现的 UI)。执行器被告知打开它——
claude -p使用其 Read 工具读取 PNG——并构建匹配的应用程序;案例将路径记录为reference_image,评分将生成的应用程序与目标进行比较(步骤 6)。这是 UI 技能最强的视觉测试:"构建这个。"
遵守 AskUserQuestion 的每个问题 4 个选项上限,并遵循以下优先级(需要避免的错误:上传选项在四个槽位填满时被静默丢弃):
- 始终为 "从上传的截图构建" 预留一个槽位。 这是视觉评估的全部意义,绝不能成为被丢弃的选项。
- 不要添加显式的 "自定义文本提示词" 选项——自动的 "输入内容" / 其他条目已经覆盖了它。
- 将剩余的 ≤3 个槽位填入内置/代表性提示词,默认选中。如果超过 3 个,将它们合并为一个默认选中的 "所有内置提示词(默认)" 选项,并在简短后续询问中提供子集选择,以便上传选项仍然适合。
将其呈现为多选。当选择了 "从上传的截图构建" 时,在后续询问中提供目标图像路径。每个选中的提示词(内置、输入或图像)成为一个评估案例(带技能和不带技能运行)。
始终确认要验证的内容,除非请求中明确说明。提供以下选项,让用户选择一个或多个(基于技能的 references/runtime-matrix.md 条目建议的默认值为粗体):
| 选项 | 作用 | 何时建议为默认值 |
|---|---|---|
| 运行时 + 截图 | 完整管道:fixture → 执行器 → 静态检查 → 在 iOS/Android 上运行应用程序并截图。运行器(Expo Go 或开发构建)是独立问题——不要在这里提及。 | 默认用于任何渲染应用程序屏幕的技能(references/runtime-matrix.md 中的 expo-go/dev-build 行)。需要已启动的模拟器/模拟器。 |
| 触发准确性 | 通过 claude -p 运行逼真的提示词,检查技能是否被读取。衡量召回率(仅限应触发的查询)。 | 始终作为独立检查有用。 |
| 代码检查(无设备) | tsc --noEmit + 差异感知 lint + expo export,以及评分者根据你提供的任何自定义期望检查生成的代码。无需设备。 | 默认用于 static-only 和 n/a 技能,以及每次你想验证代码模式(正确的导入路径、Host 包装器等)而不运行应用程序时。 |
将这些呈现为一个多选问题——"你想验证什么?" 这些是评分维度(如何判断构建的内容)——与提示词阶段(构建什么)不同。用户可以选择任意组合。当提示词是上传的截图(参见提示词)时,包括 "运行时 + 截图",以便测试框架捕获生成的应用程序,并且评分者可以将其与目标进行对比。
在建议之前阅读 references/runtime-matrix.md 以找到技能的默认模式。如果请求已经指定了模式(例如 "只检查它是否触发","在设备上运行"),跳过该问题并继续。
选择 Expo SDK 版本——一次,提前完成。 使用 bash /abs/path/expo-skill-eval/scripts/latest-sdk.sh 检测最新版本(它打印主版本号,例如 56;内部使用 bun 运行 npm view expo dist-tags --json 并通过 JSON.parse/semver 读取主版本,且该脚本受 bash-scripts 规则覆盖——因此不要自己内联运行注册表查询,这会提示)。然后使用 AskUserQuestion 确认:默认使用该最新 SDK,或让用户固定一个较旧的版本(例如重现特定版本的问题)。在构建 fixture 的任何地方使用所选版本——将其作为 <sdk> 参数传递给 make-fixture.sh,并将其写入每个评估案例的 runtime.sdk。如果请求已经指定了版本("在 SDK 54 上评估"),跳过检测并使用它。
默认使用最新版——它与 expo start 在设备上安装的 Expo Go 保持兼容。固定一个早于设备上安装的 Expo Go 的 SDK 会使 expo start 尝试提示 "安装推荐的 Expo Go 版本?";由于缺少 TTY(快照脚本从 /dev/null 读取 stdin),它会因 Input is required, but 'npx expo' is in non-interactive mode 而死,并且每个快照都会失败。因此,仅在你同时预先在模拟器/模拟器上安装匹配的 Expo Go 时才固定较旧的 SDK——否则坚持使用最新版。
选择运行器——Expo Go(默认)或开发构建。 使用 AskUserQuestion 询问(如果请求已说明,则跳过):
- Expo Go(默认)——快照脚本按原样使用
expo start --ios/expo start --android运行应用程序。速度快(无原生编译),并且可以运行 Expo Go 捆绑的任何内容(包括 SDK 56 上的@expo/ui)。无法运行自定义原生代码(expo-modules、配置插件、Expo Go 中不存在的原生依赖项)。 - 开发构建——快照脚本改为运行
expo run:ios/expo run:android,为每个 fixture 编译原生开发客户端。适用于需要自定义原生代码的技能输出(否则会是static-only的情况)。速度慢得多——expo run会预先构建并原生编译每个 fixture(需要几分钟,尤其第一次),并且需要完整的 iOS/Android 构建工具链——因此仅在技能实际需要原生代码时才选择它。磁盘密集型: 每个 fixture 的原生构建大小为数 GB。快照阶段在每个 fixture 后运行clean-fixture.sh,以将峰值使用量控制在约一个构建,但仍然建议在开发构建运行中使用更少的评估案例和单一平台,并保持几 GB 的可用空间。clean-fixture.sh移除每个 fixture 的构建输出(node_modules、ios、android、.expo、dist以及 fixture 的 iOS DerivedData),并保留应用程序源代码和 git。开发构建的磁盘控制杠杆是更少的评估案例 + 单一平台——它仅回收每个 fixture 的构建输出,从不触及共享的依赖项缓存,因此无需重新下载任何内容。
通过 EXPO_SKILL_EVAL_RUNNER 环境变量(expo-go 默认或 dev-build)将选择传递给快照脚本,并将其反映在每个评估案例的 runtime.mode(expo-go 或 dev-build)中。参见步骤 5。
选择平台——始终询问,无论技能如何。 使用 AskUserQuestion 提供 iOS / Android / Web(多选);默认为 iOS + Android,但始终将 Web 作为选项提供——不要根据技能预过滤。Web 对大多数技能来说是有效选择:@expo/ui 的通用组件(Host、Row、Column、Button、List 等)在 Web 上渲染,expo-dom、NativeWind/Tailwind、API 路由和纯 React Native 也是如此。唯一不会在 Web 上显示的是特定于平台的原生树(@expo/ui/swift-ui 或 @expo/ui/jetpack-compose),它会显示空白——而这空白本身就是一个有用的信号,因此仍然由用户决定。Web 通过 snapshot-web.sh(expo start --web + Playwright/Chromium)运行,无论运行器如何(expo run 仅限原生;没有 Web 开发构建),并且是最少被执行的路径。将所选平台写入每个评估案例的 runtime.platforms,并让 run_snapshots.py 循环处理它们。
确认 claude -p 子进程如何运行——一次,开始之前。 使用 AskUserQuestion 询问它们是否可以使用 --dangerously-skip-permissions 运行,然后将相同答案应用于本次运行的每个子进程(不要在运行期间重新提示):
- 跳过权限(推荐)——传递
--dangerously-skip-permissions。每个子进程在/private/tmp/expo-skill-eval-*下的可丢弃 fixture 内无人值守运行,可以无需提示地写入文件并运行设置命令。 - 仅接受编辑——改为传递
--permission-mode acceptEdits。Bash/安装将被自动拒绝(无 TTY),因此某些评估可能产生部分输出。
既没有 --dangerously-skip-permissions 也没有 --permission-mode acceptEdits 的裸 claude -p 根本无法写入文件。如果请求已经指定了偏好("跳过权限"、"不要使用危险标志"),跳过该问题。
确认如何交付结果查看器——一次,提前完成。 发布到 claude.ai 是对外的,因此永远不要在运行中途意外进行;在同一个预先的 AskUserQuestion 中询问(连同权限标志):
- 仅本地(默认)——
generate_viewer.py写入viewer.html并在本地浏览器中打开。不会离开机器。 - 发布可共享的 Artifact——除此之外,在最后将查看器渲染为 claude.ai Artifact(一个默认私有的网页,用户可以分享给队友)。仅当用户在此选择加入时才执行。
如果请求已经说明了是否分享/发布,跳过该问题。有关发布机制,请参阅查看器部分。
评估案例模式
你生成运行的评估案例——每个选中的提示词对应一个——并将它们写入 <workspace>/iteration-N/evals.json(查看器从中读取)。每个案例 e
…
[因翻译截断]