MobileGym: A Verifiable and Highly Parallel Simulation Platform for Mobile GUI Agent Research
Paper: arXiv:2605.26114 Code: Purewhiter/mobilegym Code reference:
main@8cf1e6af(2026-05-27)
1. Motivation (研究动机)
Mobile GUI agent 现在已经能从截图和自然语言指令出发操作手机,但训练与评测环境仍然卡在一个根本矛盾上:真实日常 App 覆盖面高但不可控,emulator 环境可复现但重、慢、覆盖窄。AndroidWorld / AndroidLab 这类 emulator benchmark 可以做较稳定的系统工具或开源 App 评测,但每个实例通常需要数 GB 内存,在线 RL 很难并行扩展;MobileBench-OL 这类真机 benchmark 能覆盖真实日常 App,却要面对账号、后端状态、App 版本漂移、真实资金/消息/删除操作等不可控因素,几乎无法安全地 reset、fork 和并行 rollout。
论文把这个问题拆成四个结构性障碍:
- unreadable:日常 App 的余额、订单、私信、设置等关键状态不容易通过
adb/ accessibility tree 可靠读出;VLM judge 只能看离散截图,既贵又容易误判。 - unwritable:可复现实验需要把环境恢复到某个初始状态,但真实 App 的状态分散在本地缓存、私有存储和远程服务里,无法像游戏环境一样随意写入。
- unforkable:GRPO 等 group-based RL 需要从同一个初始状态复制出多条 rollout;真实手机和 App 后端很难提供低成本、严格相同的状态分叉。
- irreversible:发消息、转账、注销账号、删除数据等操作不能在真实系统中任意试错,这让训练和安全评估都受限。
MobileGym 试图解决的不是“再做一个更强 agent”,而是给移动 GUI agent 提供一个可验证、可写入、可 fork、低成本并行的研究环境。它的目标是保留 agent 真实需要学习的 interaction surface:屏幕、触控/输入反馈、导航、跨 App 跳转、任务相关状态变化,而不是复刻 Android 内核或真实商业后端。这个问题值得研究,因为一旦环境可控,移动 GUI agent 才能像 web/game/robotics simulator 那样进行大规模在线 RL、可复现实验、side-effect 安全诊断和低成本 benchmark 迭代。
2. Idea (核心思想)
核心洞察是:GUI agent 看到的是截图,执行的是离散动作;因此训练环境不必复刻真实 App 后端,只要复刻足够真实的交互语义,并把完整环境状态显式表示成结构化 JSON,就能同时获得交互保真和可验证性。MobileGym 用 browser-hosted Android-like simulator 承载 28 个模拟 App,把 world data、per-environment runtime overlay 和 OS runtime state 分层,让同一份结构化状态既能渲染界面,又能 reset / snapshot / fork / diff / judge。
关键创新可以概括为三点:第一,用结构化状态替代黑盒 App 后端,使 benchmark verdict 和 RL reward 可以由 deterministic state-based judge 给出;第二,用轻量浏览器实例替代 Android emulator,使单机可托管数百个并行环境,论文报告约 400 MB/instance、约 3 s cold start;第三,用 AnswerSheet 协议把 query task 的答案提交也变成 GUI 表单状态,避免自由文本匹配把“同义答案”误拒或把“推理里泄露 gold answer”误接收。
与 AndroidWorld / AndroidLab 的主要差异是:后者在 emulator 上评测真实/开源系统组件,verification 通常依赖 adb、UI tree、LLM verifier 或 app snapshot;MobileGym 则主动构造“可读写的模拟日常 App”,用 JSON state 做 ms-level snapshot/restore 和全环境 diff。与 MobileBench-OL 的差异是:MobileBench-OL 接触真实 App,但真机状态不可 fork、XPath rule 脆弱、无法做大规模并行 RL;MobileGym 牺牲真实后端,换取可控、可并行和可验证。
3. Method (方法)
3.1 Overall framework:从 task template 到 rollout / judge / reward
Figure 2 解读:这张图展示 MobileGym 的闭环。左侧先把自然语言 task template 绑定参数并 patch 到初始 structured state,例如把联系人、消息、订单等写入 runtime overlay;中间把同一个初始状态 clone 成多条并行 rollout,agent 只看到 screenshots,并通过 Tap / Type / Swipe / Home / Back / Wait / Drag / Complete 等动作交互;右侧用 state diff 检查最终状态是否满足预期目标、是否有 unexpected side effects;最后把判断结果转成 benchmark metrics 和 RL reward。这里最关键的是 state 同时服务于渲染、fork、verification 和 reward,不再需要额外的截图 VLM judge 作为主路径。
MobileGym 的执行单元是 browser page / context / browser 中的一个 Android-like simulator。论文报告在一个 server 上可跑数百个并行实例;released code 中 bench_env/env/pool.py 提供 pages、contexts、browsers 三种 isolation,bench_env/run.py 暴露 --parallel、--processes、--isolation、--browsers,对应论文中“低成本并行 rollout”的工程入口。
3.2 Interaction fidelity:只复刻 agent 需要交互的表面
Figure 1 解读:示例屏幕说明 MobileGym 并不是抽象文本环境,而是保留移动端 GUI 的视觉和操作界面:launcher、消息页、状态栏、按钮、输入框等都以截图形式暴露给 agent。agent 仍然需要从视觉状态中定位目标、点击、输入、返回、切换 App;但研究者在背后可以直接控制 JSON state。这种折中使环境既不像纯 API benchmark 那样脱离 GUI,又不像真机/模拟器那样被黑盒状态和真实后端锁住。
论文称目标是 interaction fidelity,不是 pixel-perfect Android 或真实商业后端复刻。它实现的是 agent 会接触到的交互语义:视觉 screen、touch/type response、navigation、cross-app handoff、permission / notification / keyboard / back-key dispatch、task-relevant state transition。这样做的直觉是:如果策略学习的关键是“看见页面状态 → 执行 GUI 动作 → 观察页面变化”,那么环境需要准确建模这些可观测/可操作的闭环,而不是复制不可观测的服务端实现。
3.3 Layered state model:World Data + Runtime Overlay + OS Runtime
Figure 3 解读:MobileGym 把状态分为三层。World Data 是大规模、基本只读的公共实体,例如帖子、商品、车站、地点;Runtime Overlay 是每个环境实例中可变的小状态,例如当前用户资料、草稿、点赞、设置、发送消息、对 world entity 的覆盖;OS Runtime 提供任务栈、生命周期、back dispatch、键盘、通知、共享资源、权限和 intent routing。App view 在渲染时把 runtime overlay 覆盖到 world data 上,而 benchmark 的 snapshot / reset / judge 主要信任 structured state 中可变的 runtime 部分。
这种分层有两个好处。第一,snapshot 很小:只复制每个 rollout 真正会变的 overlay,而不是复制完整 App 数据集。第二,judge 更精确:由于 agent 的写操作最终都会落到 runtime state,MobileGym 可以比较 episode 初始和终止状态,判断哪些 diff 是 task expected changes,哪些是 unexpected side effects。例如 agent 完成订票但顺手改了网络设置,截图 judge 可能看不到,state diff 可以直接发现。
released code 对应关系比较清楚:docs/platform/architecture.md 明确说明 __SIM__.getState() 返回一个包含 os 与 apps 的 JSON;bench_env/env/mobile_gym.py 的 get_state() / set_state() 通过 __SIM__ 读取和 patch 状态;apps/*/data/defaults.json、os/data/defaults.json 和各 App 的 state/navigation 文件承载 world/default/runtime 结构。
3.4 Task template 与参数化实例化
MobileGym-Bench 包含 416 个 parameterized task templates,分为 256 test 和 160 train,覆盖 28 个 App(12 everyday + 16 system)。每个 template 不是一个固定实例,而是在运行时通过三类变化实例化:
- instruction variation:同一个目标有多个语义等价表述;
- parameter sampling:slot values 从 curated sets、数值范围或当前环境状态中采样;
- environment configuration:rollout 前把联系人、订单历史、消息会话、系统设置等注入初始状态。
论文报告有限参数范围已经可产生超过 27,000 个 distinct task instances,还不包括连续范围。released code 中 bench_env/task/sampler.py 的 TaskSampler 负责从 schema、env state 或 custom sampler 中采样;bench_env/task/base.py 的 BaseTask.setup() 顺序是 reset → warm/open apps → _prepare() → get_state() → sample params → _post_sample() → initial observation。这个顺序保证 sampler 能看到 App store 已经创建后的默认状态,_post_sample() 也能根据最终参数写入任务相关初始状态。
3.5 State-based judging 与 diagnostics
MobileGym 的主评测路径是 programmatic state verification。每个 task 实现 check_goals(input) 或继承 CriteriaTask / AnswerTask 的通用逻辑,输入包含初始 state、终止 state、route 和 answer state。JudgeResult 不是只返回 success,而是拆成:
success:目标状态是否达成;clean:是否没有 unexpected state changes;progress:goal checks 通过比例;issues:未通过的具体字段;warnings:未预期 state diff。
最终 benchmark metrics 包括 SR(Success Rate)、PR(Progress Rate)、FC(False Complete:agent 说 COMPLETE 但没有真正成功)、OT(Overdue Termination:目标已达成但 agent 继续到截断)和 USE(Unexpected Side Effects)。released code 中 EpisodeResult.success 要求 stop_reason == COMPLETE 且 judge.passed,而 judge.passed = success and clean,这意味着“达成目标但产生副作用”不会被算作完整成功。
3.6 AnswerSheet:把 query answer 变成 GUI 表单状态
Figure 4 解读:传统 query task 经常要求 agent 最后输出自由文本,再由 string similarity 或 substring heuristic 判断答案;这会误拒等价表达,也会误收“推理文本中包含 gold answer 但最终答案不规范”的情况。AnswerSheet 把回答动作改成打开一个 GUI 表单 App,字段有类型和格式 hint,agent 必须像真实手机填表一样提交。judge 检查的是 answer_sheet app 的 typed state,而不是聊天输出文本。
在 released code 中,Controller.setup() 如果检测到 task 有 answer_fields 且环境支持 state injection,就向 apps.answer_sheet 注入 question、hint、fields、空 answers 和 submitted=False,并把 task instruction 追加“打开 答题卡 APP 在里面回答问题并提交”。Evaluator._evaluate_with_state() 随后读取 answer_sheet state;若是无自定义 check_goals 的 AnswerTask,会走 grounded exact field comparison;若 task 同时有 custom goals,则把表单答案 hydrate 成 JudgeInput.answer 后再让 task 自己评测。论文 Appendix 还指出 AnswerSheet 对弱模型是 conservative gate:在 19 个 AnswerSheet task 上 trained model 的 sim/real 表现接近(sim 71.1% vs real 73.7%),而 base model 在 real free-text 下高于 simulation AnswerSheet,这说明它更像格式门槛,不是泄题。
3.7 RL reward:PR-shaped dense signal + penalties
论文中的 GRPO 训练 reward 来自结构化 rollout artifact。令 为目标检查通过比例;AnswerSheet task 如果提交了表单但任何 answer field 错误,就去掉 answer_sheet.submitted 这类 bookkeeping check 后重新计算 ,避免“提交错误表单”拿到额外进度分。随后使用乘法惩罚:
这里 goal success 表示目标状态达成,clean 表示没有 unexpected state changes,false complete 表示 agent 用 COMPLETE 结束但不是 full success,post-success abort 表示已达目标却用 ABORT 结束,overdue 表示已达目标但持续行动直到截断。直觉上,reward 既鼓励部分进度,又避免 agent 通过乱改状态、错误提交、提前宣称完成或达成后不停止来刷分。
论文公式与 released code 实现差异:当前 Purewhiter/mobilegym release 包含 simulator、benchmark runner、state judge、task definitions 和 README 中的训练 recipe,但未包含 GRPO 训练 launch script / experiment config;因此上面的 GRPO hyperparameters 与 reward 公式来自论文 Appendix G / README,而不是可复现的训练脚本。另一个细节是论文 Appendix 写通用模型评测使用 loop_detect=10 和 0.8 s action wait;released CLI 的 bench_env/run.py / bench_env/config.py 默认 loop_detect=0(released code default loop_detect=0),--delay-after-action parser default 为 1.0,所以复现实验时应显式设置与论文一致的运行参数,不能只依赖 CLI default。
3.8 Python-style pseudocode:关键组件
3.8.1 参数化 task setup
async def setup_mobilegym_task(task, env):
# bench_env/task/base.py::BaseTask.setup
await env.reset(app_ids=task.apps or None)
if len(task.apps) > 1:
await env.warm_apps(task.apps)
elif len(task.apps) == 1:
await env.open_app(task.apps[0], wait_stable=True)
await task._prepare(env)
state = await env.get_state(required_apps=task.apps or None)
if task.sampler and env.supports_state_injection:
sampled = task.sampler.sample(state, task=task)
for name, value in sampled.params.items():
if name not in task.user_params:
task.params[name] = value
if env.supports_state_injection:
await task._post_sample(env)
return await env.get_observation()3.8.2 AnswerSheet 注入与 grounded evaluation
async def inject_answersheet_if_needed(task, env, eval_mode="grounded"):
# bench_env/runner/base.py::Controller.setup
obs = await task.setup(env)
if eval_mode == "grounded" and task.answer_fields and env.supports_state_injection:
await env.set_state({
"apps": {
"answer_sheet": {
"question": task.resolve_answer_question() or task.description,
"hint": getattr(task, "answer_hint", None),
"fields": task.resolve_answer_fields(),
"answers": {},
"submitted": False,
}
}
}, deep=True, reload=False)
task.task_name = task.description + " 然后打开 答题卡 APP 在里面回答问题并提交"
return obs
def evaluate_answersheet_task(task, init_obs, final_obs, exec_result):
sheet_state = final_obs.state["apps"].get("answer_sheet", {})
judge_input = JudgeInput(init_obs=init_obs, last_obs=final_obs)
if task.has_answer_fields() and task.has_no_custom_check_goals():
checks = build_grounded_checks(task, judge_input, sheet_state)
return task._evaluate_with_checks(judge_input, checks)
if sheet_state.get("submitted"):
judge_input.answer = flatten(sheet_state.get("answers", {}))
return task.evaluate(judge_input)3.8.3 state diff judge 与 side-effect filtering
def evaluate_with_state_diff(task, init_state, final_state):
# bench_env/task/base.py + bench_env/task/judge.py
judge_input = JudgeInput(init_obs=init_state, last_obs=final_state)
checks = task.check_goals(judge_input)
passed = [c["passed"] for c in checks]
goal_success = all(passed) if passed else task.is_successful(judge_input)
progress = sum(passed) / max(len(passed), 1)
all_diffs = StateComparator.diff_states(init_state, final_state)
expected = task.get_expected_changes()
unexpected = StateComparator.filter_unexpected_changes(all_diffs, expected)
return JudgeResult(
success=goal_success,
clean=(len(unexpected) == 0),
progress=progress,
issues=[c for c in checks if not c["passed"]],
warnings=unexpected,
)3.8.4 并行 rollout pool
async def run_parallel_benchmark(tasks, agent_factory, env_url, parallel=96):
# bench_env/env/pool.py + bench_env/runner/parallel.py
async with EnvPool(url=env_url, n=parallel, isolation="pages") as pool:
queue = asyncio.Queue()
for task in tasks:
queue.put_nowait(task)
async def worker(env):
agent = agent_factory()
while not queue.empty():
task = await queue.get()
init_obs, _ = await Controller.setup(env, task, eval_mode="grounded")
exec_result, init_obs, final_obs, episode, task = await Controller.run(
env, agent, task, init_obs,
max_steps=task.max_steps,
loop_threshold=10,
)
judge = await Evaluator(judge_mode="state").evaluate(
task, init_obs, final_obs, exec_result, episode
)
yield EpisodeResult.from_parts(task, exec_result, judge)
return await asyncio.gather(*(worker(env) for env in pool))3.8.5 GRPO reward shaping(论文配置;released repo 未给训练脚本)
def mobilegym_dense_reward(judge, exec_result, answer_sheet_wrong=False):
# Paper Appendix G reward; not found as a released training script.
p = judge.progress
if answer_sheet_wrong:
p = recompute_progress_without_bookkeeping_submit(judge)
reward = p
if judge.success and not judge.clean:
reward *= 0.8
if exec_result.stop_reason == "COMPLETE" and not (judge.success and judge.clean) and p > 0:
reward *= 0.8
if judge.success and exec_result.stop_reason == "ABORT":
reward *= 0.5
if exec_result.truncated and judge.success:
reward *= 0.5
return reward3.9 Code-to-paper mapping
Code reference:
main@8cf1e6af(2026-05-27) — pseudocode and mapping based on this commit
| Paper Concept | Source File | Key Class/Function |
|---|---|---|
| Browser-hosted simulator / screenshot-only interaction | bench_env/env/mobile_gym.py, bench_env/env/base.py | MobileGymEnv, ActionType, Observation, StepResult |
| Parallel lightweight rollout pool | bench_env/env/pool.py, bench_env/runner/parallel.py | EnvPool, ParallelRunner.from_config |
| Unified benchmark CLI and runtime parameters | bench_env/run.py, bench_env/config.py | create_parser, RunnerConfig.from_args |
| Task lifecycle and parameterized instantiation | bench_env/task/base.py, bench_env/task/sampler.py | BaseTask.setup, TaskSampler.sample, _prepare, _post_sample |
| Deterministic state-based judging | bench_env/task/base.py, bench_env/task/judge.py | BaseTask.evaluate, _evaluate_with_checks, JudgeResult, StateComparator.diff_states |
| AnswerSheet grounded query checking | bench_env/runner/base.py, bench_env/task/common_tasks.py | Controller.setup, Evaluator._evaluate_with_state, AnswerTask, build_grounded_checks |
| False Complete / Overdue / USE diagnostics | bench_env/runner/base.py, bench_env/task/judge.py | EpisodeResult.false_complete, EpisodeResult.overdue_termination, StateComparator.filter_unexpected_changes |
| Train/test/payment/high-risk split management | bench_env/splits.py, bench_env/splits/*.txt | resolve_split, base_task_id |
| Concrete task templates and expected state changes | bench_env/task/*/tasks.py | e.g. Reddit_CreatePostToCommunity, CriteriaTask, AnswerTask |
| App navigation and runtime data model | apps/*/navigation.declaration.ts, apps/*/data/defaults.json, system/*/navigation.declaration.ts, os/data/defaults.json | declarative routes, default data, per-app runtime stores |
| GRPO training recipe / reward formula | Paper Appendix G, README.md | released repo gives recipe summary, but no training launch/config script found |
4. Experimental Setup (实验设置)
4.1 Dataset / benchmark scale
MobileGym-Bench 共 416 个 parameterized task templates:256 test + 160 train,二者严格 disjoint。它覆盖 28 个模拟 App,其中 12 个 everyday apps 和 16 个 system apps。论文 Appendix 的 app coverage 表按类别列出:社交/内容类包括 WeChat、Reddit、X、RedBook、Bilibili、WeChat Reading、Spotify;交易/出行类包括 Alipay、eBay、Railway12306、Map;生产力/系统类包括 Calendar、Notes、Calculator、Browser、File Manager、Clock、Theme Store、SMS、Weather、Compass、Gallery、Settings、Contacts 等。
Test256 的维度分布是:Difficulty 为 L1=20、L2=73、L3=83、L4=80;Objective 为 operate=170、query=48、hybrid=38;Composition 为 atomic=22、sequential=110、transfer=56、deep_dive=68。Difficulty 不是人工直觉标签,而是用 8 个 reference models 的 mean SR / PR 做 post-hoc empirical calibration:L1 要求 SR ≥ 75% 且 PR ≥ 75%,L2 是剩余任务中 SR ≥ 25% 且 PR ≥ 50%,L3 是剩余任务中 SR > 0 且 PR ≥ 25%,L4 是其余任务。
4.2 Baselines
论文评测 9 个 agent:
- proprietary models:Gemini 3.1 Pro、Doubao-Seed-2.0-Pro、Qwen3.6-Plus;
- open-source GUI-specialized models:AutoGLM-Phone-9B、UI-TARS-1.5-8B、UI-Venus-1.5-8B、GUI-Owl-1.5-8B-Think、Step-GUI-4B;
- open-source generalist base:Qwen3-VL-4B-Instruct,也是 Sim-to-Real GRPO 训练的初始 policy。
Open-source models 用 4 trials 并重新采样参数;proprietary models 因 API cost 主要用 single run,Gemini 3.1 Pro 作为最强模型额外跑一次估计 variation。
4.3 Metrics
- SR (Success Rate):最终任务被 deterministic judge 判为成功且 clean 的比例,是主指标。
- PR (Progress Rate):sub-goal / check 通过比例,给部分完成提供密集信号。
- FC (False Complete):agent 发出
COMPLETE,但目标未达成或有 unexpected side effects。 - OT (Overdue Termination):目标已达成,但 agent 未正确停止,继续行动直到 step limit 或 loop termination。
- USE (Unexpected Side Effects):终止状态相对初始状态出现了不属于 expected outcome 的 state mutation。
4.4 Inference and training config
一般模型的 inference 配置由论文 Appendix G 给出:temperature=0.1、top_p=0.95、frequency_penalty=0、max_tokens=4096、单步 LLM call timeout 300 s、动作后等待 0.8 s、loop_detect=10。截图以 1080 x 2400 physical resolution 提供,坐标归一化到 0--1000。
Sim-to-Real 训练用 Qwen3-VL-4B-Instruct 作为初始 policy,在 MobileGym 的 160 train templates 上做 GRPO 10 steps。硬件和并行度是 single node、3 张 RTX Pro 6000、96 个并行环境实例。关键超参:vLLM asynchronous mode、group size 、train batch size 12、PPO mini-batch size 12、per-GPU micro-batch size 2、learning rate 1e-6、gradient clipping 1.0、、、KL coefficient 0.01、DAPO-style asymmetric clipping 0.2/0.28、maximum prompt length 32768、maximum response length 1024、rollout maximum model length 40960、training-time decoding temperature 0.7、validation temperature 0.1。
需要注意:这些训练配置在论文 Appendix G / README 中出现,但 released repo main@8cf1e6af 未包含完整 GRPO training launch script / experiment config;因此目前只能把它们视为 paper-reported recipe,而不是已经由公开训练脚本校验过的配置。
5. Experimental Results (实验结果)
5.1 Benchmark main results:强模型仍未饱和,L4 是 frontier discriminator
主要结果在 256-task test set 上。关键数值如下:
| 模型 | SR | PR | L1 | L2 | L3 | L4 | FC | OT | USE |
|---|---|---|---|---|---|---|---|---|---|
| Gemini 3.1 Pro | 58.8 ±1.4 | 72.1 | 97.5 | 83.6 | 63.3 | 21.9 | 34.0 | 0.2 | 5.5 |
| Doubao-Seed-2.0-Pro | 52.0† | 63.6 | 100.0 | 93.2 | 48.2 | 6.2 | 33.6 | 0.4 | 4.7 |
| Qwen3.6-Plus | 45.7† | 59.2 | 100.0 | 78.1 | 44.6 | 3.8 | 34.0 | 0.4 | 14.5 |
| AutoGLM-Phone-9B | 20.0 ±1.3 | 35.3 | 86.2 | 33.6 | 9.6 | 1.9 | 39.6 | 0.6 | 12.6 |
| UI-TARS-1.5-8B | 13.8 ±1.7 | 26.3 | 77.5 | 21.9 | 3.0 | 1.6 | 38.6 | 0.2 | 11.0 |
| UI-Venus-1.5-8B | 15.4 ±2.4 | 28.3 | 85.0 | 21.9 | 6.0 | 1.9 | 22.9 | 0.5 | 7.7 |
| GUI-Owl-1.5-8B-Think | 15.1 ±0.9 | 28.8 | 76.2 | 26.0 | 4.2 | 1.2 | 30.4 | 0.9 | 14.1 |
| Step-GUI-4B | 12.9 ±1.1 | 25.7 | 83.8 | 17.8 | 2.4 | 1.6 | 37.0 | 0.8 | 7.6 |
| Qwen3-VL-4B-Instruct | 9.4 ±0.6 | 20.1 | 71.2 | 12.3 | 0.6 | 0.3 | 15.9 | 0.4 | 10.0 |
结论有两层。第一,MobileGym-Bench 没有被当前模型打穿:总体 SR 从 9.4% 到 58.8%,L4 上只有 Gemini 3.1 Pro 还能保持 21.9%,其他 proprietary models 最高 6.2%,开源 GUI specialist 最高 1.9%。第二,USE 不随能力单调下降:9 个模型的 USE 从 4.7% 到 14.5%,说明“完成任务”和“不乱改状态”是两个不同能力维度,必须靠 full-environment state diff 才能测出来。
5.2 与已有环境的资源/能力比较
论文 Table 1 的核心对比是:AndroidWorld / AndroidLab / MobileWorld 主要基于 emulator,MobileBench-OL 基于 real device,而 MobileGym 基于 browser。MobileGym 覆盖 28 / 416 templates,verification 是 state-based programmatic,支持 full environment state comparison、JSON ms-level snapshot/restore、full JSON state customization 和 online RL-ready。资源上,MobileGym 报告约 400 MB/instance、约 50 MB core disk footprint、约 3 s cold start;AndroidWorld 对应约 4.5 GB memory、约 20 GB disk、约 78 s cold start,AndroidLab 约 6 GB memory、约 9 GB disk,MobileWorld 至少继承 AndroidWorld 的 ≥4.5 GB / ≥20 GB。
效率实验进一步报告:256 个并行实例在单 server 上 CPU 使用率 <10%,RAM 约 100 GB,完整 256-task benchmark 约 6 分钟完成。作为对比,MAI-UI 为达到 512 个并行 Android-emulator instances 需要 10 台 bare-metal cloud servers(总计 960 vCPUs、3840 GB RAM)。这说明 MobileGym 的主要价值不是单个环境更真实,而是让 online RL 的吞吐变得接近“普通 browser workload”。
5.3 GRPO simulation-side gain
用 Qwen3-VL-4B-Instruct 在 160-task train set 上做 10-step GRPO 后,256-task test set 上整体 SR 从 9.4 ±0.6 提升到 22.2 ±1.2,增益 +12.8 pt。分 difficulty:
| 模型 | L1 (20) | L2 (73) | L3 (83) | L4 (80) | All |
|---|---|---|---|---|---|
| Qwen3-VL-4B-Instruct base | 71.2 | 12.3 | 0.6 | 0.3 | 9.4 ±0.6 |
| Qwen3-VL-4B-10s trained | 92.5 | 37.7 | 11.7 | 1.2 | 22.2 ±1.2 |
| Δ training gain | +21.3 | +25.4 | +11.1 | +0.9 | +12.8 |
这个分解说明训练最有效的位置是 base model 已有中等能力的 L1/L2/L3,尤其 L2 的 +25.4 pt;L4 只涨 +0.9 pt,表明最难任务仍主要受模型基础能力限制。trained 4B 在 L1—L3 上超过 9B AutoGLM-Phone-9B,但双方在 L4 仍接近零。
5.4 Sim-to-Real transfer
Figure 5 解读:这张图按 outcome bucket 比较 simulation 与 real-device 上 base/trained 的 SR。headline subset 是 59 个 signal-bucket tasks:simulation 中 base 到 trained 从 33.9% 到 76.7%,增益 +42.8 pt;真实 Redmi Note 12 Turbo 上从 32.2% 到 72.9%,增益 +40.7 pt,保留了 simulation-side gain 的 95.1%。图的意义不是证明 MobileGym 覆盖所有真实手机分布,而是说明这种 interaction-fidelity simulator 学到的策略变化可以在真实设备上保留大部分。
real-device 评估设计比较谨慎:先用 base/trained 各 4 次 simulator rollout 把 256 个 test tasks 分桶,Uplift 26、Stable-pass 21、Mid 20、Regression 0、Stable-fail 189。从前三个 signal buckets 选择 67 个任务,其中 8 个因真实设备无法等价复现(例如不可逆账号级修改、非可回滚消费操作、需要合成历史状态)被剔除,最终 59 个 headline signal tasks;另外从 Stable-fail 随机采样 15 个作 negative-control,真实设备上 base/trained 都是 0/15 成功。
5.5 OOD case study:Reddit flair requirement

Figure 6 解读:这个真实设备案例来自 Reddit_CreatePostToCommunity。目标社区要求发帖前必须选 flair。base model 从 step 10 开始反复点击灰色 Post,直到 60 step budget 用完;trained model 第 13/14 步也先点击失败,但第 15 步注意到 Add tags & flair 上的星号和灰色按钮状态,推断还有必填项,进入 tag selector,选择 Tech,第 18 步看到 Post 变蓝后提交,并在 22 步完成。这个例子支持论文观点:MobileGym 的训练可能学到可迁移的交互 primitive,例如识别 disabled state、必填标记和重新规划,而不是只记住模拟器里的具体页面。
5.6 VLM judge audit 与 code-level judging 的成本优势
真实设备评估需要 VLM judge,但论文手工审计 59-task signal subset 的 118 条轨迹后发现 Qwen3.6-Plus judge 有 12/118 = 10.2% 误判:base 5/59,trained 7/59。用 GPT-5.4 重新 judge 同一批轨迹,aggregate error rate 仍是 12/118 = 10.2%,只是错误分布不同。这说明误判不是某个 judge 模型的偶然问题,而是 screenshot/VLM 判断本身在复杂 GUI trajectory 上不稳定。MobileGym 的 state-based judge 在 simulator 内避免了这类 API cost 和视觉误判。
成本表也量化了这点:如果 256-task full evaluation 都改用 VLM judge,估计 Qwen3.6-Plus 约 USD 2.6,GPT-5.4 约 USD 23;GRPO 100 steps / 9.6K trajectories 约 USD 100 / USD 850,1K steps / 96K trajectories 约 USD 1,000 / USD 8.5K,10K steps / 960K trajectories 约 USD 10K / USD 85K。code-level judging 在这些规模下 API cost 是 USD 0,并且没有云设备/模拟器租赁成本。
5.7 Limitations
论文自己列出的限制主要有三类。第一,视觉建模仍有差异:MobileGym 与真实 App 的细微布局、动画、App-specific icons 不完全一致,对强依赖精确图标识别的任务可能影响 transfer。第二,后端与动态内容是可控 JSON 近似:广告、弹窗、推荐流、实时消息、服务端风控、延迟 spike、server-side policy change 不会自然出现,除非显式建模为 controllable state。第三,模拟 App 只覆盖主要日常场景,不是完整 feature surface,长尾功能需要未来扩展。
更重要的边界是 safety。High-Risk subset 有 14 个任务(7 个 payment + 7 个 Test256 high-risk),Gemini 3.1 Pro 在 Payment 为 64.3%、Test256-Risk 为 71.4%、All 为 67.9%;这只是“执行能力”测量,不表示应该让 agent 执行这些不可逆操作。MobileGym 的价值在于把这类高风险能力放进无真实后果的 sandbox 中,便于后续做拒答、谨慎执行和安全对齐研究。