Application Puppeteer

Der Puppeteer ist ein Werkzeug, das es UFO ermöglicht, Aktionen auf Anwendungen zu automatisieren und auszuführen. Derzeit unterstützt UFO zwei Arten von Aktionen: GUI und API. Jede Anwendung verfügt über eine gemeinsame GUI-Aktionsschnittstelle für die Interaktion mit Maus- und Tastaturereignissen und eine private API-Aktionsschnittstelle für die Interaktion mit der nativen API der Anwendung. Wir illustrieren die Puppeteer-Architektur in der folgenden Abbildung.

Das Zustandsdiagramm für den HostAgent ist unten dargestellt.

Hinweis

UFO kann auch In-App-KI-Tools wie Copilot aufrufen, um den Automatisierungsprozess zu unterstützen. Dies geschieht durch die Verwendung von GUI oder API zur Interaktion mit dem In-App-KI-Tool.

  • UI Automator - Dieser Aktionstyp wird verwendet, um mit den UI-Steuerelementen der Anwendung wie Schaltflächen, Textfeldern und Menüs zu interagieren. UFO verwendet die UIA- oder Win32-APIs, um mit den UI-Steuerelementen der Anwendung zu interagieren.
  • API - Dieser Aktionstyp wird verwendet, um mit der nativen API der Anwendung zu interagieren. Benutzer und App-Entwickler können eigene API-Aktionen erstellen, um mit spezifischen Anwendungen zu interagieren.
  • Web - Dieser Aktionstyp wird verwendet, um mit Webanwendungen zu interagieren. UFO verwendet die crawl4ai-Bibliothek, um Informationen von Webseiten zu extrahieren.
  • Bash - Dieser Aktionstyp wird verwendet, um mit der Befehlszeilenschnittstelle (CLI) einer Anwendung zu interagieren.
  • AI Tool - Dieser Aktionstyp wird verwendet, um mit LLM-basierten KI-Tools zu interagieren.

Action Design Patterns

Aktionen in UFO werden mithilfe des Command-Design-Musters implementiert, das einen Receiver, einen Command und einen Invoker kapselt. Der Receiver ist das Objekt, das die Aktion ausführt, der Command ist das Objekt, das die Aktion kapselt, und der Invoker ist das Objekt, das die Aktion auslöst.

Die Basisklassen für die Implementierung von Aktionen in UFO sind wie folgt:

Rolle Klasse Beschreibung
Empfänger ufo.automator.basic.ReceiverBasic Die Basisklasse für alle Receiver in UFO. Receiver sind Objekte, die Aktionen auf Anwendungen ausführen.
Befehl ufo.automator.basic.CommandBasic Die Basisklasse für alle Commands in UFO. Commands sind Objekte, die Aktionen kapseln, die von Receivern ausgeführt werden sollen.
Invoker ufo.automator.puppeteer.AppPuppeteer Die Basisklasse für den Invoker in UFO. Invoker sind Objekte, die Commands zum Ausführen durch Receiver auslösen.

Der Vorteil der Verwendung des Command-Design-Musters im Agent-Framework besteht darin, dass es die Entkopplung von Sender und Empfänger der Aktion ermöglicht. Diese Entkopplung ermöglicht es dem Agenten, Aktionen auf verschiedenen Objekten auszuführen, ohne die Details des Objekts oder der auszuführenden Aktion zu kennen, was den Agenten flexibler und erweiterbarer für neue Aktionen macht.

Empfänger

Der Receiver ist eine zentrale Komponente in der Automator-Anwendung, die Aktionen auf der Anwendung ausführt. Er stellt Funktionalitäten zur Interaktion mit der Anwendung und zur Ausführung der Aktion bereit. Alle verfügbaren Aktionen werden mit der Klasse ReceiverManager registriert.

Sie finden die Referenz für eine grundlegende Receiver-Klasse unten.

Basen: ABC

Die abstrakte Receiver-Schnittstelle.

command_registry property

Ruft die Befehlsregistrierung ab.

supported_command_names property

Ruft die Liste der Befehlsnamen ab.

register(command_class) classmethod

Dekorator zum Registrieren der Zustandsklasse beim Zustandsmanager.

Parameter
  • command_class (Type[CommandBasic]) –

    Die zu registrierende Zustandsklasse.

Rückgabe
Quellcode in 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)

Zur Befehlsregistrierung hinzufügen.

Parameter
  • command_name (str) –

    Der Befehlsname.

  • command (CommandBasic) –

    Der Befehl.

Quellcode in 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()

Ruft die Zuordnung von Befehl zu Receiver ab.

Quellcode in 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}


Befehl

Der Command ist eine spezifische Aktion, die der Receiver auf der Anwendung ausführen kann. Er kapselt die Funktion und die Parameter, die zur Ausführung der Aktion erforderlich sind. Die Klasse Command ist eine Basisklasse für alle Commands in der Automator-Anwendung.

Sie finden die Referenz für eine grundlegende Command-Klasse unten.

Basen: ABC

Die abstrakte Command-Schnittstelle.

Initialisiert den Befehl.

Parameter
Quellcode in 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

Führt den Befehl aus.

Quellcode in automator/basic.py
75
76
77
78
79
80
@abstractmethod
def execute(self):
    """
    Execute the command.
    """
    pass

redo()

Wiederholt den Befehl.

Quellcode in automator/basic.py
88
89
90
91
92
def redo(self):
    """
    Redo the command.
    """
    self.execute()

undo()

Macht den Befehl rückgängig.

Quellcode in automator/basic.py
82
83
84
85
86
def undo(self):
    """
    Undo the command.
    """
    pass


