Batch-Modus

Der Batch-Modus ist eine Funktion von UFO, die es dem Agenten ermöglicht, Aufgaben automatisiert im Stapelverarbeitungsmodus auszuführen.

Schnellstart

Schritt 1: Erstellen einer Planungsdatei

Bevor Sie den Batch-Modus starten, müssen Sie eine Planungsdatei erstellen, die die Liste der Schritte enthält, denen der Agent folgen soll. Die Planungsdatei ist eine JSON-Datei, die die folgenden Felder enthält:

Feld Beschreibung Typ
task Die Aufgabenbeschreibung. String
object Die Anwendung oder Datei, mit der interagiert werden soll. String
close Bestimmt, ob die entsprechende Anwendung oder Datei nach Abschluss der Aufgabe geschlossen werden soll. Boolean

Nachfolgend finden Sie ein Beispiel für eine Planungsdatei

{
    "task": "Type in a text of 'Test For Fun' with heading 1 level",
    "object": "draft.docx",
    "close": False
}

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 Batch-Modus gestartet wird. Die Struktur Ihrer Dateien sollte wie folgt aussehen, wobei tasks das Verzeichnis für Ihre Aufgaben und files der Speicherort Ihrer Objektdateien ist

  • Parent
  • tasks
  • files

Schritt 2: Starten des Batch-Modus

Um den Batch-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 batch_normal --plan {plan_file}

Tipp

Ersetzen Sie {task_name} durch den Namen der Aufgabe und {plan_file} durch den Pfad zur Path_to_Parent/Plan_file.

Evaluierung

Möglicherweise möchten Sie bewerten, ob die task erfolgreich abgeschlossen wurde oder nicht, indem Sie dem Plan folgen. 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 Evaluierungsprotokoll in der Datei logs/{task_name}/evaluation.log einsehen.

Referenzen

Der Batch-Modus verwendet einen PlanReader, um die Planungsdatei zu parsen und eine FromFileSession zu erstellen, um dem Plan zu folgen.

PlanReader

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

Der Leser für eine Planungsdatei.

Initialisiert einen Planungsleser.

Parameter
  • plan_file (str) –

    Der Pfad zur Planungsdatei.

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 werden soll.

Rückgabe
  • bool

    True, wenn der Plan geschlossen werden soll, 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 anfängliche Anfrage im Plan ab.

Rückgabe
  • str

    Die anfängliche 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 Stammverzeichnispfad des Plans ab.

Rückgabe
  • str

    Der Stammverzeichnispfad 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 Namen der Aufgabe ab.

Rückgabe
  • str

    Der Name der Aufgabe.

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 FromFileSession befindet sich ebenfalls in der Datei ufo/module/sessions/session.py.

Bases: BaseSession

Eine Sitzung für UFO aus Dateien.

Initialisiert eine Sitzung.

Parameter
  • task (str) –

    Der Name der aktuellen Aufgabe.

  • plan_file (str) –

    Der Pfad zur Planungsdatei, der gefolgt werden soll.

  • should_evaluate (bool) –

    Ob die Sitzung bewertet werden soll.

  • id (int) –

    Die ID der Sitzung.

Quellcode in module/sessions/session.py
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
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_file = plan_file
    self.plan_reader = PlanReader(plan_file)
    self.support_apps = self.plan_reader.get_support_apps()
    self.close = self.plan_reader.get_close()
    self.task_name = task.split("/")[1]
    self.object_name = ""

create_new_round()

Erstellt eine neue Runde.

Quellcode in module/sessions/session.py
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
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

    self._host_agent.set_state(ContinueHostAgentState())

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

    self.add_round(round.id, round)

    return round

get_app_com(object_name)

Ruft den COM-Objektnamen basierend auf dem Objektnamen ab.

Parameter
  • object_name (str) –

    Der Name des Objekts.

Rückgabe
  • str

    Der COM-Objektname.

Quellcode in module/sessions/session.py
467
468
469
470
471
472
473
474
475
476
477
478
479
def get_app_com(self, object_name: str) -> str:
    """
    Get the COM object name based on the object name.
    :param object_name: The name of the object.
    :return: The COM object name.
    """
    application_mapping = {
        ".docx": "Word.Application",
        ".xlsx": "Excel.Application",
        ".pptx": "PowerPoint.Application",
    }
    self.app_name = application_mapping.get(object_name)
    return self.app_name

get_app_name(object_name)

Ruft den Anwendungsnamen basierend auf dem Objektnamen ab.

Parameter
  • object_name (str) –

    Der Name des Objekts.

Rückgabe
  • str

    Der Anwendungsname.

