今天就用 EAS Workflows 在几分钟内为 Expo 应用构建一个 AI QA Agent
Callstack 工程师用 EAS Workflows 和 AI SDK 搭建轻量级 QA agent,让 AI 生成的 Expo 应用代码在 iOS 和 Android 上自动验证。
中文
复制

本文是 Michał Pierzchała 的客座文章——他是 Callstack 研发孵化器的首席工程师,创建了 agent-device、React Native Testing Library。
…
AI agent 很擅长快速产出大量代码。代码越多,针对代码库的 PR 就越多,对一套能正常工作、并能随 agent 生成代码的规模一起扩展的代码质量保障流程的需求也就越大。
AI agent 参与后端代码库时,靠集成测试通常就能做得不错;前端这边就没这么乐观了。要生成产出 UI 的代码,比如移动端 iOS 和 Android 应用,最新的模型往往表现出色,甚至不用检查结果(感谢 React 的声明式 UI 让这件事变简单了)。但很多时候它们就是会跑偏。想象一个硬核 React Native 开发者只能读代码,手边没有移动设备。光看代码,你有多大把握他能把活干对?提示:把握不大。验证才是关键。
这正是我们想为 Expo 应用补上的缺口。本文会告诉你如何用现有工具做到这一点,不额外花钱。开始吧!
用 EAS Workflows,你已经可以复用构建、在 Android 和 iOS 上运行自定义 job、在 GitHub pull request 上评论。事实证明,这已经足够今天搭出一个轻量级 QA agent,不用引入一套庞大的自建平台。

我在这里放了一个最小模板:callstackincubator/eas-agent-device。
这套设置刻意做得很小:
-
使用 CNG 的 Expo 应用
-
用 EAS Workflows 做编排
-
一个用 AI SDK 写的极小 Node.js QA agent
-
用
agent-device做 Android 和 iOS 自动化 -
一条 GitHub 评论,附上 Android 和 iOS 的 QA 结果
重点不在“AI”这个标签,而在于你几分钟内就能有一个可用的基线,之后随着团队需要更多覆盖范围再逐步扩展。
目标
对每个 pull request,我们希望:
尽可能复用现有的移动端 release 构建,并带上最新的 JS
启动 emulator 或 simulator
安装并启动应用
让 agent 检查 UI 并截图
在 PR 下贴一份简短的 QA 总结
就这样。不是什么庞大的测试框架,也不打算取代所有 E2E 测试。只是在移动端 UI 改动周围加一个实用的 QA 闭环。
为什么 EAS Workflows 很适合这件事
主要原因是 EAS Workflows 本身就懂移动端 CI 的那些特殊之处。
开箱即用的能力:
-
fingerprint检测原生改动 -
get-build找到可复用的构建 -
repack在只有 JS 变化时跳过原生代码重建 -
linux和macosrunner,带虚拟化,能跑 Android 和 iOS 设备 -
github-comment把结果发回 PR
所以不必把移动端自动化硬塞进一个通用 CI 系统,整条流水线可以留在移动端工具本来就在的地方。这样配置起来也容易理解得多。
这里要说明一下,Android 任务需要一个 linux-medium-nested-virtualization 镜像才能打开 Android Emulator,这个得从零装;iOS Simulator 需要 macos-medium 镜像(或更大),不过模拟器本身已经可用。不得不说,这些机器的灵活程度、以及能脚本化到什么程度,让我挺意外。
从这样的 workflow 结构开始
核心 workflow 很简单,一屏就能放下:
jobs:
fingerprint:
type: fingerprint
android_get_build:
type: get-build
params:
platform: android
profile: qa-release
android_repack:
type: repack
android_build:
type: build
qa_android:
runs_on: linux-medium-nested-virtualization
steps:
- uses: eas/checkout
- uses: eas/install_node_modules
- uses: eas/download_build
- id: provision_android_emulator
run: bash ./scripts/agent-qa/provision-android-emulator.sh
- id: run_agent_qa
run: bash ./scripts/agent-qa/run-and-export.sh "${{ steps.download_build.outputs.artifact_path }}"
env:
AGENT_DEVICE_SESSION: qa-android
AGENT_DEVICE_PLATFORM: android
qa_comment:
type: github-comment
iOS 是同样的思路,只是跑在 macOS worker 上,用 simulator 构建。完整可用的版本,Android 和 iOS 并行运行,在仓库里:.eas/workflows/agent-qa-mobile.yml
关键设计取舍:把 bootstrap 和 QA 拆开
这一点我强烈建议照做。
乍一看,你可能会想让 agent 包办一切:安装应用、打开、导航、检查、汇报。实际做下来,这会让系统变得不必要地脆弱。Agent 会用错我们的工具,会不去读我们要求它读的说明,也会给正在用的 CLI 编造出根本不存在的 flag(我踩过)。
所以,要让 AI agent 稳定地为我们干活、而不是偶尔灵一次,关键在于尽可能让 workflow 保持确定性。用 agent-device 就能很轻松地脚本化这些步骤,保证在设备上安装和打开应用时,bootstrap 参数永远正确:
#!/usr/bin/env bash
# Phase 1: deterministic bootstrap
agent-device install "${APP_ID}" "${APP_PATH}"
agent-device open "${APP_ID}" --relaunch
agent-device 我们之所以知道该跑哪个平台,是因为在 job 里设置了 AGENT_DEVICE_PLATFORM 环境变量。
QA 流程中还有一部分更难脚本化,可以交给 agent 驱动。agent 会从 PR 推断验收标准,通过 accessibility tree 以省 token 的方式检查 UI,做少量导航,截图,并总结发生了什么
# Phase 2: variable agent-driven flow
npm run agent-qa
过去一个月我构建过各种 agent,从我的经验看,这种拆分让整个工作流可靠得多。这意味着 agent 永远不必猜 artifact 路径或安装命令。
agent 可以保持得非常小
应用已经在运行之后,agent 只需要几个工具:
-
读取 PR 上下文
-
加载
agent-deviceskill -
通过
agent-device执行snapshot、press、screenshot这类 UI 操作 -
写最终报告
简化版大致是这样:
import { ToolLoopAgent } from 'ai';
const agent = new ToolLoopAgent({
model: 'openai/gpt-5.4-mini',
instructions: `
You are a mobile QA agent running inside EAS Workflows.
Treat the app as a black box.
Infer acceptance criteria from the PR.
The app is already installed and launched.
Use agent-device to inspect the UI, navigate, take screenshots, and write a report.
If the result is visually plausible but not fully confirmed from structured UI output, use "unsure".
You must call write_report exactly once.
`,
tools: {
get_pr_context,
load_skill,
read_skill_file,
agent_device,
write_report,
},
});
这足以让我们快速起步,再迭代到足够好。我用的是 Vercel 的 AI SDK,它在可控性和开箱即用之间平衡得不错。在此之上,我可以通过他们的 AI Gateway 服务访问所有我喜欢的模型(不加价,至少目前如此);如果你不想再多一份订阅,也可以用你熟悉的 OPENAI_API_KEY,改用 openai provider。这一点同样适用于其他 provider,不只 OpenAI。
完整的 agent 在这里:scripts/agent-qa/index.ts——我尽量写得简短。
只报告必要的内容
输出保持小而有用。在我们的模板里,每个平台给出一个验证状态,取值为 passed、failed、blocked、unsure 之一。然后是一小节内容,包含总结、执行过的检查、发现的问题、截图,以及放在可折叠块里的完整 JSON 报告(主要用于调试失败的 QA 验证)。
最后那个状态 unsure 很重要。移动端 UI 光靠结构化自动化输出并不总是容易验证。有时 accessibility tree 帮助很大,有时截图是最有力的证据。如果 agent 无法干净地证明结果,它就应该如实说明,并附上图片。
这比假装自己知道要好得多。
最后一步:在 Pull Request 下留一条评论
和人工验证一样,我们往往只需要在工作下面留一条简短的评论,就能大致了解 agent 验证了什么、没验证什么,同时附上视觉确认——要真正判断这次改动有没有弄坏应用,我们常常很需要它。一条 PR 评论就足够好用:
## Agent QA
| Platform | Status |
| -------- | --------- |
| Android | ✅ passed |
| iOS | 🤔 unsure |
### Android
Short summary...
Screenshots
### iOS
Short summary...
Screenshots
这样结果就出现在 reviewer 本来就在的地方,必要的信息和视觉反馈都触手可及。

