uiautomator2-based device control with AI vision selectors
Project description
visionauto
uiautomator2 设备控制 + AI 视觉选择器。用 u2 做"手"(截图、点击坐标、手势、弹窗 watcher),用 VLM 做"眼"(定位元素)。用法对齐 u2:d(text="你好").click(),但定位走视觉,能识别自绘/Canvas/图标按钮等无障碍树拿不到的控件。
安装
pip install -e ".[dev]" # dev 含 pytest
adb devices # 确认有一台设备在线
快速开始
import visionauto as va
d = va.connect(api_key="sk-xxx", provider="qwen") # 或纯走环境变量
d.app_start("com.tencent.mm")
d(text="微信").wait(timeout=15)
d(description="右上角的搜索按钮").click() # 图标无文字 → 语义定位
d(description="搜索输入框").input("你好")
d(text="发送").click()
配置
优先级:传参 > 环境变量 > 默认值。所有字段通用,不按 provider 区分。
| 字段 | 环境变量 | 默认 | 说明 |
|---|---|---|---|
provider |
VISIONAUTO_PROVIDER |
glm |
glm / qwen / openai |
api_key |
VISIONAUTO_API_KEY |
- | API key |
model |
VISIONAUTO_MODEL |
provider 默认 | 模型名 |
base_url |
VISIONAUTO_BASE_URL |
provider 默认 | OpenAI 兼容端点 |
temperature |
VISIONAUTO_TEMPERATURE |
0.0 |
不支持的模型自动省略 |
opencv_threshold |
VISIONAUTO_OPENCV_THRESHOLD |
0.8 |
image 兜底置信度 |
opencv_method |
VISIONAUTO_OPENCV_METHOD |
auto |
auto/template/multiscale/keypoint |
opencv_rgb |
VISIONAUTO_OPENCV_RGB |
True |
OpenCV 兜底 RGB 二次校验 |
cache_ttl |
VISIONAUTO_CACHE_TTL |
2.0 |
截图复用秒数 |
default_timeout |
VISIONAUTO_DEFAULT_TIMEOUT |
10.0 |
wait() 默认超时 |
normalize_text |
VISIONAUTO_NORMALIZE_TEXT |
True |
文字匹配前折叠空白 |
debug |
VISIONAUTO_DEBUG |
False |
调试追踪开关 |
debug_dir |
VISIONAUTO_DEBUG_DIR |
out/trace |
追踪输出目录 |
内置 provider:
| provider | 默认模型 | 默认端点 |
|---|---|---|
glm |
GLM-5V-Turbo |
智谱 open.bigmodel.cn |
qwen |
qwen3.7-max-2026-06-08 |
阿里 DashScope |
openai |
(需自备) | OpenAI 兼容任意端点 |
选择器
d(**query) 返回 Selector,懒执行。定位方式由传入的键决定:
| 键 | 定位方式 | 示例 |
|---|---|---|
text / textContains / textStartsWith / textMatches |
AI 识别所有可点击控件 + OCR 文字,客户端按模式过滤 | d(text="设置")、d(textMatches=r"设置.*") |
description |
语义定位:把自然语言描述直接喂 AI 选目标 | d(description="左上角的红色图标") |
image |
VLM 优先;找不到时 airtest OpenCV(template/multiscale/keypoint)兜底 | d(image="./btn.png") |
index |
在匹配结果里取第 n 个(从 0) | d(text="删除", index=2) |
文字匹配默认折叠空白(normalize_text=True),"设置 中心" 仍能匹配 "设置中心",规避 AI 拆字。
Selector 方法
查询类:
| 方法 | 说明 |
|---|---|
exists() |
是否存在 |
wait(timeout=None, interval=0.5) |
轮询等待出现,返回是否出现 |
wait_gone(timeout=None) |
轮询等待消失 |
count() |
匹配数量 |
all() |
返回全部匹配 Located |
get_text() |
元素文字(text 必有;description/image 命中时 AI 会 OCR 回填) |
center() |
中心点 (x, y)(绝对像素) |
bounds() |
(x1, y1, x2, y2) 绝对像素 |
动作类(任意 locator 通用,找不到抛 ElementNotFound):
| 方法 | 说明 |
|---|---|
click() |
点击中心 |
long_click(duration=None) |
长按 |
click_exists(timeout=None) |
出现就点,返回是否点了 |
input(text, clear=False) |
点击聚焦后输入文字 |
drag_to(**query, duration=0.5) |
拖到另一个元素(同张截图取两点坐标) |
swipe(direction, scale=0.9, duration=0.5) |
从该元素出发按方向滑,u2 swipe_ext 风格 |
d(text="A").drag_to(text="B") # 注意:目标是 kwargs,不是 d(...)
d(text="搜索框").input("visionauto", clear=True)
d(text="列表项").swipe("left", scale=0.5) # left/right/up/down
弹窗处理
直接用 uiautomator2 的 watcher(基于无障碍树,即时且不耗 AI)。d.watcher 已透传到 u2:
for txt in ["允许", "知道了", "暂不", "关闭"]:
d.watcher.when(txt).click() # 出现即点
d.watcher.start(2.0) # 后台每 2s 检查一次
try:
d.app_start("com.tencent.mm") # 主流程;弹窗被自动处理
finally:
d.watcher.stop()
d.watcher.reset()
标准系统弹窗(有 accessibility text)用 watcher 最稳;自绘/无障碍缺失的弹窗再用 d(description="...").click() 视觉处理。完整示例见 examples/popup_watcher.py。
直接使用 uiautomator2 API
d 透传底层 u2 设备的全部 API,无需 .u2 即可调用:
d.app_start("com.android.settings")
d.press("back") # home/back/enter/volume_up...
d.swipe(100, 800, 100, 200)
d.window_size()
d.screen_off()
d.app_stop("com.tencent.mm")
视觉选择器走 d(text=...)(__call__),u2 API 走属性访问(__getattr__ 委托),两者互不冲突。需要显式拿到原始 u2 设备时用 d.u2。
调试可视化
开启 debug 后每次 AI 识别自动落盘,跑完看 out/trace/ 回放全流程,定位是哪一步识别错了——无需手动调任何 API:
d.start_debug("out/trace") # 或 config(debug=True),或 VISIONAUTO_DEBUG=1
d(text="设置").click()
d(description="返回按钮").click()
d.stop_debug()
out/trace/ 产物:
trace.log—— 完整时间线:每步的 query / 定位类型 / 命中节点数 / 是否缓存命中 / 执行的动作。NNNN_<kind>.png—— 每次识别的标注截图:被点击的框用绿色粗框 + 写你的指令(description='...'/text='...'),其余匹配框灰色标序号;不写 OCR 文字,方便核对"prompt 是否点对了"。
临时看整屏识别结果(视觉版 dump_hierarchy):
nodes = d.dump("out/dump.png") # 返回所有 clickable 节点并保存标注图(带 OCR 文字)
坐标约定
VLM 返回 [x1,y1,x2,y2],GLM-V / Qwen-VL 通常归一化到 0-999。coords.py 自适应量纲并换算到设备像素:
max ≤ 1.0→ 视为[0,1]max ≤ norm_scale(默认 1000,provider 可在COORD_NORM_SCALE声明)→ 按norm_scale归一max > norm_scale→ 视为绝对像素,按截图实际尺寸还原
内部统一转成 [0,1] 规范坐标,再 device_x = vx * window_width。返回的 JSON 先经 json-repair 修复(容忍尾逗号、单引号、代码围栏、夹带说明文字)再解析。
Provider 与扩展模型
每个 provider 各自实现 supports_temperature(),对 thinking/reasoning 类模型(qwq、*-thinking、GLM-Z1、o1/o3 等)自动不传 temperature,普通模型传 temperature=0。
加一个新模型 provider:
from visionauto.providers.base import OpenAICompatibleProvider
from visionauto.providers import register_provider
class MyProvider(OpenAICompatibleProvider):
DEFAULT_BASE_URL = "https://api.example.com/v1/"
DEFAULT_MODEL = "my-vl-model"
def supports_temperature(self) -> bool:
return "thinking" not in (self._model or "").lower()
register_provider("my", MyProvider)
# 然后 VISIONAUTO_PROVIDER=my
架构
visionauto/
├── config.py 通用配置(api_key/model/base_url/阈值/超时/debug)
├── device.py VisionDevice: 包 u2.Device + d(...) 工厂 + 截图缓存 + dump
├── selector.py Selector: 懒执行链式 API(exists/click/wait/drag_to/swipe...)
├── located.py Located: bbox + text
├── coords.py [0,1] 规范坐标 → 设备像素,自适应 0-999/[0,1]/绝对像素
├── cache.py 截图+解析短 TTL 缓存
├── prompts.py 三套 prompt(统一 schema:clickable 节点 + bbox + OCR text)
├── utils.py json-repair 解析
├── viz.py 标注绘图(trace 指令标签 / dump OCR 标签)
├── debug.py DebugRecorder: 自动记录每次 AI 识别 + 动作时间线
├── exceptions.py ElementNotFound 等
├── providers/ 传输+鉴权层(OpenAI 兼容)
│ ├── base.py OpenAICompatibleProvider + supports_temperature
│ ├── glm.py GLM(智谱)
│ ├── qwen.py Qwen(阿里 DashScope)
│ └── openai.py 通用 OpenAI 兼容
├── matching/ image 兜底:airtest OpenCV(template/multiscale/keypoint)
│ └── opencv.py
└── locator/ 策略层
├── text.py 一次 VLM 取全部可点击控件+OCR 文字,客户端按模式过滤
├── description.py 语义定位:描述直接喂 AI
└── image.py VLM 优先,airtest OpenCV 兜底
示例
examples/ 下:
wechat_search.py—— 打开微信 → 搜索 → 进聊天 → 发消息(完整视觉流程)search_download.py—— 主页搜索"微信" → 判断第一个结果右侧"下载/打开"按钮 → 点击(含条件分支)popup_watcher.py—— u2 watcher 自动处理权限/升级弹窗_config.py—— 示例统一的 provider/model/api_key 配置(默认 qwen,env 可覆盖)
运行:
python examples/wechat_search.py
python examples/search_download.py
python examples/popup_watcher.py
测试
# 用 adb 真机截图跑三个核心 prompt,识别结果画框存到 out/
VISIONAUTO_API_KEY=... VISIONAUTO_PROVIDER=glm pytest -v -s tests/test_core_prompts.py
# 测试多个模型的图像问答能力
VISIONAUTO_PROVIDER=qwen VISIONAUTO_API_KEY=sk-... \
VISIONAUTO_TEST_IMAGE=./x.png \
VISIONAUTO_TEST_MODELS=qwen3.7-max-2026-06-08,qwen3.7-plus \
pytest -v -s tests/test_provider_vision.py
发布到 PyPI
发布前先补全 pyproject.toml 里的 authors 和 project.urls(标了 TODO),并确认 visionauto 名称在 PyPI 未被占用。
pip install -e ".[dev]" # 含 build / twine
python -m build # 生成 dist/*.whl 和 dist/*.tar.gz
twine check dist/* # 校验元数据
twine upload --repository testpypi dist/* # 先传 TestPyPI 验证
pip install -i https://test.pypi.org/simple/ visionauto # 试装
# 正式发布
twine upload dist/*
# 之后任何人: pip install visionauto
要点:
- 包代码在
visionauto/,tests/、examples/、out/不会被打进 wheel(hatchling 只打包packages = ["visionauto"])。 - 每次发布记得递增
pyproject.toml的version,PyPI 不允许覆盖已上传的版本。 - 重依赖(
airtest/opencv-python/uiautomator2)体积较大;若想瘦身可把 image 兜底依赖挪到[project.optional-dependencies]的 extra(如pip install visionauto[image]),需同步改matching/opencv.py的 import 容错(已有try/except ImportError)。
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file visionauto-0.1.0.tar.gz.
File metadata
- Download URL: visionauto-0.1.0.tar.gz
- Upload date:
- Size: 31.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
894d62b79c9a5345994583158514f4688960fbec2ad5e9ef62c1aa392cdb4eb9
|
|
| MD5 |
5addfc64efe9e2a50d6fc048c89a3b14
|
|
| BLAKE2b-256 |
aed41689031f9e3b260d1009b332d3ea623985fc684421d3cdc886271ece7810
|
File details
Details for the file visionauto-0.1.0-py3-none-any.whl.
File metadata
- Download URL: visionauto-0.1.0-py3-none-any.whl
- Upload date:
- Size: 34.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
34c4901de08d864dae08caeb296397bb4faae8976eeb78508b7cf059dac2a01e
|
|
| MD5 |
3237cfc1fab4b5115649bed8b90b037b
|
|
| BLAKE2b-256 |
69ad0df48b53bc5c4e36e16b9f201a11ff08f9542c00a24729e6339bfb1af32c
|