mcpField
O
oswright

oswright

本地服务
github.com/Ask-812生产力42 周前更新

关于 oswright

OSWright 是面向 AI Agent 的桌面自动化 MCP 服务,通过增量感知只重读发生变化的屏幕区域。它结合像素变化、无障碍树、OCR 和图像匹配,驱动真实桌面应用。

主要功能

  1. 01增量屏幕感知与变化检测
  2. 02按角色和名称查找界面元素
  3. 03OCR 与图像模板匹配
  4. 04窗口、鼠标、键盘和剪贴板操作
  5. 05启动应用并等待界面稳定

适用场景

  1. 01自动填写桌面应用中的表单
  2. 02在桌面应用间查找并操作界面元素
  3. 03通过剪贴板在应用间传递数据

MCP 相关

OSWright 作为 MCP 服务,向客户端暴露桌面、OCR、无障碍、鼠标键盘、窗口和剪贴板等工具,用于驱动桌面应用。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"ask-812-oswright": {
"command": "uvx",
"args": [
"oswright"
]
}
}
}
启动命令uvx oswright

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypioswright0.8.2

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

OSWright

PyPI Tests Python License

面向 AI Agent 的桌面自动化,无需每一步都为截图付费。

mcp-name: io.github.Ask-812/oswright

一个 MCP 服务,可让大模型操控真实的桌面应用——相当于桌面版的 Playwright MCP。它会在操作之间保留屏幕模型,并只重新读取发生变化的部分,因此完成相同任务所需的令牌减少一个数量级。

OSWright 将发票内容录入费用表单

从发票中读取 8 个字段并输入费用表单,再由应用本身验证。任务相同,结果相同,上下文用量比每次操作后都返回截图少 7.4×。屏幕上的每个数字都是运行过程中实测的——运行 python benchmarks/record_demo.py 即可重新生成完整演示。

为什么要开发它

大多数 GUI Agent 每一步都会重新感知整个屏幕:截图、OCR、把图像交给模型,再重复操作。在真实桌面上测得,观察内容中位数只有 0.012% 的屏幕像素发生变化。重新读取所有内容所做的工作远超变化本身所需,而且无论是否发生变化,都会消耗约 2,800 个图像令牌。

OSWright 会询问合成器发生了哪些变化,只重新扫描这些部分,并从成本最低的可用来源获取元素查询结果。下方的指标均在这台机器上测得,可通过 benchmarks/ 复现——包括对它不利的结果。

主要功能
  • 跨平台。 Windows(Win32 API)、Linux(pynput/X11)、macOS(pynput/Quartz)。
  • 辅助功能树。 通过 Windows UI Automation 按角色和名称确定性地查找元素——准确率 100%,即时完成,无需模型。
  • 快速 OCR。 Windows OCR(内置、即时),Linux/macOS 则使用 EasyOCR 作为备用方案。结果会自动缓存。
  • Windows 上轻量。 无需下载 PyTorch——Windows 使用内置 OCR 引擎,因此完整安装只需几 MB,而非几 GB。
  • 图像匹配。 通过 OpenCV 使用模板图像定位元素。
  • 窗口管理。 列出、聚焦、最小化、关闭窗口,并对指定窗口截图。
  • 截图差异检测。 使用 wait_for_change 检测屏幕是否发生变化。
  • 剪贴板访问。 读取和写入系统剪贴板以传输数据。
  • 应用启动器。 启动应用并等待其加载。
  • 自动快照。 每个操作都会返回截图,让 Agent 始终看到当前状态。
  • 43 个 MCP 工具。 涵盖屏幕、OCR、UIA、鼠标、键盘、窗口、剪贴板和组合操作。
  • 增量感知。 只重新扫描屏幕上发生变化的部分,还可以返回变化内容而非完整截图——每步令牌用量减少约 21×。
  • 屏幕记忆。 识别之前读取过的屏幕并复用结果,经像素验证——成本比重新读取低 89×。
  • 推测式感知。 学习各类操作的效果,并确认预期结果,而不是重新读取——成本降低 19–23×;如果界面出现意外情况,还会生成 surprise 报告。
  • 自适应等待。 等待屏幕实际稳定下来,而不是固定休眠 300 ms——50 步任务可节省 11.9 s。
  • 分级解析。 元素查询会在找到可用的最低成本方法后停止;重复查询耗时约 0.05 ms。
  • DPI 坐标准确。 坐标始终使用物理像素,因此在缩放显示器上点击也能准确定位。
  • 测试套件。 包含 259 项自动化测试;没有可用显示器时,桌面控制相关测试会自动跳过。
