From 8b9e992b566bf2bab8b582e219200ec5e8a46a84 Mon Sep 17 00:00:00 2001 From: seehi <6580@pm.me> Date: Tue, 16 Jul 2024 16:39:43 +0800 Subject: [PATCH] update serializers --- metagpt/exp_pool/decorator.py | 2 +- metagpt/exp_pool/serializers/base.py | 4 ++-- metagpt/exp_pool/serializers/role_zero.py | 8 +++++--- metagpt/exp_pool/serializers/simple.py | 4 ++-- metagpt/prompts/di/role_zero.py | 19 ++++++++--------- metagpt/roles/di/role_zero.py | 25 +++++++++++------------ 6 files changed, 30 insertions(+), 32 deletions(-) diff --git a/metagpt/exp_pool/decorator.py b/metagpt/exp_pool/decorator.py index 566127f59..7a2f926c5 100644 --- a/metagpt/exp_pool/decorator.py +++ b/metagpt/exp_pool/decorator.py @@ -121,7 +121,7 @@ class ExpCacheHandler(BaseModel): self.serializer = self.serializer or SimpleSerializer() self.tag = self.tag or self._generate_tag() - self._req = self.serializer.serialize_req(self.kwargs["req"]) + self._req = self.serializer.serialize_req(**self.kwargs) return self diff --git a/metagpt/exp_pool/serializers/base.py b/metagpt/exp_pool/serializers/base.py index 9d00a05b2..c09488e12 100644 --- a/metagpt/exp_pool/serializers/base.py +++ b/metagpt/exp_pool/serializers/base.py @@ -10,10 +10,10 @@ class BaseSerializer(BaseModel, ABC): model_config = ConfigDict(arbitrary_types_allowed=True) @abstractmethod - def serialize_req(self, req: Any) -> str: + def serialize_req(self, **kwargs) -> str: """Serializes the request for storage. - Do not modify req. If modification is necessary, use copy.deepcopy to create a copy first. + Do not modify kwargs. If modification is necessary, use copy.deepcopy to create a copy first. Note that copy.deepcopy may raise errors, such as TypeError: cannot pickle '_thread.RLock' object. """ diff --git a/metagpt/exp_pool/serializers/role_zero.py b/metagpt/exp_pool/serializers/role_zero.py index 967e53e41..89dd73f39 100644 --- a/metagpt/exp_pool/serializers/role_zero.py +++ b/metagpt/exp_pool/serializers/role_zero.py @@ -7,7 +7,7 @@ from metagpt.exp_pool.serializers.simple import SimpleSerializer class RoleZeroSerializer(SimpleSerializer): - def serialize_req(self, req: list[dict]) -> str: + def serialize_req(self, **kwargs) -> str: """Serialize the request for database storage, ensuring it is a string. Only extracts the necessary content from `req` because `req` may be very lengthy and could cause embedding errors. @@ -18,18 +18,20 @@ class RoleZeroSerializer(SimpleSerializer): {"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}, {"role": "user", "content": "context"}, - {"role": "user", "content": "context exp part"}, ] Returns: str: The serialized request as a JSON string. """ + req = kwargs.get("req", []) if not req: return "" filtered_req = self._filter_req(req) - filtered_req.append(req[-1]) + + if state_data := kwargs.get("state_data"): + filtered_req.append({"role": "user", "content": state_data}) return json.dumps(filtered_req) diff --git a/metagpt/exp_pool/serializers/simple.py b/metagpt/exp_pool/serializers/simple.py index 32fe29c9f..ebd06e0e0 100644 --- a/metagpt/exp_pool/serializers/simple.py +++ b/metagpt/exp_pool/serializers/simple.py @@ -6,10 +6,10 @@ from metagpt.exp_pool.serializers.base import BaseSerializer class SimpleSerializer(BaseSerializer): - def serialize_req(self, req: Any) -> str: + def serialize_req(self, **kwargs) -> str: """Just use `str` to convert the request object into a string.""" - return str(req) + return str(kwargs.get("req", "")) def serialize_resp(self, resp: Any) -> str: """Just use `str` to convert the response object into a string.""" diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index f1de5bf1b..41b9e023e 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -8,16 +8,6 @@ Note: 2. Carefully review your progress at the current task, if your actions so far has not fulfilled the task instruction, you should continue with current task. Otherwise, finish current task by Plan.finish_current_task explicitly. 3. Each time you finish a task, use RoleZero.reply_to_human to report your progress. """ -CMD_PROMPT_EXP_PART = """ -# Current Plan -{plan_status} - -# Current Task -{current_task} - -# Instruction -{instruction} -""" # To ensure compatibility with hard-coded experience, do not add any other content between "# Example" and "# Available Commands". CMD_PROMPT = """ # Data Structure @@ -38,7 +28,14 @@ Special Command: Use {{"command_name": "end"}} to do nothing or indicate complet # Available Task Types {task_type_desc} -{cmd_prompt_exp_part} +# Current Plan +{plan_status} + +# Current Task +{current_task} + +# Instruction +{instruction} Pay close attention to the Example provided, you can reuse the example for your current situation if it fits. 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. diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 8411cf24b..ce305707e 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -16,7 +16,6 @@ from metagpt.exp_pool.serializers import RoleZeroSerializer from metagpt.logs import logger from metagpt.prompts.di.role_zero import ( CMD_PROMPT, - CMD_PROMPT_EXP_PART, JSON_REPAIR_PROMPT, QUICK_THINK_PROMPT, ROLE_INSTRUCTION, @@ -147,39 +146,39 @@ class RoleZero(Role): tool_info = json.dumps({tool.name: tool.schemas for tool in tools}) ### Make Decision Dynamically ### - cmd_prompt_exp_part = CMD_PROMPT_EXP_PART.format( - plan_status=plan_status, - current_task=current_task, - instruction=self.instruction.strip(), - ) + instruction = self.instruction.strip() prompt = self.cmd_prompt.format( example=example, available_commands=tool_info, task_type_desc=self.task_type_desc, - cmd_prompt_exp_part=cmd_prompt_exp_part, + plan_status=plan_status, + current_task=current_task, + instruction=instruction, ) memory = self.rc.memory.get(self.memory_k) memory = await self.parse_browser_actions(memory) - req = self.llm.format_msg(memory + [UserMessage(content=prompt), UserMessage(content=cmd_prompt_exp_part)]) + req = self.llm.format_msg(memory + [UserMessage(content=prompt)]) async with ThoughtReporter(enable_llm_stream=True) as reporter: await reporter.async_report({"type": "react"}) - self.command_rsp = await self.llm_cached_aask(req=req, system_msgs=self.system_msg) + state_data = dict( + plan_status=plan_status, + current_task=current_task, + instruction=instruction, + ) + self.command_rsp = await self.llm_cached_aask(req=req, system_msgs=self.system_msg, state_data=state_data) self.rc.memory.add(AIMessage(content=self.command_rsp)) return True @exp_cache(context_builder=RoleZeroContextBuilder(), serializer=RoleZeroSerializer()) - async def llm_cached_aask(self, *, req: list[dict], system_msgs: list[str]) -> str: + async def llm_cached_aask(self, *, req: list[dict], system_msgs: list[str], **kwargs) -> str: """Use `exp_cache` to automatically manage experiences. The `RoleZeroContextBuilder` attempts to add experiences to `req`. The `RoleZeroSerializer` extracts essential parts of `req` for the experience pool, trimming lengthy entries to retain only necessary parts. """ - # Remove the "cmd_prompt_exp_part", it is only used within the exp_cache decorator. - if req: - req.pop() return await self.llm.aask(req, system_msgs=system_msgs)