MCPcopy Index your code
hub / github.com/OpenBMB/AgentCPM-GUI

github.com/OpenBMB/AgentCPM-GUI @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
617 symbols 2,136 edges 65 files 197 documented · 32%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

AgentCPM-GUI Logo

【<a href="https://github.com/OpenBMB/AgentCPM-GUI/raw/main/README.md">English</a> | 中文】

概述快速开始模型评测数据技术报告

更新日志

  • [2025-06-03] 📄📄📄 我们发布了AgentCPM-GUI的技术报告!点击这里查看。
  • [2025-05-13] 🚀🚀🚀 我们开源了AgentCPM-GUI,面向端侧的GUI Agent,拥有中英文APP操作能力,并基于RFT优化思考能力。

概述

AgentCPM-GUI是由清华大学THUNLP实验室、中国人民大学与面壁智能团队联合开发的开源端侧智能体大模型,基于MiniCPM-V构建,总参数量8B,接受手机屏幕图像作为输入,自动执行用户提出的任务。AgentCPM-GUI的主要特性包括:

  • 高质量GUI Grounding:通过在大规模中英文Android数据集上进行预训练,有效提升了对常见GUI控件(如按钮、输入框、标签、图标等)的定位与理解能力;
  • 中文APP操作能力:首个针对中文APP精细优化的开源GUI Agent,覆盖高德地图、大众点评、哔哩哔哩、小红书等30余个主流中文APP;
  • 增强的规划推理能力:通过强化微调技术(RFT),让模型输出动作前进行推理思考,有效提升复杂任务执行的成功率;
  • 紧凑的动作空间设计:采用优化的动作空间和紧凑的JSON格式,平均动作长度压缩至9.7个token,提升端侧推理的效率。

任务示例(1倍速):

https://github.com/user-attachments/assets/694d3c2c-12ce-4084-8feb-4937ca9ad247

快速开始

安装依赖

git clone https://github.com/OpenBMB/AgentCPM-GUI
cd AgentCPM-GUI
conda create -n gui_agent python=3.11
conda activate gui_agent
pip install -r requirements.txt

模型下载

从Hugging face下载模型AgentCPM-GUI,将模型保存于目录 model/AgentCPM-GUI

Huggingface推理

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
from PIL import Image
import json

# 1. 加载模型和分词器
model_path = "model/AgentCPM-GUI"  # 模型路径
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_path, trust_remote_code=True, torch_dtype=torch.bfloat16)
model = model.to("cuda:0") 

# 2. 构造输入
instruction = "请点击屏幕上的‘会员’按钮"  # 示例指令
image_path = "assets/test.jpeg"  # 你的图片路径
image = Image.open(image_path).convert("RGB")

# 3. 将图片长边缩放至1120以降低计算和显存压力
def __resize__(origin_img):
    resolution = origin_img.size
    w,h = resolution
    max_line_res = 1120
    if max_line_res is not None:
        max_line = max_line_res
        if h > max_line:
            w = int(w * max_line / h)
            h = max_line
        if w > max_line:
            h = int(h * max_line / w)
            w = max_line
    img = origin_img.resize((w,h),resample=Image.Resampling.LANCZOS)
    return img
image = __resize__(image)

# 4. 构造消息格式
messages = [{
    "role": "user",
    "content": [
        f"<Question>{instruction}</Question>\n当前屏幕截图:",
        image
    ]
}]

# 5. 推理
ACTION_SCHEMA = json.load(open('eval/utils/schema/schema.json', encoding="utf-8"))
items = list(ACTION_SCHEMA.items())
insert_index = 3
items.insert(insert_index, ("required", ["thought"])) # enable/disable thought by setting it to "required"/"optional"
ACTION_SCHEMA = dict(items)
SYSTEM_PROMPT = f'''# Role
你是一名熟悉安卓系统触屏GUI操作的智能体,将根据用户的问题,分析当前界面的GUI元素和布局,生成相应的操作。

# Task
针对用户问题,根据输入的当前屏幕截图,输出下一步的操作。

# Rule
- 以紧凑JSON格式输出
- 输出操作必须遵循Schema约束

# Schema
{json.dumps(ACTION_SCHEMA, indent=None, ensure_ascii=False, separators=(',', ':'))}'''

outputs = model.chat(
    image=None,
    msgs=messages,
    system_prompt=SYSTEM_PROMPT,
    tokenizer=tokenizer,
    temperature=0.1,
    top_p=0.3,
    n=1,
)

# 6. 输出结果
print(outputs)

预期输出:

{"thought":"任务目标是点击屏幕上的‘会员’按钮。当前界面显示了应用的推荐页面,顶部有一个导航栏。点击‘会员’按钮可以访问应用的会员相关内容。","POINT":[729,69]}

