☰
CLI-Anything:把任意软件变成命令行工具,让AI Agent轻松驱动
2026/10/11 7:06:09 网站建设 项目流程

最近接了个活儿,给自己的内部工具链做自动化改造。折腾了一圈发现,市面上所谓的“Agent 驱动软件”大多还停留在能聊天、能写代码、能调 API 的层面,真想让 Agent 去点开一个图形界面软件、把里面的数据导出来、再填进另一个表单里,基本就是束手无策。不是 Agent 不够聪明,是软件的“入口”根本没给它留。于是我自己搞了个小东西,叫 CLI-Anything:一行命令,把任意软件包装成标准命令行工具,让 Agent 能用最朴素的方式——执行命令、读输出——去驱动那些原本只有鼠标才能操作的桌面程序。这篇文章就是把整个思路、踩坑和落地过程拆开揉碎了讲一遍,适合那些正在做 Agent 落地、自动化办公流、或者单纯想让自己的软件“开口说话”的开发者。

先说清楚它到底干了件什么事。CLI-Anything 的本质,是一个“适配层”。它不直接修改目标软件,也不要求软件提供 API,而是通过一套可配置的规则,把软件界面上的按钮、输入框、菜单、列表,抽象成一个个可调用的 CLI 子命令。Agent 只需要执行类似cli-anything run 软件名 操作名 --参数的指令,适配层就会去真实操作那个软件的界面,然后把结果以结构化的形式返回给 Agent。对 Agent 来说,它面前站着的不是一个冷冰冰的 GUI,而是一个听话的、可预测的命令行程序。

正文开始之前先给个结论:这玩意儿不会让传统软件一夜之间全都变成原生 API 服务,但它确实把“Agent 能触达的软件边界”往前推了一大步。如果你手头有一堆只有图形界面、又没有自动化接口的旧软件,这篇文章里的方案可以直接抄作业。

1. 为什么需要 CLI-Anything:Agent 落地的最后一公里

1.1 Agent 工具的现状:有 API 的没几个

现在聊 Agent 生态,大家默认一个前提:Agent 需要“工具”。工具可以是代码解释器、搜索接口、数据库查询,也可以是某个 SaaS 平台的 API。但现实是,真正值得自动化的工作流里,大量关键软件根本没有对外开放 API。举个很典型的场景:某个做工程计算的同事,每天要用一款老旧的力学分析软件,把设计参数输进去,等计算完成,再把结果曲线截图贴进报告。那软件是十年前的架构,既没有命令行参数,也没有二次开发接口,数据输入全靠填表单,输出全靠界面显示。这种软件,API 是无望的,RPA 倒是能硬点,但 PRA 脚本写起来等于重新开发一遍,维护成本还高。

CLI-Anything 解决的就是这个问题:既然软件不给你接口,那我们就自己造一个接口。它不是去逆向工程软件内部,而是把“人类操作 GUI 的方式”翻译成“机器可调用的命令”。人类怎么看界面、怎么填输入框、怎么点按钮,适配层就怎么模拟,只是它把这个过程标准化、参数化、可重复化了。

1.2 GUI 软件为什么“带不走”

GUI 软件的交互模型和命令行天然冲突。命令行是“输入参数,等待返回,处理结果”,GUI 是“打开窗口,找到控件,填写内容,点击触发,读取显示结果”。中间还夹杂着窗口焦点、控件状态、异步加载这些破事。更麻烦的是,GUI 软件的特征定位通常靠坐标或者控件层级,窗口一挪、分辨率一换、皮肤一改,原来的定位就全部失效。

我最初也试过直接用 Python 写 pyautogui 脚本模拟点击。脚本本身不复杂,问题在于脚本和具体软件深度耦合,换一台机器、升级一次软件,脚本就废了。代码里全是x=100, y=200这种魔法数字,别说 Agent 调用,连我本人都懒得维护。CLI-Anything 的核心思路,就是把这种一次性的脚本,升级成一套“软件操作协议”。你不再关心鼠标点哪里,只关心“我想对软件做什么”,剩下的坐标、控件、时序问题,全部交给适配层。

