From 91affc2a60a4ea89349b1859275eca965e22a508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Thu, 15 Aug 2024 20:29:34 +0800 Subject: [PATCH 1/6] format RoreZero Prompt to fit into exp_pool --- metagpt/exp_pool/context_builders/role_zero.py | 9 ++++++++- metagpt/prompts/di/role_zero.py | 5 ++++- metagpt/roles/di/role_zero.py | 18 ++++++++---------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/metagpt/exp_pool/context_builders/role_zero.py b/metagpt/exp_pool/context_builders/role_zero.py index 924bd56aa..44685411c 100644 --- a/metagpt/exp_pool/context_builders/role_zero.py +++ b/metagpt/exp_pool/context_builders/role_zero.py @@ -31,7 +31,14 @@ class RoleZeroContextBuilder(BaseContextBuilder): return req_copy def replace_example_content(self, text: str, new_example_content: str) -> str: - return self.replace_content_between_markers(text, "# Example", "# Instruction", new_example_content) + return self.replace_content_of_example_tag(text, new_example_content) + # return self.replace_content_between_markers(text, "# Example", "# Instruction", new_example_content) + + @staticmethod + def replace_content_of_example_tag(self, text: str, new_example_content: str) -> str: + pattern = "# Past Experience\n" + replaced_text = text.replace(pattern, "# Example\n" + new_example_content) + return replaced_text @staticmethod def replace_content_between_markers(text: str, start_marker: str, end_marker: str, new_content: str) -> str: diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index cc78d809c..24ca29af5 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -46,6 +46,9 @@ Special Command: Use {{"command_name": "end"}} to do nothing or indicate complet """ CMD_PROMPT = """ +# Past Experience + + {current_state} # Current Plan @@ -206,4 +209,4 @@ Response Category: AMBIGUOUS. QUICK_RESPONSE_SYSTEM_PROMPT = """ {role_info} However, you MUST respond to the user message by yourself directly, DON'T ask your team members. -""" \ No newline at end of file +""" diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 3ceee8b43..32508910c 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -21,10 +21,10 @@ from metagpt.prompts.di.role_zero import ( ASK_HUMAN_COMMAND, CMD_PROMPT, JSON_REPAIR_PROMPT, - QUICK_THINK_PROMPT, - QUICK_THINK_EXAMPLES, - QUICK_THINK_SYSTEM_PROMPT, QUICK_RESPONSE_SYSTEM_PROMPT, + QUICK_THINK_EXAMPLES, + QUICK_THINK_PROMPT, + QUICK_THINK_SYSTEM_PROMPT, REGENERATE_PROMPT, ROLE_INSTRUCTION, SYSTEM_PROMPT, @@ -269,7 +269,7 @@ class RoleZero(Role): rsp = await self._act() actions_taken += 1 return rsp # return output from the last action - + def format_quick_system_prompt(self) -> str: """Format the system prompt for quick thinking.""" return QUICK_THINK_SYSTEM_PROMPT.format(examples=QUICK_THINK_EXAMPLES, role_info=self._get_prefix()) @@ -289,7 +289,10 @@ class RoleZero(Role): if "QUICK" in intent_result or "AMBIGUOUS" in intent_result: # llm call with the original context async with ThoughtReporter(enable_llm_stream=True) as reporter: await reporter.async_report({"type": "quick"}) - answer = await self.llm.aask(self.llm.format_msg(memory), system_msgs=[QUICK_RESPONSE_SYSTEM_PROMPT.format(role_info=self._get_prefix())]) + answer = await self.llm.aask( + self.llm.format_msg(memory), + system_msgs=[QUICK_RESPONSE_SYSTEM_PROMPT.format(role_info=self._get_prefix())], + ) elif "SEARCH" in intent_result: query = "\n".join(str(msg) for msg in memory) answer = await SearchEnhancedQA().run(query) @@ -427,11 +430,6 @@ class RoleZero(Role): def _get_plan_status(self) -> Tuple[str, str]: plan_status = self.planner.plan.model_dump(include=["goal", "tasks"]) - for task in plan_status["tasks"]: - task.pop("code") - task.pop("result") - task.pop("is_success") - # print(plan_status) current_task = ( self.planner.plan.current_task.model_dump(exclude=["code", "result", "is_success"]) if self.planner.plan.current_task From 1de01c1f2402d624a8dc3d3ca014c12cc7812eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Thu, 15 Aug 2024 20:38:32 +0800 Subject: [PATCH 2/6] fix format issues --- metagpt/exp_pool/context_builders/role_zero.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/exp_pool/context_builders/role_zero.py b/metagpt/exp_pool/context_builders/role_zero.py index 44685411c..3145ca998 100644 --- a/metagpt/exp_pool/context_builders/role_zero.py +++ b/metagpt/exp_pool/context_builders/role_zero.py @@ -37,7 +37,7 @@ class RoleZeroContextBuilder(BaseContextBuilder): @staticmethod def replace_content_of_example_tag(self, text: str, new_example_content: str) -> str: pattern = "# Past Experience\n" - replaced_text = text.replace(pattern, "# Example\n" + new_example_content) + replaced_text = text.replace(pattern, "# Past Example\n" + new_example_content) return replaced_text @staticmethod From 0a1c965933e5d012ab252992a469d977ac65a952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Fri, 16 Aug 2024 10:47:59 +0800 Subject: [PATCH 3/6] remove replace_content_between_markers function of RoleZeroContextBuilder and update tests --- .../exp_pool/context_builders/role_zero.py | 28 ++----------------- .../test_rolezero_context_builder.py | 18 ++++++------ 2 files changed, 10 insertions(+), 36 deletions(-) diff --git a/metagpt/exp_pool/context_builders/role_zero.py b/metagpt/exp_pool/context_builders/role_zero.py index 3145ca998..4a9042765 100644 --- a/metagpt/exp_pool/context_builders/role_zero.py +++ b/metagpt/exp_pool/context_builders/role_zero.py @@ -1,7 +1,6 @@ """RoleZero context builder.""" import copy -import re from typing import Any from metagpt.exp_pool.context_builders.base import BaseContextBuilder @@ -32,32 +31,9 @@ class RoleZeroContextBuilder(BaseContextBuilder): def replace_example_content(self, text: str, new_example_content: str) -> str: return self.replace_content_of_example_tag(text, new_example_content) - # return self.replace_content_between_markers(text, "# Example", "# Instruction", new_example_content) @staticmethod - def replace_content_of_example_tag(self, text: str, new_example_content: str) -> str: + def replace_content_of_example_tag(text: str, new_example_content: str) -> str: pattern = "# Past Experience\n" - replaced_text = text.replace(pattern, "# Past Example\n" + new_example_content) - return replaced_text - - @staticmethod - def replace_content_between_markers(text: str, start_marker: str, end_marker: str, new_content: str) -> str: - """Replace the content between `start_marker` and `end_marker` in the text with `new_content`. - - Args: - text (str): The original text. - new_content (str): The new content to replace the old content. - start_marker (str): The marker indicating the start of the content to be replaced, such as '# Example'. - end_marker (str): The marker indicating the end of the content to be replaced, such as '# Instruction'. - - Returns: - str: The text with the content replaced. - """ - - pattern = re.compile(f"({start_marker}\n)(.*?)(\n{end_marker})", re.DOTALL) - - def replacement(match): - return f"{match.group(1)}{new_content}\n{match.group(3)}" - - replaced_text = pattern.sub(replacement, text) + replaced_text = text.replace(pattern, "# Past Experience\n" + new_example_content) return replaced_text diff --git a/tests/metagpt/exp_pool/test_context_builders/test_rolezero_context_builder.py b/tests/metagpt/exp_pool/test_context_builders/test_rolezero_context_builder.py index b7182602d..795e8c26b 100644 --- a/tests/metagpt/exp_pool/test_context_builders/test_rolezero_context_builder.py +++ b/tests/metagpt/exp_pool/test_context_builders/test_rolezero_context_builder.py @@ -30,22 +30,20 @@ class TestRoleZeroContextBuilder: assert result == [{"content": "Updated content"}] def test_replace_example_content(self, context_builder, mocker): - mocker.patch.object(RoleZeroContextBuilder, "replace_content_between_markers", return_value="Replaced content") + mocker.patch.object(RoleZeroContextBuilder, "replace_content_of_example_tag", return_value="Replaced content") result = context_builder.replace_example_content("Original text", "New example content") assert result == "Replaced content" - context_builder.replace_content_between_markers.assert_called_once_with( - "Original text", "# Example", "# Instruction", "New example content" - ) + context_builder.replace_content_of_example_tag.assert_called_once_with("Original text", "New example content") - def test_replace_content_between_markers(self): - text = "Start\n# Example\nOld content\n# Instruction\nEnd" + def test_replace_content_of_example_tag(self): + text = "Start\n# Past Experience\n\n\n# Instruction\nEnd" new_content = "New content" - result = RoleZeroContextBuilder.replace_content_between_markers(text, "# Example", "# Instruction", new_content) - expected = "Start\n# Example\nNew content\n\n# Instruction\nEnd" + result = RoleZeroContextBuilder.replace_content_of_example_tag(text, new_content) + expected = "Start\n# Past Experience\nNew content\n\n# Instruction\nEnd" assert result == expected - def test_replace_content_between_markers_no_match(self): + def test_replace_content_of_example_tag_no_match(self): text = "Start\nNo markers\nEnd" new_content = "New content" - result = RoleZeroContextBuilder.replace_content_between_markers(text, "# Example", "# Instruction", new_content) + result = RoleZeroContextBuilder.replace_content_of_example_tag(text, new_content) assert result == text From b27ba8476f2b81fffcba272f82ce1f92f2804050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Fri, 16 Aug 2024 11:22:02 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metagpt/exp_pool/context_builders/role_zero.py | 8 ++++---- .../test_rolezero_context_builder.py | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/metagpt/exp_pool/context_builders/role_zero.py b/metagpt/exp_pool/context_builders/role_zero.py index 4a9042765..b25308153 100644 --- a/metagpt/exp_pool/context_builders/role_zero.py +++ b/metagpt/exp_pool/context_builders/role_zero.py @@ -30,10 +30,10 @@ class RoleZeroContextBuilder(BaseContextBuilder): return req_copy def replace_example_content(self, text: str, new_example_content: str) -> str: - return self.replace_content_of_example_tag(text, new_example_content) + return self.fill_experience(text, new_example_content) @staticmethod - def replace_content_of_example_tag(text: str, new_example_content: str) -> str: - pattern = "# Past Experience\n" - replaced_text = text.replace(pattern, "# Past Experience\n" + new_example_content) + def fill_experience(text: str, new_example_content: str) -> str: + pattern = "" + replaced_text = text.replace(pattern, new_example_content) return replaced_text diff --git a/tests/metagpt/exp_pool/test_context_builders/test_rolezero_context_builder.py b/tests/metagpt/exp_pool/test_context_builders/test_rolezero_context_builder.py index 795e8c26b..ee56ea094 100644 --- a/tests/metagpt/exp_pool/test_context_builders/test_rolezero_context_builder.py +++ b/tests/metagpt/exp_pool/test_context_builders/test_rolezero_context_builder.py @@ -30,20 +30,20 @@ class TestRoleZeroContextBuilder: assert result == [{"content": "Updated content"}] def test_replace_example_content(self, context_builder, mocker): - mocker.patch.object(RoleZeroContextBuilder, "replace_content_of_example_tag", return_value="Replaced content") + mocker.patch.object(RoleZeroContextBuilder, "fill_experience", return_value="Replaced content") result = context_builder.replace_example_content("Original text", "New example content") assert result == "Replaced content" - context_builder.replace_content_of_example_tag.assert_called_once_with("Original text", "New example content") + context_builder.fill_experience.assert_called_once_with("Original text", "New example content") - def test_replace_content_of_example_tag(self): + def test_fill_experience(self): text = "Start\n# Past Experience\n\n\n# Instruction\nEnd" new_content = "New content" - result = RoleZeroContextBuilder.replace_content_of_example_tag(text, new_content) + result = RoleZeroContextBuilder.fill_experience(text, new_content) expected = "Start\n# Past Experience\nNew content\n\n# Instruction\nEnd" assert result == expected - def test_replace_content_of_example_tag_no_match(self): + def test_fill_experience_no_match(self): text = "Start\nNo markers\nEnd" new_content = "New content" - result = RoleZeroContextBuilder.replace_content_of_example_tag(text, new_content) + result = RoleZeroContextBuilder.fill_experience(text, new_content) assert result == text From 70fa7b54ce3125298b038560bd98c342b0963532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Fri, 16 Aug 2024 15:01:23 +0800 Subject: [PATCH 5/6] Add EXPERIENCE_MASK to replace the experience placeholder --- metagpt/const.py | 2 ++ metagpt/exp_pool/context_builders/role_zero.py | 4 ++-- metagpt/prompts/di/role_zero.py | 12 +++++++++--- .../test_rolezero_context_builder.py | 3 ++- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/metagpt/const.py b/metagpt/const.py index 0dfe9e5ec..a83416cdb 100644 --- a/metagpt/const.py +++ b/metagpt/const.py @@ -83,6 +83,8 @@ MESSAGE_ROUTE_TO_ALL = "" MESSAGE_ROUTE_TO_NONE = "" MESSAGE_ROUTE_TO_SELF = "" # Add this tag to replace `ActionOutput` +EXPERIENCE_MASK = "" + REQUIREMENT_FILENAME = "requirement.txt" BUGFIX_FILENAME = "bugfix.txt" PACKAGE_REQUIREMENTS_FILENAME = "requirements.txt" diff --git a/metagpt/exp_pool/context_builders/role_zero.py b/metagpt/exp_pool/context_builders/role_zero.py index b25308153..cbda72fc5 100644 --- a/metagpt/exp_pool/context_builders/role_zero.py +++ b/metagpt/exp_pool/context_builders/role_zero.py @@ -3,6 +3,7 @@ import copy from typing import Any +from metagpt.const import EXPERIENCE_MASK from metagpt.exp_pool.context_builders.base import BaseContextBuilder @@ -34,6 +35,5 @@ class RoleZeroContextBuilder(BaseContextBuilder): @staticmethod def fill_experience(text: str, new_example_content: str) -> str: - pattern = "" - replaced_text = text.replace(pattern, new_example_content) + replaced_text = text.replace(EXPERIENCE_MASK, new_example_content) return replaced_text diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index 4c407fb97..3c47ebf5f 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -1,3 +1,5 @@ +from metagpt.const import EXPERIENCE_MASK + ROLE_INSTRUCTION = """ Based on the context, write a plan or modify an existing plan to achieve the goal. A plan consists of one to 3 tasks. If plan is created, you should track the progress and update the plan accordingly, such as Plan.finish_current_task, Plan.append_task, Plan.reset_task, Plan.replace_task, etc. @@ -10,7 +12,6 @@ Note: 4. Don't forget to append task first when all existing tasks are finished and new tasks are required. 5. Avoid repeating tasks you have already completed. And end loop when all requirements are met. """ -# To ensure compatibility with hard-coded experience, do not add any other content between "# Example" and "# Instruction". ########################## ignore guidance @@ -47,10 +48,14 @@ Special Command: Use {{"command_name": "end"}} to do nothing or indicate complet {instruction} """ -CMD_PROMPT = """ +CMD_EXPERIENCE_MASK = f""" # Past Experience - +{EXPERIENCE_MASK} +""" +CMD_PROMPT = ( + CMD_EXPERIENCE_MASK + + """ {current_state} # Current Plan @@ -82,6 +87,7 @@ Output should adhere to the following format. ``` Notice: your output JSON data section must start with **```json [** """ +) THOUGHT_GUIDANCE = """ First, describe the actions you have taken recently. diff --git a/tests/metagpt/exp_pool/test_context_builders/test_rolezero_context_builder.py b/tests/metagpt/exp_pool/test_context_builders/test_rolezero_context_builder.py index ee56ea094..82a3622a5 100644 --- a/tests/metagpt/exp_pool/test_context_builders/test_rolezero_context_builder.py +++ b/tests/metagpt/exp_pool/test_context_builders/test_rolezero_context_builder.py @@ -1,5 +1,6 @@ import pytest +from metagpt.const import EXPERIENCE_MASK from metagpt.exp_pool.context_builders.base import BaseContextBuilder from metagpt.exp_pool.context_builders.role_zero import RoleZeroContextBuilder @@ -36,7 +37,7 @@ class TestRoleZeroContextBuilder: context_builder.fill_experience.assert_called_once_with("Original text", "New example content") def test_fill_experience(self): - text = "Start\n# Past Experience\n\n\n# Instruction\nEnd" + text = f"Start\n# Past Experience\n{EXPERIENCE_MASK}\n\n# Instruction\nEnd" new_content = "New content" result = RoleZeroContextBuilder.fill_experience(text, new_content) expected = "Start\n# Past Experience\nNew content\n\n# Instruction\nEnd" From d3ee0ddf77892797305a615edb2c1cce5142a6e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Fri, 16 Aug 2024 15:52:49 +0800 Subject: [PATCH 6/6] Adjust the position of constants --- metagpt/const.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/metagpt/const.py b/metagpt/const.py index a83416cdb..c53e8494a 100644 --- a/metagpt/const.py +++ b/metagpt/const.py @@ -83,7 +83,6 @@ MESSAGE_ROUTE_TO_ALL = "" MESSAGE_ROUTE_TO_NONE = "" MESSAGE_ROUTE_TO_SELF = "" # Add this tag to replace `ActionOutput` -EXPERIENCE_MASK = "" REQUIREMENT_FILENAME = "requirement.txt" BUGFIX_FILENAME = "bugfix.txt" @@ -155,3 +154,6 @@ IMAGES = "images" # SWE agent SWE_SETUP_PATH = get_metagpt_package_root() / "metagpt/tools/swe_agent_commands/setup_default.sh" + +# experience pool +EXPERIENCE_MASK = ""