应用程序控制者

Puppeteer 是一个工具,允许 UFO 自动化应用程序并对其采取行动。目前,UFO 支持两种类型的动作:GUIAPI。每个应用程序都有一个共享的 GUI 动作接口,用于处理鼠标和键盘事件,以及一个私有的 API 动作接口,用于处理应用程序的本机 API。我们在下图中说明了 Puppeteer 架构

HostAgent 的状态机图如下所示

注意

UFO 还可以调用应用内 AI 工具,例如 Copilot,以协助自动化过程。这可以通过使用 GUIAPI 与应用内 AI 工具进行交互来实现。

  • UI 自动化器 - 此动作类型用于与应用程序的 UI 控件交互,例如按钮、文本框和菜单。UFO 使用 UIAWin32 API 与应用程序的 UI 控件交互。
  • API - 此动作类型用于与应用程序的本机 API 交互。用户和应用程序开发人员可以创建自己的 API 动作以与特定应用程序交互。
  • Web - 此动作类型用于与 Web 应用程序交互。UFO 使用 crawl4ai 库从网页中提取信息。
  • Bash - 此动作类型用于与应用程序的命令行界面 (CLI) 交互。
  • AI 工具 - 此动作类型用于与基于 LLM 的 AI 工具交互。

动作设计模式

UFO 中的动作是使用 命令 设计模式实现的,该模式封装了一个接收器、一个命令和一个调用者。接收器是执行动作的对象,命令是封装动作的对象,调用者是触发动作的对象。

UFO 中实现动作的基本类如下

角色 描述
接收器 ufo.automator.basic.ReceiverBasic UFO 中所有接收器的基类。接收器是对应用程序执行动作的对象。
命令 ufo.automator.basic.CommandBasic UFO 中所有命令的基类。命令是封装由接收器执行的动作的对象。
调用者 ufo.automator.puppeteer.AppPuppeteer UFO 中调用者的基类。调用者是触发命令由接收器执行的对象。

在代理框架中使用命令设计模式的优点是,它允许动作的发送者和接收者解耦。这种解耦使代理能够在不知道对象或正在执行的动作的详细信息的情况下,在不同的对象上执行动作,从而使代理对于新动作更具灵活性和可扩展性。

接收器

Receiver 是 Automator 应用程序中的一个核心组件,它对应用程序执行动作。它提供了与应用程序交互和执行动作的功能。所有可用的动作都在 ReceiverManager 类中注册。

您可以在下面找到基本 Receiver 类的参考

基类:ABC

抽象接收器接口。

command_registry property

获取命令注册表。

supported_command_names property

获取命令名称列表。

register(command_class) classmethod

用于将状态类注册到状态管理器的装饰器。

参数
  • command_class (Type[CommandBasic]) –

    要注册的状态类。

返回
源代码位于 automator/basic.py
46
47
48
49
50
51
52
53
54
@classmethod
def register(cls, command_class: Type[CommandBasic]) -> Type[CommandBasic]:
    """
    Decorator to register the state class to the state manager.
    :param command_class: The state class to be registered.
    :return: The state class.
    """
    cls._command_registry[command_class.name()] = command_class
    return command_class

register_command(command_name, command)

添加到命令注册表。

参数
  • command_name (str) –

    命令名称。

  • command (CommandBasic) –

    命令。

源代码位于 automator/basic.py
24
25
26
27
28
29
30
31
def register_command(self, command_name: str, command: CommandBasic) -> None:
    """
    Add to the command registry.
    :param command_name: The command name.
    :param command: The command.
    """

    self.command_registry[command_name] = command

self_command_mapping()

获取命令-接收器映射。

源代码位于 automator/basic.py
40
41
42
43
44
def self_command_mapping(self) -> Dict[str, CommandBasic]:
    """
    Get the command-receiver mapping.
    """
    return {command_name: self for command_name in self.supported_command_names}


命令

CommandReceiver 可以在应用程序上执行的特定动作。它封装了执行动作所需的功能和参数。Command 类是 Automator 应用程序中所有命令的基类。

