Follower-Modus

Der Follower-Modus ist eine Funktion von UFO, bei der der Agent eine Liste vordefinierter Schritte in natürlicher Sprache befolgt, um Aktionen auf Anwendungen auszuführen. Im Gegensatz zum normalen Modus erstellt dieser Modus einen FollowerAgent, der die vom Benutzer bereitgestellte Zielliste befolgt, um mit der Anwendung zu interagieren, anstatt den Plan selbst zu generieren. Dieser Modus ist nützlich für Debugging und Softwaretests oder Verifizierungen.

Schnellstart

Schritt 1: Erstellen Sie eine Plan-Datei

Bevor Sie den Follower-Modus starten, müssen Sie eine Plan-Datei erstellen, die die Liste der Schritte enthält, die der Agent befolgen soll. Die Plan-Datei ist eine JSON-Datei, die die folgenden Felder enthält

Feld Beschreibung Typ
task Die Aufgabenbeschreibung. String
steps Die Liste der Schritte, die der Agent befolgen soll. Liste von Zeichenketten
object Die Anwendung oder Datei, mit der interagiert werden soll. String

Unten ist ein Beispiel für eine Plan-Datei

{
    "task": "Type in a text of 'Test For Fun' with heading 1 level",
    "steps": 
    [
        "1.type in 'Test For Fun'", 
        "2.Select the 'Test For Fun' text",
        "3.Click 'Home' tab to show the 'Styles' ribbon tab",
        "4.Click 'Styles' ribbon tab to show the style 'Heading 1'",
        "5.Click 'Heading 1' style to apply the style to the selected text"
    ],
    "object": "draft.docx"
}

Hinweis

Das Feld object ist die Anwendung oder Datei, mit der der Agent interagieren wird. Das Objekt muss aktiv sein (kann minimiert werden), wenn der Follower-Modus gestartet wird.

Schritt 2: Starten Sie den Follower Mode

Um den Follower-Modus zu starten, führen Sie den folgenden Befehl aus

# assume you are in the cloned UFO folder
python ufo.py --task_name {task_name} --mode follower --plan {plan_file}

Tipp

Ersetzen Sie {task_name} durch den Namen der Aufgabe und {plan_file} durch den Pfad zur Plan-Datei.

Schritt 3: Im Batch-Modus ausführen (Optional)

Sie können den Follower-Modus auch im Batch-Modus ausführen, indem Sie einen Ordner mit mehreren Plan-Dateien angeben. Der Agent wird die Pläne im Ordner nacheinander ausführen. Um im Batch-Modus auszuführen, führen Sie den folgenden Befehl aus

# assume you are in the cloned UFO folder
python ufo.py --task_name {task_name} --mode follower --plan {plan_folder}

UFO erkennt automatisch die Plan-Dateien im Ordner und führt sie nacheinander aus.

Tipp

Ersetzen Sie {task_name} durch den Namen der Aufgabe und {plan_folder} durch den Pfad zum Ordner mit den Plan-Dateien.

Evaluierung

Möglicherweise möchten Sie task anhand des Plans erfolgreich abgeschlossen oder nicht bewerten. UFO ruft den EvaluationAgent auf, um die Aufgabe zu bewerten, wenn EVA_SESSION in der Datei config_dev.yaml auf True gesetzt ist.

Sie können das Evaluationsprotokoll in der Datei logs/{task_name}/evaluation.log überprüfen.

Referenzen

Der Follower-Modus verwendet einen PlanReader, um die Plan-Datei zu parsen und eine FollowerSession zu erstellen, um dem Plan zu folgen.

PlanReader

Der PlanReader befindet sich in der Datei ufo/module/sessions/plan_reader.py.

Der Reader für eine Plan-Datei.

Initialisiert einen Plan-Reader.

Parameter
  • plan_file (str) –

    Der Pfad zur Plan-Datei.

Quellcode in module/sessions/plan_reader.py
18
19
20
21
22
23
24
25
26
27
28
def __init__(self, plan_file: str):
    """
    Initialize a plan reader.
    :param plan_file: The path of the plan file.
    """

    self.plan_file = plan_file
    with open(plan_file, "r") as f:
        self.plan = json.load(f)
    self.remaining_steps = self.get_steps()
    self.support_apps = ["WINWORD.EXE", "EXCEL.EXE", "POWERPNT.EXE"]

