今天就用 EAS Workflows 在几分钟内为 Expo 应用构建一个 AI QA Agent

Callstack 工程师用 EAS Workflows 和 AI SDK 搭建轻量级 QA agent,让 AI 生成的 Expo 应用代码在 iOS 和 Android 上自动验证。

中文
复制
Build an AI QA Agent for Expo Apps with EAS Workflows in minutes today

本文是 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,不用引入一套庞大的自建平台。

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 变化时跳过原生代码重建

  • linuxmacos runner,带虚拟化,能跑 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-device skill

  • 通过 agent-device 执行 snapshotpressscreenshot 这类 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——我尽量写得简短。

只报告必要的内容

输出保持小而有用。在我们的模板里,每个平台给出一个验证状态,取值为 passedfailedblockedunsure 之一。然后是一小节内容,包含总结、执行过的检查、发现的问题、截图,以及放在可折叠块里的完整 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 本来就在的地方,必要的信息和视觉反馈都触手可及。

QA Agent 前后对比
来自我们 QA Agent 的一条评论,“看到”了我们的应用!

💡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,你可以把它改造成任何你需要的样子(它相当灵活)。

这也是我喜欢这套方案的原因:今天就能搭起来,几分钟,从一个简单模板开始,只在真正需要更多的时候才扩展。

从这里开始:callstackincubator/eas-agent-device

来源: Expo Blog← 返回首页