Einwickeln der nativen API Ihrer App

UFO führt Aktionen auf Anwendungen basierend auf UI-Steuerelementen durch, aber die Bereitstellung nativer API für seine Toolkits kann die Effizienz und Genauigkeit der Aktionen verbessern. Dieses Dokument bietet Anleitungen zum Einwickeln der nativen API Ihrer Anwendung in die Toolkits von UFO.

Wie wickeln Sie die native API Ihrer App ein?

Vor der Entwicklung der Wrapper für native APIs empfehlen wir dringend, sich mit dem Design des Automator vertraut zu machen.

Schritt 1: Erstellen Sie einen Empfänger für die native API

Der Receiver ist eine Klasse, die die Aufrufe der nativen API vom AppAgent empfängt und ausführt. Um die native API Ihrer Anwendung einzubinden, müssen Sie eine Receiver-Klasse erstellen, die die Methoden zur Ausführung der Aufrufe der nativen API enthält.

Um eine Receiver-Klasse zu erstellen, befolgen Sie diese Schritte:

1. Erstellen Sie einen Ordner für Ihre Anwendung

  • Navigieren Sie zum Verzeichnis ufo/automator/app_api/.
  • Erstellen Sie einen Ordner mit dem Namen Ihrer Anwendung.

2. Erstellen Sie eine Python-Datei

  • Fügen Sie im gerade erstellten Ordner eine Python-Datei mit dem Namen Ihrer Anwendung hinzu, z. B. {your_application}_client.py.

3. Definieren Sie die Empfängerklasse

  • Definieren Sie in der Python-Datei eine Klasse namens {Your_Receiver}, die von der Klasse ReceiverBasic aus ufo/automator/basic.py erbt.
  • Initialisieren Sie die Klasse Your_Receiver mit dem Objekt, das die Aufrufe der nativen API ausführt. Wenn Ihre API beispielsweise auf einem com-Objekt basiert, initialisieren Sie das com-Objekt in der Methode __init__ der Klasse Your_Receiver.

Beispiel für die Klasse WinCOMReceiverBasic

class WinCOMReceiverBasic(ReceiverBasic):
    """
    The base class for Windows COM client.
    """

    _command_registry: Dict[str, Type[CommandBasic]] = {}

    def __init__(self, app_root_name: str, process_name: str, clsid: str) -> None:
        """
        Initialize the Windows COM client.
        :param app_root_name: The app root name.
        :param process_name: The process name.
        :param clsid: The CLSID of the COM object.
        """

        self.app_root_name = app_root_name
        self.process_name = process_name
        self.clsid = clsid
        self.client = win32com.client.Dispatch(self.clsid)
        self.com_object = self.get_object_from_process_name()

4. Definieren Sie Methoden zur Ausführung von Aufrufen der nativen API

  • Definieren Sie die Methoden in der Klasse Your_Receiver, um die Aufrufe der nativen API auszuführen.

Beispiel für die Klasse ExcelWinCOMReceiver

def table2markdown(self, sheet_name: str) -> str:
    """
    Convert the table in the sheet to a markdown table string.
    :param sheet_name: The sheet name.
    :return: The markdown table string.
    """

    sheet = self.com_object.Sheets(sheet_name)
    data = sheet.UsedRange()
    df = pd.DataFrame(data[1:], columns=data[0])
    df = df.dropna(axis=0, how="all")
    df = df.applymap(self.format_value)

    return df.to_markdown(index=False)

5. Erstellen Sie eine Factory-Klasse

  • Erstellen Sie Ihre Factory-Klasse, die von der Klasse APIReceiverFactory erbt, um mehrere Receiver-Klassen zu verwalten, die denselben API-Typ gemeinsam nutzen.
  • Implementieren Sie die Methoden create_receiver und name in der Klasse ReceiverFactory. Die Methode create_receiver sollte die Receiver-Klasse zurückgeben.
  • Standardmäßig nimmt create_receiver die Parameter app_root_name und process_name entgegen und gibt die Receiver-Klasse zurück.
  • Registrieren Sie die ReceiverFactory-Klasse mit dem Decorator @ReceiverManager.register.

Beispiel für die Klasse COMReceiverFactory

from ufo.automator.puppeteer import ReceiverManager

@ReceiverManager.register
class COMReceiverFactory(APIReceiverFactory):
    """
    The factory class for the COM receiver.
    """

    def create_receiver(self, app_root_name: str, process_name: str) -> WinCOMReceiverBasic:
        """
        Create the wincom receiver.
        :param app_root_name: The app root name.
        :param process_name: The process name.
        :return: The receiver.
        """

        com_receiver = self.__com_client_mapper(app_root_name)
        clsid = self.__app_root_mappping(app_root_name)

        if clsid is None or com_receiver is None:
            # print_with_color(f"Warning: Win32COM API is not supported for {process_name}.", "yellow")
            return None

        return com_receiver(app_root_name, process_name, clsid)

    @classmethod
    def name(cls) -> str:
        """
        Get the name of the receiver factory.
        :return: The name of the receiver factory.
        """
        return "COM"