注意: AgentCPM-GUI输出范围0-1000的相对坐标,绝对坐标和相对坐标的转换关系如下:

rel_x, rel_y = [int(abs_x / width * 1000), int(abs_y / height * 1000)]
abs_x, abs_y = [int(rel_x / 1000 * width), int(rel_y / 1000 * height)]

其中,“width”和“height”分别指图像的原始宽度和高度。

vLLM推理

# 启动vLLM服务
# 如果显存不足,可以尝试参数 --max_model_len 2048
vllm serve model/AgentCPM-GUI --served-model-name AgentCPM-GUI --tensor_parallel_size 1 --trust-remote-code --limit-mm-per-prompt image=10
import base64
import io
import json
import requests
from PIL import Image

# vLLM服务启动的地址和端口
END_POINT = "http://localhost:8000/v1/chat/completions"  # Replace with actual endpoint

# system prompt
ACTION_SCHEMA = json.load(open('eval/utils/schema/schema.json', encoding="utf-8"))
items = list(ACTION_SCHEMA.items())
insert_index = 3
items.insert(insert_index, ("required", ["thought"])) # enable/disable thought by setting it to "required"/"optional"
ACTION_SCHEMA = dict(items)
SYSTEM_PROMPT = f'''# Role
你是一名熟悉安卓系统触屏GUI操作的智能体,将根据用户的问题,分析当前界面的GUI元素和布局,生成相应的操作。

# Task
针对用户问题,根据输入的当前屏幕截图,输出下一步的操作。

# Rule
- 以紧凑JSON格式输出
- 输出操作必须遵循Schema约束

# Schema
{json.dumps(ACTION_SCHEMA, indent=None, ensure_ascii=False, separators=(',', ':'))}'''

def encode_image(image: Image.Image) -> str:
    """Convert PIL Image to base64-encoded string."""
    with io.BytesIO() as in_mem_file:
        image.save(in_mem_file, format="JPEG")
        in_mem_file.seek(0)
        return base64.b64encode(in_mem_file.read()).decode("utf-8")

def __resize__(origin_img):
    resolution = origin_img.size
    w,h = resolution
    max_line_res = 1120
    if max_line_res is not None:
        max_line = max_line_res
        if h > max_line:
            w = int(w * max_line / h)
            h = max_line
        if w > max_line:
            h = int(h * max_line / w)
            w = max_line
    img = origin_img.resize((w,h),resample=Image.Resampling.LANCZOS)
    return img

def predict(text_prompt: str, image: Image.Image):
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": [
            {"type": "text", "text": f"<Question>{text_prompt}</Question>\n当前屏幕截图:(<image>./</image>)"},
            {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{encode_image(image)}"}}
        ]}
    ]

    payload = {
        "model": "AgentCPM-GUI",  # Your model name
        "temperature": 0.1,
        "messages": messages,
        "max_tokens": 2048,
    }

    headers = {
        "Content-Type": "application/json",
    }

    response = requests.post(END_POINT, headers=headers, json=payload)
    assistant_msg = response.json()["choices"][0]["message"]["content"]
    return assistant_msg

image = __resize__(Image.open("assets/test.jpeg"))
instruction = "请点击屏幕上的‘会员’按钮"
response = predict(instruction, image)
print(response)

动作空间

在每一步中,智能体都会输出一个 JSON 对象,其中包含:

  • 唯一的原子动作(需从下表中选择);
  • 可选修饰符(duration, thought)和/或任务级标志位(STATUS)。

请注意,所有关键字均 区分大小写,并且我们使用 紧凑 JSON(即无多余空格),这会影响 tokenizer 的行为。

Action 必填字段 可选字段 功能说明 例子
Click POINT:[x,y] duration,thought,STATUS 在归一化坐标系 (0–1000,原点位于左上角) 执行一次轻触。 {"POINT":[480,320]}
Long Press POINT:[x,y]