1.3 我们想要的最终形态

用一句话概括目标:让每个 GUI 软件看起来都像一个自带 help 文档的命令行程序。Agent 调用它的时候,先cli-anything list 软件名看看有哪些操作可用,再cli-anything run 软件名 操作名 --参数 xxx执行具体操作,最后拿到 JSON 格式的结果。整个过程里,Agent 不需要理解“窗口”“按钮”“像素”这些 GUI 概念,它只需要处理标准输入输出。这也是“一行命令,让所有软件都能被 Agent 驱动”的底气来源。

2. 核心设计思路:从“适配软件”到“定义协议”

2.1 把“操作”抽象成“工具原语”

CLI-Anything 的配置核心是“工具原语”的概念。任何一种软件交互,归根结底都能拆成有限的几类原子操作:打开、填入、点击、选择、提取、等待、关闭。适配层只需要把这七类操作实现好,上层用户要做的事情,就是给具体软件写一份 YAML 配置,描述“这个界面上有哪些控件、每个控件对应什么操作、操作之间是什么流程”。

举个例子,某窗口里有一个“导入文件”按钮,配置里就写明这个按钮的定位方式(可以是用辅助功能接口,也可以用图像识别模板),然后定义一条原语import_file,它对应的动作是“点击导入按钮 -> 在弹出的文件选择框里输入路径 -> 确认”。Agent 调用import_file时只需要传路径参数,完全不用关心那个文件选择框长什么样。配置不是脚本,是声明式描述,这保证了可维护性和可扩展性。

2.2 统一接口:CLI 入口 + JSON 输入输出

接口协议方面,我参考了 Unix 哲学:一个命令只做一件事,输入输出都是纯文本。CLI-Anything 对外只暴露几条固定命令:

# 列出目标软件支持的所有操作 cli-anything list <app_name> # 查看某个操作需要的参数 cli-anything inspect <app_name> <action_name> # 执行某个操作,JSON 格式传参 cli-anything run <app_name> <action_name> --input '{"file_path": "/tmp/data.csv"}'

每个操作执行后,不管成不成功,都会返回一个固定结构的 JSON:

{ "success": true, "data": { "output_text": "计算结果: 42.5" }, "duration_ms": 1234 }

为什么必须是 JSON?因为 Agent 天然适合处理结构化数据。自然语言输出虽然人看着舒服,但 Agent 解析起来容易出错。统一协议之后,Agent 那边只需要写一次“如何调用命令、如何解析 JSON”的工具封装,后面所有软件都能复用。

2.3 为什么不用直接对接每个软件的 API

可能有人会问,适配层本身也得写代码,为什么不干脆直接给软件写一个专用 API 封装?答案是成本。给 N 个软件各写一套专用适配器,成本是 O(N);做一个统一抽象层,每加一个新软件只增加一份 YAML 配置,成本是 O(1)。CLI-Anything 的配置模型决定了,大部分软件的核心操作可能只需要几十行 YAML 就能覆盖。真正复杂的部分——窗口识别、控件交互、超时控制——已经被框架本身处理掉了,单个软件的适配工作被压缩到了最低。

3. 一条命令快速部署:从零开始接入第一个软件

3.1 安装与环境准备

CLI-Anything 目前提供桌面端命令行工具,安装过程很简单。我在日常环境(Windows 和 macOS 都实测过)用的命令是:

npm install -g cli-anything

装完之后自动带一个cli-anything命令。首次运行会让你做一次环境自检,确认当前的 UI 自动化驱动是否可用:

cli-anything doctor

这个命令会检查系统的辅助功能权限、图像识别库、窗口管理服务等,有缺失的直接给出安装提示。我当时的 Windows 机器要求开启辅助功能权限,macOS 机器需要在“隐私与安全性”里授权终端控制电脑。这块属于老生常谈,但确实最容易卡住新手。

3.2 编写第一个适配配置