您可以在下面找到基本 Command 类的参考

基类:ABC

抽象命令接口。

初始化命令。

参数
源代码位于 automator/basic.py
67
68
69
70
71
72
73
def __init__(self, receiver: ReceiverBasic, params: Dict = None) -> None:
    """
    Initialize the command.
    :param receiver: The receiver of the command.
    """
    self.receiver = receiver
    self.params = params if params is not None else {}

execute() abstractmethod

执行命令。

源代码位于 automator/basic.py
75
76
77
78
79
80
@abstractmethod
def execute(self):
    """
    Execute the command.
    """
    pass

redo()

重做命令。

源代码位于 automator/basic.py
88
89
90
91
92
def redo(self):
    """
    Redo the command.
    """
    self.execute()

undo()

撤销命令。

源代码位于 automator/basic.py
82
83
84
85
86
def undo(self):
    """
    Undo the command.
    """
    pass


注意

每个命令必须使用 register_command 装饰器向特定的 Receiver 注册才能执行。例如:@ReceiverExample.register class CommandExample(CommandBasic): ...

调用者 (AppPuppeteer)

AppPuppeteer 在 Automator 应用程序中扮演调用者的角色。它触发命令由接收器执行。AppPuppeteer 使 AppAgent 能够与应用程序的 UI 控件交互。它提供了将动作字符串转换为特定动作并执行它们的功能。所有可用的动作都在 Puppeteer 中使用 ReceiverManager 类注册。

您可以在 ufo/automator/puppeteer.py 文件中找到 AppPuppeteer 类的实现,其参考如下所示。

用于在 Windows 环境中自动化应用程序的 App Puppeteer 类。

初始化 App Puppeteer。

参数
  • process_name (str) –

    应用程序的进程名称。

  • app_root_name (str) –

    应用程序根名称,例如 WINWORD.EXE。

源代码位于 automator/puppeteer.py
22
23
24
25
26
27
28
29
30
31
32
def __init__(self, process_name: str, app_root_name: str) -> None:
    """
    Initialize the app puppeteer.
    :param process_name: The process name of the app.
    :param app_root_name: The app root name, e.g., WINWORD.EXE.
    """

    self._process_name = process_name
    self._app_root_name = app_root_name
    self.command_queue: Deque[CommandBasic] = deque()
    self.receiver_manager = ReceiverManager()

full_path property

获取进程的完整路径。仅适用于 COM 接收器。

返回
  • 字符串

    进程的完整路径。

add_command(command_name, params, *args, **kwargs)

将命令添加到命令队列。

参数
  • command_name (str) –

    命令名称。

  • params (Dict[str, Any]) –

    参数。

源代码位于 automator/puppeteer.py
 94
 95
 96
 97
 98
 99
100
101
102
103
def add_command(
    self, command_name: str, params: Dict[str, Any], *args, **kwargs
) -> None:
    """
    Add the command to the command queue.
    :param command_name: The command name.
    :param params: The arguments.
    """
    command = self.create_command(command_name, params, *args, **kwargs)
    self.command_queue.append(command)

close()

关闭应用程序。仅适用于 COM 接收器。

源代码位于 automator/puppeteer.py
145
146
147
148
149
150
151
def close(self) -> None:
    """
    Close the app. Only works for COM receiver.
    """
    com_receiver = self.receiver_manager.com_receiver
    if com_receiver is not None:
        com_receiver.close()

create_command(command_name, params, *args, **kwargs)

创建命令。

参数
  • command_name (str) –

    命令名称。

  • params (Dict[str, Any]) –

    命令的参数。

源代码位于 automator/puppeteer.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def create_command(
    self, command_name: str, params: Dict[str, Any], *args, **kwargs
) -> Optional[CommandBasic]:
    """
    Create the command.
    :param command_name: The command name.
    :param params: The arguments for the command.
    """
    receiver = self.receiver_manager.get_receiver_from_command_name(command_name)
    command = receiver.command_registry.get(command_name.lower(), None)

    if receiver is None:
        raise ValueError(f"Receiver for command {command_name} is not found.")

    if command is None:
        raise ValueError(f"Command {command_name} is not supported.")

    return command(receiver, params, *args, **kwargs)