Hinweis

Die Methode create_receiver sollte None zurückgeben, wenn die Anwendung nicht unterstützt wird.

Hinweis

Sie müssen Ihre ReceiverFactory mit dem Decorator @ReceiverManager.register registrieren, damit der ReceiverManager die ReceiverFactory verwalten kann.

Die Receiver-Klasse ist nun bereit, die Aufrufe der nativen API vom AppAgent zu empfangen.

Schritt 2: Erstellen Sie einen Befehl für die native API

Befehle sind die Aktionen, die der AppAgent auf der Anwendung ausführen kann. Um einen Befehl für die native API zu erstellen, müssen Sie eine Command-Klasse erstellen, die die Methode zur Ausführung der Aufrufe der nativen API enthält.

1. Erstellen Sie eine Befehlsklasse

  • Erstellen Sie eine Command-Klasse in derselben Python-Datei, in der sich die Receiver-Klasse befindet. Die Command-Klasse sollte von der Klasse CommandBasic aus ufo/automator/basic.py erben.

Beispiel

class WinCOMCommand(CommandBasic):
    """
    The abstract command interface.
    """

    def __init__(self, receiver: WinCOMReceiverBasic, params=None) -> None:
        """
        Initialize the command.
        :param receiver: The receiver of the command.
        """
        self.receiver = receiver
        self.params = params if params is not None else {}

    @abstractmethod
    def execute(self):
        pass

    @classmethod
    def name(cls) -> str:
        """
        Get the name of the command.
        :return: The name of the command.
        """
        return cls.__name__

2. Definieren Sie die Execute-Methode

  • Definieren Sie die Methode execute in der Command-Klasse, um den Empfänger aufzurufen und die Aufrufe der nativen API auszuführen.

Beispiel

def execute(self):
    """
    Execute the command to insert a table.
    :return: The inserted table.
    """
    return self.receiver.insert_excel_table(
        sheet_name=self.params.get("sheet_name", 1),
        table=self.params.get("table"),
        start_row=self.params.get("start_row", 1),
        start_col=self.params.get("start_col", 1),
    )

3. Registrieren Sie die Befehlsklasse

  • Registrieren Sie die Command-Klasse in der entsprechenden Receiver-Klasse mit dem Decorator @your_receiver.register.

Beispiel

@ExcelWinCOMReceiver.register
class InsertExcelTable(WinCOMCommand):
    ...

Die Command-Klasse ist nun in der Receiver-Klasse registriert und für den AppAgent verfügbar, um die Aufrufe der nativen API auszuführen.

Schritt 3: Stellen Sie Prompt-Beschreibungen für die native API bereit

Damit der AppAgent die Nutzung der nativen API-Aufrufe versteht, müssen Sie Prompt-Beschreibungen bereitstellen.

1. Erstellen Sie eine api.yaml-Datei

- Create an `api.yaml` file in the `ufo/prompts/apps/{your_app_name}` directory.

2. Definieren Sie Prompt-Beschreibungen

  • Definieren Sie die Prompt-Beschreibungen für die Aufrufe der nativen API in der Datei api.yaml.

Beispiel

table2markdown:
summary: |-
    "table2markdown" is to get the table content in a sheet of the Excel app and convert it to markdown format.
class_name: |-
    GetSheetContent
usage: |-
    [1] API call: table2markdown(sheet_name: str)
    [2] Args:
    - sheet_name: The name of the sheet in the Excel app.
    [3] Example: table2markdown(sheet_name="Sheet1")
    [4] Available control item: Any control item in the Excel app.
    [5] Return: the markdown format string of the table content of the sheet.

Hinweis

table2markdown ist der Name des Aufrufs der nativen API. Er MUSS mit dem in der entsprechenden Command-Klasse definierten name() übereinstimmen!

3. Registrieren Sie die Prompt-Adresse in config_dev.yaml

  • Registrieren Sie die Prompt-Adresse, indem Sie das Feld APP_API_PROMPT_ADDRESS der Datei config_dev.yaml mit dem Programmnamen der Anwendung als Schlüssel und der Adresse der Prompt-Datei als Wert hinzufügen.

Beispiel

APP_API_PROMPT_ADDRESS: {
    "WINWORD.EXE": "ufo/prompts/apps/word/api.yaml",
    "EXCEL.EXE": "ufo/prompts/apps/excel/api.yaml",
    "msedge.exe": "ufo/prompts/apps/web/api.yaml",
    "chrome.exe": "ufo/prompts/apps/web/api.yaml"
    "your_application_program_name": "YOUR_APPLICATION_API_PROMPT"
} 

Hinweis

your_application_program_name muss dem Namen des Anwendungsprogramms entsprechen.

Der AppAgent kann nun die Prompt-Beschreibungen verwenden, um die Nutzung der Aufrufe der nativen API zu verstehen.


Wenn Sie diese Schritte befolgen, haben Sie die native API Ihrer Anwendung erfolgreich in die Toolkits von UFO integriert, sodass der AppAgent die Aufrufe der nativen API auf der Anwendung ausführen kann!