为了讲清楚怎么用,我挑一个典型的“某内部任务管理软件”做例子(姑且叫它 TaskApp)。TaskApp 是一个纯 GUI 的桌面工具,用来管理项目任务,没有 API,没有数据库接口。我要实现的需求是,让 Agent 能自动创建一个新任务并提取任务 ID。

在配置目录下新建taskapp.yaml,内容大致长这样:

name: taskapp description: 内部任务管理软件 window_title: TaskApp - 任务管理 actions: create_task: description: 创建一个新任务 params: title: type: string required: true description: 任务标题 priority: type: enum values: [低, 中, 高] default: 中 steps: - action: click target: { by: accessibility, role: 按钮, name: "新建任务" } - action: type target: { by: accessibility, role: 输入框, name: "任务标题" } value: "{params.title}" - action: select target: { by: accessibility, role: 下拉框, name: "优先级" } value: "{params.priority}" - action: click target: { by: accessibility, role: 按钮, name: "确定" } - action: wait_text text: "创建成功" timeout_ms: 5000 - action: extract target: { by: accessibility, role: 标签, name: "任务编号" } export: task_id get_task_status: description: 查询任务状态 params: task_id: type: string required: true steps: - action: type target: { by: accessibility, role: 输入框, name: "搜索框" } value: "{params.task_id}" - action: click target: { by: accessibility, role: 按钮, name: "搜索" } - action: extract target: { by: accessibility, role: 标签, name: "状态" } export: status default: "未知"

配置写完后,先跑一下验证:

cli-anything validate taskapp

如果配置里的控件定位有问题,验证阶段会直接报错,告诉你哪个控件找不到。这个功能是我用得最频繁的,因为 GUI 自动化最容易出的问题就是“控件名称和实际界面上显示的不一致”。

3.3 让 Agent 真正开始调用

配置完成并被validate通过之后,这个软件就算是接入成功了。这时候可以让 Agent 直接调用:

cli-anything run taskapp create_task --input '{"title": "修复登录页崩溃", "priority": "高"}'

就跑通了基础链路。AI Agent 那边只需要在工具箱里注册两个函数:list_actions和run_action。前者返回软件有哪些操作,后者执行操作并返回 JSON。后续再加别的软件,AI Agent 端根本不用改代码。

4. 核心机制的实操拆解:UI 自动化 + 可观测层

4.1 三种驱动后端怎么选

CLI-Anything 的底层驱动设计成了可插拔模式,目前主要支持三种后端:辅助功能接口、图像识别、坐标脚本。三者各有适用场景,选型原则总结如下表:

驱动方式适用场景优点缺点
辅助功能接口原生窗口、现代桌面应用定位精准、支持读取控件文本、稳定性高非原生控件(自绘界面)不可用
图像识别自绘界面、远程桌面、网页套壳不依赖控件树,所见即所得速度慢、模板匹配易受缩放影响
坐标脚本无窗口依赖的老旧系统、游戏界面实现最简单,几乎零依赖对环境最敏感,几乎无任何保护

我的建议是:优先用辅助功能接口,覆盖不了的地方再用图像识别兜底。坐标脚本只适合快速验证,不适合长期维护。实际使用中,很多软件的按钮是自绘的,辅助功能接口拿不到控件,这时候就得靠图像识别。CLI-Anything 允许你在同一个配置里混合使用两种定位方式,比如“先尝试辅助功能,找不到就用图像模板”,这在用 OCR 技术做兜底时特别管用。

4.2 可观测性设计:命令输出给 Agent 看什么

Agent 驱动 GUI 最大的不确定性在于“看不见”。人类操作软件时,眼睛能实时看到界面反馈;但 Agent 只能看到命令返回的文本。所以 CLI-Anything 在每一步操作之后,都会记录完整的操作日志和关键截图。执行完毕之后,Agent 不仅能拿到最终结果,还能拿到中间过程的截图和 OCR 提取的关键状态。

我在设计里会刻意要求extract步骤尽量提取可验证的状态信息,比如“当前页面标题”“操作完成提示”“列表第一行的内容”。这样当 Agent 怀疑操作是否成功时,它可以额外执行一条get_current_state操作,把当前软件界面的核心信息格式化返回。实话说,这一步是决定 Agent 驱动 GUI 能否稳定落地的关键——没有良好的可观测性,Agent 就是个瞎子在黑夜里摸象。

