ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

@gui-agent/operator-nutjs 实战指南:基于 nut.js 的桌面 GUI Agent 操作器

@gui-agent/operator-nutjs 实战指南:基于 nut.js 的桌面 GUI Agent 操作器 gui-agent/operator-nutjs 实战指南基于 nut.js 的桌面 GUI Agent 操作器【免费下载链接】UI-TARS-desktopThe Open-Source Multimodal AI Agent Stack: Connecting Cutting-Edge AI Models and Agent Infra项目地址: https://gitcode.com/GitHub_Trending/ui/UI-TARS-desktopNutJS Operator是 UI-TARS 多模态开源仓库multimodal/gui-agent/operator-nutjs目录下提供的一个「电脑操作器」Computer Operator它以 nut.jsNative UI Testing风格的底层库为驱动把鼠标移动/点击、键盘输入、滚轮、截屏、等待等桌面原语统一封装成 GUI Agent 标准动作。本文以该包的官方 README 为骨架结合仓库源码NutJSOperator.ts、共享动作类型 与 操作器抽象基类展开讲解读完你将掌握它的安装、初始化、坐标换算、动作执行模型与关键实现细节并能把它直接接入你自己的 GUI Agent 任务循环。一、NutJS Operator 是什么在 GUI Agent 的典型工作流中Agent 模型观察屏幕截图、规划动作随后需要有一个「操作器」真正把动作落到真实的桌面环境中。gui-agent/operator-nutjs扮演的正是这个角色官方 README 对其定位的描述是面向 GUI Agent 的桌面电脑操作器提供一套与桌面环境交互的 API覆盖截屏、鼠标操作、键盘操作等能力。官方文档列出的核心特性如下截屏Screenshot捕获屏幕画面并对高 DPI 显示器做正确缩放处理鼠标操作Mouse Operations移动、单击、双击、右键、拖拽等键盘操作Keyboard Operations键入文本、按下快捷键等滚动Scroll向上 / 向下滚动等待Wait等待指定时间。从包元信息看当前仓库中该包的版本为0.3.0构建产物经 rslib 输出为 ESM/CJS/类型声明三份见 package.json并声明了computer-use/nut-js、agent-infra/logger、gui-agent/sharedworkspace 内联依赖、jimp图像缩放等依赖。需要说明的是仓库中另有一份旧版同名思路的 SDK packages/ui-tars/operators/nut-js属于另一条产品线ui-tars/operator-nut-js本文讨论的是multimodal/gui-agent下的gui-agent/operator-nutjs。二、安装与包结构官方文档给出三种包管理器安装方式npm install gui-agent/operator-nutjsyarn add gui-agent/operator-nutjspnpm add gui-agent/operator-nutjs包源码结构非常精简对应仓库路径 multimodal/gui-agent/operator-nutjs路径作用src/index.ts仅一行核心导出export { NutJSOperator } from ./NutJSOperator;src/NutJSOperator.ts操作器完整实现examples/test-runner.ts可运行示例初始化、截屏并把动作逐一跑在 Google 首页上package.json提供pnpm exampletsx运行示例与pnpm testvitest脚本需要注意一点代码中实际 import 的是computer-use/nut-js而非 README 中提到的 nut-tree 原仓库命名空间它向操作器暴露了screen、mouse、keyboard、clipboard、Button、Key、Point、straightTo、sleep等底层能力。三、快速开始最小可用示例README 提供了一个可直接照抄的最小示例创建 logger 与操作器实例先截一张屏再依次执行「点击屏幕中心」「键入文本」两个动作import { NutJSOperator } from gui-agent/operator-nutjs; import { ConsoleLogger, LogLevel } from agent-infra/logger; // Create a logger const logger new ConsoleLogger(undefined, LogLevel.DEBUG); // Create an operator instance const operator new NutJSOperator(logger); // Take a screenshot const screenshot await operator.screenshot(); console.log(Screenshot taken:, screenshot.status); // Execute actions const result await operator.execute({ actions: [ { type: click, inputs: { point: { normalized: { x: 0.5, y: 0.5 } // Click at the center of the screen } } }, { type: type, inputs: { content: Hello, World! } } ] });需要特别指出的是README 示例中的screenshot()、execute()是操作器内部受保护/原始方法在真实接入场景里更推荐使用抽象基类 operator.ts 暴露的三个带初始化保障的公共入口await operator.doInitialize()幂等初始化内部_initialized/_initializing状态机保证只初始化一次、并发调用共享同一个 Promiseawait operator.doScreenshot()先确保初始化再截屏异常时返回status: failed而非抛出await operator.doExecute(params)先确保初始化再顺序执行动作列表异常时同样折叠为status: failederrorMessage的返回结构。官方示例 examples/test-runner.ts 就是这一写法的完整示范operator.doInitialize()之后调用operator.doScreenshot()并把 base64 解码落盘为dumps/*.jpg再对 Google 首页逐条执行move → click → type → hotkey(Enter) → wait → scroll → right_click → double_click → drag等动作每一步结束后都会重新截屏留档。四、类与 API 参考4.1NutJSOperator类NutJSOperator继承自共享抽象基类Operator见 operator.ts必须实现四个抽象方法这也构成了理解全包的索引抽象方法NutJS 实现位置职责initialize(): PromisevoidNutJSOperator.ts#L39-L48抓取一帧屏幕并采集逻辑分辨率与像素密度构建ScreenContextsupportedActions(): SupportedActionType[]NutJSOperator.ts#L50-L69声明本操作器支持的动作类型screenContext(): ScreenContextNutJSOperator.ts#L71-L77返回屏幕上下文未初始化时抛错screenshot(): PromiseScreenshotOutput/execute(params): PromiseExecuteOutputNutJSOperator.ts#L79-L125截屏与批量执行4.2 构造函数constructor(logger: ConsoleLogger defaultLogger)loggeragent-infra/logger的ConsoleLogger实例。构造函数内部会用logger.spawn([NutJSOperator])派生带前缀的子 logger所有动作日志统一携带[NutJSOperator]标记默认值为模块级常量defaultLogger new ConsoleLogger(undefined, LogLevel.DEBUG)。4.3screenshot()输出返回ScreenshotOutput字段如下类型定义见 agents.tsbase64base64 编码的图片数据保持物理像素尺寸contentTypeimage/jpegstatussuccess | failed失败时额外带errorMessage。4.4execute()输入输出execute(params: ExecuteParams)接收{ actions: BaseAction[] }可附带模型原始回复等字段见 agents.ts按数组顺序逐条执行动作全部成功返回{ status: success }。任何一条动作参数非法或类型不识别都会让整批失败——源码中singleActionExecutor对「必需坐标缺失」「拖拽缺起点/终点」「type 内容为空」「非法快捷键」「非法滚动方向」等场景都会直接throw。五、两种坐标系统与高 DPI 换算NutJS Operator 的精髓之一是它屏蔽了「屏幕物理像素」与「Agent 视角归一化坐标」之间的换算这也是 README 强调「截屏对高 DPI 显示器做正确缩放」的底层原因。5.1Coordinates结构共享类型 actions.ts 中定义了坐标结构export interface Coordinates { raw?: { x: number; y: number }; // 原始像素坐标 normalized?: { x: number; y: number }; // 归一化坐标0–1 referenceBox?: { x1: number; y1: number; x2: number; y2: number }; referenceSystem?: screen | window | browserPage | string; }5.2 归一化坐标如何换算成真实坐标源码 calculateRealCoords 实现换算规则若提供normalizedrealX normalized.x * screenContext.screenWidthrealY normalized.y * screenContext.screenHeight若未提供normalized但提供raw直接使用原始像素坐标两者皆缺抛出Invalid coordinates。screenWidth/screenHeight来自 initialize截获一帧后读取pixelDensity.scaleX/scaleY用物理分辨率除以缩放因子得到逻辑分辨率this._screenContext { screenWidth: screenWithScale.width / screenWithScale.pixelDensity.scaleX, screenHeight: screenWithScale.height / screenWithScale.pixelDensity.scaleY, scaleX: screenWithScale.pixelDensity.scaleX, scaleY: screenWithScale.pixelDensity.scaleY, };5.3 截屏的反向缩放截屏路径则与之相反物理像素 → 逻辑像素见 screenshotscreen.grab()抓取原始帧.toRGB()得到 RGB 位图与pixelDensity用jimp按目标宽高物理宽 / scaleX、物理高 / scaleY缩放编码为 JPEG 并 base64 输出。最终效果是模型看到的截图尺寸与归一化坐标使用的逻辑分辨率严格一致normalized: {x:0.5, y:0.5}永远指向截图正中心无论 Windows/Linux 下的 125%/150% 缩放或 macOS Retina 屏如何设置。源码日志会打印screenshot: ${width}x${height}, scaleFactor: ${scaleFactor}便于核对。六、Supported Actions 全量动作详解6.1 声明 vs. 执行的差异代码中有两层「动作清单」声明层supportedActions() 返回 17 种click、right_click、middle_click、double_click、mouse_down、mouse_up、mouse_move、drag、scroll、type、hotkey、press、release、wait、call_user、finished。这一层用于向 Agent 描述可用的动作空间执行层singleActionExecutor 的 switch 分支。注意mouse_down、mouse_up、call_user虽在声明列表中但 switch 中并没有对应分支落入default会抛Unsupported action——从源码结构可以推断这类动作要么是留给上层 Agent 循环自行解释如call_user表示请求用户介入要么是尚待补全的执行能力接入时需自行确认。6.2 鼠标动作README 别名表 源码实现README 给出的鼠标动作分组与别名如下源码 switch 均予支持分组动作名含别名源码行为移动move、move_to、mouse_move、hover需point先做坐标换算再mouse.move(straightTo(...))单击click、left_click、left_single左键单击双击left_double、double_click左键双击右键right_click、right_single右键单击中键middle_click中键单击拖拽left_click_drag、drag、select需start与end坐标点击类统一走 handleClick先换算并移动到目标点sleep(100)稳定指针后执行mouse.click(button)或mouse.doubleClick(button)。拖拽实现见 switch 中drag分支移动到起点 → 停顿 100ms →mouse.drag(straightTo(new Point(endX, endY)))一气呵成适合文本选中、拖动文件等场景。6.3 键盘动作type键入文本。输入处理相当精细NutJSOperator.ts#L186-L212先trim()再剥离末尾的\n或字面量\\n设置keyboard.config.autoDelayMs 0提升键入速度Windows 平台回退方案把文本写入系统剪贴板后模拟CtrlV粘贴粘贴后恢复原剪贴板内容以规避 nut.js 在 Windows 上的键入兼容问题其他平台直接keyboard.type(content)若原内容以换行结尾则补按一次Enter结束后将autoDelayMs恢复为 500ms。hotkey一次性「按下并松开」组合键press/release仅按下或仅松开用于组合出长按类操作。三者共用 getHotkeys 完成字符串到按键码的解析按键串按空白或分割keyStr.split(/[\s]/)逐段小写后查表内置别名表return → Enter、page down/page up、,→Key.Comma、方向键arrowup/arrowdown/arrowleft/arrowright等平台化修饰键meta/win/command/cmd在 macOS 映射为LeftCmd、其他平台为LeftWinctrl在 macOS 映射为LeftCmd代码如此实现需要注意该约定、其余平台为LeftControl别名表未命中的键会继续用 nut.jsKey枚举的 lowercase 字典兜底所以enter、escape、a、1等常规键名都能解析。因此{ type: hotkey, inputs: { key: ctrla } }、{ type: hotkey, inputs: { key: commandspace } }这类写法天然可用。6.4 滚动、等待与收尾动作scrollNutJSOperator.ts#L232-L251可选的point会先把鼠标移到该位置再按direction大小写不敏感执行滚动——up用mouse.scrollUp(500)、down用mouse.scrollDown(500)每次固定滚动 500 个刻度left/right方向当前不被支持会抛Unsupported scroll direction。注意示例工程里传入的amount字段在此实现中并未被读取属于预留字段wait等待指定秒数inputs.time单位秒未传时默认 5000ms。源码同时打印 warning「The operator should not process wait action」暗示理想情况下等待调度应发生在 Agent 层而非操作器内finished空操作仅打日志用于在动作序列结尾标识任务结束。七、动作类型在共享层如何定义前面表格中的动作并非只属于 NutJS Operator——它们定义在共享包gui-agent/shared中见 actions.ts。NutJSOperator通过继承Operator、声明supportedActions()与整个 GUI Agent 体系对接。这种「动作 Schema 与具体执行器解耦」的设计使得同一个动作描述可以被不同操作器解释桌面端操作器本文的 NutJS移动端 ADB / 浏览器 / 浏览器底座等其他 operator 实现。在共享动作类型中每个动作被建模为统一三元组BaseAction { type, inputs, meta? }鼠标动作携带Coordinates点、键盘动作携带字符串内容或按键串拖拽动作携带start/end两点。配套的ACTION_METADATA注册表actions.ts为每个动作标注了categorymouse/keyboard/navigation/mobile/system/wait与语义描述可用于自动生成系统提示词中的动作空间说明。八、把它跑起来的完整链路含验证建议综合 README、测试运行器示例 与源码一条可自测的接入路径为# 在 monorepo 内安装依赖后直接运行示例 pnpm example示例脚本会依次完成创建实例 →doInitialize()→ 截屏落盘dumps/screenshot-*.jpg→ 打开浏览器到 Google 首页 → 顺序执行 move/click/type/hotkey/wait/scroll/right_click/double_click/drag → 每步后截屏校验。运行环境要求操作系统当前桌面可被鼠标键盘控制真实显示器或虚拟桌面因为它执行的是真实的系统级输入。如果要写自己的验证脚本建议沿用基类公共方法并做显式初始化const operator new NutJSOperator(logger); await operator.doInitialize(); const ctx await operator.getScreenContext(); console.log(ctx); // { screenWidth, screenHeight, scaleX, scaleY } const shot await operator.doScreenshot(); // shot.base64 可直接喂给多模态大模型做下一步决策 const out await operator.doExecute({ rawContent: click the search box, rawActionStrings: [click], actions: [{ type: click, inputs: { point: { normalized: { x: 0.5, y: 0.44 } } } }], });doExecute对参数格式比较宽容ExecuteParams本身是{ actions }与ParsedGUIResponse可选字段的交叉并允许扩展字段因此可以直接把模型解析结果原样透传。九、总结gui-agent/operator-nutjs是 UI-TARS 多模态 Agent 技术栈中面向真实桌面操作系统的执行层组件它用约 360 行实现覆盖了截屏、鼠标、键盘、滚动、等待等 GUI 原语并通过归一化坐标与高 DPI 换算让「模型看到什么坐标系就点哪个点」这件事在不同缩放率的屏幕上保持正确。核心知识点可归纳为继承Operator抽象基类公共入口用doInitialize / doScreenshot / doExecute自动处理幂等初始化与错误折叠Coordinates同时支持raw像素坐标与normalized0–1 归一化坐标动作类型是共享层 Schema动作执行支持 README 中的全部别名如move_to、left_single、select等type在 Windows 走剪贴板粘贴回退、hotkey/press/release通过别名表 Key枚举解析按键串当前实现的滚动仅支持up/downmouse_down/mouse_up/call_user处于「已声明、未落地执行」状态接入时需留意。如果你正在构建桌面端 GUI Agent例如结合 apps/ui-tars 的界面能力可以把该操作器作为动作执行后端与gui-agent/shared的动作解析、agent-infra/logger的日志系统组合使用。项目采用 Apache-2.0 许可源码路径multimodal/gui-agent/operator-nutjs。【免费下载链接】UI-TARS-desktopThe Open-Source Multimodal AI Agent Stack: Connecting Cutting-Edge AI Models and Agent Infra项目地址: https://gitcode.com/GitHub_Trending/ui/UI-TARS-desktop创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表