get_close()

Prüft, ob der Plan geschlossen ist.

Rückgabe
  • bool

    True, wenn der Plan geschlossen werden muss, False andernfalls.

Quellcode in module/sessions/plan_reader.py
30
31
32
33
34
35
36
def get_close(self) -> bool:
    """
    Check if the plan is closed.
    :return: True if the plan need closed, False otherwise.
    """

    return self.plan.get("close", False)

get_host_agent_request()

Ruft die Anfrage für den Host-Agenten ab.

Rückgabe
  • str

    Die Anfrage für den Host-Agenten.

Quellcode in module/sessions/plan_reader.py
75
76
77
78
79
80
81
82
83
84
85
86
87
88
def get_host_agent_request(self) -> str:
    """
    Get the request for the host agent.
    :return: The request for the host agent.
    """

    object_name = self.get_operation_object()

    request = (
        f"Open and select the application of {object_name}, and output the FINISH status immediately. "
        "You must output the selected application with their control text and label even if it is already open."
    )

    return request

get_host_request()

Ruft die Anfrage für den Host-Agenten ab.

Rückgabe
  • str

    Die Anfrage für den Host-Agenten.

Quellcode in module/sessions/plan_reader.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def get_host_request(self) -> str:
    """
    Get the request for the host agent.
    :return: The request for the host agent.
    """

    task = self.get_task()
    object_name = self.get_operation_object()
    if object_name in self.support_apps:
        request = task
    else:
        request = (
            f"Your task is '{task}'. And open the application of {object_name}. "
            "You must output the selected application with their control text and label even if it is already open."
        )
    return request

get_initial_request()

Ruft die initiale Anfrage im Plan ab.

Rückgabe
  • str

    Die initiale Anfrage.

Quellcode in module/sessions/plan_reader.py
62
63
64
65
66
67
68
69
70
71
72
73
def get_initial_request(self) -> str:
    """
    Get the initial request in the plan.
    :return: The initial request.
    """

    task = self.get_task()
    object_name = self.get_operation_object()

    request = f"{task} in {object_name}"

    return request

get_operation_object()

Ruft das Operationsobjekt im Schritt ab.

Rückgabe
  • str

    Das Operationsobjekt.

Quellcode in module/sessions/plan_reader.py
54
55
56
57
58
59
60
def get_operation_object(self) -> str:
    """
    Get the operation object in the step.
    :return: The operation object.
    """

    return self.plan.get("object", None).lower()

get_root_path()

Ruft den Stammordner des Plans ab.

Rückgabe
  • str

    Der Stammordner des Plans.

Quellcode in module/sessions/plan_reader.py
148
149
150
151
152
153
154
def get_root_path(self) -> str:
    """
    Get the root path of the plan.
    :return: The root path of the plan.
    """

    return os.path.dirname(os.path.abspath(self.plan_file))

get_steps()

Ruft die Schritte im Plan ab.

Rückgabe
  • List[str]

    Die Schritte im Plan.

Quellcode in module/sessions/plan_reader.py
46
47
48
49
50
51
52
def get_steps(self) -> List[str]:
    """
    Get the steps in the plan.
    :return: The steps in the plan.
    """

    return self.plan.get("steps", [])

get_support_apps()

Ruft die unterstützten Apps im Plan ab.

Rückgabe
  • List[str]

    Die unterstützten Apps im Plan.

Quellcode in module/sessions/plan_reader.py
103
104
105
106
107
108
109
def get_support_apps(self) -> List[str]:
    """
    Get the support apps in the plan.
    :return: The support apps in the plan.
    """

    return self.support_apps

get_task()

Ruft den Aufgabennamen ab.

Rückgabe
  • str

    Der Aufgabename.

Quellcode in module/sessions/plan_reader.py
38
39
40
41
42
43
44
def get_task(self) -> str:
    """
    Get the task name.
    :return: The task name.
    """

    return self.plan.get("task", "")

next_step()

Ruft den nächsten Schritt im Plan ab.

Rückgabe
  • Optional[str]

    Der nächste Schritt.