要求
  • Python 3.10 或更高版本
  • VS Code、Cursor、Windsurf、Claude Desktop 或任何其他 MCP 客户端

入门

首先,使用你的客户端安装 OSWright MCP 服务。

标准配置适用于大多数工具:

{
  "mcpServers": {
    "oswright": {
      "command": "uvx",
      "args": ["oswright"]
    }
  }
}

注意: 如果你没有 uvx,可以使用 pip install oswright,然后直接将 "command": "oswright" 设为命令。

Claude Desktop

按照 MCP 安装指南,使用上面的标准配置。

Claude Code
claude mcp add oswright uvx oswright
VS Code

在用户或工作区的 settings.json 中,将配置添加到 mcp.servers 下:

{
  "mcp": {
    "servers": {
      "oswright": {
        "command": "uvx",
        "args": ["oswright"]
      }
    }
  }
}

或者使用 VS Code CLI:

code --add-mcp '{"name":"oswright","command":"uvx","args":["oswright"]}'
Cursor

前往 Cursor Settings -> MCP -> Add new MCP Server。将名称设为 oswright,类型设为 command,并使用命令 uvx oswright。

Windsurf

按照 Windsurf MCP文档操作。使用上面的标准配置。

Cline

将配置添加到 cline_mcp_settings.json:

{
  "mcpServers": {
    "oswright": {
      "type": "stdio",
      "command": "uvx",
      "args": ["oswright"],
      "disabled": false
    }
  }
}
Goose

前往 Advanced settings -> Extensions -> Add custom extension。将名称设为 oswright,类型设为 STDIO,并将 command 设为 uvx oswright。

使用 pip 而非 uvx

如果你更倾向于使用标准 pip 安装:

pip install oswright

然后使用以下配置:

{
  "mcpServers": {
    "oswright": {
      "command": "oswright"
    }
  }
}

或者直接运行:

python -m oswright
Agent 技能

skills/oswright/SKILL.md 会告诉 Agent 何时使用 OSWright,以及哪些工具成本较低。它遵循开放的 Agent Skills 格式。将 skills/oswright 文件夹复制到 Agent 的技能目录中——Claude Code 的目录为 ~/.claude/skills/,其他客户端则使用对应的目录。

该技能会设置 --observation-mode delta。如果不设置,每个操作都会返回完整截图,而 OSWright 正是为了避免这种开销而存在的。

增量感知

大多数 GUI Agent 每一步都会重新感知整个屏幕:完整截图、完整 OCR,然后把一张新图交给模型。在实时桌面上测得,中位数观察结果中只有 0.012% 的像素发生变化——因此,完整扫描所做的工作约比实际变化所需的多 240 倍,而且无论是否发生变化,返回的截图都要消耗约 2,800 个图像 token。

OSWright 会在各次观察之间保留屏幕模型,只重新扫描实际发生变化的区域。

observe()  ->  {"changed": true,
                "added":   [{"text": "Saved", "x": 812, "y": 447}],
                "removed": ["Unsaved changes"],
                "screen_fraction_scanned": 0.015}

在这台机器上对一个 14 步 Agent 循环进行测量:

v0.4.0(完整 OCR + 截图)增量
每步中位数延迟212 ms33 ms
每次观察的 token 数~2,764~49
14 步的 token 总数38,6961,025
屏幕重新读取比例100%16%

屏幕越繁忙,差距越大:完整 OCR 的开销随屏幕上的文本量增长,而增量路径的开销随变化量增长。同样的对比在安静的桌面上为 6.5 倍,打开内容密集的网页时则为 14.3 倍。请通过 benchmarks/ 重新测量,而不是盲目相信这些数据。

不过,成本只是一个代理指标;如果更省成本的感知路径会在不知不觉中降低准确率,那还不如不用。因此,我们会用任务完成情况进行验证:通过真实工具界面驱动四个应用的脚本化任务,并根据各应用自身的状态评分——Calculator 和 Explorer 使用 UI Automation,Chrome 和 VS Code 使用窗口标题——绝不依据 OCR 评分。