💡GitHub 评论没有官方 API 可以上传截图,所以在这个例子里我们用 Vercel Blob 作为第三方云存储来存放图片。你可以换成适合自己场景的方案,比如 AWS S3。
如果你想现在就上手,我的建议
在 Expo dashboard 里把 GitHub 项目接入 EAS Workflows。
先从一个平台开始,比如 Android。
使用 CNG,并保持原生构建复用开启。
把 QA 构建 profile 和生产环境分开。
让 bootstrap 保持确定性。
让 agent 只做黑盒。
只发一条 PR 评论,不要发十份不同的产物。
尽早加上截图。它们帮助很大。
等这套跑通之后,再逐步扩展:
-
加上 iOS
-
把截图上传到 Blob 存储
-
加上更好的 selector
-
把成功的探索性检查转成更确定性的流程
核心结论
你不需要一个庞大的 AI 测试平台,也能获得有用的移动端 QA 自动化。
Expo 加上 EAS Workflows 已经提供了大部分基础设施:
-
构建复用:快速迭代,并通过重新打包拿到最新的 JS bundle
-
移动端 CI worker:带脚本能力和虚拟化,模拟器和仿真器随时可用
-
工作流编排:把这一切串起来
-
GitHub 集成:降低认知负担,让验证留在 Pull Request 里
在此基础上,一个定制的 QA agent 可以做得小得惊人。而且得益于基于 TypeScript 的 AI SDK,你可以把它改造成任何你需要的样子(它相当灵活)。
这也是我喜欢这套方案的原因:今天就能搭起来,几分钟,从一个简单模板开始,只在真正需要更多的时候才扩展。