Quellcode in module/sessions/session.py
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
def get_app_name(self, object_name: str) -> str:
    """
    Get the application name based on the object name.
    :param object_name: The name of the object.
    :return: The application name.
    """
    application_mapping = {
        ".docx": "WINWORD.EXE",
        ".xlsx": "EXCEL.EXE",
        ".pptx": "POWERPNT.EXE",
        # "outlook": "olk.exe",
        # "onenote": "ONENOTE.EXE",
    }
    self.app_name = application_mapping.get(object_name)
    return self.app_name

next_request()

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

Rückgabe
  • str

    Die Anfrage für den Host-Agenten.

Quellcode in module/sessions/session.py
438
439
440
441
442
443
444
445
446
447
448
449
def next_request(self) -> str:
    """
    Get the request for the host agent.
    :return: The request for the host agent.
    """

    if self.total_rounds == 0:
        utils.print_with_color(self.plan_reader.get_host_request(), "cyan")
        return self.plan_reader.get_host_request()
    else:
        self._finish = True
        return

record_task_done()

Zeichnet auf, dass die Aufgabe erledigt ist.

Quellcode in module/sessions/session.py
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
def record_task_done(self) -> None:
    """
    Record the task done.
    """
    is_record = configs.get("TASK_STATUS", True)
    if is_record:
        file_path = configs.get(
            "TASK_STATUS_FILE",
            os.path.join(self.plan_file, "../..", "tasks_status.json"),
        )
        task_done = json.load(open(file_path, "r"))
        task_done[self.task_name] = True
        json.dump(
            task_done,
            open(file_path, "w"),
            indent=4,
        )

request_to_evaluate()

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

Quellcode in module/sessions/session.py
543
544
545
546
547
548
def request_to_evaluate(self) -> str:
    """
    Get the request to evaluate.
    return: The request(s) to evaluate.
    """
    return self.plan_reader.get_task()

run()

Führt die Sitzung aus.

Quellcode in module/sessions/session.py
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
def run(self) -> None:
    """
    Run the session.
    """
    self.setup_application_environment()
    try:
        super().run()
        self.record_task_done()
    except Exception as e:
        import traceback

        traceback.print_exc()
        print(f"An error occurred: {e}")
    # Close the APP if the user ask so.
    self.terminate_application_processes()

setup_application_environment()

Richtet die Anwendungsumgebung ein, indem der Anwendungsname und der Befehl basierend auf dem Operationsobjekt ermittelt und dann die Anwendung gestartet wird.

Ausnahmen: Exception: Wenn während der Ausführung des Befehls oder bei der Interaktion mit der Anwendung über COM ein Fehler auftritt.

Quellcode in module/sessions/session.py
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
def setup_application_environment(self):
    """
    Sets up the application environment by determining the application name and
    command based on the operation object, and then launching the application.

    Raises:
        Exception: If an error occurs during the execution of the command or
                   while interacting with the application via COM.
    """
    self.object_name = self.plan_reader.get_operation_object()
    if self.object_name:
        suffix = os.path.splitext(self.object_name)[1]
        self.app_name = self.get_app_name(suffix)
        print("app_name:", self.app_name)
        if self.app_name not in self.support_apps:
            print(f"The app {self.app_name} is not supported.")
            return  # The app is not supported, so we don't need to setup the environment.
        file = self.plan_reader.get_file_path()
        code_snippet = f"import os\nos.system('start {self.app_name} \"{file}\"')"
        code_snippet = code_snippet.replace("\\", "\\\\")  # escape backslashes
        try:
            exec(code_snippet, globals())
            app_com = self.get_app_com(suffix)
            time.sleep(2)  # wait for the app to boot
            word_app = win32com.client.Dispatch(app_com)
            word_app.WindowState = 1  # wdWindowStateMaximize
        except Exception as e:
            print(f"An error occurred: {e}")

terminate_application_processes()

Beendet spezifische Anwendungsprozesse basierend auf den bereitgestellten Bedingungen.

Quellcode in module/sessions/session.py
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
def terminate_application_processes(self):
    """
    Terminates specific application processes based on the provided conditions.
    """
    if self.close:
        if self.object_name:
            for process in psutil.process_iter(["name"]):
                if process.info["name"] == self.app_name:
                    os.system(f"taskkill /f /im {self.app_name}")
                    time.sleep(1)
        else:
            app_names = ["WINWORD.EXE", "EXCEL.EXE", "POWERPNT.EXE"]
            for process in psutil.process_iter(["name"]):
                if process.info["name"] in app_names:
                    os.system(f"taskkill /f /im {process.info['name']}")
                    time.sleep(1)