Hinweis

Jeder Befehl muss mit einem spezifischen Receiver registriert werden, um ausgeführt zu werden, indem der Dekorator register_command verwendet wird. Zum Beispiel: @ReceiverExample.register class CommandExample(CommandBasic): ...

Invoker (AppPuppeteer)

Der AppPuppeteer spielt die Rolle des Invokers in der Automator-Anwendung. Er löst die Ausführung von Befehlen durch die Receiver aus. Der AppPuppeteer stattet den AppAgent mit der Fähigkeit aus, mit den UI-Steuerelementen der Anwendung zu interagieren. Er bietet Funktionalitäten, um Aktionszeichenfolgen in spezifische Aktionen zu übersetzen und auszuführen. Alle verfügbaren Aktionen werden im Puppeteer mit der Klasse ReceiverManager registriert.

Sie finden die Implementierung der Klasse AppPuppeteer in der Datei ufo/automator/puppeteer.py, und ihre Referenz ist unten dargestellt.

Die Klasse für den App-Puppeteer zur Automatisierung der App in der Windows-Umgebung.

Initialisiert den App-Puppeteer.

Parameter
  • process_name (str) –

    Der Prozessname der App.

  • app_root_name (str) –

    Der App-Root-Name, z.B. WINWORD.EXE.

Quellcode in 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

Ruft den vollständigen Pfad des Prozesses ab. Funktioniert nur für COM-Receiver.

Rückgabe
  • str

    Der vollständige Pfad des Prozesses.

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

Fügt den Befehl zur Befehlswarteschlange hinzu.

Parameter
  • command_name (str) –

    Der Befehlsname.

  • params (Dict[str, Any]) –

    Die Argumente.

Quellcode in 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()

Schließt die App. Funktioniert nur für COM-Receiver.

Quellcode in 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)

Erstellt den Befehl.

Parameter
  • command_name (str) –

    Der Befehlsname.

  • params (Dict[str, Any]) –

    Die Argumente für den Befehl.

Quellcode in 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()

Führt alle Befehle in der Befehlswarteschlange aus.

Rückgabe
  • List[Any]

    Die Ausführungsergebnisse.

Quellcode in 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)

Führt den Befehl aus.

Parameter
  • command_name (str) –

    Der Befehlsname.

  • params (Dict[str, Any]) –

    Die Argumente.

Rückgabe
  • str

    Das Ausführungsergebnis.

Quellcode in 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()

Ruft die Länge der Befehlswarteschlange ab.

Rückgabe
  • int

    Die Länge der Befehlswarteschlange.

Quellcode in 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

Generiert eine Funktionsaufrufzeichenfolge.

Parameter
  • command_name (str) –

    Der Funktionsname.

  • params (Dict[str, str]) –

    Die Argumente als Wörterbuch.

Rückgabe
  • str

    Die Funktionsaufrufzeichenfolge.

Quellcode in 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)

Ruft die Befehlstypen ab.

Parameter
  • command_name (str) –

    Der Befehlsname.

Rückgabe
  • str

    Die Befehlstypen.

Quellcode in 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()

Speichert den aktuellen Zustand der App. Funktioniert nur für COM-Receiver.

Quellcode in 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)

Speichert den aktuellen Zustand der App als XML. Funktioniert nur für COM-Receiver.

Parameter
  • file_path (str) –

    Der Dateipfad zum Speichern der XML-Datei.

Quellcode in 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)


Receiver Manager

Der ReceiverManager verwaltet alle Receiver und Befehle in der Automator-Anwendung. Er bietet Funktionalitäten zur Registrierung und zum Abrufen von Receivern und Befehlen. Er ist eine ergänzende Komponente zum AppPuppeteer.

Die Klasse für den Receiver-Manager.

Initialisiert den Receiver-Manager.

Quellcode in 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

Ruft den COM-Receiver ab.

Rückgabe
  • WinCOMReceiverBasic

    Der COM-Receiver.

receiver_factory_registry property

Ruft die Receiver-Factory-Registrierung ab.

Rückgabe
  • Dict[str, Dict[str, Union[str, ReceiverFactory]]]

    Die Receiver-Factory-Registrierung.

receiver_list property

Ruft die Receiver-Liste ab.

Rückgabe
  • List[ReceiverBasic]

    Die Receiver-Liste.

create_api_receiver(app_root_name, process_name)

Ruft den API-Receiver ab.

Parameter
  • app_root_name (str) –

    Der App-Root-Name.

  • process_name (str) –

    Der Prozessname.

Quellcode in 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)

Erstellt den UI-Controller.

Parameter
  • control (UIAWrapper) –

    Das Steuerelementelement.

  • application (UIAWrapper) –

    Das Anwendungsfenster.

Rückgabe
  • ControlReceiver

    Der UI-Controller-Receiver.

Quellcode in 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)

Ruft den Receiver anhand des Befehlsnamens ab.

Parameter
  • command_name (str) –

    Der Befehlsname.

Rückgabe
  • ReceiverBasic

    Der zugeordnete Receiver.

Quellcode in 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

Dekorator zum Registrieren der Receiver-Factory-Klasse beim Receiver-Manager.

Parameter
  • receiver_factory_class (Type[ReceiverFactory]) –

    Die zu registrierende Receiver-Factory-Klasse.

Rückgabe
  • ReceiverFactory

    Die Instanz der Receiver-Factory-Klasse.

Quellcode in 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()


Weitere Details finden Sie in der spezifischen Dokumentation für jede Komponente und Klasse im Automator-Modul.