HostAgent 🤖

Der HostAgent übernimmt drei Hauptaufgaben

  • Aufgabendekomposition. Ausgehend von der natürlichsprachlichen Eingabe des Benutzers identifiziert der HostAgent das zugrunde liegende Aufgabenziel und zerlegt es in einen abhängigkeitsgeordneten Unteraufgabengraphen.

  • Anwendungslebenszyklus-Management. Für jede Unteraufgabe prüft der HostAgent die Metadaten der Systemprozesse (über UIA-APIs), um festzustellen, ob die Zielanwendung läuft. Wenn nicht, startet er das Programm und registriert es beim Runtime.

  • AppAgent Instanziierung. Der HostAgent startet den entsprechenden AppAgent für jede aktive Anwendung und stellt ihm den Aufgabenkontext, Speicherreferenzen und relevante Toolchains (z. B. APIs, Dokumentation) zur Verfügung.

  • Aufgabenplanung und -steuerung. Der globale Ausführungsplan wird in eine endliche Zustandsmaschine (FSM) serialisiert, die es dem HostAgent ermöglicht, die Ausführungsreihenfolge zu erzwingen, Fehler zu erkennen und Abhängigkeiten zwischen Agenten aufzulösen.

  • Gemeinsame Zustandskommunikation. Der HostAgent liest von und schreibt in ein globales Whiteboard, das die Kommunikation zwischen Agenten und die Systembeobachtbarkeit für Debugging und Wiedergabe ermöglicht.

Unten sehen Sie ein Diagramm, das die Architektur des HostAgent und seine Interaktionen mit anderen Komponenten veranschaulicht

Blackboard Image

Der HostAgent aktiviert seinen Processor, um die Anfrage des Benutzers zu verarbeiten und sie in Unteraufgaben zu zerlegen. Jede Unteraufgabe wird dann einem AppAgent zur Ausführung zugewiesen. Der HostAgent überwacht den Fortschritt der AppAgents und stellt die erfolgreiche Erledigung der Anfrage des Benutzers sicher.

HostAgent Eingabe

Der HostAgent empfängt die folgenden Eingaben

Input Beschreibung Typ
Benutzeranfrage Die Anfrage des Benutzers in natürlicher Sprache. String
Anwendungsinformationen Informationen über die vorhandenen aktiven Anwendungen. Liste von Zeichenketten
Desktop-Screenshots Screenshots des Desktops, um dem HostAgent Kontext zu geben. Bild
Vorherige Unteraufgaben Die vorherigen Unteraufgaben und deren Abschlussstatus. Liste von Zeichenketten
Vorheriger Plan Der vorherige Plan für die folgenden Unteraufgaben. Liste von Zeichenketten
Blackboard Der gemeinsame Speicherbereich zum Speichern und Teilen von Informationen zwischen den Agenten. Dictionary

Durch die Verarbeitung dieser Eingaben bestimmt der HostAgent die geeignete Anwendung zur Erfüllung der Benutzeranfrage und orchestriert die AppAgents zur Ausführung der notwendigen Aktionen.

HostAgent Ausgabe

Mit den bereitgestellten Eingaben generiert der HostAgent die folgenden Ausgaben

Ausgabe Beschreibung Typ
Beobachtung Die Beobachtung aktueller Desktop-Screenshots. String
Gedanke Der logische Denkprozess des HostAgent. String
Aktuelle Unteraufgabe Die aktuelle Unteraufgabe, die vom AppAgent ausgeführt werden soll. String
Nachricht Die Nachricht, die an den AppAgent zur Erledigung der Unteraufgabe gesendet werden soll. String
ControlLabel Der Index der ausgewählten Anwendung zur Ausführung der Unteraufgabe. String
ControlText Der Name der ausgewählten Anwendung zur Ausführung der Unteraufgabe. String
Plan Der Plan für die folgenden Unteraufgaben nach der aktuellen Unteraufgabe. Liste von Zeichenketten
Status Der Status des Agenten, zugeordnet dem AgentState. String
Kommentar Zusätzliche Kommentare oder Informationen für den Benutzer. String
Fragen Die Fragen, die dem Benutzer zur zusätzlichen Information gestellt werden. Liste von Zeichenketten
Bash Der Bash-Befehl, der vom HostAgent ausgeführt werden soll. Er kann zum Öffnen von Anwendungen oder zur Ausführung von Systembefehlen verwendet werden. String