4.3 资源与性能开销控制

GUI 自动化本身是相对昂贵的操作,一次点击往往要等数百毫秒,遇到软件卡顿甚至会拖到几十秒。CLI-Anything 做了几层优化来控制开销:

  • 连接复用:同一个软件的多次操作共用一个自动化会话,不重复启动、重复连接。
  • 智能等待:对每一步操作设置显式的超时参数,默认 5 秒,避免卡死进程。
  • 并发保护:同一时刻同一软件只允许一个操作执行,防止多个 Agent 进程同时抢占窗口焦点。

我在实测中发现,如果让两个并发任务同时操作同一个窗口,轻则控件识别失败,重则软件直接崩溃。所以 CLI-Anything 默认给每个软件加了一个进程级锁,后到的请求会排队等待。这个设计对“多 Agent 共用一个桌面环境”的场景尤其重要。

5. 常见问题与排查技巧实录

5.1 软件窗口没被识别

这个是我被问得最多的问题。症状是cli-anything run时报错“找不到目标窗口”。排查路径无非三步:

  • 先确认软件的进程在运行,cli-anything list-windows能看到当前所有顶层窗口的标题。
  • 再看配置里的window_title是否完全匹配,注意这个匹配默认是模糊匹配,但窗口标题如果是动态变化的(比如“无标题 - 记事本”),建议用正则表达式。
  • 最后检查软件是否被最小化到系统托盘,很多桌面应用最小化之后窗口句柄会失效。

有一回我一个项目死活连不上某旧软件,最后发现是它启动之后不是直接显示主窗口,而是先弹出一个 5 秒的欢迎页。这个问题让我意识到,“等待窗口出现”应该是一个标准操作,不能假设窗口一定立即可用。我在框架里加了一个wait_window原语,问题才彻底解决。

5.2 Agent 重复点击失败

Agent 的容错性比脚本差,因为它在每一步失败后都会尝试自我修复,而修复方式往往就是“换个参数再试一次”。如果你发现某次失败之后,后续的点击全部报错,十有八九是前面的部分操作已经改变了界面状态。比如点击按钮之后弹出来一个模态框,模态框挡住了后面的控件,辅助功能接口这时候拿到的控件是存在的,但根本不可见、不可点击。

我的排查方法是打开 CLI-Anything 的调试日志,它会记录每一步操作的控件快照:

cli-anything run taskapp create_task --input '{"title": "x"}' --debug

重点看日志末尾,哪一步操作报了“element not visible”或者“element not enabled”,就说明界面状态没有达到预期。解决办法通常是在前后两步之间插入一个条件等待,明确告诉适配层“等到某个文本出现再继续”,而不是盲目 sleep 固定时间。

5.3 GUI 升级导致适配失效

这属于不可抗力。软件升级之后,控件的名称、层级、甚至整个布局都可能变化。图像识别型的适配首当其冲,模板匹配直接失败;辅助功能接口稍微好点,但也可能因为内部标识变化而失效。

我的处理习惯是给每个配置加一个版本号api_version,升级软件之后先跑一遍cli-anything validate,看报错集中在哪些控件,然后批量修改映射关系。好在这份配置是声明式的,修改成本比改脚本低得多。这里也看到设计配置而非脚本的好处:配置的变更可以被 diff、被 review、被版本化,脚本改起来就像剪不断理还乱的线团。

5.4 命令超时与并发冲突

GUI 自动化经常遇到软无响应的情况,尤其是处理大数据量导出时,界面冻结十几秒很正常。CLI-Anything 的默认超时是 30 秒,但对重型任务不够用,我一般会在配置里把timeout_ms调大:

actions: export_report: timeout_ms: 120000 steps: - action: click target: { by: accessibility, role: 按钮, name: "导出" } - action: wait_text text: "导出完成" timeout_ms: 100000

