From 2637af2a40bf54c275c86c95a3f6421b5bbd2677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Wed, 25 Sep 2024 11:27:57 +0800 Subject: [PATCH 01/11] add static coding instrction in engineer prompt --- metagpt/prompts/di/engineer2.py | 7 +++++-- metagpt/roles/di/engineer2.py | 10 ++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 016884d2b..5c43d401b 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -108,8 +108,11 @@ WRITE_CODE_PROMPT = """ # Current Coding File {file_path} -# Further Instruction -{instruction} +# File Description +{file_description} + +# Instruction +Your task is to write the {file_name} according to the User Requirement. # Output While some concise thoughts are helpful, code is absolutely required. Always output one and only one code block in your response. Output code in the following format: diff --git a/metagpt/roles/di/engineer2.py b/metagpt/roles/di/engineer2.py index 3d80e6a26..2404949cb 100644 --- a/metagpt/roles/di/engineer2.py +++ b/metagpt/roles/di/engineer2.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os from pathlib import Path from pydantic import Field @@ -113,19 +114,20 @@ class Engineer2(RoleZero): command_output += await super()._run_special_command(cmd) return command_output - async def write_new_code(self, path: str, instruction: str = "Write code for the current file.") -> str: + async def write_new_code(self, path: str, file_description: str = "") -> str: """Write a new code file. Args: path (str): The absolute path of the file to be created. - instruction (optional, str): Further hints or notice other than the current task instruction, must be very concise and can be empty. Defaults to "". + file_description (optional, str): Further hints or notice other than the current task description, must be very concise and can be empty. Defaults to "". """ plan_status, _ = self._get_plan_status() prompt = WRITE_CODE_PROMPT.format( user_requirement=self.planner.plan.goal, - file_path=path, plan_status=plan_status, - instruction=instruction, + file_path=path, + file_description=file_description, + file_name=os.path.basename(path), ) # Sometimes the Engineer repeats the last command to respond. # Replace the last command with a manual prompt to guide the Engineer to write new code. From eda046f78f7cae76c9b9c55c140ec9cdee278b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Wed, 25 Sep 2024 12:58:24 +0800 Subject: [PATCH 02/11] add incremental development sample in quick think example --- metagpt/prompts/di/role_zero.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index 2697ecef4..830044c7b 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -71,7 +71,7 @@ you must respond in {respond_language}. Pay close attention to the Example provided, you can reuse the example for your current situation if it fits. If you open a file, the line number is displayed at the front of each line. You may use any of the available commands to create a plan or update the plan. You may output mutiple commands, they will be executed sequentially. -If you finish current task, you will automatically take the next task in the existing plan, use Plan.finish_task, DON'T append a new task. +If you finish current task, you will automatically take the next task in the existing plan, use Plan.finish_current_task, DON'T append a new task. Review the latest plan's outcome, focusing on achievements. If your completed task matches the current, consider it finished. Using Editor.insert_content_at_line and Editor.edit_file_by_replace more than once in the current command list is forbidden. Because the command is mutually exclusive and will change the line number after execution. In your response, include at least one command. @@ -226,8 +226,10 @@ Response Category: TASK. Thought: The request is vague and lacks specifics, requiring clarification on the process to optimize. Response Category: AMBIGUOUS. +9. Request: "Change the color of the text to blue in styles.css, add a new button in web page, delete the old background image." +Thought: The request is a incremental development task needing to modify a file. +Response Category: TASK. """ - QUICK_RESPONSE_SYSTEM_PROMPT = """ {role_info} However, you MUST respond to the user message by yourself directly, DON'T ask your team members. @@ -237,11 +239,11 @@ REPORT_TO_HUMAN_PROMPT = """ ## Examlpe example 1: User requirement: create a 2048 game -reply: The development of the 2048 game has been completed. All files (index.html, style.css, and script.js) have been created and reviewed. +Reply: The development of the 2048 game has been completed. All files (index.html, style.css, and script.js) have been created and reviewed. example 2: User requirement: Crawl and extract all the herb names from the website, Tell me the number of herbs. -reply : The herb names have been successfully extracted. A total of 8 herb names were extracted. +Reply : The herb names have been successfully extracted. A total of 8 herb names were extracted. ------------ From 3598c5c3a6930b17a0dea0322f39188dea8b13c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Wed, 25 Sep 2024 19:18:57 +0800 Subject: [PATCH 03/11] The team leader can only send one message per round. --- metagpt/prompts/di/team_leader.py | 1 + metagpt/roles/di/team_leader.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/metagpt/prompts/di/team_leader.py b/metagpt/prompts/di/team_leader.py index 139021ae1..6f3b9ad3c 100644 --- a/metagpt/prompts/di/team_leader.py +++ b/metagpt/prompts/di/team_leader.py @@ -36,6 +36,7 @@ Note: 13. Instructions and reply must be in the same language. 14. Default technology stack is HTML (.html), CSS (.css), and Pure JavaScript (.js). Web app is the default option when developing software. 15. You are the only one who decides the programming language for the software, so the instruction must contain the programming language. +16. Data Collection and Web/Software Devlopment is two tasks. You must assign these tasks to Data Analysis and Engineer Separately. You muat wait for Data Collection to finish before starting coding. """ TL_THOUGHT_GUIDANCE = ( THOUGHT_GUIDANCE diff --git a/metagpt/roles/di/team_leader.py b/metagpt/roles/di/team_leader.py index 112ca5a84..a548ba785 100644 --- a/metagpt/roles/di/team_leader.py +++ b/metagpt/roles/di/team_leader.py @@ -74,7 +74,11 @@ class TeamLeader(RoleZero): Publish a message to a team member, use member name to fill send_to args. You may copy the full original content or add additional information from upstream. This will make team members start their work. DONT omit any necessary info such as path, link, environment, programming language, framework, requirement, constraint from original content to team members because you are their sole info source. """ + if self.rc.todo is None: + # Teamleader have send message to ask a teammember to do something in this turn, the following message will not be sent to team member + return "This message will not be sent to team leader, you have already sent a message to a team member in this turn." self._set_state(-1) # each time publishing a message, pause to wait for the response + if send_to == self.name: return # Avoid sending message to self # Specify the outer send_to to overwrite the default "no one" value. Use UserMessage because message from self is like a user request for others. From 722bb7282eb2e24f7310b457b7e95eee573c4701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Wed, 25 Sep 2024 19:57:41 +0800 Subject: [PATCH 04/11] fix typo --- metagpt/prompts/di/role_zero.py | 4 ++-- metagpt/prompts/di/team_leader.py | 2 +- metagpt/roles/di/team_leader.py | 4 ---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index 830044c7b..58ef6827b 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -125,7 +125,7 @@ ASK_HUMAN_COMMAND = """ { "command_name": "RoleZero.ask_human", "args": { - "question": "I'm a little uncertain about the next step, could you provide me with some guidance?" + "question": "I'm a little uncertain about the next step, could you provide me with some guidance? If want to stop, please contain \"\" in your response." } } ] @@ -227,7 +227,7 @@ Thought: The request is vague and lacks specifics, requiring clarification on th Response Category: AMBIGUOUS. 9. Request: "Change the color of the text to blue in styles.css, add a new button in web page, delete the old background image." -Thought: The request is a incremental development task needing to modify a file. +Thought: The request is an incremental development task that requires modifying one or more files. Response Category: TASK. """ QUICK_RESPONSE_SYSTEM_PROMPT = """ diff --git a/metagpt/prompts/di/team_leader.py b/metagpt/prompts/di/team_leader.py index 6f3b9ad3c..9b0163710 100644 --- a/metagpt/prompts/di/team_leader.py +++ b/metagpt/prompts/di/team_leader.py @@ -36,7 +36,7 @@ Note: 13. Instructions and reply must be in the same language. 14. Default technology stack is HTML (.html), CSS (.css), and Pure JavaScript (.js). Web app is the default option when developing software. 15. You are the only one who decides the programming language for the software, so the instruction must contain the programming language. -16. Data Collection and Web/Software Devlopment is two tasks. You must assign these tasks to Data Analysis and Engineer Separately. You muat wait for Data Collection to finish before starting coding. +16. Data collection and web/software development are two separate tasks. You must assign these tasks to data analysts and engineers, respectively. Wait for the data collection to be completed before starting the coding. """ TL_THOUGHT_GUIDANCE = ( THOUGHT_GUIDANCE diff --git a/metagpt/roles/di/team_leader.py b/metagpt/roles/di/team_leader.py index a548ba785..112ca5a84 100644 --- a/metagpt/roles/di/team_leader.py +++ b/metagpt/roles/di/team_leader.py @@ -74,11 +74,7 @@ class TeamLeader(RoleZero): Publish a message to a team member, use member name to fill send_to args. You may copy the full original content or add additional information from upstream. This will make team members start their work. DONT omit any necessary info such as path, link, environment, programming language, framework, requirement, constraint from original content to team members because you are their sole info source. """ - if self.rc.todo is None: - # Teamleader have send message to ask a teammember to do something in this turn, the following message will not be sent to team member - return "This message will not be sent to team leader, you have already sent a message to a team member in this turn." self._set_state(-1) # each time publishing a message, pause to wait for the response - if send_to == self.name: return # Avoid sending message to self # Specify the outer send_to to overwrite the default "no one" value. Use UserMessage because message from self is like a user request for others. From 128312be59d053abdfa5a4f2526f90ad776b266d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Wed, 25 Sep 2024 20:36:42 +0800 Subject: [PATCH 05/11] Fix: Engineer do not complete all the unit tests task --- metagpt/prompts/di/engineer2.py | 3 ++- metagpt/prompts/di/role_zero.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 5c43d401b..8a3860742 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -80,7 +80,8 @@ Note: 21. When using the editor, pay attention to the editor's current directory. When you use editor tools, the paths must be either absolute or relative to the editor's current directory. 22. The default programming languages are Native HTML. 23. When planning, consider whether images are needed. If you are developing a showcase website, start by using ImageGetter.get_image to obtain the necessary images. -24. If you finish all the tasks, use the command "end" to end. +24. When planning, merge multiple tasks that operate on the same file into a single task. For example, create one task for writing unit tests for all functions in a class. Also in using the editor, merge multiple tasks that operate on the same file into a single task. +25. When create unit tests for a code file, use Editor.read() to read the code file before planing. And create one plan to writing the unit test for the whole file. """ CURRENT_STATE = """ The current editor state is: diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index 58ef6827b..e86d9dc47 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -125,7 +125,7 @@ ASK_HUMAN_COMMAND = """ { "command_name": "RoleZero.ask_human", "args": { - "question": "I'm a little uncertain about the next step, could you provide me with some guidance? If want to stop, please contain \"\" in your response." + "question": "I'm a little uncertain about the next step, could you provide me with some guidance? If you want to stop, please include in your response." } } ] From 955ec263c6bf4a4f24adb980f1ab0fd96c5de9b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Thu, 26 Sep 2024 13:42:43 +0800 Subject: [PATCH 06/11] Add a function to analyze human responses. --- metagpt/prompts/di/role_zero.py | 24 +++++++++++++----------- metagpt/roles/di/role_zero.py | 22 ++++++++++++++++++---- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index e86d9dc47..a703b1d41 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -119,18 +119,20 @@ END_COMMAND = """ ``` """ -ASK_HUMAN_COMMAND = """ -```json -[ - { - "command_name": "RoleZero.ask_human", - "args": { - "question": "I'm a little uncertain about the next step, could you provide me with some guidance? If you want to stop, please include in your response." - } - } -] -``` +SUMMARY_TROUBLE = """ +You are repeating the same action. +Please summarize what is confusing or troubling you, and tell me directly. +Do not output any commands. +Your response must be in {language} and should be within 30 words. """ +DUPULICATE_QUESTTION_FORMAT = """ +I meet the following trouble: +{trouble} +Could you provide me with some guidance? +If want to stop, please contain \"\" in your guidance. +""" +ASK_HUMAN_COMMAND = [{"command_name": "RoleZero.ask_human", "args": {"question": ""}}] + JSON_REPAIR_PROMPT = """ ## json data {json_data} diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 0e7e04969..a78202378 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -21,6 +21,7 @@ from metagpt.prompts.di.role_zero import ( ASK_HUMAN_COMMAND, CMD_PROMPT, DETECT_LANGUAGE_PROMPT, + DUPULICATE_QUESTTION_FORMAT, END_COMMAND, JSON_REPAIR_PROMPT, QUICK_RESPONSE_SYSTEM_PROMPT, @@ -31,6 +32,7 @@ from metagpt.prompts.di.role_zero import ( REPORT_TO_HUMAN_PROMPT, ROLE_INSTRUCTION, SUMMARY_PROMPT, + SUMMARY_TROUBLE, SYSTEM_PROMPT, ) from metagpt.roles import Role @@ -120,7 +122,7 @@ class RoleZero(Role): "Plan.append_task": self.planner.plan.append_task, "Plan.reset_task": self.planner.plan.reset_task, "Plan.replace_task": self.planner.plan.replace_task, - "RoleZero.ask_human": self.ask_human, + "RoleZero.ask_human": self.request_and_analyze_human_response, "RoleZero.reply_to_human": self.reply_to_human, "SearchEnhancedQA.run": SearchEnhancedQA().run, } @@ -386,7 +388,7 @@ class RoleZero(Role): async def _check_duplicates(self, req: list[dict], command_rsp: str, check_window: int = 10): past_rsp = [mem.content for mem in self.rc.memory.get(check_window)] - if command_rsp in past_rsp: + if command_rsp in past_rsp and '"command_name": "end"' not in command_rsp: # Normal response with thought contents are highly unlikely to reproduce # If an identical response is detected, it is a bad response, mostly due to LLM repeating generated content # In this case, ask human for help and regenerate @@ -395,10 +397,15 @@ class RoleZero(Role): # Hard rule to ask human for help if past_rsp.count(command_rsp) >= 3: if '"command_name": "Plan.finish_current_task",' in command_rsp: - # Detect the deplicate of "Plan.finish_current_task" command, use command "end" to finish the task + # Detect the duplicate of the 'Plan.finish_current_task' command, and use the 'end' command to finish the task. logger.warning(f"Duplicate response detected: {command_rsp}") return END_COMMAND - return ASK_HUMAN_COMMAND + trouble_content = await self.llm.aask( + req + [UserMessage(content=SUMMARY_TROUBLE.format(language=self.respond_language))] + ) + ASK_HUMAN_COMMAND[0]["args"]["question"] = DUPULICATE_QUESTTION_FORMAT.format(trouble=trouble_content) + ask_human_command = "```json\n" + json.dumps(ASK_HUMAN_COMMAND, indent=4, ensure_ascii=False) + "\n```" + return ask_human_command # Try correction by self logger.warning(f"Duplicate response detected: {command_rsp}") regenerate_req = req + [UserMessage(content=REGENERATE_PROMPT)] @@ -557,6 +564,13 @@ class RoleZero(Role): return "Not in MGXEnv, command will not be executed." return await self.rc.env.ask_human(question, sent_from=self) + async def request_and_analyze_human_response(self, question: str) -> str: + """This fucntion will call ask_human and then analyze and decorate the human response.""" + human_response = await self.ask_human(question) + if "" in human_response: + human_response += f"\nUser ask you to stop the current task immediately.\nIn your next response, use end command to stop the current task.\nLike:\n{END_COMMAND}" + return human_response + async def reply_to_human(self, content: str) -> str: """Reply to human user with the content provided. Use this when you have a clear answer or solution to the user's question.""" # NOTE: Can be overwritten in remote setting From e343def83150992c0c965e4e8651329f3983af6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Fri, 27 Sep 2024 10:59:20 +0800 Subject: [PATCH 07/11] fix typo --- metagpt/prompts/di/role_zero.py | 16 +++++----------- metagpt/roles/di/engineer2.py | 2 +- metagpt/roles/di/role_zero.py | 8 ++++---- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index a703b1d41..e04155fe8 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -119,17 +119,11 @@ END_COMMAND = """ ``` """ -SUMMARY_TROUBLE = """ -You are repeating the same action. -Please summarize what is confusing or troubling you, and tell me directly. -Do not output any commands. -Your response must be in {language} and should be within 30 words. -""" -DUPULICATE_QUESTTION_FORMAT = """ -I meet the following trouble: -{trouble} -Could you provide me with some guidance? -If want to stop, please contain \"\" in your guidance. +SUMMARY_PROBLEM_WHEN_DUPLICATE = """Please directly tell me what is confusing or troubling you. Your response should be in {language} and within 30 words.""" +ASK_HUMAN_GUIDANCE_FORMAT = """ +I am facing the following problem: +{problem} +Could you please provide me with some guidance?If you want to stop, please include "" in your guidance. """ ASK_HUMAN_COMMAND = [{"command_name": "RoleZero.ask_human", "args": {"question": ""}}] diff --git a/metagpt/roles/di/engineer2.py b/metagpt/roles/di/engineer2.py index 2404949cb..044ff0afd 100644 --- a/metagpt/roles/di/engineer2.py +++ b/metagpt/roles/di/engineer2.py @@ -119,7 +119,7 @@ class Engineer2(RoleZero): Args: path (str): The absolute path of the file to be created. - file_description (optional, str): Further hints or notice other than the current task description, must be very concise and can be empty. Defaults to "". + file_description (optional, str): "Brief description and important notes of the file content, must be very concise and can be empty. Defaults to "". """ plan_status, _ = self._get_plan_status() prompt = WRITE_CODE_PROMPT.format( diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index a78202378..9fca8f468 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -19,9 +19,9 @@ from metagpt.exp_pool.serializers import RoleZeroSerializer from metagpt.logs import logger from metagpt.prompts.di.role_zero import ( ASK_HUMAN_COMMAND, + ASK_HUMAN_GUIDANCE_FORMAT, CMD_PROMPT, DETECT_LANGUAGE_PROMPT, - DUPULICATE_QUESTTION_FORMAT, END_COMMAND, JSON_REPAIR_PROMPT, QUICK_RESPONSE_SYSTEM_PROMPT, @@ -31,8 +31,8 @@ from metagpt.prompts.di.role_zero import ( REGENERATE_PROMPT, REPORT_TO_HUMAN_PROMPT, ROLE_INSTRUCTION, + SUMMARY_PROBLEM_WHEN_DUPLICATE, SUMMARY_PROMPT, - SUMMARY_TROUBLE, SYSTEM_PROMPT, ) from metagpt.roles import Role @@ -401,9 +401,9 @@ class RoleZero(Role): logger.warning(f"Duplicate response detected: {command_rsp}") return END_COMMAND trouble_content = await self.llm.aask( - req + [UserMessage(content=SUMMARY_TROUBLE.format(language=self.respond_language))] + req + [UserMessage(content=SUMMARY_PROBLEM_WHEN_DUPLICATE.format(language=self.respond_language))] ) - ASK_HUMAN_COMMAND[0]["args"]["question"] = DUPULICATE_QUESTTION_FORMAT.format(trouble=trouble_content) + ASK_HUMAN_COMMAND[0]["args"]["question"] = ASK_HUMAN_GUIDANCE_FORMAT.format(trouble=trouble_content) ask_human_command = "```json\n" + json.dumps(ASK_HUMAN_COMMAND, indent=4, ensure_ascii=False) + "\n```" return ask_human_command # Try correction by self From 5cda228f5b77ac246744565911b8523321ba7196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Fri, 27 Sep 2024 16:18:10 +0800 Subject: [PATCH 08/11] fix typo --- metagpt/roles/di/role_zero.py | 4 +++- metagpt/tools/libs/editor.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 9fca8f468..b8aedcdd6 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -568,7 +568,9 @@ class RoleZero(Role): """This fucntion will call ask_human and then analyze and decorate the human response.""" human_response = await self.ask_human(question) if "" in human_response: - human_response += f"\nUser ask you to stop the current task immediately.\nIn your next response, use end command to stop the current task.\nLike:\n{END_COMMAND}" + human_response += "\nCommand end executed:" + human_response += await self._end() + return human_response return human_response async def reply_to_human(self, content: str) -> str: diff --git a/metagpt/tools/libs/editor.py b/metagpt/tools/libs/editor.py index ca8cd9ccf..5391ff48a 100644 --- a/metagpt/tools/libs/editor.py +++ b/metagpt/tools/libs/editor.py @@ -773,8 +773,8 @@ class Editor(BaseModel): lines = content.splitlines(True) total_lines = len(lines) check_list = [ - ("first_replaced", first_replaced_line_number, first_replaced_line_content), - ("last_replaced", last_replaced_line_number, last_replaced_line_content), + ("first", first_replaced_line_number, first_replaced_line_content), + ("last", last_replaced_line_number, last_replaced_line_content), ] for position, line_number, line_content in check_list: if lines[line_number - 1].rstrip() != line_content: From 1e004d72cbe78f978fef40c6c72f093b4b0efde7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Fri, 27 Sep 2024 16:25:28 +0800 Subject: [PATCH 09/11] fix typo --- metagpt/roles/di/role_zero.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index b8aedcdd6..8d6fbf7b2 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -400,10 +400,10 @@ class RoleZero(Role): # Detect the duplicate of the 'Plan.finish_current_task' command, and use the 'end' command to finish the task. logger.warning(f"Duplicate response detected: {command_rsp}") return END_COMMAND - trouble_content = await self.llm.aask( + problem = await self.llm.aask( req + [UserMessage(content=SUMMARY_PROBLEM_WHEN_DUPLICATE.format(language=self.respond_language))] ) - ASK_HUMAN_COMMAND[0]["args"]["question"] = ASK_HUMAN_GUIDANCE_FORMAT.format(trouble=trouble_content) + ASK_HUMAN_COMMAND[0]["args"]["question"] = ASK_HUMAN_GUIDANCE_FORMAT.format(problem=problem) ask_human_command = "```json\n" + json.dumps(ASK_HUMAN_COMMAND, indent=4, ensure_ascii=False) + "\n```" return ask_human_command # Try correction by self From 1a937e91526fbbf60b9ecbd793309f51f7aa0614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Fri, 27 Sep 2024 21:40:38 +0800 Subject: [PATCH 10/11] add ask_human to special command list --- metagpt/prompts/di/role_zero.py | 7 ++++++- metagpt/roles/di/engineer2.py | 11 ++++++----- metagpt/roles/di/role_zero.py | 26 ++++++++++++------------- tests/metagpt/tools/libs/test_editor.py | 12 ++++++------ 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index e04155fe8..aea00934c 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -119,7 +119,12 @@ END_COMMAND = """ ``` """ -SUMMARY_PROBLEM_WHEN_DUPLICATE = """Please directly tell me what is confusing or troubling you. Your response should be in {language} and within 30 words.""" +SUMMARY_PROBLEM_WHEN_DUPLICATE = """ +You has meet a problem and cause duplicate command. +Please directly tell me what is confusing or troubling you. +Do Not output any command. +Ouput you problem in {language} and within 30 words. +""" ASK_HUMAN_GUIDANCE_FORMAT = """ I am facing the following problem: {problem} diff --git a/metagpt/roles/di/engineer2.py b/metagpt/roles/di/engineer2.py index 044ff0afd..6352a217f 100644 --- a/metagpt/roles/di/engineer2.py +++ b/metagpt/roles/di/engineer2.py @@ -107,11 +107,7 @@ class Engineer2(RoleZero): async def _run_special_command(self, cmd) -> str: """command requiring special check or parsing.""" # finish current task before end. - command_output = "" - if cmd["command_name"] == "end" and not self.planner.plan.is_plan_finished(): - self.planner.plan.finish_all_tasks() - command_output += "All tasks are finished.\n" - command_output += await super()._run_special_command(cmd) + command_output = await super()._run_special_command(cmd) return command_output async def write_new_code(self, path: str, file_description: str = "") -> str: @@ -154,3 +150,8 @@ class Engineer2(RoleZero): else: command_output = await self.terminal.run_command(cmd) return command_output + + async def _end(self): + if not self.planner.plan.is_plan_finished(): + self.planner.plan.finish_all_tasks() + return await super()._end() diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 8d6fbf7b2..0aadaac81 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -75,7 +75,7 @@ class RoleZero(Role): tools: list[str] = [] # Use special symbol [""] to indicate use of all registered tools tool_recommender: Optional[ToolRecommender] = None tool_execution_map: Annotated[dict[str, Callable], Field(exclude=True)] = {} - special_tool_commands: list[str] = ["Plan.finish_current_task", "end", "Bash.run"] + special_tool_commands: list[str] = ["Plan.finish_current_task", "end", "Bash.run", "RoleZero.ask_human"] # List of exclusive tool commands. # If multiple instances of these commands appear, only the first occurrence will be retained. exclusive_tool_commands: list[str] = [ @@ -122,7 +122,7 @@ class RoleZero(Role): "Plan.append_task": self.planner.plan.append_task, "Plan.reset_task": self.planner.plan.reset_task, "Plan.replace_task": self.planner.plan.replace_task, - "RoleZero.ask_human": self.request_and_analyze_human_response, + "RoleZero.ask_human": self.ask_human, "RoleZero.reply_to_human": self.reply_to_human, "SearchEnhancedQA.run": SearchEnhancedQA().run, } @@ -229,7 +229,6 @@ class RoleZero(Role): await reporter.async_report({"type": "react"}) self.command_rsp = await self.llm_cached_aask(req=req, system_msgs=[system_prompt], state_data=state_data) self.command_rsp = await self._check_duplicates(req, self.command_rsp) - return True @exp_cache(context_builder=RoleZeroContextBuilder(), serializer=RoleZeroSerializer()) @@ -403,7 +402,7 @@ class RoleZero(Role): problem = await self.llm.aask( req + [UserMessage(content=SUMMARY_PROBLEM_WHEN_DUPLICATE.format(language=self.respond_language))] ) - ASK_HUMAN_COMMAND[0]["args"]["question"] = ASK_HUMAN_GUIDANCE_FORMAT.format(problem=problem) + ASK_HUMAN_COMMAND[0]["args"]["question"] = ASK_HUMAN_GUIDANCE_FORMAT.format(problem=problem).strip() ask_human_command = "```json\n" + json.dumps(ASK_HUMAN_COMMAND, indent=4, ensure_ascii=False) + "\n```" return ask_human_command # Try correction by self @@ -513,7 +512,15 @@ class RoleZero(Role): elif cmd["command_name"] == "end": command_output = await self._end() - + elif cmd["command_name"] == "RoleZero.ask_human": + human_response = await self.ask_human(**cmd["args"]) + if "" in human_response: + human_response += "The user has asked me to stop because I have encountered a problem." + self.rc.memory.add(UserMessage(content=human_response, cause_by=RunCommand)) + end_output = "\nCommand end executed:" + end_output += await self._end() + return end_output + return human_response # output from bash.run may be empty, add decorations to the output to ensure visibility. elif cmd["command_name"] == "Bash.run": tool_obj = self.tool_execution_map[cmd["command_name"]] @@ -564,15 +571,6 @@ class RoleZero(Role): return "Not in MGXEnv, command will not be executed." return await self.rc.env.ask_human(question, sent_from=self) - async def request_and_analyze_human_response(self, question: str) -> str: - """This fucntion will call ask_human and then analyze and decorate the human response.""" - human_response = await self.ask_human(question) - if "" in human_response: - human_response += "\nCommand end executed:" - human_response += await self._end() - return human_response - return human_response - async def reply_to_human(self, content: str) -> str: """Reply to human user with the content provided. Use this when you have a clear answer or solution to the user's question.""" # NOTE: Can be overwritten in remote setting diff --git a/tests/metagpt/tools/libs/test_editor.py b/tests/metagpt/tools/libs/test_editor.py index b56f7bf0e..4181383c8 100644 --- a/tests/metagpt/tools/libs/test_editor.py +++ b/tests/metagpt/tools/libs/test_editor.py @@ -550,9 +550,9 @@ def test_edit_file_by_replace(temp_py_file): MISMATCH_ERROR = """ -Error: The `first_replaced_replaced_line_number` does not match the `first_replaced_replaced_line_content`. Please correct the parameters. -The `first_replaced_replaced_line_number` is 5 and the corresponding content is " b = 2". -But the `first_replaced_replaced_line_content ` is "". +Error: The `first_replaced_line_number` does not match the `first_replaced_line_content`. Please correct the parameters. +The `first_replaced_line_number` is 5 and the corresponding content is " b = 2". +But the `first_replaced_line_content ` is "". The content around the specified line is: The 002 line is "def test_function_for_fm():" The 003 line is " "some docstring"" @@ -561,9 +561,9 @@ The 005 line is " b = 2" The 006 line is " c = 3" The 007 line is " # this is the 7th line" Pay attention to the new content. Ensure that it aligns with the new parameters. -Error: The `last_replaced_replaced_line_number` does not match the `last_replaced_replaced_line_content`. Please correct the parameters. -The `last_replaced_replaced_line_number` is 5 and the corresponding content is " b = 2". -But the `last_replaced_replaced_line_content ` is "". +Error: The `last_replaced_line_number` does not match the `last_replaced_line_content`. Please correct the parameters. +The `last_replaced_line_number` is 5 and the corresponding content is " b = 2". +But the `last_replaced_line_content ` is "". The content around the specified line is: The 002 line is "def test_function_for_fm():" The 003 line is " "some docstring"" From cff6f66bd88c3689aaf958ba13fd0b837a11cd32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Fri, 27 Sep 2024 21:47:33 +0800 Subject: [PATCH 11/11] fix typo --- metagpt/prompts/di/role_zero.py | 7 +------ metagpt/tools/libs/editor.py | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index aea00934c..2ccb2bed0 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -119,12 +119,7 @@ END_COMMAND = """ ``` """ -SUMMARY_PROBLEM_WHEN_DUPLICATE = """ -You has meet a problem and cause duplicate command. -Please directly tell me what is confusing or troubling you. -Do Not output any command. -Ouput you problem in {language} and within 30 words. -""" +SUMMARY_PROBLEM_WHEN_DUPLICATE = """You has meet a problem and cause duplicate command.Please directly tell me what is confusing or troubling you. Do Not output any command.Ouput you problem in {language} and within 30 words.""" ASK_HUMAN_GUIDANCE_FORMAT = """ I am facing the following problem: {problem} diff --git a/metagpt/tools/libs/editor.py b/metagpt/tools/libs/editor.py index 5391ff48a..5009821f8 100644 --- a/metagpt/tools/libs/editor.py +++ b/metagpt/tools/libs/editor.py @@ -937,7 +937,7 @@ class Editor(BaseModel): Args: file_name: (str): The name of the file to edit. - line_number (int): The line number (starting from 1) to insert the content after.the insert content will be add between the line of line_number-1 and line_number + line_number (int): The line number (starting from 1) to insert the content after. The insert content will be add between the line of line_number-1 and line_number insert_content (str): The content to insert betweed the previous_line_content and current_line_content.The insert_content must be a complete block of code at. NOTE: