Spekulative Multi-Aktions-Ausführung

UFO² führt ein neues Feature namens Spekulative Multi-Action-Ausführung ein. Dieses Feature ermöglicht es dem Agenten, mehrere vorhergesagte Schritte zu einem einzigen LLM-Aufruf zu bündeln, die dann live validiert werden. Dieser Ansatz kann zu bis zu 51 % weniger Abfragen führen, im Vergleich zur separaten Inferenz jedes Schritts. Der Agent wird zunächst einen Stapel wahrscheinlicher Aktionen vorhersagen und diese dann in einem einzigen Durchgang gegen den Live-UIA-Status validieren. Wir illustrieren die spekulative Multi-Action-Ausführung in der Abbildung unten.

Speculative Multi-Action Execution

Konfiguration

Um die spekulative Multi-Action-Ausführung zu aktivieren, müssen Sie ACTION_SEQUENCE in der Datei config_dev.yaml auf True setzen.

ACTION_SEQUENCE: True

Referenzen

Die Implementierung der spekulativen Multi-Action-Ausführung befindet sich in der Datei ufo/agents/processors/actions.py. Die folgenden Klassen werden für die spekulative Multi-Action-Ausführung verwendet.

Quellcode in agents/processors/actions.py
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def __init__(
    self,
    function: str = "",
    args: Dict[str, Any] = {},
    control_label: str = "",
    control_text: str = "",
    after_status: str = "",
    results: Optional[ActionExecutionLog] = None,
    configs=Config.get_instance().config_data,
):
    self._function = function
    self._args = args
    self._control_label = control_label
    self._control_text = control_text
    self._after_status = after_status
    self._results = ActionExecutionLog() if results is None else results
    self._configs = configs
    self._control_log = BaseControlLog()

after_status property

Ruft den Status ab.

Rückgabe
  • str

    Der Status.

args property

Ruft die Argumente ab.

Rückgabe
  • Dict[str, Any]

    Die Argumente.

command_string property

Generiert eine Funktionsaufrufzeichenfolge.

Rückgabe
  • str

    Die Funktionsaufrufzeichenfolge.

control_label property

Ruft das Steuerelementetikett ab.

Rückgabe
  • str

    Das Steuerelementetikett.

control_log property writable

Ruft das Steuerelementprotokoll ab.

Rückgabe
  • BaseControlLog

    Das Steuerelementprotokoll.

control_text property

Ruft den Steuerelementtext ab.

Rückgabe
  • str

    Der Steuerelementtext.

function property

Ruft den Funktionsnamen ab.

Rückgabe
  • str

    Die Funktion.

results property writable

Ruft die Ergebnisse ab.

Rückgabe
  • ActionExecutionLog

    Die Ergebnisse.

action_flow(puppeteer, control_dict, application_window)

Führt den Aktionsfluss aus.

Parameter
  • puppeteer (AppPuppeteer) –

    Der Puppenspieler, der die Anwendung steuert.

  • control_dict (Dict[str, UIAWrapper]) –

    Das Steuerelement-Dictionary.

  • application_window (UIAWrapper) –

    Das Anwendungsfenster, in dem sich das Steuerelement befindet.

Rückgabe
  • Tuple[ActionExecutionLog, BaseControlLog]

    Das Aktionsausführungsprotokoll.

Quellcode in agents/processors/actions.py
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
293
294
295
296
297
298
299
300
301
302
303
304
def action_flow(
    self,
    puppeteer: AppPuppeteer,
    control_dict: Dict[str, UIAWrapper],
    application_window: UIAWrapper,
) -> Tuple[ActionExecutionLog, BaseControlLog]:
    """
    Execute the action flow.
    :param puppeteer: The puppeteer that controls the application.
    :param control_dict: The control dictionary.
    :param application_window: The application window where the control is located.
    :return: The action execution log.
    """
    control_selected: UIAWrapper = control_dict.get(self.control_label, None)

    # If the control is selected, but not available, return an error.
    if control_selected is not None and not self._control_validation(
        control_selected
    ):
        self.results = ActionExecutionLog(
            status="error",
            traceback="Control is not available.",
            error="Control is not available.",
        )
        self._control_log = BaseControlLog()

        return self.results

    # Create the control receiver.
    puppeteer.receiver_manager.create_ui_control_receiver(
        control_selected, application_window
    )

    if self.function:

        if self._configs.get("SHOW_VISUAL_OUTLINE_ON_SCREEN", True):
            if control_selected:
                control_selected.draw_outline(colour="red", thickness=3)
                time.sleep(self._configs.get("RECTANGLE_TIME", 0))

        self._control_log = self._get_control_log(
            control_selected=control_selected, application_window=application_window
        )

        try:
            return_value = self.execute(puppeteer=puppeteer)
            if not utils.is_json_serializable(return_value):
                return_value = ""

            self.results = ActionExecutionLog(
                status="success",
                return_value=return_value,
            )

        except Exception as e:

            import traceback

            self.results = ActionExecutionLog(
                status="error",
                traceback=traceback.format_exc(),
                error=str(e),
            )
        return self.results