配置CalculatorFile ExplorerChrometoken 数
v0.4 式(完整截图)9/93/33/3118,858
仅增量9/93/33/35,252
增量 + 记忆9/93/33/35,099
增量 + 记忆 + 预测9/93/33/37,981

**各配置的准确率完全相同,而 token 成本降低了 23 倍。**运行 python benchmarks/bench_tasks.py。

为何兼用像素与辅助功能

这一设计的前提是,两种感知路径都无法在所有情况下胜出。关闭各自的一半,就能实际测量这一点,而不是只作断言:

配置CalculatorFile ExplorerChrome
完整级联9/93/33/3
仅辅助功能9/90/30/3
仅像素6/93/33/3

仅用辅助功能——大多数 Windows GUI Agent 采用的方式——在 XAML 上表现完美,但无法识别 Win32 列表视图和网页内容。对 VS Code 进行探测时,它只能看到 18 个元素:整个 IDE 被识别为一个名为 Chrome Legacy Window 的节点;而 OCR 能读取 94 个元素,包括每个文件名。

仅用像素无法识别 Calculator 的按钮,因为人类看作 7 的按钮,名称却是 Seven,而 Windows OCR 完全无法从 Calculator 识别出数字。

只有级联配置能在所有场景中通过测试。

分层解析

find_element 和 click_element 会在找到第一个能给出答案的方法后停止,因此成本取决于请求有多么新颖,而不是屏幕有多大:

层级方法典型成本
0已在屏幕模型中约 0.05 毫秒
1仅重新扫描发生变化的部分约 70 毫秒
2无障碍树(知道某个 Button 就是按钮)约 40 毫秒
3通过 UIA TextPattern 读取应用自身的文本缓冲区——获取精确字符约 400 毫秒
4全屏 OCR约 250 毫秒

查找模型已经知道的文本,成本比 v0.4.0 路径低 约 5,000 倍(0.05 毫秒,而非 244 毫秒)。响应会报告是哪个层级给出了答案,因此你可以了解任务实际耗费的成本。

第 3 层值得了解:UIA 的 TextRange.FindText 会搜索应用自身的文本缓冲区,并返回精确的边界矩形。它不受字体、DPI、抗锯齿和 OCR 错误影响。它排在像素层级之后,仅仅是因为扫描窗口控件需要几百毫秒的跨进程 COM 调用——它是精确的一层,而不是快速的一层。

关于顺序的说明。 这些层级的排序依据是测量结果,而非理论。常见建议是将无障碍树作为主要方法,但在实际应用中,它不一定更省时:这里遍历 Chrome 的无障碍树耗时 537 毫秒,比全屏 OCR 还慢;而 VS Code 只向它暴露了 18 个元素。无论是像素还是无障碍,在所有情况下都不是最佳选择,因此这里采用的是分层解析,而非二选一。

询问合成器,而不是自行检查

在 Windows 上,桌面合成器已经知道哪些像素发生了变化,并通过 DXGI Desktop Duplication 将这些信息公开。向它查询只需 0.14 毫秒,且无需传输任何像素;相比之下,捕获一帧再发现画面完全相同需要几十毫秒——因此,在空闲观察时会完全跳过捕获。

发生变化时,该帧仍保留在合成器中,因此会直接从 GPU 读取其像素,而不是再通过另一个 API 获取一次——根据这里的测量结果,比 mss 快 1.5–2.3 倍。

合成器仅用作检测变化的快速否定判断。当它报告发生变化时,脏区域仍通过对捕获帧进行哈希来确定:两者的测量时间区间略有不同,因此合成器矩形可能比实际捕获的像素范围报告得更少,而少报的区域中可能包含不会再次读取的文本。在 Desktop Duplication 不可用时,它会静默退化为分块哈希和常规捕获。

使用 --observation-mode delta 为操作工具启用增量观察。为兼容现有客户端,默认值仍为 screenshot。

亲自复现上述全部内容: 请参阅 benchmarks/。 每项决策背后的理由(包括失败的尝试)均记录在 docs/ENGINEERING_LOG.md 中。

配置

OSWright MCP 服务支持以下参数。可以将它们作为 "args" 列表的一部分写入 JSON 配置:

选项描述环境变量
--port <port>SSE 传输使用的端口。省略时使用 stdio(默认)。FASTMCP_PORT
--host <host>HTTP/SSE 服务绑定的主机。默认值:127.0.0.1。FASTMCP_HOST
--transport <mode>传输协议:stdio、sse、streamable-http。根据 --port 自动检测。
--ocr-languages <langs>OCR 语言(默认值:en)。示例:--ocr-languages en es frOSWRIGHT_OCR_LANGUAGES
--timeout <seconds>自动等待操作的默认超时时间(默认值:10)。OSWRIGHT_TIMEOUT
--snapshot-max-width <px>缩小每次操作后返回的自动快照。0(默认值)表示保留完整分辨率。较低的值可大幅降低 token 开销。OSWRIGHT_SNAPSHOT_MAX_WIDTH
--observation-mode <mode>操作工具返回的内容:screenshot(默认)、delta(仅返回发生变化的内容,token 数量约减少 30 倍)或 both。OSWRIGHT_OBSERVATION_MODE
--no-atlas不跨访问记录屏幕信息。OSWRIGHT_NO_ATLAS
--no-speculate不预测操作结果。OSWRIGHT_NO_SPECULATE
--allow-remote绑定非环回地址时必须使用。请参阅安全性。
--log-level <level>日志级别:DEBUG、INFO、WARNING、ERROR。默认值:INFO。OSWRIGHT_LOG_LEVEL

显式指定的命令行标志始终优先于相应的环境变量。

示例:多语言 OCR
{
  "mcpServers": {
    "oswright": {
      "command": "uvx",
      "args": ["oswright", "--ocr-languages", "en", "es", "fr"]
    }
  }
}
独立 MCP 服务(SSE)

从工作进程或另一台机器运行时,请使用 SSE 传输:

uvx oswright --port 8931

然后在 MCP 客户端配置中:

{
  "mcpServers": {
    "oswright": {
      "url": "http://127.0.0.1:8931/sse"
    }
  }
}

安全性

**OSWright 没有身份验证。**任何能够访问此端口的人都可以完全控制这台机器的键盘、鼠标、屏幕和剪贴板——这相当于远程接管桌面,而不是使用沙盒化 API。

因此,服务默认绑定到 127.0.0.1,除非传入 --allow-remote,否则拒绝在非环回地址上启动。若要从另一台机器访问,请优先使用 SSH 隧道,而不是暴露端口:

ssh -L 8931:127.0.0.1:8931 user@desktop-host

stdio 传输(默认方式,也是上述所有 MCP 客户端配置使用的方式)完全不会暴露到网络中,是运行 OSWright 的推荐方式。

可能破坏工作的工具会相应标注:close_window 标记为具有破坏性,launch_app 会启动任意程序。截图工具不会覆盖现有的 save_path。

平台说明

平台输入后端OCR 后端额外下载内容
WindowsWin32 API (SendInput)Windows OCR(即时、内置)**无。**不需要 PyTorch。包含 UI Automation。
Linuxpynput (X11)EasyOCRPyTorch(约 2.5 GB)。需要 X11;对 Wayland 的支持有限。
macOSpynput (Quartz)EasyOCRPyTorch(约 2.5 GB)。请在“系统设置”>“隐私”>“辅助功能”中授予辅助功能权限。

在 Windows 上不会安装 EasyOCR,因为内置的 Windows OCR 引擎速度更快,也无需下载模型。只有在需要使用 Windows OCR 不支持的语言时,才安装它:

pip install "oswright[easyocr]"
坐标

OCR、图像匹配和 UI Automation 返回的所有坐标都是绝对物理屏幕像素,可以直接传给 mouse_click。子区域以及虚拟桌面原点为负值的多显示器设置也同样适用。screenshot 还会报告 origin_x/origin_y,即图像左上角像素的绝对位置,方便你自行从图像中读取坐标。

工具

屏幕
  • screenshot -- 截取整个屏幕或指定区域。以 MCP 原生图像内容的形式返回图像。也可以选择将图像保存到文件路径。
    • 只读:是
  • get_screen_info —— 获取屏幕尺寸和显示器数量。
    • 只读:true
OCR / 文本查找
  • find_text_on_screen —— 使用 OCR 查找屏幕上所有匹配的文本。返回匹配项的坐标和置信度。

    • 参数:text、exact、区域边界、monitor
    • 只读:true
  • read_screen_text —— 使用 OCR 读取屏幕上所有可见文本。返回每个检测到的文本元素及其位置。

    • 参数:区域边界、monitor
    • 只读:true