duration:1000 | duration,thought,STATUS | 在指定坐标执行长按操作(需设置较长持续时间,例如 > 200 ms)。 | {"POINT":[480,320]","duration":1000} | | Swipe | POINT:[x,y]

to:"up" \| "down" \| "left" \| "right" to:[x,y] | duration,thought,STATUS | 从起始点滑向指定方向 另一坐标。 | {"POINT":[500,200],"to":"down"} | | Press key | PRESS:"HOME" \| "BACK" \| "ENTER" | duration,thought,STATUS | 触发硬件 / 导航按键。 | {"PRESS":"HOME"} | | Type text | TYPE:"<text>" | duration,thought,STATUS | 在当前输入焦点处输入给定文本。 | {"TYPE":"Hello, world!"} | | Wait | duration | thought,STATUS | 在指定时长内保持空闲,不执行任何其他动作。 | {"duration":500} | | Task-level status | STATUS:"start" \| "continue" \| "finish" \| "satisfied" \| "impossible" \| "interrupt" \| "need_feedback" | duration,thought | 上报任务进度;可 单独 出现,也可与原子动作 同时 出现。 | {"STATUS":"finish"} |

模型微调

我们开源了训练模型的SFT和RFT代码,参考文档SFTRFT

性能评估

Grounding Benchmark

Model Fun2Point Text2Point Bbox2text Average
AgentCPM-GUI-8B 79.1 76.5 58.2 71.3
Qwen2.5-VL-7B 59.8 59.3 50.0 56.4
Intern2.5-VL-8B 17.2 24.2 45.9 29.1
Intern2.5-VL-26B 14.8 16.6 36.3 22.6
OS-Genesis-7B 8.3 5.8 4.0 6.0
UI-TARS-7B 56.8 66.7 1.4 41.6
OS-Atlas-7B 53.6 60.7 0.4 38.2
Aguvis-7B 60.8 76.5 0.2 45.8
GPT-4o 22.1 19.9 14.3 18.8
GPT-4o with Grounding 44.3 44.0 14.3 44.2

Agent Benchmark

Dataset Android Control-Low TM Android Control-Low EM Android Control-High TM Android Control-High EM GUI-Odyssey TM GUI-Odyssey EM AITZ TM AITZ EM Chinese APP (CAGUI) TM Chinese APP (CAGUI) EM
AgentCPM-GUI-8B 94.39 90.20 77.70 69.17 90.85 74.96 85.71 76.38 96.86 91.28
Qwen2.5-VL-7B 94.14 84.96 75.10 62.90 59.54 46.28 78.41 54.61 74.18 55.16
UI-TARS-7B 95.24 91.79 81.63 74.43 86.06 67.90 80.42 65.77 88.62 70.26
OS-Genesis-7B 90.74 74.22 65.92 44.43 11.67 3.63 19.98 8.45 38.10 14.50
OS-Atlas-7B 73.03 67.25 70.36 56.53 91.83* 76.76* 74.13 58.45 81.53 55.89
Aguvis-7B 93.85 89.40 65.56 54.18 26.71 13.54 35.71 18.99 67.43 38.20
OdysseyAgent-7B 65.10 39.16 58.80 32.74 90.83 73.67 59.17 31.60 67.56 25.44
GPT-4o - 19.49 - 20.80 - 20.39 70.00 35.30 3.67 3.67
Gemini 2.0 - 28.50 - 60.20 - 3.27 - - - -
Claude - 19.40 - 12.50 60.90 - - - - -

*不一致的训练/测试集划分

TM和EM分别代表类型匹配(Type Match)完全匹配(Exact Match)。我们开源了评测所用的数据和代码,更多信息请参见这里

评测数据

我们开源了面向中文APP场景的评测数据集CAGUI,涵盖groundingagent两类任务,详情见Huggingface

FAQs

点击查看 FAQs

趋势

Star History Chart

模型协议

  • 本仓库中代码依照 Apache-2.0 协议开源。

更多项目

   <img src="https://github.com/OpenBMB/MiniCPM-V/blob/main/assets/minicpmv.png" height=4

Core symbols most depended-on inside this repo

encode
called by 16
eval/utils/utils_odyssey/visual.py
generate
called by 14
eval/utils/utils_odyssey/modeling_qwen.py
convert_tokens_to_ids
called by 9
eval/utils/utils_odyssey/tokenization_qwen.py
get_dataset_dir
called by 8
eval/utils/utils.py
compact_json_dumps
called by 8
rft/trainer/utils/dataset.py
save
called by 7
eval/utils/utils_odyssey/tokenization_qwen.py
start
called by 7
rft/trainer/zmq.py
_save
called by 5
sft/trainer.py

Shape

Function 356
Method 212
Class 49

Languages

Python100%

Modules by API surface

eval/utils/utils_odyssey/modeling_qwen.py56 symbols
eval/utils/utils_odyssey/tokenization_qwen.py38 symbols
rft/trainer/zmq.py27 symbols
rft/trainer/arl.py25 symbols
eval/utils/utils_qwen/agent_function_call.py25 symbols
eval/utils/utils_odyssey/visual.py25 symbols
sft/dataset.py19 symbols
eval/utils/utils_odyssey/qwen_generation_utils.py16 symbols
eval/utils/evaluator.py16 symbols
eval/grounding_eval/code/Intern2.5-VL/evaluate_grounding_text2bbox.py13 symbols
eval/grounding_eval/code/Intern2.5-VL/evaluate_grounding_fun2bbox.py13 symbols
eval/grounding_eval/code/Intern2.5-VL/evaluate_grounding_bbox2text.py13 symbols

For agents

$ claude mcp add AgentCPM-GUI \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page