Lernen aus eigener Erfahrung

Wenn UFO eine Aufgabe erfolgreich abschließt, kann der Benutzer entscheiden, die erfolgreiche Erfahrung zu speichern, um den AppAgent zu stärken. Der AppAgent kann aus seinen eigenen erfolgreichen Erfahrungen lernen, um seine Leistung in Zukunft zu verbessern.

Mechanismus

Schritt 1: Sitzung abschließen

  • Ereignis: UFO schließt eine Sitzung ab

Schritt 2: Benutzer auffordern, Erfahrung zu speichern

  • Aktion: Der Agent fordert den Benutzer mit der Option auf, die erfolgreiche Erfahrung zu speichern

Save Experience

Schritt 3: Benutzer entscheidet sich für das Speichern

  • Aktion: Wenn der Benutzer sich entscheidet, die Erfahrung zu speichern

Schritt 4: Erfahrung zusammenfassen und speichern

  • Werkzeug: ExperienceSummarizer
  • Prozess:
  • Fasse die Erfahrung in einem Demonstrationsbeispiel zusammen
  • Speichere das Demonstrationsbeispiel im Verzeichnis EXPERIENCE_SAVED_PATH, wie in der Datei config_dev.yaml angegeben
  • Das Demonstrationsbeispiel enthält ähnliche Felder wie die, die im Prompt des AppAgent verwendet werden

Schritt 5: Gespeicherte Erfahrung abrufen und nutzen

  • Wann: Der AppAgent trifft in Zukunft auf eine ähnliche Aufgabe
  • Aktion: Rufe die gespeicherte Erfahrung aus der Erfahrungsdatenbank ab
  • Ergebnis: Nutze die abgerufene Erfahrung, um einen Plan zu generieren

Workflow-Diagramm

graph TD;
    A[Complete Session] --> B[Ask User to Save Experience]
    B --> C[User Chooses to Save]
    C --> D[Summarize with ExperienceSummarizer]
    D --> E[Save in EXPERIENCE_SAVED_PATH]
    F[AppAgent Encounters Similar Task] --> G[Retrieve Saved Experience]
    G --> H[Generate Plan]

Lernen aus eigener Erfahrung aktivieren

Schritt 1: AppAgent konfigurieren

Konfiguriere die folgenden Parameter, damit UFO RAG aus seiner eigenen Erfahrung nutzen kann

Konfigurationsoption Beschreibung Typ Standardwert
RAG_EXPERIENCE Ob RAG aus seiner eigenen Erfahrung verwendet werden soll Boolean False
RAG_EXPERIENCE_RETRIEVED_TOPK Die Topk für die offline abgerufenen Dokumente Integer 5

Referenz

Erfahrungszusammenfasser

Die Klasse ExperienceSummarizer befindet sich in der Datei ufo/experience/experience_summarizer.py. Die Klasse ExperienceSummarizer stellt die folgenden Methoden zur Verfügung, um die Erfahrung zusammenzufassen

Die ExperienceSummarizer-Klasse ist der Zusammenfasser für das Lernerlebnis.

Initialisiere den ApplicationAgentPrompter.

Parameter
  • is_visual (bool) –

    Ob die Anfrage für ein visuelles Modell bestimmt ist.

  • prompt_template (str) –

    Der Pfad zur Prompt-Vorlage.

  • example_prompt_template (str) –

    Der Pfad zur Vorlage für das Beispiel-Prompt.

  • api_prompt_template (str) –

    Der Pfad zur Vorlage für das API-Prompt.

Quellcode in experience/summarizer.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def __init__(
    self,
    is_visual: bool,
    prompt_template: str,
    example_prompt_template: str,
    api_prompt_template: str,
):
    """
    Initialize the ApplicationAgentPrompter.
    :param is_visual: Whether the request is for visual model.
    :param prompt_template: The path of the prompt template.
    :param example_prompt_template: The path of the example prompt template.
    :param api_prompt_template: The path of the api prompt template.
    """
    self.is_visual = is_visual
    self.prompt_template = prompt_template
    self.example_prompt_template = example_prompt_template
    self.api_prompt_template = api_prompt_template

build_prompt(log_partition)

Erstelle den Prompt.

Parameter
  • log_partition (dict) –

    Die Protokollpartition. Rückgabe: Der Prompt.

Quellcode in experience/summarizer.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def build_prompt(self, log_partition: dict) -> list:
    """
    Build the prompt.
    :param log_partition: The log partition.
    return: The prompt.
    """
    experience_prompter = ExperiencePrompter(
        self.is_visual,
        self.prompt_template,
        self.example_prompt_template,
        self.api_prompt_template,
    )
    experience_system_prompt = experience_prompter.system_prompt_construction()
    experience_user_prompt = experience_prompter.user_content_construction(
        log_partition
    )
    experience_prompt = experience_prompter.prompt_construction(
        experience_system_prompt, experience_user_prompt
    )

    return experience_prompt

create_or_update_vector_db(summaries, db_path) staticmethod

Erstelle oder aktualisiere die Vektordatenbank.

Parameter
  • summaries (list) –

    Die Zusammenfassungen.

  • db_path (str) –

    Der Pfad zur Vektordatenbank.