图像匹配
  • find_image_on_screen —— 使用 OpenCV 模板匹配,在屏幕上查找模板图像的所有匹配项。
    • 参数:template_path、threshold、monitor
    • 只读:true
鼠标
  • mouse_click —— 在指定坐标或当前位置单击鼠标。返回屏幕截图。

    • 参数:x、y、button、clicks
  • mouse_double_click —— 在指定坐标或当前位置双击。返回屏幕截图。

  • mouse_move —— 将鼠标光标移至屏幕坐标。

  • mouse_scroll —— 滚动鼠标滚轮。返回屏幕截图。

    • 参数:amount、x、y
  • mouse_drag —— 从一个点拖动到另一个点。返回屏幕截图。

    • 参数:start_x、start_y、end_x、end_y、button、duration
  • get_mouse_position —— 获取当前鼠标光标位置。

    • 只读:true
键盘
  • type_text —— 逐个字符输入文本。返回屏幕截图。

    • 参数:text、delay
  • press_key —— 按下按键或组合键,例如 Enter、Ctrl+C、Alt+Tab。返回屏幕截图。

    • 参数:key
组合操作
  • click_text —— 通过 OCR 查找文本并单击。会自动重试,直到找到文本或超时。返回屏幕截图。

    • 参数:text、exact、button、timeout、poll_interval、monitor
  • double_click_text —— 通过 OCR 查找文本并双击。返回屏幕截图。

  • right_click_text —— 通过 OCR 查找文本并右击。返回屏幕截图。

  • hover_text —— 通过 OCR 查找文本并将鼠标悬停在其上。返回屏幕截图。

  • fill_field —— 查找标签,单击该标签、清除内容并输入值。返回屏幕截图。

    • 参数:target_text、value、exact、timeout、monitor
  • fill_form —— 一次调用即可填写多个字段,减少往返调用。

    • 参数:fields({label, value} 列表)、timeout、monitor
  • wait_for_text —— 等待文本出现在屏幕上。通过 OCR 轮询。

    • 参数:text、exact、timeout、poll_interval、monitor
    • 只读:true
  • wait_for_text_gone —— 等待文本从屏幕上消失。

    • 参数:text、exact、timeout、poll_interval、monitor
    • 只读:true
  • wait_for_time —— 等待指定时长(最长为 30s),然后截取屏幕截图。

窗口管理
  • list_windows —— 列出所有可见窗口。可按标题子字符串筛选。

    • 参数:title_filter
    • 只读:true
  • focus_window —— 按标题将窗口置于前台。返回屏幕截图。

    • 参数:title
  • close_window —— 按标题关闭窗口(发送 WM_CLOSE)。返回屏幕截图。

    • 参数:title
  • minimize_window —— 按标题最小化窗口。返回屏幕截图。

    • 参数:title
  • screenshot_window —— 仅截取一个窗口的屏幕截图。

    • 参数:title、save_path
    • 只读:true
剪贴板
  • get_clipboard —— 获取系统剪贴板中的当前文本内容。

    • 只读:true
  • set_clipboard —— 将文本复制到系统剪贴板。

    • 参数:text
应用管理
  • launch_app -- 启动应用,并可选择等待其加载。直接运行程序,不会通过 shell 运行。

    • 参数:command、args、wait_text、timeout
    • 报告 wait_text_found,以便判断应用是否确实已加载。
  • get_ocr_info -- 获取当前 OCR 后端及可用后端的信息。

    • 只读:true
增量感知
  • observe -- 报告自上次观察以来屏幕上的变化。仅重新扫描移动过的区域。跟踪状态时优先使用此工具,而非 screenshot。

    • 参数:force_full
    • 只读:true
  • find_element -- 使用能够回答问题的最省资源的方法查找屏幕上的文本。报告哪个级联层级响应了请求。

    • 参数:text、exact、window_title
    • 只读:true
  • click_element -- 通过级联查找文本并点击它。比 click_text 更省资源的替代方案。

    • 参数:text、exact、button、window_title
  • read_model_text -- 从增量模型读取屏幕上的文本,无需重新对显示内容进行 OCR。

    • 参数:query、limit
    • 只读:true
  • perception_stats -- 报告模型避免了多少感知工作。

    • 只读:true
  • remember_screen -- 记住当前屏幕,以便后续访问时跳过读取。此记忆会跨会话保留。

    • 只读:true
  • atlas_stats -- 报告屏幕图谱记住了哪些内容,以及它发挥作用的频率。

    • 只读:true
