From 32458e574eecfe308a7581264b4dd31482adcba0 Mon Sep 17 00:00:00 2001 From: lidanyang Date: Fri, 26 Jul 2024 14:06:18 +0800 Subject: [PATCH 1/4] refine prompt --- metagpt/prompts/di/data_analyst.py | 13 +++---------- metagpt/prompts/task_type.py | 2 +- metagpt/strategy/experience_retriever.py | 8 ++++---- metagpt/strategy/planner.py | 2 ++ 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/metagpt/prompts/di/data_analyst.py b/metagpt/prompts/di/data_analyst.py index b99dcba1e..cb15155c9 100644 --- a/metagpt/prompts/di/data_analyst.py +++ b/metagpt/prompts/di/data_analyst.py @@ -2,9 +2,9 @@ from metagpt.strategy.task_type import TaskType EXTRA_INSTRUCTION = """ 6. Carefully choose to use or not use the browser tool to assist you in web tasks. - - When no click action is required, no need to use the browser tool to navigate to the webpage before scraping. - - If you need detail HTML content, write code to get it but not to use the browser tool. - - Make sure the command_name are certainly in Available Commands when you use the browser tool. + - When no click action is required, no need to use the Browser tool to navigate to the webpage before scraping. + - Write code to view the HTML content rather than using the Browser tool. + - Make sure the command_name are certainly in Available Commands when you use the Browser tool. 7. When you are making plan. It is highly recommend to plan and append all the tasks in first response once time. 8. Don't finish_current_task multiple times for the same task. 9. Finish current task timely, such as when the code is written and executed successfully. @@ -20,10 +20,3 @@ CODE_STATUS = """ **Execution status**: {status} **Execution result**: {result} """ - - -BROWSER_INFO = """ -Here are ordered web actions in the browser environment, note that you can not use the browser tool in the current environment. -{browser_actions} -The latest url is the one you should use to view the page. If view page has been done, directly use the variable and html content in executing result. -""" diff --git a/metagpt/prompts/task_type.py b/metagpt/prompts/task_type.py index 2e4af4c1d..201e57f08 100644 --- a/metagpt/prompts/task_type.py +++ b/metagpt/prompts/task_type.py @@ -57,5 +57,5 @@ The current task is about converting image into webpage code. please note the fo # Prompt for taking on "web_scraping" tasks WEB_SCRAPING_PROMPT = """ - Remember to view and print the necessary HTML content in a separate task to understand the structure first before scraping data. Such as `html_content = await view_page_element_to_scrape(...)\nprint(html_content)`. -- Since the data required by user may not correspond directly to the actual HTML element names, you should thoroughly analyze the HTML structure and meanings of all elements in the executing result first. Ensure the `class_` in your code should derived from the actual HTML structure directly, not based on your knowledge. To ensure it, analyse the most suitable location of the 'class_' in the actual HTML content before code. +- Since the data required by user may not correspond directly to the actual HTML element names, you should thoroughly analyze the HTML structure and meanings of all elements in your context first. Ensure the `class_` in your code should derived from the actual HTML structure directly, not based on your knowledge. To ensure it, analyse the most suitable location of the 'class_' in the actual HTML content before code. """ diff --git a/metagpt/strategy/experience_retriever.py b/metagpt/strategy/experience_retriever.py index 0f805d44c..b87f97f37 100644 --- a/metagpt/strategy/experience_retriever.py +++ b/metagpt/strategy/experience_retriever.py @@ -937,7 +937,7 @@ Explanation: The requirement is to scrape data from a website and extract inform "args": { "task_id": "3", "dependent_task_ids": ["2"], - "instruction": "View the html content of the search result page before scrap data to understand the structure.", + "instruction": "View and print the html content of the search result page before scrap data to understand the structure.", "assignee": "David" } }, @@ -988,7 +988,7 @@ Explanation: Since the Browser has successfully navigated to the website, and I ``` ## action 4 -Explanation: Since the Browser has successfully search the keyword `beef`, I will finish the current task and then write code to view the html content of the page. +Explanation: Since the Browser has successfully search the keyword `beef`, I will finish the current task and then write code to view and print the html content of the page. ```json [ @@ -1034,7 +1034,7 @@ Explanation: The requirement is to scrape data from a website and extract inform "args": { "task_id": "1", "dependent_task_ids": [], - "instruction": "View the html content of the page before scrap data to understand the structure.", + "instruction": "View and print the html content of the page before scrap data to understand the structure.", "assignee": "David" } }, @@ -1051,7 +1051,7 @@ Explanation: The requirement is to scrape data from a website and extract inform ``` ## action 2 -Explanation: To scrap data from the website, I will first view the html content of the page. +Explanation: To scrap data from the website, I will first view and print the html content of the page. ```json [ diff --git a/metagpt/strategy/planner.py b/metagpt/strategy/planner.py index d195cc03b..fbb5fd838 100644 --- a/metagpt/strategy/planner.py +++ b/metagpt/strategy/planner.py @@ -42,7 +42,9 @@ PLAN_STATUS = """ ## Finished Section of Current Task ### code +```python {current_task_code} +``` ### execution result {current_task_result} From 8ef095b945e56530753c7c97daa72fa1b376b6af Mon Sep 17 00:00:00 2001 From: lidanyang Date: Fri, 26 Jul 2024 14:07:22 +0800 Subject: [PATCH 2/4] send memory to write_code directly --- metagpt/actions/di/write_analysis_code.py | 4 +- metagpt/roles/di/data_analyst.py | 63 ++++++++++------------- 2 files changed, 30 insertions(+), 37 deletions(-) diff --git a/metagpt/actions/di/write_analysis_code.py b/metagpt/actions/di/write_analysis_code.py index aaf4911f2..4d21b2cec 100644 --- a/metagpt/actions/di/write_analysis_code.py +++ b/metagpt/actions/di/write_analysis_code.py @@ -43,6 +43,7 @@ class WriteAnalysisCode(Action): tool_info: str = "", working_memory: list[Message] = None, use_reflection: bool = False, + memory: list[Message] = None, **kwargs, ) -> str: structual_prompt = STRUCTUAL_PROMPT.format( @@ -52,7 +53,8 @@ class WriteAnalysisCode(Action): ) working_memory = working_memory or [] - context = self.llm.format_msg([Message(content=structual_prompt, role="user")] + working_memory) + memory = memory or [] + context = self.llm.format_msg(memory + [Message(content=structual_prompt, role="user")] + working_memory) # LLM call if use_reflection: diff --git a/metagpt/roles/di/data_analyst.py b/metagpt/roles/di/data_analyst.py index 1debd681c..d1ef92c35 100644 --- a/metagpt/roles/di/data_analyst.py +++ b/metagpt/roles/di/data_analyst.py @@ -1,17 +1,20 @@ from __future__ import annotations import re -from typing import List from pydantic import Field, model_validator from metagpt.actions.di.execute_nb_code import ExecuteNbCode from metagpt.actions.di.write_analysis_code import WriteAnalysisCode from metagpt.logs import logger -from metagpt.prompts.di.data_analyst import EXTRA_INSTRUCTION, TASK_TYPE_DESC, CODE_STATUS, BROWSER_INFO +from metagpt.prompts.di.data_analyst import ( + CODE_STATUS, + EXTRA_INSTRUCTION, + TASK_TYPE_DESC, +) from metagpt.prompts.di.role_zero import ROLE_INSTRUCTION from metagpt.roles.di.role_zero import RoleZero -from metagpt.schema import TaskResult, Message +from metagpt.schema import Message, TaskResult from metagpt.strategy.experience_retriever import ExpRetriever, KeywordExpRetriever from metagpt.tools.tool_recommend import BM25ToolRecommender, ToolRecommender from metagpt.tools.tool_registry import register_tool @@ -21,12 +24,12 @@ from metagpt.tools.tool_registry import register_tool class DataAnalyst(RoleZero): name: str = "David" profile: str = "DataAnalyst" - goal: str = "Take on any data-related tasks, such as data analysis, machine learning, deep learning, web browsing, web scraping, web searching, web deployment, terminal operation, git and github operation, etc." + goal: str = "Take on any data-related tasks, such as data analysis, machine learning, deep learning, web browsing, web scraping, web searching, web deployment, terminal operation, etc." instruction: str = ROLE_INSTRUCTION + EXTRA_INSTRUCTION task_type_desc: str = TASK_TYPE_DESC tools: list[str] = ["Plan", "DataAnalyst", "RoleZero", "Browser"] - custom_tools: list[str] = ["machine learning", "web scraping", "Terminal"] + custom_tools: list[str] = ["web scraping", "Terminal"] custom_tool_recommender: ToolRecommender = None experience_retriever: ExpRetriever = KeywordExpRetriever() @@ -40,39 +43,26 @@ class DataAnalyst(RoleZero): self.custom_tool_recommender = BM25ToolRecommender(tools=self.custom_tools) def _update_tool_execution(self): - self.tool_execution_map.update({ - "DataAnalyst.write_and_exec_code": self.write_and_exec_code, - }) + self.tool_execution_map.update( + { + "DataAnalyst.write_and_exec_code": self.write_and_exec_code, + } + ) - async def parse_browser_actions(self, memory: List[Message]) -> List[Message]: - memory = await super().parse_browser_actions(memory) - browser_actions = [] - for index, msg in enumerate(memory): - if msg.cause_by == "browser": - browser_url = re.search('URL: (.*?)\\n', msg.content).group(1) - pattern = re.compile(r"Command Browser\.(\w+) executed") - browser_actions.append({ - 'command': pattern.match(memory[index - 1].content).group(1), - 'current url': browser_url - }) - if browser_actions: - browser_actions = BROWSER_INFO.format(browser_actions=browser_actions) - self.rc.working_memory.add(Message(content=browser_actions, role="user", cause_by="browser")) - return memory - - async def write_and_exec_code(self, instruction: str = ""): - """Write a code block for current task and execute it in an interactive notebook environment. - - Args: - instruction: The specific task description for which the code needs to be written. - """ + async def write_and_exec_code(self): + """Write a code block for current task step and execute it in an interactive notebook environment.""" counter = 0 success = False await self.execute_code.init_code() # plan info - plan_status = self.planner.get_plan_status() - plan_status = plan_status + f"\nFurther Task Instruction: {instruction}" + try: + plan_status = self.planner.get_plan_status() + plan_status = re.sub( + r"### execution result.*?(?=## Current Task|## Task Guidance)", "", plan_status, flags=re.DOTALL + ) + except AttributeError as e: + return "All tasks have been completed. Please check to end or append task first before writing code." # tool info if self.custom_tool_recommender: @@ -84,8 +74,8 @@ class DataAnalyst(RoleZero): while not success and counter < 3: ### write code ### - logger.info(f"ready to WriteAnalysisCode") - use_reflection = (counter > 0 and self.use_reflection) # only use reflection after the first trial + logger.info("ready to WriteAnalysisCode") + use_reflection = counter > 0 and self.use_reflection # only use reflection after the first trial code = await self.write_code.run( user_requirement=self.planner.plan.goal, @@ -93,6 +83,7 @@ class DataAnalyst(RoleZero): tool_info=tool_info, working_memory=self.rc.working_memory.get(), use_reflection=use_reflection, + memory=self.rc.memory.get(self.memory_k), ) self.rc.working_memory.add(Message(content=code, role="assistant", cause_by=WriteAnalysisCode)) @@ -108,9 +99,9 @@ class DataAnalyst(RoleZero): task_result = TaskResult(code=code, result=result, is_success=success) self.planner.current_task.update_task_result(task_result) - status = 'Success' if success else 'Failed' + status = "Success" if success else "Failed" output = CODE_STATUS.format(code=code, status=status, result=result) if success: - output += 'The code written has been executed successfully.' + output += "The code written has been executed successfully." self.rc.working_memory.clear() return output From 8d6a2801a0928ebfd0d04963180f8ee76f35134a Mon Sep 17 00:00:00 2001 From: lidanyang Date: Mon, 29 Jul 2024 11:01:43 +0800 Subject: [PATCH 3/4] refine code --- metagpt/roles/di/data_analyst.py | 14 +++++--------- metagpt/strategy/planner.py | 12 +++++++----- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/metagpt/roles/di/data_analyst.py b/metagpt/roles/di/data_analyst.py index d1ef92c35..25e504e2f 100644 --- a/metagpt/roles/di/data_analyst.py +++ b/metagpt/roles/di/data_analyst.py @@ -1,7 +1,5 @@ from __future__ import annotations -import re - from pydantic import Field, model_validator from metagpt.actions.di.execute_nb_code import ExecuteNbCode @@ -56,13 +54,11 @@ class DataAnalyst(RoleZero): await self.execute_code.init_code() # plan info - try: - plan_status = self.planner.get_plan_status() - plan_status = re.sub( - r"### execution result.*?(?=## Current Task|## Task Guidance)", "", plan_status, flags=re.DOTALL - ) - except AttributeError as e: - return "All tasks have been completed. Please check to end or append task first before writing code." + if self.planner.current_task: + # clear task result from plan to save token, since it has been in memory + plan_status = self.planner.get_plan_status(exclude=["task_result"]) + else: + return "No current_task found now. Please use command Plan.append_task to add a task first." # tool info if self.custom_tool_recommender: diff --git a/metagpt/strategy/planner.py b/metagpt/strategy/planner.py index fbb5fd838..c54f6be15 100644 --- a/metagpt/strategy/planner.py +++ b/metagpt/strategy/planner.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +from typing import List from pydantic import BaseModel, Field @@ -165,8 +166,9 @@ class Planner(BaseModel): return context_msg + self.working_memory.get() - def get_plan_status(self) -> str: + def get_plan_status(self, exclude: List[str] = None) -> str: # prepare components of a plan status + exclude = exclude or [] finished_tasks = self.plan.get_finished_tasks() code_written = [remove_comments(task.code) for task in finished_tasks] code_written = "\n\n".join(code_written) @@ -178,11 +180,11 @@ class Planner(BaseModel): # combine components in a prompt prompt = PLAN_STATUS.format( - code_written=code_written, - task_results=task_results, + code_written=code_written if "code" not in exclude else "omit here", + task_results=task_results if "task_result" not in exclude else "omit here", current_task=self.current_task.instruction, - current_task_code=self.current_task.code if self.current_task.code else "", - current_task_result=self.current_task.result if self.current_task.result else "", + current_task_code=self.current_task.code if "code" not in exclude else "omit here", + current_task_result=self.current_task.result if "task_result" not in exclude else "omit here", guidance=guidance, ) From 7ea23a48e4c88ee30232a68e61976be1e0cac0df Mon Sep 17 00:00:00 2001 From: lidanyang Date: Mon, 29 Jul 2024 15:21:38 +0800 Subject: [PATCH 4/4] refine code --- metagpt/strategy/planner.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/metagpt/strategy/planner.py b/metagpt/strategy/planner.py index c54f6be15..1b4b1cca9 100644 --- a/metagpt/strategy/planner.py +++ b/metagpt/strategy/planner.py @@ -169,6 +169,7 @@ class Planner(BaseModel): def get_plan_status(self, exclude: List[str] = None) -> str: # prepare components of a plan status exclude = exclude or [] + exclude_prompt = "omit here" finished_tasks = self.plan.get_finished_tasks() code_written = [remove_comments(task.code) for task in finished_tasks] code_written = "\n\n".join(code_written) @@ -180,11 +181,11 @@ class Planner(BaseModel): # combine components in a prompt prompt = PLAN_STATUS.format( - code_written=code_written if "code" not in exclude else "omit here", - task_results=task_results if "task_result" not in exclude else "omit here", + code_written=code_written if "code" not in exclude else exclude_prompt, + task_results=task_results if "task_result" not in exclude else exclude_prompt, current_task=self.current_task.instruction, - current_task_code=self.current_task.code if "code" not in exclude else "omit here", - current_task_result=self.current_task.result if "task_result" not in exclude else "omit here", + current_task_code=self.current_task.code if "code" not in exclude else exclude_prompt, + current_task_result=self.current_task.result if "task_result" not in exclude else exclude_prompt, guidance=guidance, )