UI-TARS 源码解析 #5:prompt.py 解析:COMPUTER_USE、MOBILE_USE、GROUNDING 三种提示词模板

在前几篇文章中,我们已经完成了 UI-TARS 的整体铺垫。

第一篇,我们讲了 GUI Agent 是什么。

第二篇,我们梳理了 UI-TARS 仓库结构。

第三篇,我们分析了论文里的 Enhanced Perception、Unified Action Modeling 与 System-2 Reasoning。

第四篇,我们对比了 UI-TARS 和传统 RPA 的区别。

从这一篇开始,我们正式进入源码。

今天要看的文件是:

复制代码
codes/ui_tars/prompt.py

这个文件很短,整个文件只有几十行,但它非常重要。

因为它定义了 UI-TARS 模型在执行 GUI 任务时:

复制代码
应该以什么身份行动
应该输出什么格式
可以使用哪些动作
什么时候应该思考
什么时候应该直接输出动作

换句话说:

prompt.py 是 UI-TARS 的"动作说明书"。


一、为什么 prompt.py 这么重要?

很多人读 GUI Agent 源码时,会直接跳到动作解析、坐标转换、pyautogui 执行这些部分。

但在 UI-TARS 里,prompt.py 其实是整个工程链路的起点。

原因很简单:

后面的 action parser 能不能稳定解析,取决于前面的 prompt 有没有把模型输出约束好。

如果没有 Prompt 约束,模型可能会输出:

复制代码
我建议你点击右上角的搜索框,然后输入关键词,最后按回车。

这句话人能看懂,但程序很难执行。

程序真正需要的是:

复制代码
Thought: 我需要点击搜索框以便输入关键词。
Action: click(point='<point>850 120</point>')

也就是说,Prompt 的作用不是简单地"提示模型一下",而是把一个自由聊天模型约束成一个 GUI 动作生成器。

UI-TARS 的 README 也明确说明,项目提供了三类 Prompt 模板,用来适配不同设备环境和任务复杂度:COMPUTER_USEMOBILE_USEGROUNDING。其中桌面端用于 Windows、Linux、macOS 等环境,移动端用于手机或 Android 模拟器,GROUNDING 用于只关注动作输出或模型评估的轻量场景。


二、prompt.py 里有哪三个模板?

打开 prompt.py,可以看到三个核心字符串常量:

复制代码
COMPUTER_USE_DOUBAO = """..."""

MOBILE_USE_DOUBAO = """..."""

GROUNDING_DOUBAO = """..."""

这三个模板分别对应三种使用场景:

复制代码
COMPUTER_USE_DOUBAO
适合桌面端 GUI 操作,例如 Windows、Linux、macOS。

MOBILE_USE_DOUBAO
适合移动端 GUI 操作,例如 Android 手机或模拟器。

GROUNDING_DOUBAO
适合只测试模型定位能力,通常只输出 Action,不输出 Thought。

这和 README 中的 Prompt Usage Guide 是对应的。README 里明确提到,COMPUTER_USE 支持桌面常见操作,MOBILE_USE 包含移动端特有动作,GROUNDING 则只输出 Action,不包含推理过程。

所以,prompt.py 虽然代码少,但已经把 UI-TARS 的使用场景分成了三类。


三、COMPUTER_USE:桌面端 GUI Agent 的核心模板

我们先看 COMPUTER_USE_DOUBAO

它的开头大意是:

复制代码
你是一个 GUI agent。
你会获得任务、动作历史和截图。
你需要执行下一步动作来完成任务。

这句话看似普通,但它包含三个关键信息。

第一,模型的身份是 GUI Agent。

它不是普通聊天助手,也不是只回答问题的 VLM,而是要操作图形界面的 Agent。

第二,模型不只看当前任务,还会看到 action history。

也就是说,它可以根据之前执行过什么动作,判断当前任务进行到哪一步。

第三,模型的目标不是一次性输出完整方案,而是输出"下一步动作"。

这一点非常关键。

GUI Agent 的执行方式不是:

复制代码
一次性生成完整脚本

而是:

复制代码
看当前截图
输出下一步动作
执行动作
再截图
再输出下一步动作

这就是 Agent Loop。


