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
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
| 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.
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.
Quellcode in module/sessions/plan_reader.py
| 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.
Quellcode in module/sessions/plan_reader.py
| 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.
Quellcode in module/sessions/plan_reader.py
| 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.
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) –
|
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.
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.
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
| 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)
|