execute_all_commands()

执行命令队列中的所有命令。

返回
  • List[Any]

    执行结果。

源代码位于 automator/puppeteer.py
82
83
84
85
86
87
88
89
90
91
92
def execute_all_commands(self) -> List[Any]:
    """
    Execute all the commands in the command queue.
    :return: The execution results.
    """
    results = []
    while self.command_queue:
        command = self.command_queue.popleft()
        results.append(command.execute())

    return results

execute_command(command_name, params, *args, **kwargs)

执行命令。

参数
  • command_name (str) –

    命令名称。

  • params (Dict[str, Any]) –

    参数。

返回
  • 字符串

    执行结果。

源代码位于 automator/puppeteer.py
68
69
70
71
72
73
74
75
76
77
78
79
80
def execute_command(
    self, command_name: str, params: Dict[str, Any], *args, **kwargs
) -> str:
    """
    Execute the command.
    :param command_name: The command name.
    :param params: The arguments.
    :return: The execution result.
    """

    command = self.create_command(command_name, params, *args, **kwargs)

    return command.execute()

get_command_queue_length()

获取命令队列的长度。

返回
  • int

    命令队列的长度。

源代码位于 automator/puppeteer.py
105
106
107
108
109
110
def get_command_queue_length(self) -> int:
    """
    Get the length of the command queue.
    :return: The length of the command queue.
    """
    return len(self.command_queue)

get_command_string(command_name, params) staticmethod

生成函数调用字符串。

参数
  • command_name (str) –

    函数名称。

  • params (Dict[str, str]) –

    作为字典的参数。

返回
  • 字符串

    函数调用字符串。

源代码位于 automator/puppeteer.py
153
154
155
156
157
158
159
160
161
162
163
164
165
@staticmethod
def get_command_string(command_name: str, params: Dict[str, str]) -> str:
    """
    Generate a function call string.
    :param command_name: The function name.
    :param params: The arguments as a dictionary.
    :return: The function call string.
    """
    # Format the arguments
    args_str = ", ".join(f"{k}={v!r}" for k, v in params.items())

    # Return the function call string
    return f"{command_name}({args_str})"

get_command_types(command_name)

获取命令类型。

参数
  • command_name (str) –

    命令名称。

返回
  • 字符串

    命令类型。

源代码位于 automator/puppeteer.py
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def get_command_types(self, command_name: str) -> str:
    """
    Get the command types.
    :param command_name: The command name.
    :return: The command types.
    """

    try:
        receiver = self.receiver_manager.get_receiver_from_command_name(
            command_name
        )
        return receiver.type_name
    except:
        return ""

save()

保存应用程序的当前状态。仅适用于 COM 接收器。

源代码位于 automator/puppeteer.py
124
125
126
127
128
129
130
def save(self) -> None:
    """
    Save the current state of the app. Only works for COM receiver.
    """
    com_receiver = self.receiver_manager.com_receiver
    if com_receiver is not None:
        com_receiver.save()

save_to_xml(file_path)

将应用程序的当前状态保存到 XML。仅适用于 COM 接收器。

参数
  • file_path (字符串) –

    保存 XML 的文件路径。

源代码位于 automator/puppeteer.py
132
133
134
135
136
137
138
139
140
141
142
143
def save_to_xml(self, file_path: str) -> None:
    """
    Save the current state of the app to XML. Only works for COM receiver.
    :param file_path: The file path to save the XML.
    """
    com_receiver = self.receiver_manager.com_receiver
    dir_path = os.path.dirname(file_path)
    if not os.path.exists(dir_path):
        os.makedirs(dir_path)

    if com_receiver is not None:
        com_receiver.save_to_xml(file_path)


接收器管理器

ReceiverManager 管理 Automator 应用程序中的所有接收器和命令。它提供了注册和检索接收器和命令的功能。它是 AppPuppeteer 的补充组件。