Unten ist ein Beispiel für die Ausgabe des HostAgent

{
    "Observation": "Desktop screenshot",
    "Thought": "Logical reasoning process",
    "Current Sub-Task": "Sub-task description",
    "Message": "Message to AppAgent",
    "ControlLabel": "Application index",
    "ControlText": "Application name",
    "Plan": ["Sub-task 1", "Sub-task 2"],
    "Status": "AgentState",
    "Comment": "Additional comments",
    "Questions": ["Question 1", "Question 2"],
    "Bash": "Bash command"
}

Info

Die Ausgabe des HostAgent wird von LLMs als JSON-Objekt formatiert und kann von der Methode json.loads in Python geparst werden.

HostAgent Zustand

Der HostAgent durchläuft verschiedene Zustände, wie im Modul ufo/agents/states/host_agent_states.py definiert. Die Zustände umfassen

Status Beschreibung
FORTSETZEN Standardzustand für Aktionsplanung und -ausführung.
AUSSTEHEND Aufgerufen für sicherheitskritische Aktionen (z. B. zerstörerische Operationen); erfordert Benutzerbestätigung.
BEENDEN Aufgabe abgeschlossen; Ausführung endet.
FEHLGESCHLAGEN Irrecoverable Fehler erkannt (z. B. Anwendungsabsturz, Berechtigungsfehler).

Das Zustandsdiagramm für den HostAgent ist unten dargestellt

Der HostAgent wechselt zwischen diesen Zuständen basierend auf der Anfrage des Benutzers, den Anwendungsinformationen und dem Fortschritt der AppAgents bei der Ausführung der Unteraufgaben.

Aufgabendekomposition

Nach Erhalt der Benutzeranfrage zerlegt der HostAgent diese in Unteraufgaben und weist jede Unteraufgabe einem AppAgent zur Ausführung zu. Der HostAgent ermittelt die geeignete Anwendung zur Erfüllung der Benutzeranfrage basierend auf den Anwendungsinformationen und der Benutzeranfrage. Er orchestriert dann die AppAgents, um die notwendigen Aktionen zur Erledigung der Unteraufgaben auszuführen. Wir zeigen den Prozess der Aufgabendekomposition in der folgenden Abbildung

Task Decomposition Image

Erstellung und Registrierung von AppAgents

Wenn der HostAgent die Notwendigkeit eines neuen AppAgent zur Erfüllung einer Unteraufgabe feststellt, erstellt er eine Instanz des AppAgent und registriert ihn beim HostAgent, indem er die Methode create_subagent aufruft

def create_subagent(
        self,
        agent_type: str,
        agent_name: str,
        process_name: str,
        app_root_name: str,
        is_visual: bool,
        main_prompt: str,
        example_prompt: str,
        api_prompt: str,
        *args,
        **kwargs,
    ) -> BasicAgent:
        """
        Create an SubAgent hosted by the HostAgent.
        :param agent_type: The type of the agent to create.
        :param agent_name: The name of the SubAgent.
        :param process_name: The process name of the app.
        :param app_root_name: The root name of the app.
        :param is_visual: The flag indicating whether the agent is visual or not.
        :param main_prompt: The main prompt file path.
        :param example_prompt: The example prompt file path.
        :param api_prompt: The API prompt file path.
        :return: The created SubAgent.
        """
        app_agent = self.agent_factory.create_agent(
            agent_type,
            agent_name,
            process_name,
            app_root_name,
            is_visual,
            main_prompt,
            example_prompt,
            api_prompt,
            *args,
            **kwargs,
        )
        self.appagent_dict[agent_name] = app_agent
        app_agent.host = self
        self._active_appagent = app_agent

        return app_agent

Der HostAgent weist dann die Unteraufgabe dem AppAgent zur Ausführung zu und überwacht dessen Fortschritt.

Referenz