四、COMPUTER_USE 的输出格式:Thought + Action

COMPUTER_USE_DOUBAO 要求输出格式为:

复制代码
Thought: ...
Action: ...

源码中这个格式非常明确。prompt.pyCOMPUTER_USE_DOUBAOMOBILE_USE_DOUBAO 都要求模型输出 ThoughtAction 两部分。

这也是 UI-TARS 和普通坐标脚本的一个重要区别。

普通脚本只有动作:

复制代码
click(800, 300)

UI-TARS 要求模型先说明自己的判断:

复制代码
Thought: 我需要点击右上角的搜索框,准备输入关键词。
Action: click(point='<point>850 120</point>')

Thought 的作用有三个。

第一,帮助模型先做一步推理。

它不是看到按钮就点,而是要先判断当前状态和下一步目标。

第二,方便调试。

如果模型点错了,我们可以从 Thought 里看出它到底理解错了什么。

第三,方便后续做安全控制。

如果 Thought 里出现高风险意图,比如删除文件、提交表单、支付等,外层系统可以提前拦截。

所以,Thought + Action 并不是为了好看,而是为了让 GUI Agent 的行为更可解释、更容易调试。


五、COMPUTER_USE 的动作空间

COMPUTER_USE_DOUBAO 中定义的动作空间包括:

复制代码
click
left_double
right_single
drag
hotkey
type
scroll
wait
finished

这些动作基本覆盖了桌面软件自动化中最常见的操作。源码中可以看到,桌面端模板支持单击、双击、右键、拖拽、快捷键、输入、滚动、等待和完成任务等动作。

我们逐个分析。


六、click:最基础的点击动作

click 是最常见的动作。

它的格式是:

复制代码
click(point='<point>x1 y1</point>')

这里的 x1 y1 是模型认为应该点击的位置。

例如:

复制代码
Thought: 我需要点击搜索框。
Action: click(point='<point>850 120</point>')

注意,这里的坐标不是传统 RPA 录制出来的死坐标。

它是模型根据当前截图动态判断出来的。

后续 action_parser.py 会把这个 point 解析出来,再转换成结构化坐标,最后生成 pyautogui 点击代码。


七、left_double 与 right_single:桌面端特有操作

桌面端和移动端的一个重要区别是:

桌面端有双击和右键。

在文件管理器、桌面图标、专业软件里,这两个动作很常见。

例如:

复制代码
双击文件打开
右键弹出菜单
双击桌面图标启动程序
右键图片选择另存为

所以 COMPUTER_USE_DOUBAO 中有:

复制代码
left_double(point='<point>x1 y1</point>')

right_single(point='<point>x1 y1</point>')

这说明 UI-TARS 的动作空间不是随便定义的,而是紧贴桌面操作系统的真实交互习惯。

如果只有 click、type、scroll,很多桌面任务会很难完成。


八、drag:拖拽动作

drag 的格式是:

复制代码
drag(
  start_point='<point>x1 y1</point>',
  end_point='<point>x2 y2</point>'
)

拖拽动作适合这些场景:

复制代码
拖动窗口
拖动滑块
拖拽文件
框选内容
调整分隔线
移动地图

和 click 不同,drag 需要两个坐标:

复制代码
起点
终点

所以它对模型的视觉 grounding 能力要求更高。

模型不仅要知道从哪里开始,还要知道拖到哪里结束。

这也是为什么 GUI Agent 不能只做简单的"元素识别"。真实桌面任务里,拖拽、滚动、快捷键、右键菜单都很常见。


九、hotkey:键盘快捷键

hotkey 的格式是:

复制代码
hotkey(key='ctrl c')

源码里的注释要求:快捷键用空格分隔,使用小写,并且不要超过 3 个按键。

这个限制很有工程意义。

因为如果模型随便输出:

复制代码
hotkey(key='press control and maybe shift plus something')

后面的 parser 就很难稳定解析。

所以 Prompt 直接把格式限制死:

复制代码
ctrl c
ctrl v
alt tab
ctrl shift s

这类格式非常适合被 pyautogui 转换成:

复制代码
pyautogui.hotkey("ctrl", "c")

这就是 Prompt 和 Parser 之间的配合。

Prompt 负责让模型输出可解析文本。

