数据截至 (上游 commit ead48da2032c)
状态模型:每回合的 UI 快照(无障碍树 + 截图)
30 秒导读: 这个项目让 AI 用 macOS 应用。AI 不能"看见"屏幕,所以每回合动手前,它先调
get_app_state把目标窗口读成一份AppSnapshot——一段带序号的无障碍树文本加一张压缩截图。那些序号(element_index)就是 AI 后续说"点第 12 号"时用的地址。本章只讲怎么把世界读成快照;真正的点击/输入怎么落地,见 03-action-execution.md。
1. 这是什么(零基础也能懂)
一句话定义: AppSnapshot 是"某个 macOS 窗口在某一刻的可读快照"——把一个原本给屏幕阅读器用的无障碍树(Accessibility Tree),压成 AI 能读、能引用的一段文本,外加一张这个窗口的截图。
为什么需要它。 大 语言模型没有眼睛。要让它操作"飞书"的某个按钮,你得先用它能读的形式告诉它:这个窗口里有哪些元素、各自是什么、在哪儿。截图给它"看个大概",无障碍树给它"精确的元素清单和地址"。两者一起,构成模型这一回合的全部输入。
它长什么样。 一次 get_app_state 返回的文本大致是这样(示意,格式见 §5):
App=com.apple.TextEdit (pid 8123)
Window: "未命名", App: TextEdit.
0 standard window 未命名
1 button 关闭 Secondary Actions: ...
2 text entry area Value: Hello world
...
The focused UI element is 2 text entry area.
每一行开头的数字就是 element_index。模型想改那段文本,就说"对第 2 号元素 set_value";想点关闭,就说"点第 1 号"。序号是模型与真 实 UI 之间唯一的地址。
一句话直觉/类比: 把无障碍树当成"这一屏的目录页"——每个可交互元素一个编号条目;截图是"这一屏的照片"。模型先读目录、再瞄照片,然后按编号动手。
本章只讲"怎么生成这份目录和照片"。生成之后模型怎么用编号去点、去打字,是 03-action-execution.md 的事。
2. 顶层全景(一次 get_app_state 怎么转)
2.1 为什么"每回合必须先调"
这是整个交互协议的硬性前提,写死在两处对外文本里:
- 服务器说明书(
MCPServer.swift:8,computerUseServerInstructions):"Begin by calling
get_app_stateevery turn ... Codex will automatically stop the session after each assistant turn, so this step is required before interacting with apps in a new assistant turn." - 工具描述(
ToolDefinitions.swift:73,get_app_state):"This must be called once per assistant turn before interacting with the app."
原因是