AI云识别
低于 4002 的版本里没有这个模块,from ascript.ios.vlm import ascript_ai
会直接抛 ModuleNotFoundError。请先把 AScript 升级到 4002 或更新的版本。
from ascript.ios.vlm import ascript_ai as ai
iOS 不支持加载插件,所以 AI 识别直接内置在 AScript 中,导入即用。
Android 那边为了兼容老版本,同一套能力是以 ascript_ai 插件的形式提供的,
要先 plug.load("ascript_ai")。两端的功能和参数完全一致,只有导入方式不同,
跨端移植脚本时改这一行即可。
用自然语言描述在屏幕上找东西、问值、问页面状态,推理跑在 AScript AI Studio 云端。
不需要模板图、不需要固定文字、不需要训练模型 —— 直接用一句话描述你要什么。
- 没有模板图、也没有固定文字,只能靠语义描述的目标:"那个红色的关闭按钮"
- 界面改版频繁,写死的坐标和模板图天天失效
- 需要理解页面语义:"当前是不是登录页"、"列表里价格最低的那一项"
- 从屏幕上提取结构化数据:把商品列表读成
[{"name":..., "price":...}] - 图色/OCR 都试过但认不出来的兜底方案
能用 FindImages.find()(有模板图)或 Ocr.paddleocr()(有固定文字)解决的场景,
不要用AI云识别。那些是毫秒级且免费,这个是秒级且按量计费。
AI云识别是给"前两者做不到"的场景兜底的,不是用来替代它们的。
准备工作
1. 获取密钥
登录 AScript AI Studio → 账户中心 → 「API 调用」页面创建密钥。
密钥形如 sk-as- 开头的字符串,明文只在创建时显示一次,请立即保存。丢失只能重新创建。
每个账号最多 20 个密钥。密钥创建时会封存当时的登录凭证,如果账号中心让该凭证失效,
调用会返回 credential_expired,需要重新登录网页并重新创建密钥。
2. 初始化
from ascript.ios.vlm import ascript_ai as ai
ai.init(api_key="sk-as-xxxxxxxx")
也可以设置环境变量 ASCRIPT_AI_KEY,这样脚本里连 init() 都可以省掉。
快速开始
1. 第一个脚本
from ascript.ios.vlm import ascript_ai as ai
ai.init(api_key="sk-as-xxxxxxxx")
r = ai.find("右上角的购物车图标")
print(r)
# {'text': '购物车', 'rect': [960, 120, 1040, 200],
# 'center_x': 1000, 'center_y': 160, 'confidence': 1.0}
find() 的返回结构与 Ocr.paddleocr() 的每一项完全一致
(text / rect / center_x / center_y / confidence),
OCR 认不出来的时候可以直接换过来用。
设了环境变量 ASCRIPT_AI_KEY 的话,连 ai.init() 这行都可以省掉。
2. 找到并点击
最常用的一句。click() 内部会自己截屏、定位、点中心点,找不到返回 False。
ai.click("底部的立即购买按钮")
# 点不到时要有兜底,别默认它一定成功
if not ai.click("同意并继续"):
print("没找到那个按钮,换个描述试试")
想自己控制点击方式(长按、拖拽),就用 find() 拿坐标:
from ascript.ios import action
r = ai.find("列表里第一个商品的缩略图")
if r:
action.click(r["center_x"], r["center_y"], 800) # 长按 800ms
3. 判断页面状态
# 在不在
if ai.exists("登录按钮"):
ai.click("登录按钮")
# 是不是(返回真正的 bool,不是字符串)
if ai.ask_value("当前是不是支付成功页", value_type=bool):
print("下单完成")
# 让模型用自己的话描述(原文,只适合打印给人看)
print(ai.ask("当前是什么页面,用户在做什么"))
ask() 的返回是模型原文,格式会飘,不要拿去做 if 判断。
判断一律走 exists() 或 ask_value(..., value_type=bool)。
4. 从屏幕上取数据
ask_value() 用 Python 类型声明你要什么(参数名 value_type),返回值就是那个类型:
count = ai.ask_value("购物车里有几件商品", value_type=int) # -> 3
total = ai.ask_value("订单总金额是多少", value_type=float) # -> 128.5
title = ai.ask_value("标题栏写的什么", value_type=str) # -> '订单确认'
paid = ai.ask_value("是否已经支付", value_type=bool) # -> False
多个值用 [T]:
names = ai.ask_value("所有商品名称", value_type=[str]) # -> ['面包', '牛奶']
prices = ai.ask_value("每件商品的价格", value_type=[float]) # -> [12.5, 8.0]
返回 None 表示模型说它答不出来,要判一下再用:
n = ai.ask_value("有几条未读消息", value_type=int)
if n is None:
print("看不出来") # 图里没有相关信息
elif n == 0:
print("一条都没有") # 计数为零是真实答案,和上面不是一回事
else:
print("有 %d 条" % n)
5. 把一张列表读成结构化数据
用带字段名的 dict 声明记录,字段名会一并告诉模型,它才好对号入座:
items = ai.ask_value("列表里所有商品", value_type=[{"name": str, "price": float}])
# -> [{'name': '面包', 'price': 12.5},
# {'name': '牛奶', 'price': 8.0}]
for it in items or []:
print(it["name"], it["price"])
单条记录就不加外面那层 []:
info = ai.ask_value("这个商品的信息",
value_type={"title": str, "price": float, "stock": int})
# -> {'title': '面包', 'price': 12.5, 'stock': 30}
6. 等待页面变化
r = ai.wait("加载完成的商品列表", timeout=20)
if r is None:
print("等超时了")
每轮都是一次完整的云端推理(几秒)并且都要计费,timeout=20 大概只够跑
三五轮。不要图省事写 timeout=300。
7. 只看一块区域:又快又省
rect=[left, top, right, bottom]。这是最值得养成的习惯 ——
裁剪比降采样保真得多,图更小、更快、更省钱,而返回的仍然是完整屏幕坐标,
不用你自己加偏移。
# 只看底部 1/4 屏
ai.find("确定按钮", rect=[0, 1800, 1080, 2400])
# 只看顶部状态栏
ai.ask_value("现在电量百分之多少", value_type=int, rect=[0, 0, 1080, 100])
8. 一次截屏,多次提问
默认每次调用都会自动截一次屏。同一个画面要问好几件事时,自己截一次复用:
from ascript.ios import screen
shot = screen.capture() # 返回 PIL.Image,可直接传给 ai
total = ai.ask_value("总价", value_type=float, image=shot)
count = ai.ask_value("商品件数", value_type=int, image=shot)
addr = ai.ask_value("收货地址", value_type=str, image=shot)
image= 还接受 ndarray 和图片路径,见下文。
iOS 还有个更省事的办法:screen.cache(True) 开启截图缓存后,
后续的 capture() / 找图 / 找色都复用同一张截图,不用自己传来传去。
用完记得 screen.cache(False) 关掉,否则画面变了还在用旧图。
9. 描述不准时,用 hint 补充上下文
ai.find("确定按钮", hint="在底部弹窗里,不是顶部导航栏那个")
ai.ask_value("图中算式的结果", value_type=int, hint="只算红框里那一道")
小目标看不清时再考虑调清晰度(但优先试 rect):
ai.find("底部那个很小的图标", image_tokens=2048)
10. 给找图 / OCR 做兜底
推荐的用法不是"全用 AI",而是快的先上,认不出来再交给 AI:
from ascript.ios.screen import Ocr
hits = Ocr.paddleocr(pattern="立即购买") # 毫秒级,免费,返回 list
r = hits[0] if hits else None
if r is None:
r = ai.find("底部的立即购买按钮") # 秒级,计费,但认得出语义
if r:
action.click(r["center_x"], r["center_y"])
Ocr.paddleocr() 返回的是列表(ai.find() 返回单个 dict 或 None),
取到第一项之后两者结构一致,后面的代码不用分叉。
11. 完整示例:自动下单
from ascript.ios import action, screen
from ascript.ios.vlm import ascript_ai as ai
ai.init(api_key="sk-as-xxxxxxxx")
BOTTOM = [0, 1700, 1080, 2400] # 操作区基本都在下半屏,固定裁这块
try:
# 1) 确认在商品详情页
if not ai.ask_value("当前是不是商品详情页", value_type=bool):
raise SystemExit("页面不对,先手动进详情页")
# 2) 记下价格,超预算就不买
price = ai.ask_value("这个商品的价格", value_type=float)
if price is None or price > 200:
raise SystemExit("价格不合适:%s" % price)
# 3) 走结算流程
if not ai.click("立即购买", rect=BOTTOM):
raise SystemExit("没找到立即购买")
if ai.wait("确认订单页面的提交订单按钮", timeout=20) is None:
raise SystemExit("确认订单页没出来")
# 4) 提交前核对总价(一次截屏问两件事)
shot = screen.capture()
total = ai.ask_value("订单总金额", value_type=float, image=shot)
addr = ai.ask_value("收货地址", value_type=str, image=shot)
print("总价 %s,寄到 %s" % (total, addr))
ai.click("提交订单", rect=BOTTOM)
except ai.NotReadyError:
print("没配密钥,去 AI Studio 账户中心创建")
except ai.InsufficientBalanceError:
print("余额不足,请充值")
except ai.AsaiError as e:
print("调用失败:%s" % e)
方法总览
按返回什么分成两族:
| 方法 | 返回 |
|---|---|
find(target) | dict / None,含标签、框、中心点 |
find_all(target, limit) | list[dict] |
ask(question) | str,模型原文 |
ask_value(question, value_type) | 由 value_type 决定,见下文 |
exists(target) | bool |
wait(target, timeout) | dict / None |
click(target) | bool |
init() / status() | 配置 |
位置:find / find_all
find_all
按自然语言描述找出所有匹配目标,返回屏幕坐标。
- 函数
ai.find_all(target, rect=None, image=None, limit=10, confidence=0.0, image_tokens=None, hint=None)
- 参数
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| target | str | 是 | 目标描述,越具体越准。"蓝色的登录按钮" 好过 "按钮" |
| rect | list | 否 | [left, top, right, bottom] 限定搜索区域,强烈建议传 |
| image | - | 否 | None = 自动截屏;也可传 ndarray / 图片路径 / Bitmap / PIL.Image |
| limit | int | 否 | 最多返回几个,默认 10 |
| confidence | float | 否 | 置信度过滤,默认 0 不过滤 |
| image_tokens | int | 否 | 上传图片的清晰度预算,默认 1024,见下文 |
| hint | str | 否 | 补充上下文 |
- 返回
list[dict],每项形如:
{'text': '购物车', 'rect': [960, 120, 1040, 200],
'center_x': 1000, 'center_y': 160, 'confidence': 1.0}
空列表表示没找到。
- 示例
items = ai.find_all("商品列表里的加入购物车按钮", limit=5)
for it in items:
print(it["text"], it["center_x"], it["center_y"])
find
同 find_all,但只返回可能性最高的一个,没找到返回 None。
ai.find(target, rect=None, image=None, confidence=0.0, image_tokens=None, hint=None)
r = ai.find("确定按钮", rect=[0, 1800, 1080, 2400])
if r:
action.click(r["center_x"], r["center_y"])
值:ask / ask_value
ask
开放问答,返回模型原文。
ai.ask(question, rect=None, image=None, image_tokens=None, hint=None)