返回资讯中心
外部精选
软件工程
#开发者实时流

Shopify 推出 Reflex 架构,让 AI 代理无需模拟器测试移动应用

来源标题:Reflex: An architecture for building mobile apps with agents

Shopify 公布 Reflex 架构,让 AI 代理无需模拟器即可测试移动应用行为,并用同一套测试文件同时驱动 iOS 和 Android。该架构下应用逻辑以无头方式运行,每个界面通过统一的小型契约暴露状态与操作,由 CLI 驱动。Shopify 称这解决了代理写代码快、但验证界面需模拟器和构建的反馈瓶颈。

Hacker News 最新mustafa01ali2026/11/5 · 今天30 分钟阅读

以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。

我们的架构让 agent 无需模拟器即可测试移动应用行为,并用同一套测试套件保持 iOS 与 Android 同步。

在《Native is now the future of mobile at Shopify》一文中,我们解释了为什么回归 Swift 和 Kotlin;在《Helix》一文中,我们分享了 agent 如何通过小型检查点和严格门禁重建界面。两篇文章都提到一个 CLI,它让 agent 无需模拟器就能检查应用状态并执行操作。本文讲的是它背后的架构,我们称之为 Reflex。

当我们决定原生重建应用时,我们回到起点,问了一个基本问题:当 agent 编写大部分代码时,移动应用应该怎么构建?我们最大的瓶颈是反馈循环。agent 可以在几分钟内写出一个界面,但要证明这个界面能正常工作,需要模拟器、构建、点击脚本,以及大量耐心。我们曾投入模拟器工具来加速这个过程,但很快触及了速度上限。

Reflex 就是由此诞生的。应用的逻辑以无头方式运行,每个界面都通过一份很小的契约暴露自己的状态和操作,而一个 CLI 用同一份测试文件在两个平台上驱动这份契约。这解锁了极快的反馈循环,也给了我们一种让两套原生实现保持同步的方法。下面我们逐部分说明它如何工作,以及要为你的应用构建同样的东西需要做什么。

Reflex 是一种基于 MVVM 的架构,它把应用塑造成 agent 可以快速构建、测试和驱动的东西,且无需渲染任何 UI。它建立在四大支柱之上:

这四大支柱合在一起,让 agent 能够自行探索应用,逐个功能推进,拥有极快的反馈循环,并且不依赖设备。因为同一份测试文件会同时跑在 iOS 和 Android 上,所以一个功能不可能只在一个平台上线而另一个平台跟不上。

从 agent 的视角看,它是这样的:

这就是全部的用户界面。没有 TUI、没有颜色、没有加载动画。Agent 不在乎这些,而我们没有花在终端美化上的时间,都投入到了反馈循环本身。

两个原生应用意味着每个界面都有两套实现,必须有东西让它们保持等价。我们的答案变成了一个反复回归的原则:共享测试,而非共享代码。代码写两遍,测试写一遍。它基于两个决定。

下一个问题是写什么样的测试。常见答案各有问题。

我们选择了中间路线,更接近灰盒测试。测试看不到像素,也看不到内部实现。它看到的恰好是 UI 看到的东西:ViewModel 的状态,以及 ViewModel 接受的操作。底下的一切都是真实的东西(真实的 reducer、真实的服务、真实的 GraphQL 客户端、真实的导航),只在应用原本会访问网络或设备的地方,在边缘替换为 fixture。整个测试套件里没有任何 mock 框架。

我们用一个小例子在高层次上讨论这个架构:一个简单的库存管理应用,用户从列表中选择一个产品,打开其详情界面,并调整库存数量。这足以展示整体形态;很多细节被有意省略。代码是 Swift,但 Kotlin 侧有相同的结构,读起来几乎一样。

每个平台的应用都拆成两个模块,外加一个 CLI 模块。

Core 从不导入 Mobile。Core 为它需要从平台获得的一切声明协议,并通过构造函数接收实现。这是纯粹的依赖倒置,没有 DI 框架。移动应用和 CLI 只是构造 Core 的宿主。CLI 使用 fixture 而不是真实的平台服务。

Reflex 本身没有第三方依赖:一个基础 ViewModel 类、一个用于内省的小协议(下一节),以及让 Core 保持不导入平台代码的纪律。保持这种纪律对人类来说是困难的部分。对 agent 来说,这是一个可以重复的模式,而它们擅长这个。

在 Core 内部,应用是一棵普通对象树。根上是应用对象,它拥有网络客户端、当前会话和持久化。下面的是导航器(Tab、Push、Modal),各自持有当前屏幕上路由的列表。叶子是每个界面对应的一个 ViewModel。这棵树里没有任何东西知道 UI 的存在。

View 位于 Core 之外,在 Mobile 模块中。一个 view 绑定到一个 ViewModel,读取其状态,并调用 send(action:)。这就是 view 与应用其余部分之间的全部关系。

Introspection Kit 是一个独立模块,定义 ViewModel 与 CLI 之间的契约。每个 ViewModel 得到一个小扩展,把该 ViewModel 描述为一个节点:名称、JSON 形式的状态,以及它接受的操作。CLI 和测试 SDK 只与这一层对话。应用本身从不调用它。

本节剩余部分逐一介绍这些部分:ViewModel、内省节点,以及这些节点如何组成一棵 CLI 可以遍历的树。

每个界面都由一个 ViewModel 支撑,其形态受 Elm 架构影响。组成部分:

View 的全部契约就是:读取状态,调用 send(action:)。它从不调用 ViewModel 上的方法,也从不写状态。

下面是详情界面的 ViewModel。它是伪 Swift:没有并发注解,没有访问控制,没有任何展示形态所不需要的东西。

Action 是公开表面,Message 是内部表面,update 是唯一的写入者。因为形态严格,agent 可以正确地重复几百次。

CLI 需要一种方式来读取 ViewModel 并向它发送操作。我们称这一层为 Introspection Kit,而 ViewModel 必须满足的契约只有一个协议:

(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →