跳到主要内容

ScaleCUA — 跨平台计算机使用智能体(CUA):架构与原理

30 秒导读: ScaleCUA 是一个开源的「计算机使用智能体(CUA)」框架。你给它一句话任务(比如「把这份表格里的销售额求和」),它像人一样看屏幕截图、想下一步、点鼠标敲键盘,一步步把任务做完——而且同一套代码同时跑在 Ubuntu 桌面、Android 手机、网页浏览器三种真实界面上。它的核心工程点不是「调模型」,而是「把模型说出来的那句动作,精确落到屏幕上正确的像素」。


1. 这是什么(零基础也能懂)

  • 一句话定义: 一个让**视觉语言模型(VLM,能同时看图和读文字的大模型)**去操作真实电脑/手机/网页的自动化框架。

  • 解决什么问题 / 给谁用: 假设你想让 AI 帮你在 LibreOffice 里改个单元格、在手机 App 里发条消息、在网页上填个表单。这些界面没有 API,只能靠「看屏幕 + 动鼠标键盘」。ScaleCUA 就是把这条「看→想→动」的闭环工程化,给做 GUI 自动化、CUA 研究、Agent 评测的人用。

  • 它能做什么(功能,来自 README):

    • 一个跨 6 种操作系统、3 类 GUI 任务域的大规模数据集(ScaleCUA-Data)。
    • 训练好的跨平台通用 Agent 模型(ScaleCUA-Models)。
    • 基于 Qwen2.5-VL / InternVL 的训练代码(agent-sft/)。
    • 可交互的 Playground(Ubuntu / Android / Web 真实环境)。
    • 一套在线评测基准(OSWorld、AndroidWorld、WebArena-Lite-v2 等)。
  • 用起来什么样:playground/ 里跑一条命令,进入一个「问答式」循环——你输入任务,Agent 就在虚拟机/模拟器里自己操作,最多 20 步,边做边截图存证据:

    # playground/agent_run_interactive.py
    python agent_run_interactive.py \
    --platform ubuntu \
    --env_config_path config/envs/ubuntu.yaml \
    --agent_config_path config/agent/scalecua_native_agent.yaml
    # 然后终端提示 "Query: " —— 输入 "打开 Firefox 并搜索天气" 即可
  • 一句话直觉/类比: 把 VLM 当大脑(看截图、决定「点哪个按钮」),把 ScaleCUA 当神经+手脚——它负责把大脑那句模糊的「点登录按钮」翻译成一次精确的 pyautogui.click(x=1723, y=486),并且不管手脚长在电脑、手机还是浏览器上,大脑说的话都不用变。

本节到此,不碰底层细节。记住一句话就够:模型负责「想」,ScaleCUA 负责「把想法精确落到真实屏幕上」。


2. 顶层全景(它大概怎么转)

2.1 全景闭环图

这是一次「一步操作」的数据流。从左到右是一个循环:截图进 → 模型出动作 → 落到屏幕 → 屏幕变了再截图,循环最多 20 次直到 terminate

┌────────────────── 主循环(最多 20 步)◀─────────────────┐
│ │
任务指令 ▼ │
"求和这列" ──▶ ① 环境 Env ──截图 bytes──▶ ② Agent(大脑)──▶ ③ 归一化动作 ──▶ ④ 环境执行
(跨平台) VLM 看图+想 {name,x∈[0,1]} 反归一化→真实像素
▲ │ → pyautogui/adb/playwright
└──────────────── 屏幕状态改变,进入下一步 ◀────────────────┘

怎么读这张图: ②「大脑」有两种搭法(见 §3),但对①④环境层完全透明——环境只认第③步那个「归一化到 [0,1] 的标准动作」。这就是整个框架能跨平台的关键:动作格式统一,坐标与平台无关。

2.2 部件一句话职责

部件干什么在哪个文件
主循环 run_agent反复「截图→predict→执行」最多 20 步,存截图/动作/录像playground/agent_run_interactive.py:49
Agent(大脑)看截图 + 历史,产出「想法 + 动作」playground/agents/native_agent.py:18agentic_workflow.py:14
UI 定位器 Grounder把「点登录按钮」这类描述变成一个坐标点playground/agents/ui_grounding.py:27
VLM 引擎组装消息、调 OpenAI/Anthropic/Azure/vLLM/HF 后端playground/core/mllm.py:19core/engine.py:17
环境层 Env收归一化动作 → 反归一化 → 打到真实平台playground/envs/base_env.py:12 及各平台子类
动作空间 / 提示词定义模型「能做哪些动作」的契约(以 Python 函数签名写在系统提示里)playground/config/prompt_template/ours_ubuntu.json

2.3 主线走一遍(高层)

  1. 输入: 一句任务 + 平台配置(--platform ubuntu)。主循环先 env.get_obs() 拿到一张屏幕截图(PNG 字节流,agent_run_interactive.py:60)。
  2. 想: agent.predict(instruction, observation) 把截图 + 任务 + 历史喂给 VLM,模型回一段带 <action>click(x=..., y=...)</action> 的文本(native_agent.py:70)。
  3. 归一化: Agent 把模型输出的像素坐标除以定位尺寸,压成 [0,1] 的小数(native_agent.py:98-104)——从此坐标与具体分辨率无关。
  4. 落点: env.step(code) 里,环境把 [0,1] 乘回真实屏幕宽高得到真实像素(ubuntu_env.py:281-284),再翻译成该平台的原生调用:Ubuntu 走 pyautogui、Android 走 adb、Web 走 playwright。
  5. 循环: 屏幕变化后再截图,回到第 2 步;直到模型给出 terminate 或跑满 20 步(agent_run_interactive.py:58-101)。

目标:看懂「大盘」——一切都绕着「归一化坐标 + 统一动作」这条中轴转。


3. 巧妙之处(这套设计妙在哪)

这里只点「精华」,展开的原理与源码走读在各章。

  • 坐标契约把「跨平台」变成一句话。 模型永远只输出 [0,1] 的相对坐标,环境永远负责乘回自己的真实分辨率。于是同一个模型输出,不改一个字就能落到 1920×1080 的桌面、任意尺寸的手机、任意视口的网页。归一化断言写死在 base_env.onScreen(envs/base_env.py:30-37),反归一化各平台自己做(如 ubuntu_env.py:281-284)。→ 统一动作空间与坐标契约定位落点:smart_resize 与坐标反归一化

  • 动作空间「写成 Python 函数签名」直接塞进系统提示。 click/scroll/press/hotkey/write/wait/terminate... 这些动作不是硬编码在解析器里,而是以带 docstring 的函数定义写在提示词 JSON 里(config/prompt_template/ours_ubuntu.jsonsys_prompt_planning_cot),模型照着「写代码」的方式产出动作。改动作集只改提示词,不改框架。→ 统一动作空间与坐标契约

  • 一份 Agent 接口,两种大脑搭法。 NativeAgent(一个模型同时规划+定位)和 AgenticWorkflow(强规划模型 + 专用定位模型)对主循环暴露同一个 predict(),主循环靠配置文件里有没有 ui_grounding_model 自动二选一(agent_run_interactive.py:140-169)。想用 GPT-4o 当规划、ScaleCUA 当「眼睛」也行。→ 两种推理模式

  • smart_resize:先把图缩到模型的「视觉分辨率」,再据此反算坐标。 模型看的其实是被缩放(长宽都对齐到 28 的倍数、总像素卡在区间内)后的图,所以它吐的坐标也在那个缩放空间里;smart_resize 复现同样的缩放尺寸,才能把坐标正确除回 [0,1](agents/utils.py:39-69)。这是「落点准不准」的关键一环。→ 定位落点:smart_resize 与坐标反归一化

  • VLM 引擎抽象掉了「五种后端 + 三种消息格式」。 OpenAI、Anthropic、Azure、vLLM、HuggingFace 各有各的图片/消息封装,VLMEngine.add_message 按引擎类型分派不同的 payload 结构(core/mllm.py:108-252),上层 Agent 完全无感。本地部署(vLLM)和云 API 一键切换。→ 运行时底座


4. 阅读地图(建议顺序)

按「地基 → 大脑 → 落点 → 手脚 → 底座」由浅入深读:

  1. 先读 统一动作空间与坐标契约(一切的地基) —— 弄懂「动作长什么样、坐标为什么是 [0,1]」。这是理解后面一切的前提。
  2. 再读 两种推理模式:原生单模型 vs 规划器+定位器 —— 两种「大脑」怎么搭、predict() 里各自怎么走。
  3. 然后 定位落点:smart_resize 与坐标反归一化 —— 「点得准」背后的缩放数学与坐标解析。
  4. 接着 跨平台环境层:一份动作 → pyautogui / adb / playwright —— 同一个归一化动作在三个平台上分别怎么执行。
  5. 最后 运行时底座:VLM 多后端引擎、消息组装与主循环 —— 引擎抽象、消息拼装、20 步主循环的工程细节。

想快速判断相关性:任务涉及动作格式/坐标看 §01、规划器与定位器看 §02、点不准/缩放看 §03、某个平台怎么执行看 §04、换模型后端/主循环看 §05。


5. 代码地图(导航索引)

用符号名 grep 比行号更抗漂移。以下是理解这个子库该打开的关键文件与符号。

主题文件路径符号名
交互主循环(截图→predict→执行,20 步)playground/agent_run_interactive.py:49run_agent
按配置二选一装配 Agentplayground/agent_run_interactive.py:106main(判断 ui_grounding_model)
原生单模型 Agentplayground/agents/native_agent.py:18NativeAgent.predict
规划器+定位器 Agentplayground/agents/agentic_workflow.py:14AgenticWorkflow.predict
UI 定位器(描述→坐标点)playground/agents/ui_grounding.py:27UIGroundingAgent.generate_coords
图像缩放(视觉分辨率对齐)playground/agents/utils.py:39smart_resize
从模型输出里解析坐标点playground/agents/utils.py:72parse_point_from_string
动作文本解析成 {name,parameters}playground/agents/native_agent.py:142NativeAgent.parse_action
VLM 引擎(消息组装 + 分派后端)playground/core/mllm.py:19VLMEngine.add_message
五种模型后端(OpenAI/Anthropic/Azure/vLLM/HF)playground/core/engine.py:17LMMEngineOpenAI
带重试的安全调用playground/utils/common_utils.py:27call_llm_safe
环境基类 + 动作契约(归一化断言)playground/envs/base_env.py:12BaseEnv.onScreenagent_action
Ubuntu 执行(反归一化 → pyautogui)playground/envs/ubuntu/ubuntu_env.py:271UbuntuEnv.execute_single_action
Web 执行(playwright)playground/envs/web/web_env.py:669WebEnv.execute_single_action
Android 执行(转发到 adb 环境 API)playground/envs/android/android_env.py:286AndroidEnv.execute_single_action
动作空间/提示词契约(以函数签名书写)playground/config/prompt_template/ours_ubuntu.jsonsys_prompt_planning_cot / sys_prompt_grounding
两种模式的配置样例playground/config/agent/scalecua_native_agent.yaml / scalecua_agentic_workflow.yaml

说明:本文所有引用锚定在 commit 5d92feea9f1e14b8303ce37da45b286fb1f4d3aaagent_run_interactive.py:13from agents.framework_agent import AgenticWorkflow 的模块名与实际文件 agents/agentic_workflow.py 不一致(agentic_workflow 才是 AgenticWorkflow 的定义处),读代码时以类名为准。