AI 已经可以在几分钟内生成一个完整页面,但"代码写出来了"和"界面实现正确"之间,仍然隔着一段很长的距离。
举一个实际开发中很常见的例子:设计稿里的图标容器是 27×27pt,图片资源的逻辑尺寸是 54×54pt。AI 正确设置了 UIImageView 的 frame,却把 contentMode 写成了 center。从源码和布局数据看,容器尺寸完全正确;运行以后,图标却因为没有缩放而明显偏大。只有把它改成 scaleAspectFit,最终效果才符合设计。
这个问题揭示了 AI 编写 UI 时的一处关键盲区:
编译器只能证明代码能够成立,不能证明最终像素符合预期。
Astrolabe(星盘) 是一个开源的运行时 UI 检查工具,目标是让 AI coding agent 能够读取正在运行的 App,检查节点结构、布局和样式,观察真实截图,并用运行时证据判断自己写出的 UI 是否正确。后文统一简称为 astrolabe。
AI 需要的不只是截图
截图能够告诉 AI"这里看起来不对",却很难单独回答"为什么不对"。例如一个按钮位置异常,可能来自约束错误、父视图尺寸错误、Safe Area、Transform,也可能只是截图比例处理错误。
反过来,只读取视图层级也不够。frame 正确不代表资源缩放正确,颜色属性正确也不代表遮罩、透明度和混合后的最终像素正确。
astrolabe 将三类证据放在同一条工作链路中:
| 证据 | 回答的问题 |
|---|---|
| UI 层级与节点关系 | 页面由哪些对象组成,它们如何嵌套 |
| 运行时属性与结构化断言 | frame、字体、颜色、可见性、图片模式等逻辑值是否正确 |
| 系统截图与视觉差异 | 用户最终看到的像素是否符合预期 |
AI 因此可以完成一条真正闭环的开发流程:
diff
读取设计要求
-> 修改 UI 源码
-> 编译并运行 App
-> 检查运行时节点与截图
-> 定位差异原因
-> 修改源码并重新验证
它是怎样工作的
astrolabe 目前由三个开源仓库组成:
| 仓库 | 职责 |
|---|---|
astrolabe |
Swift Host、CLI、MCP Adapter 和 AI Skill |
astrolabe-runtime-ios |
集成在 Debug App 中,采集 UIKit 与 Core Animation 运行时数据 |
astrolabe-protocol |
定义平台无关的 Wire Protocol、JSON Schema、Fixture 和 Swift DTO |
一次检查请求会经过下面的链路:
diff
AI Agent
-> astrolabe Skill
-> TypeScript MCP Adapter
-> Swift Host
-> 模拟器 Loopback TCP / 真机 USBMux
-> iOS Runtime
-> UIKit / CALayer
Runtime 在主线程读取 UIKit 对象,在越过线程和进程边界前将其转换为不可变的协议数据。Host 负责 App 发现、快照管理、节点查询、断言、截图、Baseline 和视觉差异分析。MCP Adapter 只负责工具协议适配,不重复实现检查逻辑;Skill 则告诉 AI 什么时候应该抓取页面、如何复用快照,以及什么证据能够支持什么结论。
这种拆分让 UI 采集、通信协议、产品能力和 AI 工作流保持独立。未来增加 Android Runtime 时,可以复用 Host、MCP 和平台无关协议,而不需要把 iOS 的 UIKit 语义带到 Android 中。
当前能检查什么
astrolabe 已支持 iOS 模拟器与 USB 真机,核心能力包括:
| 能力 | 用途 |
|---|---|
| App 发现 | 找到已经启用 Runtime 的模拟器或真机 App |
| 页面概览 | 提取当前屏幕中最值得检查的文本、控件和图片节点 |
| 层级与节点查询 | 按文本、类型、语义角色、可见性等条件定位节点 |
| 节点详情 | 读取 frame、字体、颜色、图片、圆角、边框、阴影、无障碍和约束等属性 |
| 布局与样式断言 | 对节点位置、尺寸和样式执行结构化检查 |
| 原生分辨率截图 | 获取模拟器或真机的最新屏幕像素 |
| Visual Diff 与 Baseline | 比较当前页面与目标图或历史基准的像素差异 |
| 冻结快照 | 让后续查询持续基于同一个页面层级事实 |
| 临时属性实验 | 在内存中调整允许修改的展示属性,快速验证 UI 假设 |
这些能力既可以通过 CLI 使用,也可以作为 MCP tools 交给 Codex。AI 不需要解析面向人类的 Inspector 界面,而是直接消费稳定、结构化且可查询的数据。
为什么不能把整棵 UI 树直接交给 AI
真实 App 的 UI 层级远比一个 Demo 页面复杂。窗口、容器、布局包装层、重复 Cell、不可见节点、Backing Layer 和 UIKit 内部节点会共同构成一棵庞大的树。如果将完整 JSON 塞进模型上下文,不仅成本高,真正有价值的业务节点也很容易被噪声淹没。
文件压缩解决不了这个问题。即使使用 gzip 将网络传输体积压小,解压后的全部文本仍然需要进入模型上下文。
astrolabe 使用的是领域语义压缩 :完整层级保存在 Host 的本地快照中,MCP 响应只返回当前任务需要的投影,并保留 snapshotId、节点 ID 和分页游标,使 AI 能够继续追踪原始事实。
以一次生产环境 USB 真机页面测试为例:
| 数据 | 规模 | 紧凑 JSON 大小 |
|---|---|---|
| 完整页面层级 | 2,753 个节点 | 2,532,537 字节,约 2.42 MiB |
| 默认推荐节点 | 12 个节点 | 2,620 字节,约 2.56 KiB |
单次推荐结果相对完整层级缩小约 966.6 倍 ,数据量减少约 99.90% 。
这不是简单地截断前 12 个节点。处理过程包括:
- 扁平化当前 UIView 与 CALayer 层级。
- 过滤窗口、Backing Layer 和缺少有效内容的基础设施节点。
- 将候选节点按文本、控件、图片和普通可见内容分类。
- 去除表达相同内容的嵌套重复节点。
- 按页面区域轮询候选,避免结果全部集中在导航栏或某个列表区域。
- 返回有界摘要,并保留继续查询原始节点的引用。
完整的 2.42 MiB 层级事实并没有被丢弃。AI 如果发现某个推荐节点值得继续检查,可以通过节点 ID 获取完整详情;如果推荐结果不包含目标,也可以继续使用 find_nodes 在同一个快照中检索。
语义压缩的目标不是让 AI"少看一点",而是让它先看到最有价值的部分,同时保持结果可解释、可恢复和可继续查询。
快照让多步检查基于同一个页面
移动页面是动态的。AI 抓取页面后,用户可能滚动列表、打开弹窗或切换路由。如果每个工具都重新抓取当前层级,一次检查流程可能混合多个时刻的数据。
astrolabe 会为页面层级生成 snapshotId。后续的查找、分页、节点检查和样式断言只要携带这个 ID,就会继续处理同一份层级事实。
快照同时明确了证据边界:
- 页面层级被冻结,不会因为当前页面变化而被替换。
- 节点详情优先读取快照缓存;尚未读取的详情通过原始对象 ID 向 Runtime 请求。
- 如果原始 UIView 已被释放,查询会明确失败,不会在新页面中猜测一个相似节点。
- 截图始终来自调用时的最新屏幕,不能假装成旧快照时刻的像素。
这套约束看起来比"每次都取最新数据"麻烦,却能阻止 AI 使用时间上不一致的证据得出确定结论。
临时修改不是代码生成,而是假设实验
排查 UI 时,人类开发者经常在调试器里临时改一个颜色或字号,先确认方向,再回到源码正式修改。AI 同样需要这种快速反馈。
astrolabe 允许 AI 对白名单内的展示属性执行内存补丁,例如文本、字号、颜色、透明度、圆角、边框、阴影和部分约束值。不同属性可以组合,同一属性也可以反复调整,并且始终保留首次修改前的原值以便回滚。
这项能力有严格边界:
- 不调用任意业务方法。
- 不修改源码、二进制、业务模型或持久化数据。
- Runtime 停止或 App 进程退出后,补丁自动失效。
- 临时效果只能验证假设,不能作为功能已经完成的证明。
例如 AI 怀疑一个标题应该使用 20pt 而不是 15pt,可以先临时调整并观察真实页面。如果判断成立,再修改源码、重新编译,并在没有活动补丁的干净进程中完成最终验收。
谁适合使用 astrolabe
如果你的工作流中已经让 AI 编写 iOS UI,astrolabe 可以用于:
- 根据 Figma 或截图还原 UIKit 页面。
- 检查不同尺寸设备上的布局适配。
- 定位 frame 正确但渲染结果错误的问题。
- 验证字体、颜色、圆角、图片模式和 Auto Layout。
- 在真机上复现模拟器无法覆盖的 UI 差异。
- 为 AI 建立"修改、运行、观察、再修改"的自动反馈循环。
它不会替代单元测试、快照测试、XCUITest 或人工设计验收。它补充的是这些工具之间长期缺失的一层:让 AI 能够直接读取运行中的 UI,并将结构、属性和像素组织成可验证的证据。
开始体验
项目目前支持 iOS 和 Codex,Host 运行在 macOS,Runtime 只在 Debug 构建中启用。安装 Host:
bash
git clone https://github.com/regulusleow/astrolabe.git
cd astrolabe
npm run install:codex
iOS App 通过 Swift Package Manager 集成 astrolabe-runtime-ios,启动 Debug App 后,Codex 就可以通过 MCP 发现并检查页面。
项目地址:
致谢
astrolabe 的最初灵感来自 Lookin 团队在 iOS UI 检查领域的探索。Lookin 及 LookinServer 对运行时视图层级、属性读取和对象通信的实践,为这个项目提供了重要启发。在此感谢 Lookin 团队及所有参与开源贡献的开发者。
接下来
astrolabe 当前正式支持 UIKit,暂不支持 SwiftUI。虽然 Runtime 可能读取到 UIHostingController 生成的部分 UIKit hosting hierarchy,但这些节点不能等同于 SwiftUI 的声明式视图树,因此不会将其作为稳定的 SwiftUI 检查能力对外承诺。
接下来的两个主要方向是:
- 增加 Android Runtime,覆盖 Android View 与 Jetpack Compose 页面。
- 接入更多 AI coding 平台,让同一套运行时检查能力不再局限于 Codex。
平台会继续扩展,但核心目标不会改变:让 AI 获得结构化、可验证且能够追溯的运行时 UI 证据。
AI 编写 UI 的速度已经很快。接下来真正重要的,不只是让它生成更多代码,而是让它能够看见运行结果、理解差异,并对自己的实现负责。