Parser 负责把可解析文本转成可执行代码。


十、type:输入文本

type 的格式是:

复制代码
type(content='xxx')

源码中特别提醒,如果要提交输入,可以在 content 结尾使用换行符;同时还要求使用转义字符,保证 content 能被按正常 Python 字符串格式解析。

这点也非常关键。

因为输入内容可能包含:

复制代码
单引号
双引号
换行符
特殊字符

如果 Prompt 不提前约束,模型可能输出无法解析的字符串。

例如:

复制代码
type(content='it's good')

这里中间的 ' 就可能破坏字符串结构。

所以 Prompt 明确要求使用转义字符。

这看起来只是一个小细节,但对自动化系统很重要。GUI Agent 不只是"模型能理解",还必须"程序能解析"。


十一、scroll:滚动页面

scroll 的格式是:

复制代码
scroll(point='<point>x1 y1</point>', direction='down or up or right or left')

它不仅需要方向,还需要一个 point。

为什么滚动也需要 point?

因为在很多 GUI 里,鼠标停在哪里会影响滚动区域。

例如:

复制代码
鼠标在左侧菜单上,滚动的是菜单
鼠标在主内容区,滚动的是页面
鼠标在表格里,滚动的是表格
鼠标在弹窗里,滚动的是弹窗

所以,滚动不是简单的:

复制代码
scroll down

而应该是:

复制代码
在某个区域滚动

这就是 point + direction 的意义。


十二、wait:等待并重新观察

wait() 的注释是等待一段时间并截图检查变化。源码中说明 wait() 会 sleep 并重新截图检查是否有变化。

这个动作非常重要。

因为 GUI 操作经常不是立即生效的。

例如:

复制代码
网页加载
文件下载
弹窗出现
动画过渡
后台处理
搜索结果刷新

如果没有 wait,模型可能在页面还没加载完时连续点击,导致任务失败。

传统脚本里经常写:

复制代码
sleep(2)

但 UI-TARS 把等待也纳入动作空间。

这意味着模型可以根据当前界面判断:

复制代码
现在不应该点击
现在应该等待页面变化

这比固定 sleep 更灵活。


十三、finished:任务完成信号

finished 的格式是:

复制代码
finished(content='xxx')

它表示任务已经完成。

例如:

复制代码
Thought: 文件已经下载完成,任务结束。
Action: finished(content='The file has been downloaded.')

这个动作的意义是结束 Agent Loop。

如果没有 finished,外层系统不知道模型什么时候认为任务完成,可能会一直循环。

所以,finished 是 GUI Agent 的终止条件。

在工程实现中,外层调度器通常会判断:

复制代码
如果 action_type == finished
则停止执行

这也说明动作空间不仅用于操作界面,还用于控制整个 Agent 生命周期。


十四、COMPUTER_USE 的 Note:语言、小计划、下一步摘要

COMPUTER_USE_DOUBAO 里还有一个 Note 部分。

它要求:

复制代码
在 Thought 部分使用指定语言
写一个小计划
最后用一句话总结下一步动作和目标元素

源码中可以看到 {language}{instruction} 两个占位符:{language} 用于控制 Thought 使用的语言,{instruction} 则用于注入用户任务。

这说明 Prompt 模板不是静态文本,而是可以被外层程序格式化。

例如:

复制代码
prompt = COMPUTER_USE_DOUBAO.format(
    language="Chinese",
    instruction="打开浏览器并搜索 UI-TARS"
)

这样模型就知道:

复制代码
用户任务是什么
Thought 应该用什么语言
输出格式是什么
能用哪些动作

这里的小计划也和论文中的 System-2 Reasoning 对应。

模型不是直接输出动作,而是先做一点任务分解和当前判断。


十五、MOBILE_USE:移动端 GUI Agent 模板

接下来是 MOBILE_USE_DOUBAO

它和 COMPUTER_USE_DOUBAO 的整体结构很像,也要求输出:

复制代码
Thought: ...
Action: ...

但动作空间不同。

移动端模板包括:

复制代码
click
long_press
type
scroll
open_app
drag
press_home
press_back
finished

README 里也明确说,MOBILE_USE 适合移动设备或 Android 模拟器,并包含 long_pressopen_apppress_homepress_back 这类移动端特有动作。


十六、为什么移动端没有 hotkey、right_single、left_double?

移动端和桌面端交互模型不一样。

桌面端常见:

复制代码
鼠标右键
鼠标双击
键盘快捷键
窗口拖动
文件管理器操作

移动端常见:

复制代码
点击
长按
滑动
返回
回到主页
打开 App

所以 MOBILE_USE_DOUBAO 里没有:

复制代码
left_double
right_single
hotkey

但增加了:

复制代码
long_press
open_app
press_home
press_back

这说明 UI-TARS 并没有强行用一套完全相同的动作覆盖所有平台,而是在"统一动作建模"的基础上,为不同设备保留必要的差异。

这也是 Unified Action Modeling 的工程化体现。

统一,不等于完全一样。

统一的是动作表达方式和解析方式;差异化的是不同平台真实存在的操作。


十七、long_press:移动端长按

long_press 是移动端常见操作。

例如:

复制代码
长按复制文本
长按图片保存
长按图标弹出菜单
长按消息显示更多操作

它的格式是:

复制代码
long_press(point='<point>x1 y1</point>')

它和 click 一样需要一个坐标,但语义不同。

同一个位置,点击和长按可能触发完全不同的结果。

所以移动端必须单独定义 long_press。


十八、open_app:直接打开应用

open_app 的格式是:

复制代码
open_app(app_name='')

这个动作是移动端比较特殊的地方。

在手机任务中,用户经常会说:

复制代码
打开微信
打开地图
打开设置
打开浏览器

如果只靠点击图标,模型必须先找到桌面上的 App 图标。

但移动端系统通常可以通过应用名启动 App。

所以 open_app 是一个更高层的动作。

它不是纯鼠标坐标操作,而是系统级动作。

这说明 UI-TARS 的动作空间不只是"点哪里",还包括一些更抽象的 GUI 操作。


十九、press_home 与 press_back:移动端导航动作

移动端还有两个非常关键的动作:

复制代码
press_home()
press_back()

这两个动作对应手机系统级导航。

桌面端一般没有"返回键"和"主页键"这种统一操作,但移动端几乎所有任务都会用到。

例如:

复制代码
返回上一页
关闭当前页面
退出 App
回到桌面
重新打开另一个 App

所以 MOBILE_USE_DOUBAO 必须包含它们。

这也说明,设计 GUI Agent 的动作空间时,不能只从程序员角度想"鼠标键盘有哪些 API",还要从真实用户习惯出发:

复制代码
人在这个设备上会怎么操作?

二十、GROUNDING:只输出 Action 的轻量模板

第三个模板是 GROUNDING_DOUBAO

和前两个不同,GROUNDING_DOUBAO 不要求输出 Thought,只要求输出:

复制代码
Action: ...

它的动作空间也非常简单,基本只保留:

复制代码
click(point='<point>x1 y1</point>')

README 里说,GROUNDING 推荐用于只关注动作输出的轻量任务,或者用于模型训练与评估;它只输出 Action,不输出 Thought,适合评估 grounding 能力。

这里的 grounding 可以理解成:

给定一个目标描述,让模型指出目标在屏幕上的位置。

例如:

复制代码
用户指令:点击搜索按钮

模型输出:
Action: click(point='<point>850 120</point>')

它不关心复杂任务规划,也不关心多步骤执行,只关心:

复制代码
模型能不能找到正确元素?
模型能不能输出正确坐标?

二十一、为什么需要 GROUNDING 模板?

如果已经有 COMPUTER_USEMOBILE_USE,为什么还需要 GROUNDING

原因是:

评估"能不能看准"和评估"能不能完成任务"是两件事。

完整 GUI Agent 任务包含很多能力:

复制代码
理解任务
识别界面
规划步骤
定位元素
执行动作
观察反馈
错误恢复
判断完成

如果一个任务失败了,我们很难立刻判断失败原因。

可能是:

复制代码
模型没有理解任务
模型找错了按钮
模型坐标点偏了
动作格式解析失败
页面加载慢
执行层出错

而 GROUNDING 模板把问题简化为:

复制代码
给你截图和目标元素
你只需要输出点击坐标

这样就可以专门评估模型的视觉定位能力。

这对训练、测试和调参都很重要。


二十二、三个模板的核心区别

可以用一张表概括:

复制代码
COMPUTER_USE
场景:桌面端
输出:Thought + Action
特点:支持点击、双击、右键、拖拽、快捷键、输入、滚动、等待、完成
适合:浏览器、办公软件、文件管理器、桌面应用

MOBILE_USE
场景:移动端
输出:Thought + Action
特点:支持点击、长按、输入、滚动、打开 App、拖拽、主页键、返回键、完成
适合:手机 App、Android 模拟器、移动端任务

GROUNDING
场景:定位评估
输出:Action
特点:通常只关注点击坐标
适合:测试模型是否能把目标元素定位到屏幕坐标

从这个对比可以看出:

复制代码
COMPUTER_USE 和 MOBILE_USE 面向完整 Agent 任务;
GROUNDING 面向单步定位能力测试。

二十三、prompt.py 和 action_parser.py 是如何配合的?

prompt.py 不是孤立存在的。

它的输出格式,必须被 action_parser.py 解析。

README 的 Quick Start 中也展示了后处理过程:先用 parse_action_to_structure_output 把模型输出解析成结构化结果,再用 parsing_response_to_pyautogui_code 转换成 pyautogui 代码。

也就是说,UI-TARS 的工程链路是:

复制代码
prompt.py
约束模型输出格式
    ↓

模型输出
Thought + Action
    ↓

action_parser.py
解析动作和坐标
    ↓

pyautogui
执行鼠标键盘动作

如果 Prompt 设计得太自由,Parser 就会很复杂。

如果 Prompt 设计得太死,模型又可能不够灵活。

UI-TARS 的做法是在两者之间取平衡:

复制代码
Thought 部分允许模型自由表达推理
Action 部分必须符合严格格式

这是一种很实用的设计。


二十四、为什么 Action 要像函数调用?

你会发现,UI-TARS 的 Action 不是自然语言,而是类似函数调用:

复制代码
click(point='<point>x1 y1</point>')

type(content='xxx')

hotkey(key='ctrl c')

finished(content='xxx')

这样设计有几个好处。

第一,容易解析。

函数名就是动作类型,参数就是动作输入。

第二,容易扩展。

以后可以增加:

复制代码
screenshot()
select_file(path='xxx')
confirm()

第三,容易校验。

比如:

复制代码
click 必须有 point
drag 必须有 start_point 和 end_point
scroll 必须有 direction
type 必须有 content

第四,容易映射到底层执行层。

例如:

复制代码
click → pyautogui.click
type → pyperclip + pyautogui.hotkey
scroll → pyautogui.scroll
hotkey → pyautogui.hotkey

这种"类函数调用"的动作格式,正是 GUI Agent 工程化的关键。


二十五、为什么 point 要写成 <point>x y</point>

UI-TARS 没有直接写:

复制代码
click(x=100, y=200)

而是使用:

复制代码
click(point='<point>100 200</point>')

这看起来有点绕,但它有实际意义。

第一,<point>...</point> 可以明确标记坐标区域。

模型输出中可能有很多数字,比如文件名、日期、金额、文本内容。用 point 标签可以帮助 parser 明确哪些数字是坐标。

第二,这种格式更适合和多模态模型的 grounding 输出对齐。

模型只需要在 point 标签里输出坐标。

第三,它方便后续统一转换。

action_parser.py 可以专门识别 point 标签,再统一转换成内部的 start_box 或其他结构化格式。

这也是 Prompt 和 Parser 之间的协议。


二十六、prompt.py 的设计思想

读完三个模板后,我们可以总结出 prompt.py 的设计思想。

第一,明确模型身份。

复制代码
你是 GUI agent。

这让模型不再以聊天助手身份回答,而是以操作执行者身份输出下一步动作。

第二,明确输入上下文。

复制代码
任务
动作历史
截图

这让模型根据当前状态做决策,而不是只根据用户一句话生成计划。

第三,明确输出格式。

复制代码
Thought + Action

这让模型既能推理,又能被程序解析。

第四,明确动作空间。

复制代码
click / type / scroll / drag / hotkey / wait / finished

这避免模型自由发挥。

第五,区分设备环境。

复制代码
桌面端一套动作
移动端一套动作
Grounding 一套轻量动作

这让系统既统一,又能适配不同平台。


二十七、从二次开发角度看 prompt.py

如果你想基于 UI-TARS 做自己的桌面自动化项目,prompt.py 是最值得改造的地方之一。

比如,你可以增加自己的动作:

复制代码
screenshot()
copy_selected_text()
switch_window(title='xxx')
open_file(path='xxx')
confirm_dialog()

但要注意一点:

修改 Prompt 的同时,必须同步修改 Parser 和执行层。

例如你在 Prompt 里增加:

复制代码
switch_window(title='xxx')

那么 action_parser.py 就必须能解析它,执行层也必须知道如何切换窗口。

否则模型即使输出了这个动作,程序也无法执行。

所以,动作扩展必须遵循这条链路:

复制代码
Prompt 增加动作定义
    ↓
Parser 增加动作解析
    ↓
Executor 增加动作执行
    ↓
测试用例覆盖新动作

这也是做 GUI Agent 工程化时最重要的原则。


二十八、prompt.py 的局限

虽然 prompt.py 很关键,但它不是万能的。

它主要解决的是:

复制代码
让模型按指定格式输出动作

但它不能保证:

复制代码
模型一定理解对了界面
坐标一定点得准
动作一定安全
任务一定成功

UI-TARS README 的限制部分也提到,模型在模糊或陌生环境中仍可能产生不准确描述、误识别 GUI 元素或采取次优动作。

所以,在真实产品里,只靠 Prompt 不够。

还需要:

复制代码
动作安全校验
高风险操作确认
坐标边界检查
失败重试
异常状态检测
任务日志
人工接管

这也是 GUI Agent 和普通脚本不同的地方。

模型可以生成动作,但外层系统必须负责安全和可靠性。


总结

这篇文章我们正式分析了 UI-TARS 的 prompt.py

这个文件虽然很短,但它定义了 UI-TARS GUI Agent 的核心交互协议。

其中:

复制代码
COMPUTER_USE_DOUBAO
用于桌面端任务,支持点击、双击、右键、拖拽、快捷键、输入、滚动、等待和完成。

MOBILE_USE_DOUBAO
用于移动端任务,支持点击、长按、输入、滚动、打开 App、拖拽、主页键、返回键和完成。

GROUNDING_DOUBAO
用于轻量定位任务,只关注 Action 输出,适合评估模型 grounding 能力。

更重要的是,prompt.py 体现了 UI-TARS 的几个关键设计:

复制代码
用 Thought + Action 分离推理和执行;
用函数调用式 Action 约束模型输出;
用标准动作空间连接模型和执行层;
用不同模板适配桌面、移动端和评估场景。

所以,prompt.py 不是普通提示词,而是 UI-TARS 工程链路的第一层协议。

相关推荐
monsion2 小时前
AG-UI 协议详解:Agent 与用户之间缺失的那一层
人工智能·ui·个人开发
Full Stack Developme3 小时前
flowable-spring-boot-starter 与 flowable-ui
ui
2501_930296993 小时前
驾驭大模型的对话艺术:深度拆解 OpenAI 官方 Prompt 指南与全场景实战心法
人工智能·prompt
一键生成网站4 小时前
AI生成UI原型支持复杂逻辑交互工具对比与场景适配分析
人工智能·ui·交互·
糯米导航13 小时前
AI 视觉回归实战:截图对比不是“找不同”,如何让智能差异分析真正服务 UI 质量
人工智能·ui·回归
Rauser Mack16 小时前
AI大模型测评:GPT-5.5翻车、Claude封神——大模型选型的真相
人工智能·gpt·prompt
CedarQR21 小时前
VS Code Copilot Chat 配置 MCP Server 全攻略(以 Next AI Draw.io 为例)
人工智能·ui·json
交友如交1 天前
对于Prompt的思考:从“手写”到提示词采样、A/B Test 与自动化评测
人工智能·自动化·prompt
console.log('npc')1 天前
网页移动端UI/UX Pro Max - 设计智能skill
前端·人工智能·ui·ux