接收器管理器类。

初始化接收器管理器。

源代码位于 automator/puppeteer.py
175
176
177
178
179
180
181
182
183
def __init__(self):
    """
    Initialize the receiver manager.
    """

    self.receiver_registry = {}
    self.ui_control_receiver: Optional[ControlReceiver] = None

    self._receiver_list: List[ReceiverBasic] = []

com_receiver property

获取 COM 接收器。

返回
  • WinCOMReceiverBasic

    COM 接收器。

receiver_factory_registry property

获取接收器工厂注册表。

返回
  • Dict[str, Dict[str, Union[str, ReceiverFactory]]]

    接收器工厂注册表。

receiver_list property

获取接收器列表。

返回
  • List[ReceiverBasic]

    接收器列表。

create_api_receiver(app_root_name, process_name)

获取 API 接收器。

参数
  • app_root_name (str) –

    应用程序根名称。

  • process_name (str) –

    进程名称。

源代码位于 automator/puppeteer.py
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
def create_api_receiver(self, app_root_name: str, process_name: str) -> None:
    """
    Get the API receiver.
    :param app_root_name: The app root name.
    :param process_name: The process name.
    """
    for receiver_factory_dict in self.receiver_factory_registry.values():

        # Check if the receiver is API
        if receiver_factory_dict.get("is_api"):
            receiver = receiver_factory_dict.get("factory").create_receiver(
                app_root_name, process_name
            )
            if receiver is not None:
                self.receiver_list.append(receiver)

    self._update_receiver_registry()

create_ui_control_receiver(control, application)

构建 UI 控制器。

参数
  • control (UIAWrapper) –

    控制元素。

  • application (UIAWrapper) –

    应用程序窗口。

返回
  • ControlReceiver

    UI 控制器接收器。

源代码位于 automator/puppeteer.py
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
def create_ui_control_receiver(
    self, control: UIAWrapper, application: UIAWrapper
) -> "ControlReceiver":
    """
    Build the UI controller.
    :param control: The control element.
    :param application: The application window.
    :return: The UI controller receiver.
    """

    # control can be None
    if not application:
        return None

    factory: ReceiverFactory = self.receiver_factory_registry.get("UIControl").get(
        "factory"
    )
    self.ui_control_receiver = factory.create_receiver(control, application)
    self.receiver_list.append(self.ui_control_receiver)
    self._update_receiver_registry()

    return self.ui_control_receiver

get_receiver_from_command_name(command_name)

从命令名称获取接收器。

参数
  • command_name (str) –

    命令名称。

返回
  • ReceiverBasic

    映射的接收器。

源代码位于 automator/puppeteer.py
235
236
237
238
239
240
241
242
243
244
def get_receiver_from_command_name(self, command_name: str) -> ReceiverBasic:
    """
    Get the receiver from the command name.
    :param command_name: The command name.
    :return: The mapped receiver.
    """
    receiver = self.receiver_registry.get(command_name, None)
    if receiver is None:
        raise ValueError(f"Receiver for command {command_name} is not found.")
    return receiver

register(receiver_factory_class) classmethod

用于将接收器工厂类注册到接收器管理器的装饰器。

参数
  • receiver_factory_class (Type[ReceiverFactory]) –

    要注册的接收器工厂类。

返回
  • ReceiverFactory

    接收器工厂类实例。

源代码位于 automator/puppeteer.py
276
277
278
279
280
281
282
283
284
285
286
287
288
289
@classmethod
def register(cls, receiver_factory_class: Type[ReceiverFactory]) -> ReceiverFactory:
    """
    Decorator to register the receiver factory class to the receiver manager.
    :param receiver_factory_class: The receiver factory class to be registered.
    :return: The receiver factory class instance.
    """

    cls._receiver_factory_registry[receiver_factory_class.name()] = {
        "factory": receiver_factory_class(),
        "is_api": receiver_factory_class.is_api(),
    }

    return receiver_factory_class()


有关更多详细信息,请参阅 Automator 模块中每个组件和类的具体文档。