这是您的 Python 代码项目的精炼文档

从用户演示学习

对于复杂任务,用户可以使用 步骤记录器 演示任务,以记录操作轨迹。UFO 可以从这些用户演示中学习,以提高 AppAgent 的性能。

机制

UFO 使用 步骤记录器 工具记录任务和操作轨迹。记录的演示保存为 zip 文件。DemonstrationSummarizer 类提取并总结演示。总结的演示保存在 config_dev.yaml 文件中指定的 DEMONSTRATION_SAVED_PATH 中。当 AppAgent 遇到类似任务时,DemonstrationRetriever 类从演示数据库中检索保存的演示,并根据检索到的演示生成计划。

信息

您可以在 用户演示提供 文档中找到如何使用步骤记录器工具记录任务和操作轨迹。

您可以找到一个从用户演示中学习的演示视频


激活从用户演示中学习

步骤 1:用户演示

请按照 用户演示提供 文档中的步骤提供用户演示。

步骤 2:配置 AppAgent

配置以下参数以允许 UFO 使用用户演示中的 RAG

配置选项 描述 类型 默认值
RAG_DEMONSTRATION 是否使用用户演示中的 RAG 布尔值 False
RAG_DEMONSTRATION_RETRIEVED_TOPK 离线检索的 Top K 文档 整数 5
RAG_DEMONSTRATION_COMPLETION_N 演示结果的完成选择数量 整数 3

参考

演示总结器

DemonstrationSummarizer 类位于 record_processor/summarizer/summarizer.py 文件中。DemonstrationSummarizer 类提供总结演示的方法

DemonstrationSummarizer 类是演示学习的总结器。它将演示记录总结为摘要列表,并将摘要保存到 YAML 文件和向量数据库中。摘要示例如下:{ "example": { "Observation": "Word.exe 已打开。", "Thought": "用户正在尝试创建一个新文件。", "ControlLabel": "1", "ControlText": "示例控件文本", "Function": "CreateFile", "Args": "filename='new_file.txt'", "Status": "成功", "Plan": "创建一个名为 'new_file.txt' 的新文件。", "Comment": "用户成功创建了一个新文件。" }, "Tips": "您可以使用 'CreateFile' 函数创建一个新文件。" }

初始化 DemonstrationSummarizer。

参数
  • is_visual (bool) –

    请求是否针对视觉模型。

  • prompt_template (str) –

    提示模板的路径。

  • demonstration_prompt_template (str) –

    演示示例提示模板的路径。

  • api_prompt_template (str) –

    API 提示模板的路径。

summarizer/summarizer.py 中的源代码
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def __init__(
    self,
    is_visual: bool,
    prompt_template: str,
    demonstration_prompt_template: str,
    api_prompt_template: str,
    completion_num: int = 1,
):
    """
    Initialize the DemonstrationSummarizer.
    :param is_visual: Whether the request is for visual model.
    :param prompt_template: The path of the prompt template.
    :param demonstration_prompt_template: The path of the example prompt template for demonstration.
    :param api_prompt_template: The path of the api prompt template.
    """
    self.is_visual = is_visual
    self.prompt_template = prompt_template
    self.demonstration_prompt_template = demonstration_prompt_template
    self.api_prompt_template = api_prompt_template
    self.completion_num = completion_num

__build_prompt(demo_record)

根据用户演示记录构建提示。

参数
  • demo_record (DemonstrationRecord) –

    用户演示记录。返回:提示。

summarizer/summarizer.py 中的源代码
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def __build_prompt(self, demo_record: DemonstrationRecord) -> list:
    """
    Build the prompt by the user demonstration record.
    :param demo_record: The user demonstration record.
    return: The prompt.
    """
    demonstration_prompter = DemonstrationPrompter(
        self.is_visual,
        self.prompt_template,
        self.demonstration_prompt_template,
        self.api_prompt_template,
    )
    demonstration_system_prompt = (
        demonstration_prompter.system_prompt_construction()
    )
    demonstration_user_prompt = demonstration_prompter.user_content_construction(
        demo_record
    )
    demonstration_prompt = demonstration_prompter.prompt_construction(
        demonstration_system_prompt, demonstration_user_prompt
    )

    return demonstration_prompt

__parse_response(response_string)

将响应字符串解析为摘要字典。

参数
  • response_string (str) –

    响应字符串。返回:摘要字典。

summarizer/summarizer.py 中的源代码
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
def __parse_response(self, response_string: str) -> dict:
    """
    Parse the response string to a dict of summary.
    :param response_string: The response string.
    return: The summary dict.
    """
    try:
        response_json = json_parser(response_string)
    except:
        response_json = None

    # Restructure the response, in case any of the keys are missing, set them to empty string.
    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

create_or_update_vector_db(summaries, db_path) staticmethod

创建或更新向量数据库。

参数
  • summaries (list) –

    摘要。

  • db_path (str) –

    向量数据库的路径。

summarizer/summarizer.py 中的源代码
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
@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

创建或更新 YAML 文件。

参数
  • summaries (list) –

    摘要。

  • yaml_path (str) –

    YAML 文件的路径。

summarizer/summarizer.py 中的源代码
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
163
164
165
166
167
168
169
170
@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_list(record)

获取记录的摘要列表

参数
  • record (DemonstrationRecord) –

    演示记录。返回:用户定义完成数量的摘要列表和成本

summarizer/summarizer.py 中的源代码
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
def get_summary_list(self, record: DemonstrationRecord) -> Tuple[list, float]:
    """
    Get the summary list for a record
    :param record: The demonstration record.
    return: The summary list for the user defined completion number and the cost
    """

    prompt = self.__build_prompt(record)
    response_string_list, cost = get_completions(
        prompt, "APPAGENT", use_backup_engine=True, n=self.completion_num
    )
    summaries = []
    for response_string in response_string_list:
        summary = self.__parse_response(response_string)
        if summary:
            summary["request"] = record.get_request()
            summary["app_list"] = record.get_applications()
            summaries.append(summary)

    return summaries, cost


演示检索器

DemonstrationRetriever 类位于 rag/retriever.py 文件中。DemonstrationRetriever 类提供检索演示的方法

基类:Retriever

用于创建演示检索器的类。

创建新的 DemonstrationRetriever。:db_path: 数据库的路径。

源代码位于 rag/retriever.py
219
220
221
222
223
224
def __init__(self, db_path) -> None:
    """
    Create a new DemonstrationRetriever.
    :db_path: The path to the database.
    """
    self.indexer = self.get_indexer(db_path)

get_indexer(db_path)

创建演示索引器。:db_path: 数据库的路径。

源代码位于 rag/retriever.py
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
def get_indexer(self, db_path: str):
    """
    Create a demonstration indexer.
    :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