Basiert auf: BasicAgent

Die HostAgent-Klasse ist der Manager der AppAgents.

Initialisiere den HostAgent. :name: Der Name des Agenten.

Parameter
  • is_visual (bool) –

    Das Flag, das angibt, ob der Agent visuell ist oder nicht.

  • main_prompt (str) –

    Der Pfad zur Haupt-Prompt-Datei.

  • example_prompt (str) –

    Der Pfad zur Beispiel-Prompt-Datei.

  • api_prompt (str) –

    Der Pfad zur API-Prompt-Datei.

Quellcode in agents/agent/host_agent.py
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def __init__(
    self,
    name: str,
    is_visual: bool,
    main_prompt: str,
    example_prompt: str,
    api_prompt: str,
) -> None:
    """
    Initialize the HostAgent.
    :name: The name of the agent.
    :param is_visual: The flag indicating whether the agent is visual or not.
    :param main_prompt: The main prompt file path.
    :param example_prompt: The example prompt file path.
    :param api_prompt: The API prompt file path.
    """
    super().__init__(name=name)
    self.prompter = self.get_prompter(
        is_visual, main_prompt, example_prompt, api_prompt
    )
    self.offline_doc_retriever = None
    self.online_doc_retriever = None
    self.experience_retriever = None
    self.human_demonstration_retriever = None
    self.agent_factory = AgentFactory()
    self.appagent_dict = {}
    self._active_appagent = None
    self._blackboard = Blackboard()
    self.set_state(self.default_state)
    self.Puppeteer = self.create_puppeteer_interface()

blackboard property

Hole das Whiteboard.

default_state property

Hole den Standardzustand.

status_manager Eigenschaft

Ruft den Statusmanager ab.

sub_agent_amount property

Hole die Anzahl der Sub-Agenten.

Rückgabe
  • int

    Die Anzahl der Sub-Agenten.

create_app_agent(application_window_name, application_root_name, request, mode)

Erstelle den App-Agenten für den Host-Agenten.

Parameter
  • application_window_name (str) –

    Der Name des Anwendungsfensters.

  • application_root_name (str) –

    Der Name der Anwendungs-Root.

  • request (str) –

    Die Anfrage des Benutzers.

  • mode (str) –

    Der Modus der Sitzung.

Rückgabe
  • AppAgent

    Der App-Agent.

Quellcode in agents/agent/host_agent.py
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
def create_app_agent(
    self,
    application_window_name: str,
    application_root_name: str,
    request: str,
    mode: str,
) -> AppAgent:
    """
    Create the app agent for the host agent.
    :param application_window_name: The name of the application window.
    :param application_root_name: The name of the application root.
    :param request: The user request.
    :param mode: The mode of the session.
    :return: The app agent.
    """

    if configs.get("ACTION_SEQUENCE", False):
        example_prompt = configs["APPAGENT_EXAMPLE_PROMPT_AS"]
    else:
        example_prompt = configs["APPAGENT_EXAMPLE_PROMPT"]

    if mode in ["normal", "batch_normal", "follower"]:

        agent_name = (
            "AppAgent/{root}/{process}".format(
                root=application_root_name, process=application_window_name
            )
            if mode == "normal"
            else "BatchAgent/{root}/{process}".format(
                root=application_root_name, process=application_window_name
            )
        )

        app_agent: AppAgent = self.create_subagent(
            agent_type="app",
            agent_name=agent_name,
            process_name=application_window_name,
            app_root_name=application_root_name,
            is_visual=configs["APP_AGENT"]["VISUAL_MODE"],
            main_prompt=configs["APPAGENT_PROMPT"],
            example_prompt=example_prompt,
            api_prompt=configs["API_PROMPT"],
            mode=mode,
        )

    elif mode in ["normal_operator", "batch_normal_operator"]:

        agent_name = (
            "OpenAIOperator/{root}/{process}".format(
                root=application_root_name, process=application_window_name
            )
            if mode == "normal_operator"
            else "BatchOpenAIOperator/{root}/{process}".format(
                root=application_root_name, process=application_window_name
            )
        )

        app_agent: OpenAIOperatorAgent = self.create_subagent(
            "operator",
            agent_name=agent_name,
            process_name=application_window_name,
            app_root_name=application_root_name,
        )

    else:
        raise ValueError(f"The {mode} mode is not supported.")

    # Create the COM receiver for the app agent.
    if configs.get("USE_APIS", False):
        app_agent.Puppeteer.receiver_manager.create_api_receiver(
            application_root_name, application_window_name
        )

    # Provision the context for the app agent, including the all retrievers.
    app_agent.context_provision(request)

    return app_agent