Quellcode in experience/summarizer.py
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
@staticmethod
def create_or_update_vector_db(summaries: list, db_path: str):
    """
    Create or update the vector database.
    :param summaries: The summaries.
    :param db_path: The path of the vector database.
    """

    document_list = []

    for summary in summaries:
        request = summary["request"]
        document_list.append(Document(page_content=request, metadata=summary))

    db = FAISS.from_documents(document_list, get_hugginface_embedding())

    # Check if the db exists, if not, create a new one.
    if os.path.exists(db_path):
        prev_db = FAISS.load_local(
            db_path,
            get_hugginface_embedding(),
            allow_dangerous_deserialization=True,
        )
        db.merge_from(prev_db)

    db.save_local(db_path)

    print(f"Updated vector DB successfully: {db_path}")

create_or_update_yaml(summaries, yaml_path) staticmethod

Erstelle oder aktualisiere die YAML-Datei.

Parameter
  • summaries (list) –

    Die Zusammenfassungen.

  • yaml_path (str) –

    Der Pfad zur YAML-Datei.

Quellcode in experience/summarizer.py
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
@staticmethod
def create_or_update_yaml(summaries: list, yaml_path: str):
    """
    Create or update the YAML file.

    :param summaries: The summaries.
    :param yaml_path: The path of the YAML file.
    """

    # Check if the file exists, if not, create a new one
    if not os.path.exists(yaml_path):
        with open(yaml_path, "w"):
            pass
        print(f"Created new YAML file: {yaml_path}")

    # Read existing data from the YAML file
    with open(yaml_path, "r") as file:
        existing_data = yaml.safe_load(file)

    # Initialize index and existing_data if file is empty
    index = len(existing_data) if existing_data else 0
    existing_data = existing_data or {}

    # Update data with new summaries
    for i, summary in enumerate(summaries):
        example = {f"example{index + i}": summary}
        existing_data.update(example)

    # Write updated data back to the YAML file
    with open(yaml_path, "w") as file:
        yaml.safe_dump(
            existing_data, file, default_flow_style=False, sort_keys=False
        )

    print(f"Updated existing YAML file successfully: {yaml_path}")

get_summary(prompt_message)

Hole die Zusammenfassung.

Parameter
  • prompt_message (list) –

    Die Prompt-Nachricht. Rückgabe: Die Zusammenfassung und die Kosten.

Quellcode in experience/summarizer.py
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
def get_summary(self, prompt_message: list) -> Tuple[dict, float]:
    """
    Get the summary.
    :param prompt_message: The prompt message.
    return: The summary and the cost.
    """

    # Get the completion for the prompt message
    response_string, cost = get_completion(
        prompt_message, "APPAGENT", use_backup_engine=True
    )
    try:
        response_json = json_parser(response_string)
    except:
        response_json = None

    # Restructure the response
    if response_json:
        summary = dict()
        summary["example"] = {}
        for key in [
            "Observation",
            "Thought",
            "ControlLabel",
            "ControlText",
            "Function",
            "Args",
            "Status",
            "Plan",
            "Comment",
        ]:
            summary["example"][key] = response_json.get(key, "")
        summary["Tips"] = response_json.get("Tips", "")

    return summary, cost

get_summary_list(logs)

Hole die Zusammenfassungsliste.

Parameter
  • logs (list) –

    Die Protokolle. Rückgabe: Die Zusammenfassungsliste und die Gesamtkosten.

Quellcode in experience/summarizer.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def get_summary_list(self, logs: list) -> Tuple[list, float]:
    """
    Get the summary list.
    :param logs: The logs.
    return: The summary list and the total cost.
    """
    summaries = []
    total_cost = 0.0
    for log_partition in logs:
        prompt = self.build_prompt(log_partition)
        summary, cost = self.get_summary(prompt)
        summary["request"] = log_partition.get("subtask")
        summary["Sub-task"] = log_partition.get("subtask")
        summary["app_list"] = [log_partition.get("application")]
        summaries.append(summary)
        total_cost += cost

    return summaries, total_cost

read_logs(log_path) staticmethod

Lese das Protokoll.

Parameter
  • log_path (str) –

    Der Pfad zur Protokolldatei.

Quellcode in experience/summarizer.py
119
120
121
122
123
124
125
126
@staticmethod
def read_logs(log_path: str) -> list:
    """
    Read the log.
    :param log_path: The path of the log file.
    """
    replay_loader = ExperienceLogLoader(log_path)
    return replay_loader.subtask_partition


Erfahrungsabrufer

Die Klasse ExperienceRetriever befindet sich in der Datei ufo/rag/retriever.py. Die Klasse ExperienceRetriever stellt die folgenden Methoden zur Verfügung, um die Erfahrung abzurufen

Basen: Retriever

Klasse zur Erstellung von Erfahrungsabrufern.

Erstelle einen neuen ExperienceRetriever.

Parameter
  • db_path

    Der Pfad zur Datenbank.

Quellcode in rag/retriever.py
138
139
140
141
142
143
def __init__(self, db_path) -> None:
    """
    Create a new ExperienceRetriever.
    :param db_path: The path to the database.
    """
    self.indexer = self.get_indexer(db_path)

get_indexer(db_path)

Erstelle einen Erfahrungsindexierer.

Parameter
  • db_path (str) –

    Der Pfad zur Datenbank.

Quellcode in rag/retriever.py
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
def get_indexer(self, db_path: str):
    """
    Create an experience indexer.
    :param db_path: The path to the database.
    """

    try:
        db = FAISS.load_local(
            db_path,
            get_hugginface_embedding(),
            allow_dangerous_deserialization=True,
        )
        return db
    except Exception as e:
        print_with_color(
            "Warning: Failed to load experience indexer from {path}, error: {error}.".format(
                path=db_path, error=e
            ),
            "yellow",
        )
        return None