辅助功能 / UI 自动化(Windows)
  • get_ui_tree -- 获取当前焦点窗口的辅助功能树。返回所有交互元素的名称、类型和位置。结果确定且即时。

    • 参数:window_title、max_depth
    • 只读:true
  • click_ui_element -- 使用辅助功能树点击 UI 元素。比 OCR 更可靠。

    • 参数:name、control_type、automation_id、window_title
  • fill_ui_element -- 设置 UI 元素的值(例如文本框)。比基于 OCR 的填充更可靠。

    • 参数:value、name、automation_id、window_title
高级屏幕功能
  • get_active_window -- 获取当前焦点窗口的信息。

    • 只读:true
  • wait_for_change -- 等待屏幕发生视觉变化。先截取基准屏幕截图,然后轮询,直到屏幕出现变化。

    • 参数:timeout、poll_interval

Python 库

OSWright 也可以作为独立的 Python 库使用,并提供类似 Playwright 的 API:

from oswright import OSWright

with OSWright() as ow:
    screen = ow.screen()
    screen.click(text="Start")
    screen.type_text("Hello World")
    screen.press("Ctrl+S")
    screen.screenshot("desktop.png")

更多内容请参见 examples/ 目录。

架构

oswright/
  __init__.py          # Package entry point (single source of __version__)
  core.py              # OSWright class (= Browser)
  screen.py            # Screen class (= Page)
  locator.py           # Locator + Assertions (= Locator + expect)
  capture.py           # Screen capture (mss - cross-platform, thread-safe)
  dirty.py             # Change detection - which parts of the screen moved
  screenmodel.py       # Persistent screen model, updated incrementally
  cascade.py           # Resolution cascade - cheapest method that can answer
  atlas.py             # Remembers screens across visits and sessions
  settle.py            # Knowing when the screen has finished responding
  speculate.py         # Predicting what an action does, instead of looking
  textprovider.py      # Exact text from the app itself via UIA TextPattern
  detect.py            # OCR dispatcher with caching (auto-selects best backend)
  _ocr_windows.py      # Windows OCR backend (instant, built-in)
  accessibility.py     # Windows UI Automation (deterministic element finding)
  cache.py             # Screenshot diffing, image hashing, OCR result cache
  _dpi.py              # Process DPI awareness (keeps every API in physical pixels)
  _dxgi_windows.py     # Compositor dirty rectangles via DXGI Desktop Duplication
  input.py             # Platform dispatcher for input backends
  _input_windows.py    # Windows input backend (Win32 API)
  _input_pynput.py     # Linux/macOS input backend (pynput)
  window.py            # Window management (list, focus, close)
  clipboard.py         # Clipboard read/write (cross-platform)
  mcp_server.py        # MCP server (43 tools for AI agents)
tests/
  conftest.py          # Fixtures that skip when no display/OCR is available
  test_core.py         # Unit tests (no desktop required)
  test_perception.py   # Incremental perception (stubbed, runs headless)
  test_atlas.py        # Screen memory and its failure modes (headless)
  test_speculate.py    # Prediction, settling, and their limits (headless)
  test_e2e.py          # End-to-end tests against the real desktop (marked `e2e`)
记住屏幕

应用的行为具有确定性——同一个对话框每次都会有相同的布局。 OSWright 会记住已读取的屏幕,并在下次访问时跨会话复用:冷读耗时 125 ms → 热回忆耗时 1.4 ms(89×)。

仅凭识别结果绝不会直接信任已记住的屏幕。复用布局前,会通过 像素 对少数区域进行抽查,因此,如果屏幕发生变化,就会拒绝该布局,而不会基于它采取操作。验证失败时会采取封闭策略:完全没有可检查内容的屏幕不会被记住。

使用 --no-atlas 禁用。已记住的屏幕存储在 ~/.oswright/atlas.json 中。

预测操作,而不是观察操作

应用程序具有确定性——点击“保存”每次都会打开相同的对话框——因此,首次观察后,操作结果便已可知。OSWright 会学习各项操作的效果,并确认预期画面,而不是再次读取:成本比观察低 19–23 倍(2.3 毫秒,而观察需要 43–50 毫秒)。

一个预测必须连续两次得到验证才会被信任;如果证明有误,就会被弃用;并且会像已记住的画面一样,用相同的两种方式进行检查。预测失败时,会向 Agent 报告为 surprise——界面出现了平常不会发生的情况,值得了解,而不是悄悄忽略。