create_puppeteer_interface()

Erstelle die Puppeteer-Schnittstelle zur Automatisierung der App.

Rückgabe
  • AppPuppeteer

    Die Puppeteer-Schnittstelle.

Quellcode in agents/agent/host_agent.py
209
210
211
212
213
214
def create_puppeteer_interface(self) -> puppeteer.AppPuppeteer:
    """
    Create the Puppeteer interface to automate the app.
    :return: The Puppeteer interface.
    """
    return puppeteer.AppPuppeteer("", "")

create_subagent(agent_type, agent_name, process_name, app_root_name, *args, **kwargs)

Erstelle einen SubAgent, der vom HostAgent gehostet wird.

Parameter
  • agent_type (str) –

    Der Typ des zu erstellenden Agenten.

  • agent_name (str) –

    Der Name des SubAgenten.

  • process_name (str) –

    Der Prozessname der App.

  • app_root_name (str) –

    Der Root-Name der App.

Rückgabe
  • BasicAgent

    Der erstellte SubAgent.

Quellcode in agents/agent/host_agent.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
def create_subagent(
    self,
    agent_type: str,
    agent_name: str,
    process_name: str,
    app_root_name: str,
    *args,
    **kwargs,
) -> BasicAgent:
    """
    Create an SubAgent hosted by the HostAgent.
    :param agent_type: The type of the agent to create.
    :param agent_name: The name of the SubAgent.
    :param process_name: The process name of the app.
    :param app_root_name: The root name of the app.
    :return: The created SubAgent.
    """
    app_agent = self.agent_factory.create_agent(
        agent_type,
        agent_name,
        process_name,
        app_root_name,
        # is_visual,
        # main_prompt,
        # example_prompt,
        # api_prompt,
        *args,
        **kwargs,
    )
    self.appagent_dict[agent_name] = app_agent
    app_agent.host = self
    self._active_appagent = app_agent

    return app_agent

get_active_appagent()

Hole den aktiven App-Agenten.

Rückgabe
  • AppAgent

    Der aktive App-Agent.

Quellcode in agents/agent/host_agent.py
146
147
148
149
150
151
def get_active_appagent(self) -> AppAgent:
    """
    Get the active app agent.
    :return: The active app agent.
    """
    return self._active_appagent

get_prompter(is_visual, main_prompt, example_prompt, api_prompt)

Ruft den Prompt für den Agenten ab.

Parameter
  • is_visual (bool) –

    Das Flag, das angibt, ob der Agent visuell ist oder nicht.

  • main_prompt (str) –

    Der Pfad zur Haupt-Prompt-Datei.

  • example_prompt (str) –

    Der Pfad zur Beispiel-Prompt-Datei.

  • api_prompt (str) –

    Der Pfad zur API-Prompt-Datei.

Rückgabe
  • HostAgentPrompter

    Die Prompter-Instanz.

Quellcode in agents/agent/host_agent.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
def get_prompter(
    self,
    is_visual: bool,
    main_prompt: str,
    example_prompt: str,
    api_prompt: str,
) -> HostAgentPrompter:
    """
    Get the prompt for the agent.
    :param is_visual: The flag indicating whether the agent is visual or not.
    :param main_prompt: The main prompt file path.
    :param example_prompt: The example prompt file path.
    :param api_prompt: The API prompt file path.
    :return: The prompter instance.
    """
    return HostAgentPrompter(is_visual, main_prompt, example_prompt, api_prompt)

message_constructor(image_list, os_info, plan, prev_subtask, request, blackboard_prompt)

Konstruiert die Nachricht.