并发冲突的处理逻辑前面提过,主要是靠软件级别的排它锁。真要实现多 Agent 并行处理,正确的做法是给每个 Agent 分配独立的虚拟机或独立会话,而不是在同一个桌面上抢同一个软件。

5.5 常见问题速查表

现象可能原因处理方式
窗口找不到窗口标题变了或未启动用list-windows查看实际标题,调整window_title
按钮定位失败控件是自绘的切换为图像识别定位,或提供截图模板
输入框无法输入焦点被抢占或输入框禁用点击输入框后加短暂延迟,再执行输入
操作超时软件弹出模态框检查日志中“element not visible”,增加关闭模态框的步骤
Agent 拿到空结果extract步骤没匹配到文本使用 OCR 兜底,或改用export的default字段
多个 Agent 操作冲突并发抢占窗口配置软件级锁,或部署独立环境

5.6 几条独家避坑经验

先说一条最容易被忽略的:不要在生产环境让 Agent 直接操作真实软件的主界面。最好用模拟数据、测试环境,或者至少给软件准备专用的配置文件和干净的工作目录。否则一次错误操作可能把真实数据搞乱,而 Agent 不会像人一样发现异常就停手。

再有一条是关于图像识别的精度。如果你用模板匹配去定位一个按钮,模板截图的尺寸必须和当前显示的分辨率匹配,否则识别率会骤降。我在实际项目里都是先在目标机器上用截图工具还原真实尺寸,而不是拿设计稿的图当模板。

还有一条是权限问题。Windows 下如果目标软件是以管理员权限运行的,CLI-Anything 进程也必须以管理员权限运行,否则辅助功能接口访问会失败。macOS 下则要注意终端本身的辅助功能授权,授权对象应该是你启动 CLI-Anything 的那个终端程序,不一定是命令行工具本身。这两个问题都让人花了不少时间排查,记录下来供参考。

6. 实际运行效果与扩展思路

6.1 一个真实场景:批量处理任务

我之前处理过一个需求,需要把某统计软件里的 50 份报表的数据表头统一修改。人工操作的话,每份报表要点击 5 次菜单、修改 3 个字段、再点保存,平均耗时两分钟。接入 CLI-Anything 之后,我只需要写一个 shell 循环:

for i in $(seq 1 50); do cli-anything run statapp rename_header --input "{\"row\": $i, \"header\": \"新表头\"}" done

整个跑完不到 40 分钟,而且因为每步操作都有日志和结果校验,中途哪一份报表失败了,脚本会直接停下来报警,不会继续无脑操作后面的数据。以前用 RPA 脚本实现这种需求,光是处理各种意外弹窗就够写一天的代码,现在只需要维护一份 YAML 配置。

6.2 配置共享与社区化

因为配置是纯文本的 YAML,天然适合放进 git 仓库管理和分享。我后来把适配过的几个软件的配置整理成了一份公共配置仓,同事拉下来就能用,不用重复踩坑。如果某个软件升级导致配置失效,只需要有人更新一份配置,其他所有使用者都能受益,维护成本被摊薄到了极低。

6.3 从“适配层”到“软件遥控器”

往远了想,CLI-Anything 的适配概念可以继续扩展成“软件遥控器”。任何设备,只要有界面、能操作,理论上都能被封装成 CLI。比如远程桌面里的虚拟机、运行在浏览器里的内部系统、甚至是手机上通过 ADB 控制的 App,底层原理都是同一套思路:把图形交互抽象成可编程接口。这个方向我还在继续摸索,但目前已经跑通的桌面 GUI 场景已经足够解决大部分实际痛点。

最后再分享一个体会:AI Agent 的应用落地,瓶颈往往不在模型能力,而在“连接”。CLI-Anything 与其说是一个工具箱,不如说是一种连接思路——让旧软件通过统一的命令行界面和新一代 AI 工具握手。如果你也在给自己的软件做 Agent 化改造,我的建议是从最常用的那个 GUI 工具开始,把它的核心操作抽象成配置,先跑通一条最简单的链路。等第一条链路稳定之后,你会慢慢发现,原本只能靠人工点点点的世界,正在一点点变得可编程起来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询