**已确认的预测能保证什么:**布局相同——控件及其位置都相同。但不保证每个字符都完全一致。一个数字的变化所改变的像素,比闪烁的插入符还少,因此无论分辨率如何,都无法通过整屏检查将两者区分开来。需要精确文本时,请使用 observe(force_full=True)。

使用 --no-speculate 禁用。

只等待所需的时间

操作工具过去会固定休眠 300 毫秒,这个时长是按最慢的情况设定的,因此每次操作都要等待最坏情况所需的时间。合成器知道屏幕何时停止变化,所以现在会在界面实际稳定时结束等待:

之前的固定休眠时间300 毫秒
实际等待时间的中位数61.5 毫秒
执行 50 步任务节省的时间11.9 秒

“稳定”是指最近没有发生大幅变化,而不是毫无变化:真实桌面永远不会静止——插入符和时钟每约 18 毫秒就会产生一次变化事件,影响约 32 个像素;而真正的 UI 变化会影响数万个像素。

尚未完成
  • Wayland 输入注入,以及作为 TextPattern 等效方案的 macOS AXTextMarker。
  • 用于既无法访问、文本又不可读的界面的视觉模型层:游戏、画布、图像编辑器。
  • 基于多个前序画面触发的状态转换,用于结果取决于不可见状态的操作。
测量了什么,未测量什么

本机同时测量了感知成本和任务成功率,并可通过 benchmarks/ 复现——在四款应用中,更低的感知成本并未牺牲准确性;像素与无障碍信息的取舍也经过了测量,而非仅凭论证。

与 Windows-MCP 对比

相同的任务、四种场景,每项都由应用本身评分。两个工具都不会给自己评分,而 Windows-MCP 使用其默认设置运行:

计算器资源管理器ChromeChrome,2 步通过令牌数
oswright5/54/55/55/519/20832
Windows-MCP,每次操作截取一次快照5/55/55/55/520/2014,053
Windows-MCP,仅截取一次快照5/55/55/55/520/208,214

如实来看:Windows-MCP 更可靠,而 oswright 的成本低了 16.9 倍。 oswright 漏掉了 20 次点击中的 1 次,发生在刚刚打开的窗口中。

成本差异源自架构,而非调优带来的优势。Windows-MCP 将屏幕内容返回给 Agent——Snapshot 会将辅助功能树渲染为 (x,y) button "Seven" [action: click]——并接收坐标,因此每次操作都会将屏幕描述计入模型上下文。oswright 接收文本并返回结果。

可靠性差距可能是速度造成的:oswright 会在约 100 毫秒内解析并点击,有时窗口刚获得焦点、尚未准备好接收输入时就已点击;而较慢的循环能给应用留出它从未要求过的等待时间。这只是一个假设,而非结论——在操作前增加等待,在十次试验中并未带来可测量的差异,因此我们记录了这一点,但未作修复。

设置 Chrome,2 步 这一场景,是因为这里的其他任务都很短,工具可以读取一次屏幕并复用那些坐标。在这个场景中,第一次点击会使控件在页面上向下移动 325 px,因此仅截取一次快照的配置不得不重新读取屏幕——所以对于界面会移动的任务,它的低成本数据并不成立,实际成本与每次操作截取快照的配置相同。

运行 python benchmarks/bench_head_to_head.py 即可复现(设置方法见文件的文档字符串)。

本测试无法证明的事项: 在一台笔记本电脑上进行的四项短任务。它并未涉及长时间多步任务、故障恢复或产品成熟度——Windows-MCP 有 OAuth、分析功能、看门狗和安装程序;oswright 则都没有。它的辅助功能遍历还会读取 Chrome 的页面内容,而 oswright 自身的辅助功能层级不会读取这些内容。主张是“每次操作的成本大幅降低,可靠性略有损失”,而不是“产品更好”。

开发

pip install -e ".[dev]"

pytest tests/                # everything available on this machine
pytest tests/ -m "not e2e"   # unit tests only, no desktop needed
ruff check oswright tests    # lint
python benchmarks/bench_pipeline.py   # reproduce the performance numbers
python benchmarks/bench_tasks.py      # task success (opens Calculator repeatedly)

设计决策、测量数据和失败尝试记录在 docs/ENGINEERING_LOG.md 中。

许可证

MIT

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。