ARTICLE DETAIL

资讯详情

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

UFO² 之 HostUIExecutor:面向 HostAgent 的 Windows 窗口选择与桌面级 UI 自动化 MCP 动作服务器

UFO² 之 HostUIExecutor:面向 HostAgent 的 Windows 窗口选择与桌面级 UI 自动化 MCP 动作服务器 UFO² 之 HostUIExecutor面向 HostAgent 的 Windows 窗口选择与桌面级 UI 自动化 MCP 动作服务器【免费下载链接】UFOUFO³: Weaving the Digital Agent Galaxy项目地址: https://gitcode.com/GitHub_Trending/uf/UFO本篇技术指南聚焦 UFO² 仓库中为HostAgent提供桌面级 UI 自动化能力的动作服务器Action Server——HostUIExecutor。它以 MCPModel Context Protocol工具的形式对外暴露select_application_window负责在 Windows 桌面环境中完成窗口发现、焦点切换、最大化与 UI 状态初始化是 HostAgent 将任务委派给 AppAgent 之前的关键选窗环节。读完本文你将掌握该服务器的工具契约、配置方法、调用模式、错误处理与故障排查并能结合仓库源码理解其底层实现原理。服务器定位与基本信息HostUIExecutor 是 UFO² 中由 HostAgent 侧调用的本地in-process动作服务器提供系统级的 UI 自动化能力窗口管理、窗口切换以及跨应用交互。它在 HostAgent 工作流中的核心职责是选中一个应用窗口使后续的 UICollector 数据采集与 AppUIExecutor 动作执行能够作用于该窗口。属性值NamespaceHostUIExecutor服务器名称UFO UI HostAgent Action MCP Server服务器类型Action动作部署方式Local进程内服务 AgentHostAgentLLM 可选择✅ 是由 LLM 决定何时执行平台Windows后端UIAutomationUIA或 Win32工具类型action工具键格式action::{tool_name}从源码看该服务器由 ui_mcp_server.py 中的create_host_action_mcp_server工厂函数创建通过MCPRegistry.register_factory_decorator(HostUIExecutor)注册到 MCP 注册表并以FastMCP(UFO UI HostAgent Action MCP Server)实例暴露工具。值得注意的细节是该模块在非 Windows 平台如 Linux上会打印警告并跳过初始化因为pywinauto等依赖与窗口自动化能力都要求 Windows 环境。核心工具select_application_windowselect_application_window是 HostUIExecutor 提供的唯一工具也是 HostAgent 工作流中窗口选择的主入口。它按 ID 与名称查找目标窗口依次执行以下步骤按 ID 和名称找到目标窗口将焦点focus设置到该窗口可选地最大化窗口取决于MAXIMIZE_WINDOW可选地在窗口周围绘制红色轮廓用于调试为后续 AppAgent 操作初始化 UI 状态。⚠️前置条件必须先调用 UICollector 的get_desktop_app_info获取有效的窗口 ID 与名称否则无法获得合法参数。参数说明参数类型必填说明idstr✅ 是目标应用窗口的精确标注 ID必须与get_desktop_app_info返回的 ID 一致namestr✅ 是目标应用窗口的精确名称必须与所选 ID 对应的名称一致在 ui_mcp_server.py 中这两个参数的 Annotated 描述明确要求 LLM严格遵守应用信息中 id 字段给出的选项且 name 必须与所选 id 匹配。工具内部通过_verify_id(id, name, app_window_dict)校验ID 为空、窗口列表为空、ID 不存在等都会抛出ToolError。返回值与 WindowInfo 结构返回值类型为Dict[str, Any]{ root_name: str, # 应用根名称例如 WINWORD.EXE window_info: dict # WindowInfo 对象包含窗口详细信息 }其中window_info为WindowInfo结构其字段与 messages.py 中定义的WindowInfo数据模型对应源码中由_window2window_info将pywinauto的UIAWrapper转换为该结构见 ui_mcp_server.py{ annotation_id: str, # 窗口标识符 name: str, # 窗口元素名称 title: str, # 窗口标题文本 handle: int, # 窗口句柄 (HWND) class_name: str, # 窗口类名 process_id: int, # 进程 ID is_visible: bool, # 可见状态 is_minimized: bool, # 最小化状态 is_maximized: bool, # 最大化状态 is_active: bool, # 是否为活动窗口 rectangle: { # 窗口包围矩形 x: int, y: int, width: int, height: int }, text_content: str, # 窗口文本 control_type: str # 控件类型通常为 Window }完整调用示例# Step 1: 获取桌面可用窗口UICollector windows await computer.run_actions([ MCPToolCall( tool_keydata_collection::get_desktop_app_info, tool_nameget_desktop_app_info, parameters{remove_empty: True} ) ]) # windows[0].data [ # {id: 1, name: Calculator, type: Window, kind: window}, # {id: 2, name: Notepad, type: Window, kind: window} # ] # Step 2: 选中 Calculator 窗口HostUIExecutor result await computer.run_actions([ MCPToolCall( tool_keyaction::select_application_window, tool_nameselect_application_window, parameters{ id: 1, name: Calculator } ) ]) # 结果示例 { root_name: ApplicationFrameHost.exe, window_info: { annotation_id: 1, title: Calculator, handle: 12345678, class_name: ApplicationFrameWindow, process_id: 9876, is_visible: True, is_minimized: False, is_maximized: False, is_active: True, rectangle: {x: 100, y: 100, width: 400, height: 600} } }run_actions是 computer.py 中定义的核心执行入口负责将MCPToolCall列表路由到对应 MCP 服务器执行。错误处理契约该工具在以下场景抛出ToolError错误信息对排查问题具有直接的指导意义# 错误 1缺少 ID ToolError(Window id is required for select_application_window) # 错误 2无可用窗口 ToolError(No application windows available. Please call get_desktop_app_info first.) # 错误 3ID 无效 ToolError(Control with id 99 not found. Available control ids: [1, 2, 3]) # 错误 4设置焦点失败 ToolError(Failed to set focus on window: {error_details})这些错误信息与源码中_verify_id的实现一一对应见 ui_mcp_server.py前三种属于参数校验失败第四种则是在执行window.set_focus()、window.maximize()、window.draw_outline()时捕获异常后包装抛出ui_mcp_server.py。值得注意的是错误 3 会附带当前可用 ID 列表帮助 LLM 或开发者纠正参数。配置行为MAXIMIZE_WINDOW 与 SHOW_VISUAL_OUTLINE_ON_SCREEN工具执行时尊重两个系统配置项它们直接控制窗口选中后的行为MAXIMIZE_WINDOW是否最大化窗口# config.yaml MAXIMIZE_WINDOW: true # 窗口选中后自动最大化SHOW_VISUAL_OUTLINE_ON_SCREEN是否绘制红色轮廓# config.yaml SHOW_VISUAL_OUTLINE_ON_SCREEN: true # 在窗口周围绘制红色轮廓需要说明的是原文档给出的默认值为MAXIMIZE_WINDOW: False、SHOW_VISUAL_OUTLINE_ON_SCREEN: True而当前仓库的 system.yaml 实际配置为两者均为FalseSkip rendering visual outline on screen if not necessary。以仓库实际配置为准生产环境默认不绘制轮廓开发调试时可手动开启。源码中通过configs.get(MAXIMIZE_WINDOW, False)与configs.get(SHOW_VISUAL_OUTLINE_ON_SCREEN, True)读取这些配置ui_mcp_server.py。副作用与内部状态变更⚠️副作用提示✅改变焦点将目标窗口带到前台✅可能最大化若启用了MAXIMIZE_WINDOW✅视觉反馈若启用了SHOW_VISUAL_OUTLINE_ON_SCREEN绘制红色轮廓源码中为colourred, thickness3✅状态初始化为该窗口设置 AppPuppeteer执行成功后共享 UI 状态发生如下变化对应源码中UIServerState.initialize_for_window见 ui_mcp_server.pyui_state.selected_app_window被设置为该窗口对象ui_state.puppeteer以AppPuppeteer初始化构造函数参数为窗口文本与应用根名称可用命令会被记录日志Available commands: ...用于调试后续 UICollector 与 AppUIExecutor 的工具即可作用于该窗口。UIServerState是单例Singleton设计UICollector、HostUIExecutor、AppUIExecutor 三个服务器共享同一份 UI 状态从而保证选窗 → 采集 → 动作整条链路的状态一致性。UICollector 的get_desktop_app_info会把窗口列表缓存在last_app_windowsselect_application_window正是从这份缓存中按 ID 查找窗口。在 mcp.yaml 中的注册配置HostUIExecutor 在 mcp.yaml 中作为 HostAgent 的 action 服务器注册HostAgent: default: data_collection: - namespace: UICollector type: local start_args: [] reset: false # 切换电脑时是否重置 MCP 服务器状态 action: - namespace: HostUIExecutor type: local start_args: [] reset: false - namespace: CommandLineExecutor type: local start_args: [] reset: false配置项说明配置项类型说明namespacestr必须为HostUIExecutortypestr部署类型local进程内start_argslist启动参数local 类型通常为空resetbool任务之间是否重置服务器状态对 HostUIExecutor 通常为false因为窗口选择状态需要在流程中保持对比同一配置文件中 AppAgent 的注册AppUIExecutorCommandLineExecutor可以看出HostAgent 与 AppAgent 共享 UICollector 数据服务器但动作服务器是分离的——HostUIExecutor 专司选窗AppUIExecutor 专司窗内动作。三种典型使用模式模式一基础窗口选择流程这是最标准的 HostAgent 流程——发现窗口 → 选中窗口 → 窗内交互# 1. 发现桌面窗口 windows await computer.run_actions([ MCPToolCall(tool_keydata_collection::get_desktop_app_info, ...) ]) # 2. 选中目标窗口 await computer.run_actions([ MCPToolCall( tool_keyaction::select_application_window, parameters{id: 1, name: Calculator} ) ]) # 3. 此后 AppAgent 即可与该窗口交互 controls await computer.run_actions([ MCPToolCall(tool_keydata_collection::get_app_window_controls_info, ...) ])模式二多窗口工作流窗口切换HostAgent 的核心价值之一就是跨应用协同先操作 Word再切换到 Excel# 先操作第一个窗口 await computer.run_actions([ MCPToolCall( tool_keyaction::select_application_window, parameters{id: 1, name: Word} ) ]) # ... 在 Word 上执行动作 ... # 切换到第二个窗口 await computer.run_actions([ MCPToolCall( tool_keyaction::select_application_window, parameters{id: 2, name: Excel} ) ]) # ... 在 Excel 上执行动作 ...每次调用都会重新初始化 AppPuppeteer 与控件字典因此窗口切换是干净且隔离的。模式三选择前校验防御式编程在调用前先确认目标窗口确实存在避免向不存在的窗口发起选择# 获取窗口列表 windows await computer.run_actions([ MCPToolCall(tool_keydata_collection::get_desktop_app_info, ...) ]) # 校验目标窗口是否存在 target_windows [w for w in windows[0].data if Calculator in w[name]] if not target_windows: logger.error(Calculator not found) else: # 选中窗口 await computer.run_actions([ MCPToolCall( tool_keyaction::select_application_window, parameters{ id: target_windows[0][id], name: target_windows[0][name] } ) ])最佳实践1. 始终使用 get_desktop_app_info 返回的精确 ID 与名称窗口 ID 是采集阶段动态分配的标注号硬编码或猜测必然失败# ✅ 正确使用 get_desktop_app_info 返回的精确 ID 和名称 windows await computer.run_actions([ MCPToolCall(tool_keydata_collection::get_desktop_app_info, ...) ]) window windows[0].data[0] # 第一个窗口 await computer.run_actions([ MCPToolCall( tool_keyaction::select_application_window, parameters{ id: window[id], # 来自响应的精确 ID name: window[name] # 来自响应的精确名称 } ) ]) # ❌ 错误硬编码或猜测 ID await computer.run_actions([ MCPToolCall( tool_keyaction::select_application_window, parameters{id: 1, name: Some Window} # 可能并不存在 ) ])2. 处理选择失败对is_error标志进行检查失败时重试或选择替代窗口try: result await computer.run_actions([ MCPToolCall( tool_keyaction::select_application_window, parameters{id: window_id, name: window_name} ) ]) if result[0].is_error: logger.error(fFailed to select window: {result[0].content}) # 重试或选择其他窗口 else: logger.info(fSelected window: {result[0].data[root_name]}) except Exception as e: logger.error(fWindow selection exception: {e})3. 选中后等待窗口就绪窗口激活存在短暂延迟选中后先等待再截图或操作# 选中窗口 await computer.run_actions([ MCPToolCall(tool_keyaction::select_application_window, ...) ]) # 等待窗口变为活动状态 await asyncio.sleep(0.5) # 现在再与窗口交互 await computer.run_actions([ MCPToolCall(tool_keydata_collection::capture_window_screenshot, ...) ])这与仓库 system.yaml 中SLEEP_TIME: 1每步之间等待窗口就绪的休眠时间的配置思路一致说明窗口就绪等待是 UFO² 动作链路中的常规考虑。4. 用视觉轮廓辅助调试开发期开启红色轮廓确认选中对象生产期关闭以减少视觉干扰# config.yaml - 开发期开启 SHOW_VISUAL_OUTLINE_ON_SCREEN: true # 在选中窗口上显示红色轮廓 # config.yaml - 生产期关闭 SHOW_VISUAL_OUTLINE_ON_SCREEN: false与 AppAgent 的集成链路select_application_window成功之后该窗口即成为AppAgent的操作目标。从源码角度看这一链条由 host_agent_processing_strategy.py 中的HostActionExecutionStrategy驱动当 LLM 输出的函数名为select_application_window类常量SELECT_APPLICATION_COMMAND见 该文件第 726 行时会走_execute_application_selection分支从返回结果中提取root_name作为selected_application_root并同步更新selected_target_id等上下文状态为后续 AppAgent 接管窗口铺路。一个完整的跨 Agent 协作示例# HostAgent选中窗口 host_result await computer.run_actions([ MCPToolCall( tool_keyaction::select_application_window, parameters{id: 1, name: Calculator} ) ]) # AppAgent获取选中窗口内的控件 app_controls await computer.run_actions([ MCPToolCall(tool_keydata_collection::get_app_window_controls_info, ...) ]) # AppAgent点击选中窗口内的按钮 app_click await computer.run_actions([ MCPToolCall( tool_keyaction::click_input, tool_nameclick_input, parameters{id: 5, name: Seven, button: left} ) ])故障排查指南场景一窗口未找到现象ToolError(Control with id X not found)解决方案以refresh_app_windowsTrue重新调用get_desktop_app_info刷新窗口缓存源码中该参数为 False 时会复用last_app_windows缓存确认窗口未被最小化或隐藏remove_empty会过滤掉无可见内容的窗口确认窗口仍然存在未被关闭。场景二焦点设置失败现象ToolError(Failed to set focus on window)解决方案检查窗口是否处于禁用或无响应状态确认窗口对应进程仍在运行确保没有模态对话框阻塞焦点稍等片刻后重试。场景三选错了同名的窗口现象选中了名称相似但并非目标的窗口解决方案使用更具体的名称匹配逻辑结合返回window_info中的process_id或class_name进行甄别在选中前用额外条件过滤窗口列表。相关文档导航UICollector —— 窗口发现服务器get_desktop_app_info的来源选窗的前置依赖AppUIExecutor —— 窗口内交互服务器选窗后动作的执行者动作服务器概念 —— Action 服务器的通用概念HostAgent 架构 —— HostAgent 的整体架构MCP 服务器配置总览 —— 服务器在 mcp.yaml 中的注册方式总结HostUIExecutor 虽然只暴露一个工具却是 UFO² 双 AgentHostAgent AppAgent协作链路上的枢纽它把桌面级任意窗口与窗内 UI 自动化这两个层次衔接起来通过共享单例 UI 状态实现选窗、采集、动作的无缝衔接。理解它的工具契约精确的 id/name 匹配、配置行为MAXIMIZE_WINDOW、SHOW_VISUAL_OUTLINE_ON_SCREEN与状态副作用是编写可靠 HostAgent 工作流、排查窗口选不中焦点抢不过等高频问题的前提。结合 ui_mcp_server.py 的源码与 mcp.yaml 的注册配置你可以在自己的 Windows 环境中快速复现并扩展这套窗口选择能力。【免费下载链接】UFOUFO³: Weaving the Digital Agent Galaxy项目地址: https://gitcode.com/GitHub_Trending/uf/UFO创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表