数 据截至 (上游 commit fac568eea7da)
第 3 章 · 视觉定位:把"点登录"变成屏幕坐标
这是全框架工程含量最高的一章。模型看图后只会说"点那个登录按钮",可
pyautogui需要的是精确的 (x, y)。这中间的翻译层——visual grounding(视觉定位)——决定了 SOC 到底点不点得准。
3.1 它要解决的小问题
模型的输出是语义的("登录按钮"、"搜索框"),而鼠标要的是几何的(第 312 像素、第 88 像素)。谁来把"登录"翻成 (312, 88)? 这就是定位要解决的核心矛盾。
为什么难: 模型直接报坐标往往很不准(它不擅长精确读像素);所以 SOC 大多数模式不信任模型报的坐标,而是让模型只说"点哪个东西",由框架用传统 CV 手段去找那个东西在哪。
3.2 三条定位路线
SOC 提供三种互补的定位法,对应三套系统 prompt(见 02-models-and-prompts.md):
| 路线 | 模型输出什么 | 框架怎么找坐标 | 代表模式 |
|---|---|---|---|
| OCR | "text": "登录" | EasyOCR 读全屏文 字,找匹配的那块,取包围盒中心 | 默认(gpt-4-with-ocr) |
| Set-of-Mark | "label": "~7" | YOLO 先检测所有按钮、画上红框+编号,模型点编号,框架查编号对应的框 | gpt-4-with-som |
| 直接百分比 | "x": "0.10", "y": "0.13" | 信模型报的坐标,直接用 | gpt-4(标准) |
三者殊途同归: 最后都得到一个 0~1 的 {x, y} 百分比,交给 click_at_percentage(见 01-control-loop.md)乘屏幕分辨率点下去。用百分比而非绝对像素,是为了跨分辨率无关。
3.3 路线一:OCR 文字定位(默认)
这是项目默认模式(README 说 OCR 实测比 SoM 和裸 GPT-4 都好)。思路:模型说点"登录"两个字,那我就在屏幕上用 OCR 找到"登录"这块文字,点它的中心。
图示
模型: click text="登录"
│
▼
EasyOCR.readtext(截图) ──► [(框1,"用户名"), (框2,"登录"), (框3,"注册") ...]
│ 每个元素 = (四点包围盒, 识别文字, 置信度)
▼
get_text_element:遍历找 "登录" in text ──► 命中 index=1
│
▼
get_text_coordinates:取框2四角 → 算中心 → 除以图宽高 ──► {x:0.42, y:0.31}
真实实现
在 call_gpt_4o_with_ocr 的 click 分支(apis.py:368-406):每遇到一个 click 动作,新建 easyocr.Reader(["en"]),readtext 整张截图,再调两个工具函数。
找哪一块文字 —— get_text_element(operate/utils/ocr.py:10):
# ocr.py:37-45(简化:线性扫 OCR 结果,子串匹配)
for index, element in enumerate(result):
text = element[1] # 识别出的文字
if search_text in text: # 子串包含即命中
found_index = index
注意是子串匹配(search_text in text),而且循环不 break——取最后一个命中。找不到就抛异常,由外层 except 触发回退(见 02 章)。
算坐标 —— get_text_coordinates(operate/utils/ocr.py:66):取包围盒四角的 min/max 算中心,再除以图片宽高转成百分比(ocr.py:84-98):
center_x = (min_x + max_x) / 2
center_y = (min_y + max_y) / 2
percent_x = round((center_x / width), 3)
percent_y = round((center_y / height), 3)
return {"x": percent_x, "y": percent_y}
Claude 的一个小 trick
call_claude_3_with_ocr 找文字时只用前 3 个字符去匹配(apis.py:991-993,text_to_click[:3]),注释说"限制提取文字能提高成功率"——因为 OCR 对长串识别容易出偏差,短前缀更容易命中。
3.4 路线二:Set-of-Mark(YOLO 框标注)
思路:让模型报坐标不靠谱,那就反过来——我先把屏幕上所有按钮框出来、编上号,模型只需要说"点 7 号框"。 这就是学术界的 Set-of-Mark Prompting(README 引了 arXiv 2310.11441)。
图示
截图 ──► YOLO(best.pt) 检测按钮 ──► 一堆包围盒
│
▼
add_labels:给每个非重叠框画红框 + 写 "~0" "~1" ...
│
┌─────────────────────────┴──────────────┐
▼ ▼
带标号的图发给模型 label_coordinates = {"~0":框, "~7":框...}
│ │
▼ │
模型: click label="~7" ──────────────────────────► 查 ~7 → 取框中心 → 百分比
真实实现
加载随包发布的 YOLO 权重 best.pt(apis.py:653-654,pkg_resources 取包内文件),跑检测。核心在 add_labels(operate/utils/label.py:55):
- 遍历 YOLO 检出的每个框,跳过与已画框重叠的(
is_overlapping,label.py:40),避免标号糊成一团。 - 给保留的框画红框、写
~N标签,同时把label → (x1,y1,x2,y2)存进label_coordinates(label.py:99-114)。 - 返回"带标注的图 base64" + 坐标字典。
模型回 label="~7" 后,get_label_coordinates(label.py:29)查字典拿到框,get_click_position_in_percent(label.py:141)算中心转百分比:
# label.py:153-158
x_center = (coordinates[0] + coordinates[2]) / 2
y_center = (coordinates[1] + coordinates[3]) / 2
x_percent = x_center / image_size[0]
y_percent = y_center / image_size[1]
可换权重: README 明说 best.pt 是个简单训练的 YOLOv8,鼓励用户换成自己更强的模型。
3.5 路线三:直接百分比
最朴素:系统 prompt(SYSTEM_PROMPT_STANDARD,prompts.py:11)直接要求模型输出 "x": "0.10", "y": "0.13",框架原样相信。用于 gpt-4(标准)、gemini-pro-vision、llava。
代价: 模型报坐标本就不准,这条路的点击命中率最差——正是它促成了 OCR / SoM 两条更可靠路线的出现。
3.6 巧妙之处小结
- 不信模型的坐标,信模型的"意图"。 OCR 和 SoM 都把"精确定位"从模型手里夺走,交给确定性的 CV,这是 computer-use 定位的关键洞察。
- 一切归一到百分比。 三条路殊途同归到 0~1 坐标,
click_at_percentage只需处理一种输入,分辨率无关。 - 重叠框去重(
is_overlapping)。 小细节,但决定了 SoM 标号图可不可读。 - 短前缀匹配(Claude 的
[:3])。 一行代码换来更高的 OCR 命中率。
3.7 边界与局限
- OCR 只配了英文:
easyocr.Reader(["en"])硬编码(如apis.py:377),非英文界面基本失效。 - 子串 + 取最后命中:
get_text_element若屏幕上有多处相同文字,取的是最后一个,可能点错。 - 每次 click 重建 Reader: EasyOCR 每次 click 都
easyocr.Reader(["en"])重新初始化(如apis.py:377),很慢。 - SoM 依赖一个弱 YOLO:
best.pt是简单模型,漏检/误检直接导致点空。 - 找不到就整轮回退: OCR 抛异常会把整轮甩给 gpt-4o 回退,而非局部重试。
3.8 横向对比
定位(grounding)是所有 computer-use agent 的共同命门,兄弟项目的取舍:
| 项目 | 定位手段 |
|---|---|
| SOC(本项目) | OCR / YOLO-SoM / 直接百分比,全是外挂的传统 CV |
agent-s(docs/agent-s/02-grounding-aci.md) | 独立的视觉定位模型(UI-TARS),把"描述→坐标"交给专门训练的模型 |
ui-tars-desktop(docs/ui-tars-desktop/index.md) | 端到端 GUI 模型,定位内建进模型,无需外部 OCR |
cua(docs/cua/03-composed-grounded.md) | 组合式定位,可插拔多种 grounder |
趋势很清楚: SOC 用外挂 OCR/YOLO 是 2023 年的做法;后来者把定位内化进专用模型,更准更快。但 SOC 的三路对照,恰好是理解"为什么需要专用定位模型"最好的反面教材。
3.9 代码地图
| 主题 | 文件 | 符号 |
|---|---|---|
| OCR 找文字 | operate/utils/ocr.py | get_text_element |
| OCR 算坐标 | operate/utils/ocr.py | get_text_coordinates |
| OCR click 分支 | operate/models/apis.py | call_gpt_4o_with_ocr(click 分支) |
| Claude 短前缀匹配 | operate/models/apis.py | call_claude_3_with_ocr(text_to_click[:3]) |
| YOLO 画框标号 | operate/utils/label.py | add_labels、is_overlapping |
| 查标号坐标 | operate/utils/label.py | get_label_coordinates、get_click_position_in_percent |
| SoM click 分支 | operate/models/apis.py | call_gpt_4o_labeled |
| 百分比→像素点击 | operate/utils/operating_system.py | click_at_percentage |