Quellcode in module/sessions/plan_reader.py
128
129
130
131
132
133
134
135
136
137
138
def next_step(self) -> Optional[str]:
    """
    Get the next step in the plan.
    :return: The next step.
    """

    if self.remaining_steps:
        step = self.remaining_steps.pop(0)
        return step

    return None

task_finished()

Prüft, ob die Aufgabe abgeschlossen ist.

Rückgabe
  • bool

    True, wenn die Aufgabe abgeschlossen ist, False andernfalls.

Quellcode in module/sessions/plan_reader.py
140
141
142
143
144
145
146
def task_finished(self) -> bool:
    """
    Check if the task is finished.
    :return: True if the task is finished, False otherwise.
    """

    return not self.remaining_steps


FollowerSession

Die FollowerSession befindet sich ebenfalls in der Datei ufo/module/sessions/session.py.

Bases: BaseSession

Eine Sitzung zum Befolgen einer Liste von Plänen für durchgeführte Aktionen. Diese Sitzung wird für den Follower-Agenten verwendet, der eine Plan-Datei zum Befolgen über den PlanReader akzeptiert.

Initialisiert eine Sitzung.

Parameter
  • task (str) –

    Der Name der aktuellen Aufgabe.

  • plan_file (str) –

    Der Pfad zur zu befolgenden Plan-Datei.

  • should_evaluate (bool) –

    Ob die Sitzung bewertet werden soll.

  • id (int) –

    Die ID der Sitzung.

Quellcode in module/sessions/session.py
285
286
287
288
289
290
291
292
293
294
295
296
297
298
def __init__(
    self, task: str, plan_file: str, should_evaluate: bool, id: int
) -> None:
    """
    Initialize a session.
    :param task: The name of current task.
    :param plan_file: The path of the plan file to follow.
    :param should_evaluate: Whether to evaluate the session.
    :param id: The id of the session.
    """

    super().__init__(task, should_evaluate, id)

    self.plan_reader = PlanReader(plan_file)

create_new_round()

Erstellt eine neue Runde.

Quellcode in module/sessions/session.py
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
350
351
352
353
def create_new_round(self) -> None:
    """
    Create a new round.
    """

    # Get a request for the new round.
    request = self.next_request()

    # Create a new round and return None if the session is finished.
    if self.is_finished():
        return None

    if self.total_rounds == 0:
        utils.print_with_color("Complete the following request:", "yellow")
        utils.print_with_color(self.plan_reader.get_initial_request(), "cyan")
        agent = self._host_agent
    else:
        self.context.set(ContextNames.SUBTASK, request)
        agent = self._host_agent.create_app_agent(
            application_window_name=self.context.get(
                ContextNames.APPLICATION_PROCESS_NAME
            ),
            application_root_name=self.context.get(
                ContextNames.APPLICATION_ROOT_NAME
            ),
            request=request,
            mode=self.context.get(ContextNames.MODE),
        )

        # Clear the memory and set the state to continue the app agent.
        agent.clear_memory()
        agent.blackboard.requests.clear()

        agent.set_state(ContinueAppAgentState())

    round = BaseRound(
        request=request,
        agent=agent,
        context=self.context,
        should_evaluate=configs.get("EVA_ROUND", False),
        id=self.total_rounds,
    )

    self.add_round(round.id, round)

    return round

next_request()

Ruft die Anfrage für die neue Runde ab.

Quellcode in module/sessions/session.py
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
def next_request(self) -> str:
    """
    Get the request for the new round.
    """

    # If the task is finished, return an empty string.
    if self.plan_reader.task_finished():
        self._finish = True
        return ""

    # Get the request from the plan reader.
    if self.total_rounds == 0:
        return self.plan_reader.get_host_agent_request()
    else:
        return self.plan_reader.next_step()

request_to_evaluate()

Ruft die Anfrage zur Bewertung ab. Rückgabe: Die Anfrage(n) zur Bewertung.

Quellcode in module/sessions/session.py
371
372
373
374
375
376
377
def request_to_evaluate(self) -> str:
    """
    Get the request to evaluate.
    return: The request(s) to evaluate.
    """

    return self.plan_reader.get_task()