count_repeat_times(previous_actions)

Ruft die Anzahl der gleichen Aktionen in den vorherigen Aktionen ab.

Parameter
  • previous_actions (List[Dict[str, Any]]) –

    Die vorherigen Aktionen.

Rückgabe
  • int

    Die Anzahl der gleichen Aktionen in den vorherigen Aktionen.

Quellcode in agents/processors/actions.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
def count_repeat_times(self, previous_actions: List[Dict[str, Any]]) -> int:
    """
    Get the times of the same action in the previous actions.
    :param previous_actions: The previous actions.
    :return: The times of the same action in the previous actions.
    """

    count = 0
    for action in previous_actions[::-1]:
        if self.is_same_action(action):
            count += 1
        else:
            break
    return count

execute(puppeteer)

Führt die Aktion aus.

Parameter
  • puppeteer (AppPuppeteer) –

    Der Puppenspieler, der die Anwendung steuert.

Quellcode in agents/processors/actions.py
234
235
236
237
238
239
def execute(self, puppeteer: AppPuppeteer) -> Any:
    """
    Execute the action.
    :param puppeteer: The puppeteer that controls the application.
    """
    return puppeteer.execute_command(self.function, self.args)

get_operation_point_list()

Ruft die Operationspunkte der Aktion ab.

Rückgabe
  • List[Tuple[int]]

    Die Operationspunkte der Aktion.

Quellcode in agents/processors/actions.py
364
365
366
367
368
369
370
371
372
373
374
375
def get_operation_point_list(self) -> List[Tuple[int]]:
    """
    Get the operation points of the action.
    :return: The operation points of the action.
    """

    if "path" in self.args:
        return [(point["x"], point["y"]) for point in self.args["path"]]
    elif "x" in self.args and "y" in self.args:
        return [(self.args["x"], self.args["y"])]
    else:
        return []

is_same_action(action_to_compare)

Prüft, ob die beiden Aktionen gleich sind.

Parameter
  • action_to_compare (Dict[str, Any]) –

    Die Aktion, die mit der aktuellen Aktion verglichen werden soll.

Rückgabe
  • bool

    Ob die beiden Aktionen gleich sind.

Quellcode in agents/processors/actions.py
159
160
161
162
163
164
165
166
167
168
169
170
def is_same_action(self, action_to_compare: Dict[str, Any]) -> bool:
    """
    Check whether the two actions are the same.
    :param action_to_compare: The action to compare with the current action.
    :return: Whether the two actions are the same.
    """

    return (
        self.function == action_to_compare.get("Function")
        and self.args == action_to_compare.get("Args")
        and self.control_text == action_to_compare.get("ControlText")
    )

print_result()

Gibt das Ergebnis der Aktionsausführung aus.

Quellcode in agents/processors/actions.py
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
def print_result(self) -> None:
    """
    Print the action execution result.
    """

    utils.print_with_color(
        "Selected item🕹️: {control_text}, Label: {label}".format(
            control_text=self.control_text, label=self.control_label
        ),
        "yellow",
    )
    utils.print_with_color(
        "Action applied⚒️: {action}".format(action=self.command_string), "blue"
    )

    result_color = "red" if self.results.status != "success" else "green"

    utils.print_with_color(
        "Execution result📜: {result}".format(result=asdict(self.results)),
        result_color,
    )

to_dict(previous_actions)

Konvertiert die Aktion in ein Dictionary.

Parameter
  • previous_actions (Optional[List[Dict[str, Any]]]) –

    Die vorherigen Aktionen.

Rückgabe
  • Dict[str, Any]

    Das Dictionary der Aktion.

Quellcode in agents/processors/actions.py
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
def to_dict(
    self, previous_actions: Optional[List[Dict[str, Any]]]
) -> Dict[str, Any]:
    """
    Convert the action to a dictionary.
    :param previous_actions: The previous actions.
    :return: The dictionary of the action.
    """

    action_dict = {
        "Function": self.function,
        "Args": self.args,
        "ControlLabel": self.control_label,
        "ControlText": self.control_text,
        "Status": self.after_status,
        "Results": asdict(self.results),
    }

    # Add the repetitive times of the same action in the previous actions if the previous actions are provided.
    if previous_actions:
        action_dict["RepeatTimes"] = self.count_repeat_times(previous_actions)

    return action_dict

to_string(previous_actions)

Konvertiert die Aktion in eine Zeichenfolge.

Parameter
  • previous_actions (Optional[List[OneStepAction]]) –

    Die vorherigen Aktionen.

Rückgabe
  • str

    Die Zeichenfolge der Aktion.

Quellcode in agents/processors/actions.py
211
212
213
214
215
216
217
def to_string(self, previous_actions: Optional[List["OneStepAction"]]) -> str:
    """
    Convert the action to a string.
    :param previous_actions: The previous actions.
    :return: The string of the action.
    """
    return json.dumps(self.to_dict(previous_actions), ensure_ascii=False)