Parameter
  • image_list (List[str]) –

    Die Liste der Screenshot-Bilder.

  • os_info (str) –

    Die OS-Informationen.

  • prev_subtask (List[Dict[str, str]]) –

    Die vorherige Unteraufgabe.

  • plan (List[str]) –

    Der Plan.

  • request (str) –

    Die Anfrage.

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

    Die Nachricht.

Quellcode in agents/agent/host_agent.py
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
def message_constructor(
    self,
    image_list: List[str],
    os_info: str,
    plan: List[str],
    prev_subtask: List[Dict[str, str]],
    request: str,
    blackboard_prompt: List[Dict[str, str]],
) -> List[Dict[str, Union[str, List[Dict[str, str]]]]]:
    """
    Construct the message.
    :param image_list: The list of screenshot images.
    :param os_info: The OS information.
    :param prev_subtask: The previous subtask.
    :param plan: The plan.
    :param request: The request.
    :return: The message.
    """
    hostagent_prompt_system_message = self.prompter.system_prompt_construction()
    hostagent_prompt_user_message = self.prompter.user_content_construction(
        image_list=image_list,
        control_item=os_info,
        prev_subtask=prev_subtask,
        prev_plan=plan,
        user_request=request,
    )

    if blackboard_prompt:
        hostagent_prompt_user_message = (
            blackboard_prompt + hostagent_prompt_user_message
        )

    hostagent_prompt_message = self.prompter.prompt_construction(
        hostagent_prompt_system_message, hostagent_prompt_user_message
    )

    return hostagent_prompt_message

print_response(response_dict)

Gibt die Antwort aus.

Parameter
  • response_dict (Dict) –

    Das auszugebende Antwortwörterbuch.

Quellcode in agents/agent/host_agent.py
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
def print_response(self, response_dict: Dict) -> None:
    """
    Print the response.
    :param response_dict: The response dictionary to print.
    """

    application = response_dict.get("ControlText")
    if not application:
        application = "[The required application needs to be opened.]"
    observation = response_dict.get("Observation")
    thought = response_dict.get("Thought")
    bash_command = response_dict.get("Bash", None)
    subtask = response_dict.get("CurrentSubtask")

    # Convert the message from a list to a string.
    message = list(response_dict.get("Message", ""))
    message = "\n".join(message)

    # Concatenate the subtask with the plan and convert the plan from a list to a string.
    plan = list(response_dict.get("Plan"))
    plan = [subtask] + plan
    plan = "\n".join([f"({i+1}) " + str(item) for i, item in enumerate(plan)])

    status = response_dict.get("Status")
    comment = response_dict.get("Comment")

    utils.print_with_color(
        "Observations👀: {observation}".format(observation=observation), "cyan"
    )
    utils.print_with_color("Thoughts💡: {thought}".format(thought=thought), "green")
    if bash_command:
        utils.print_with_color(
            "Running Bash Command🔧: {bash}".format(bash=bash_command), "yellow"
        )
    utils.print_with_color(
        "Plans📚: {plan}".format(plan=plan),
        "cyan",
    )
    utils.print_with_color(
        "Next Selected application📲: {application}".format(
            application=application
        ),
        "yellow",
    )
    utils.print_with_color(
        "Messages to AppAgent📩: {message}".format(message=message), "cyan"
    )
    utils.print_with_color("Status📊: {status}".format(status=status), "blue")

    utils.print_with_color("Comment💬: {comment}".format(comment=comment), "green")

process(context)

Verarbeitet den Agenten.

Parameter
  • context (Context) –

    Der Kontext.

Quellcode in agents/agent/host_agent.py
198
199
200
201
202
203
204
205
206
207
def process(self, context: Context) -> None:
    """
    Process the agent.
    :param context: The context.
    """
    self.processor = HostAgentProcessor(agent=self, context=context)
    self.processor.process()

    # Sync the status with the processor.
    self.status = self.processor.status

process_comfirmation()

TODO: Die Bestätigung verarbeiten.

Quellcode in agents/agent/host_agent.py
294
295
296
297
298
def process_comfirmation(self) -> None:
    """
    TODO: Process the confirmation.
    """
    pass