diff --git a/metagpt/actions/minecraft/design_curriculumn.py b/metagpt/actions/minecraft/design_curriculumn.py index 3e2f33705..62555f54a 100644 --- a/metagpt/actions/minecraft/design_curriculumn.py +++ b/metagpt/actions/minecraft/design_curriculumn.py @@ -92,7 +92,7 @@ class DesignCurriculum(Action): def __init__(self, name="", context=None, llm=None): super().__init__(name, context, llm) - async def generate_qa(self, events, qa_cache, human_msg, system_msg): + async def generate_qa(self, events, qa_cache, qa_cache_questions_vectordb, human_msg, system_msg): """ Generate qa for DesignTask's HumanMessage """ @@ -104,9 +104,9 @@ class DesignCurriculum(Action): questions = [] answers = [] for question in questions_new: - if self.qa_cache_questions_vectordb._collection.count() > 0: + if qa_cache_questions_vectordb._collection.count() > 0: docs_and_scores = ( - self.qa_cache_questions_vectordb.similarity_search_with_score( + qa_cache_questions_vectordb.similarity_search_with_score( question, k=1 ) ) @@ -120,12 +120,12 @@ class DesignCurriculum(Action): answer = await self.generate_qa_step2(question=question) assert question not in qa_cache qa_cache[question] = answer - self.qa_cache_questions_vectordb.add_texts( + qa_cache_questions_vectordb.add_texts( texts=[question], ) with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "w") as f: json.dump(qa_cache, f) - self.qa_cache_questions_vectordb.persist() + qa_cache_questions_vectordb.persist() questions.append(question) answers.append(answer) assert len(questions_new) == len(questions) == len(answers) @@ -170,7 +170,7 @@ class DesignCurriculum(Action): # logger.info(f"Curriculum Agent generate_qa_step2 answer: {answer}") return answer - async def get_context_from_task(self, task, qa_cache): + async def get_context_from_task(self, task, qa_cache, qa_cache_questions_vectordb): """ Args: task Returns: context: "Question: {question}\n{answer}" @@ -186,16 +186,16 @@ class DesignCurriculum(Action): else: answer = await self.generate_qa_step2(question=question) qa_cache[question] = answer - self.qa_cache_questions_vectordb.add_texts( + qa_cache_questions_vectordb.add_texts( texts=[question], ) with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "w") as f: json.dump(qa_cache, f) - self.qa_cache_questions_vectordb.persist() + qa_cache_questions_vectordb.persist() context = f"Question: {question}\n{answer}" return context - async def generate_context(self, task, qa_cache, max_retries=5): + async def generate_context(self, task, qa_cache, qa_cache_questions_vectordb, max_retries=5): """ Refer to the code in the voyager/agents/curriculum.py propose_next_ai_task() for implementation details. Returns: context @@ -206,7 +206,7 @@ class DesignCurriculum(Action): raise RuntimeError("Max retries reached, failed to propose context.") try: context = await self.get_context_from_task( - task=task, qa_cache=qa_cache + task=task, qa_cache=qa_cache, qa_cache_questions_vectordb=qa_cache_questions_vectordb ) # Curriculum Agent Question: How to craft 4 wooden planks in Minecraft? & Curriculum Agent Answer: ... return context except Exception as e: @@ -214,14 +214,15 @@ class DesignCurriculum(Action): return await self.generate_context( task=task, qa_cache=qa_cache, + qa_cache_questions_vectordb=qa_cache_questions_vectordb, max_retries=max_retries - 1, ) - async def run(self, task, qa_cache, human_msg, system_msg, *args, **kwargs): + async def run(self, task, qa_cache, qa_cache_questions_vectordb, human_msg, system_msg, *args, **kwargs): logger.info(f"run {self.__repr__()}") # Generate curriculum-related questions and answers. # curriculum_qustion = await self.generate_qa_step1(events, human_msg, system_msg) - curriculum_context = await self.generate_context(task, qa_cache) + curriculum_context = await self.generate_context(task, qa_cache, qa_cache_questions_vectordb) # Return the generated questions and answers. return curriculum_context diff --git a/metagpt/actions/minecraft/manage_skills.py b/metagpt/actions/minecraft/manage_skills.py index 208bb9360..b5c51e270 100644 --- a/metagpt/actions/minecraft/manage_skills.py +++ b/metagpt/actions/minecraft/manage_skills.py @@ -5,8 +5,6 @@ import os import json -from traits.trait_types import self - from metagpt.logs import logger from metagpt.actions.minecraft.player_action import PlayerActions as Action from metagpt.const import CKPT_DIR @@ -17,18 +15,18 @@ class RetrieveSkills(Action): Action class for retrieving skills. Refer to the code in the voyager/agents/skill.py for implementation details. """ - + def __init__(self, name="", context=None, llm=None): super().__init__(name, context, llm) self.llm.model = "gpt-3.5-turbo" - - async def run(self, query, skills, *args, **kwargs): + + async def run(self, query, skills, vectordb, *args, **kwargs): # Implement the logic for retrieving skills here. - k = min(self.vectordb._collection.count(), self.retrieval_top_k) + k = min(vectordb._collection.count(), self.retrieval_top_k) if k == 0: return [] logger.info(f"Skill Manager retrieving for {k} skills") - docs_and_scores = self.vectordb.similarity_search_with_score(query, k=k) + docs_and_scores = vectordb.similarity_search_with_score(query, k=k) logger.info( f"Skill Manager retrieved skills: " f"{', '.join([doc.metadata['name'] for doc, _ in docs_and_scores])}" @@ -44,13 +42,13 @@ class AddNewSkills(Action): Action class for adding new skills. Refer to the code in the voyager/agents/skill.py for implementation details. """ - + def __init__(self, name="", context=None, llm=None): super().__init__(name, context, llm) self.llm.model = "gpt-3.5-turbo" - + async def run( - self, task, program_name, program_code, skills, skill_desp, *args, **kwargs + self, task, program_name, program_code, skills, skill_desp, vectordb, *args, **kwargs ): # Implement the logic for adding new skills here. # TODO: Fix this @@ -65,26 +63,27 @@ class AddNewSkills(Action): if program_name in skills: logger.info(f"Skill {program_name} already exists. Rewriting!") - self.vectordb._collection.delete(ids=[program_name]) + vectordb._collection.delete(ids=[program_name]) i = 2 while f"{program_name}V{i}.js" in os.listdir(f"{CKPT_DIR}/skill/code"): i += 1 dumped_program_name = f"{program_name}V{i}" else: dumped_program_name = program_name - self.vectordb.add_texts( + vectordb.add_texts( texts=[skill_desp], ids=[program_name], metadatas=[{"name": program_name}], ) - + logger.debug(f"ADD_CHECK: There are {vectordb._collection.count()} skills in vectordb") + with open(f"{CKPT_DIR}/skill/code/{dumped_program_name}.js", "w") as f: f.write(program_code) with open(f"{CKPT_DIR}/skill/description/{dumped_program_name}.txt", "w") as f: f.write(skill_desp) with open(f"{CKPT_DIR}/skill/skills.json", "w") as f: json.dump(skills, f) - self.vectordb.persist() + vectordb.persist() return { "code": program_code, "description": skill_desp, @@ -96,13 +95,13 @@ class GenerateSkillDescription(Action): Action class for generating skill descriptions. Refer to the code in the voyager/agents/skill.py for implementation details. """ - + def __init__(self, name="", context=None, llm=None): super().__init__(name, context, llm) self.llm.model = "gpt-3.5-turbo" - + async def run(self, program_name, human_message, system_message, *args, **kwargs): # Implement the logic for generating skill descriptions here. rsp = await self._aask(prompt=human_message, system_msgs=system_message) - skill_description = f" // {rsp}" + skill_description = f" // { rsp}" return f"async function {program_name}(bot) {{\n{skill_description}\n}}" diff --git a/metagpt/actions/minecraft/player_action.py b/metagpt/actions/minecraft/player_action.py index 58f121878..677887d51 100644 --- a/metagpt/actions/minecraft/player_action.py +++ b/metagpt/actions/minecraft/player_action.py @@ -2,68 +2,14 @@ # @Date : 2023/9/23 17:06 # @Author : stellahong (stellahong@fuzhi.ai) # @Desc : -import json - from metagpt.actions import Action -from langchain.vectorstores import Chroma -from langchain.embeddings.openai import OpenAIEmbeddings -from metagpt.const import CKPT_DIR -from metagpt.config import CONFIG class PlayerActions(Action): def __init__(self, name="", context=None, llm=None): super().__init__(name, context, llm) - self.skills_check = {} # for skills.json - self.qa_cache_check = {} - self.check_init = True - - if ( - CONFIG.resume - ): # TODO: now for assert only, no update, cuz program using in step() - with open(f"{CKPT_DIR}/skill/skills.json", "r") as f: - self.skills_check = json.load(f) - - with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "r") as f: - self.qa_cache_check = json.load(f) - self.retrieval_top_k = 5 - self.vectordb = Chroma( - collection_name="skill_vectordb", - embedding_function=OpenAIEmbeddings(), - persist_directory=f"{CKPT_DIR}/skill/vectordb", - ) - - self.qa_cache_questions_vectordb = Chroma( - collection_name="qa_cache_questions_vectordb", - embedding_function=OpenAIEmbeddings(), - persist_directory=f"{CKPT_DIR}/curriculum/vectordb", - ) - - # FIXME - if self.check_init: - # Check if Skill Manager's vectordb right using - assert self.vectordb._collection.count() >= len(self.skills_check), ( - f"Skill Manager's vectordb is not synced with skills.json.\n" - f"There are {self.vectordb._collection.count()} skills in vectordb but {len(self.skills_check)} skills in skills.json.\n" - f"Did you set resume=False when initializing the manager?\n" - f"You may need to manually delete the vectordb directory for running from scratch." - ) - # Check if Skill Manager's vectordb right using - assert self.qa_cache_questions_vectordb._collection.count() >= len( - self.qa_cache_check - ), ( - f"Curriculum Agent's qa cache question vectordb is not synced with qa_cache.json.\n" - f"There are {self.qa_cache_questions_vectordb._collection.count()} questions in vectordb " - f"but {len(self.qa_cache_check)} questions in qa_cache.json.\n" - f"Did you set resume=False when initializing the agent?\n" - f"You may need to manually delete the qa cache question vectordb directory for running from scratch.\n" - ) - self.check_init = False - # TODO: change to FaissStore - # self.qa_cache_questions_vectordb = FaissStore( {CKPT_DIR}/ 'curriculum/vectordb' - """Minecraft player info without any implementation details""" async def run(self, *args, **kwargs): diff --git a/metagpt/minecraft_team.py b/metagpt/minecraft_team.py index e86f6dfe8..0aac8c1cb 100644 --- a/metagpt/minecraft_team.py +++ b/metagpt/minecraft_team.py @@ -31,7 +31,7 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): """ 游戏环境的记忆,用于多个agent进行信息的共享和缓存,而不需要重复在自己的角色内维护缓存 """ - + event: dict[str, Any] = Field(default_factory=dict) current_task: str = Field(default="Mine 1 wood log") task_execution_time: float = Field(default=float) @@ -39,32 +39,32 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): default="You can mine one of oak, birch, spruce, jungle, acacia, dark oak, or mangrove logs." ) code: str = Field(default="") - program_code: str = Field(default="") # write in skill/code/*.js + program_code: str = Field(default="") # write in skill/code/*.js program_name: str = Field(default="") critique: str = Field(default="") skills: dict = Field(default_factory=dict) # for skills.json retrieve_skills: list[str] = Field(default_factory=list) event_summary: str = Field(default="") - + qa_cache: dict[str, str] = Field(default_factory=dict) completed_tasks: list[str] = Field(default_factory=list) # Critique things failed_tasks: list[str] = Field(default_factory=list) - + skill_desp: str = Field(default="") - + chest_memory: dict[str, Any] = Field( default_factory=dict ) # eg: {'(1344, 64, 1381)': 'Unknown'} chest_observation: str = Field(default="") # eg: "Chests: None\n\n" - + mf_instance: MineflayerEnv = Field(default_factory=MineflayerEnv) runtime_status: bool = False # equal to action execution status: success or failed - + @property def progress(self): # return len(self.completed_tasks) + 10 # Test only return len(self.completed_tasks) - + @property def programs(self): programs = "" @@ -75,25 +75,41 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): for primitives in load_skills_code(): programs += f"{primitives}\n\n" return programs - + @property def warm_up(self): return self.mf_instance.warm_up - + @property def core_inv_items_regex(self): return self.mf_instance.core_inv_items_regex + @property + def qa_cache_questions_vectordb(self): + return Chroma( + collection_name="qa_cache_questions_vectordb", + embedding_function=OpenAIEmbeddings(), + persist_directory=f"{CKPT_DIR}/curriculum/vectordb", + ) + + @property + def vectordb(self): + return Chroma( + collection_name="skill_vectordb", + embedding_function=OpenAIEmbeddings(), + persist_directory=f"{CKPT_DIR}/skill/vectordb", + ) + def set_mc_port(self, mc_port): self.mf_instance.set_mc_port(mc_port) self.set_mc_resume() - + def set_mc_resume(self): if CONFIG.resume: logger.info(f"Loading Action Developer from {CKPT_DIR}/action") with open(f"{CKPT_DIR}/action/chest_memory.json", "r") as f: self.chest_memory = json.load(f) - + logger.info(f"Loading Curriculum Agent from {CKPT_DIR}/curriculum") with open(f"{CKPT_DIR}/curriculum/completed_tasks.json", "r") as f: self.completed_tasks = json.load(f) @@ -103,25 +119,72 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): logger.info(f"Loading Skill Manager from {CKPT_DIR}/skill\033[0m") with open(f"{CKPT_DIR}/skill/skills.json", "r") as f: self.skills = json.load(f) - + logger.info(f"Loading Qa Cache from {CKPT_DIR}/curriculum\033[0m") with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "r") as f: - self.qa_cache = json.load(f) - + self.qa_cache = json.load(f) + + if self.vectordb._collection.count() == 0: + # Set vdvs for skills & qa_cache + skill_desps = [ + skill["description"] for program_name, skill in self.skills.items() + ] + program_names = [ + program_name for program_name, skill in self.skills.items() + ] + metadatas = [{"name": program_name} for program_name in program_names] + # add vectordb from file + ids = self.vectordb.add_texts( + texts=skill_desps, + ids=program_names, + metadatas=metadatas, + ) + self.vectordb.persist() + + if self.qa_cache_questions_vectordb._collection.count() == 0: + questions = [question for question, answer in self.qa_cache.items()] + self.qa_cache_questions_vectordb.add_texts(texts=questions) + + self.qa_cache_questions_vectordb.persist() + + logger.info( + f"INIT_CHECK: There are {self.vectordb._collection.count()} skills in vectordb and {len(self.skills)} skills in skills.json." + ) + # Check if Skill Manager's vectordb right using + assert self.vectordb._collection.count() >= len(self.skills), ( + f"Skill Manager's vectordb is not synced with skills.json.\n" + f"There are {self.vectordb._collection.count()} skills in vectordb but {len(self.skills)} skills in skills.json.\n" + f"Did you set resume=False when initializing the manager?\n" + f"You may need to manually delete the vectordb directory for running from scratch." + ) + + logger.info( + f"INIT_CHECK: There are {self.qa_cache_questions_vectordb._collection.count()} qa_cache in vectordb and {len(self.qa_cache)} questions in qa_cache.json." + ) + assert self.qa_cache_questions_vectordb._collection.count() >= len( + self.qa_cache + ), ( + f"Curriculum Agent's qa cache question vectordb is not synced with qa_cache.json.\n" + f"There are {self.qa_cache_questions_vectordb._collection.count()} questions in vectordb " + f"but {len(self.qa_cache)} questions in qa_cache.json.\n" + f"Did you set resume=False when initializing the agent?\n" + f"You may need to manually delete the qa cache question vectordb directory for running from scratch.\n" + ) + def register_roles(self, roles: Iterable[Minecraft]): for role in roles: role.set_memory(self) - + def update_event(self, event: Dict): if self.event == event: return self.event = event # self.update_chest_memory(event) # self.event_summary = self.summarize_chatlog(event) - + def update_task(self, task: str): self.current_task = task - + def update_context(self, context: str): self.context = context @@ -130,22 +193,22 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): def update_code(self, code: str): self.code = code # action_developer.gen_action_code to HERE - + def update_program_name(self, program_name: str): self.program_name = program_name - + def update_critique(self, critique: str): self.critique = critique # critic_agent.check_task_success to HERE - + def append_skill(self, skill: dict): self.skills[self.program_name] = skill # skill_manager.retrieve_skills to HERE - + def update_retrieve_skills(self, retrieve_skills: list): self.retrieve_skills = retrieve_skills - + def update_skill_desp(self, skill_desp: str): self.skill_desp = skill_desp - + def update_chest_memory(self, events: Dict): """ Input: events: Dict @@ -165,13 +228,13 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): self.chest_memory[position] = chest with open(f"{CKPT_DIR}/action/chest_memory.json", "w") as f: json.dump(self.chest_memory, f) - + def update_chest_observation(self): """ update chest_memory to chest_observation. Refer to @ https://github.com/MineDojo/Voyager/blob/main/voyager/agents/action.py """ - + chests = [] for chest_position, chest in self.chest_memory.items(): if isinstance(chest, dict) and len(chest) > 0: @@ -189,7 +252,7 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): self.chest_observation = f"Chests:\n{chests}\n\n" else: self.chest_observation = f"Chests: None\n\n" - + def summarize_chatlog(self, events): def filter_item(message: str): craft_pattern = r"I cannot make \w+ because I need: (.*)" @@ -198,25 +261,28 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): ) mine_pattern = r"I need at least a (.*) to mine \w+!" if re.match(craft_pattern, message): - self.event_summary = re.match(craft_pattern, message).groups()[0] + self.event_summary = re.match(craft_pattern, message).groups()[0] elif re.match(craft_pattern2, message): self.event_summary = "a nearby crafting table" elif re.match(mine_pattern, message): self.event_summary = re.match(mine_pattern, message).groups()[0] else: self.event_summary = "" + chatlog = set() for event_type, event in events: if event_type == "onChat": item = filter_item(event["onChat"]) if item: chatlog.add(item) - self.event_summary = "I also need " + ", ".join(chatlog) + "." if chatlog else "" - + self.event_summary = ( + "I also need " + ", ".join(chatlog) + "." if chatlog else "" + ) + def reset_block_info(self): # revert all the placing event in the last step pass - + def update_exploration_progress(self, success: bool): """ Split task into completed_tasks or failed_tasks @@ -252,9 +318,9 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): ) self.event[-1][1]["inventory"] = new_events[-1][1]["inventory"] self.event[-1][1]["voxels"] = new_events[-1][1]["voxels"] - + self.save_sorted_tasks() - + def save_sorted_tasks(self): updated_completed_tasks = [] # record repeated failed tasks @@ -263,21 +329,21 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): for task in self.completed_tasks: if task not in updated_completed_tasks: updated_completed_tasks.append(task) - + # remove completed tasks from failed tasks for task in updated_completed_tasks: while task in updated_failed_tasks: updated_failed_tasks.remove(task) - + self.completed_tasks = updated_completed_tasks self.failed_tasks = updated_failed_tasks - + # dump to json with open(f"{CKPT_DIR}/curriculum/completed_tasks.json", "w") as f: json.dump(self.completed_tasks, f) with open(f"{CKPT_DIR}/curriculum/failed_tasks.json", "w") as f: json.dump(self.failed_tasks, f) - + async def on_event_retrieve(self, *args): """ Retrieve Minecraft events. @@ -295,9 +361,7 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): "wait_ticks": 20, } ) - difficulty = ( - "easy" if len(self.completed_tasks) > 15 else "peaceful" - ) + difficulty = "easy" if len(self.completed_tasks) > 15 else "peaceful" events = self.mf_instance.step( "bot.chat(`/time set ${getNextTime()}`);\n" + f"bot.chat('/difficulty {difficulty}');" @@ -318,7 +382,8 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): ) self.update_event(events) logger.error(f"Failed to retrieve Minecraft events: {str(e)}") - + return events + async def on_event_execute(self, *args): """ Execute Minecraft events. @@ -334,7 +399,7 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): """ try: events = self.mf_instance.step( - code = self.code, + code=self.code, programs=self.programs, ) self.update_event(events) @@ -353,6 +418,7 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): ) self.update_event(events) logger.error(f"Failed to execute Minecraft events: {str(e)}") + return events class MinecraftPlayer(SoftwareCompany): @@ -360,16 +426,16 @@ class MinecraftPlayer(SoftwareCompany): Software Company: Possesses a team, SOP (Standard Operating Procedures), and a platform for instant messaging, dedicated to writing executable code. """ - + environment: Environment = Field(default_factory=Environment) game_memory: GameEnvironment = Field(default_factory=GameEnvironment) investment: float = Field(default=50.0) task: str = Field(default="") game_info: dict = Field(default={}) - + def set_port(self, mc_port): self.game_memory.set_mc_port(mc_port) - + def check_complete_round(self): complete_round = [] for role in self.environment.roles.values(): @@ -381,7 +447,7 @@ class MinecraftPlayer(SoftwareCompany): complete_round_tag = all(complete_round) logger.info(f"complete_round {complete_round}") return complete_round_tag - + def update_round(self): for role in self.environment.roles.values(): role.finish_step = False @@ -389,11 +455,11 @@ class MinecraftPlayer(SoftwareCompany): role._rc.todo = None role.finish_state = len(role._actions) logger.info(f"round_id:{role.round_id}") - + def hire(self, roles: list[Role]): self.environment.add_roles(roles) self.game_memory.register_roles(roles) - + def start(self, task, round=0): """Start a project from publishing boss requirement.""" self.task = task @@ -401,14 +467,14 @@ class MinecraftPlayer(SoftwareCompany): Message(role="Player", content=task, cause_by=PlayerActions, round_id=round) ) logger.info(self.game_info) - + def _save(self): logger.info(self.json()) - + def _reset(self): for role_profile, role in self.environment.roles.items(): role.reset_state() - + async def run(self, n_round=3): """Run company until target round or no money""" round_id = 0 @@ -434,18 +500,17 @@ class MinecraftPlayer(SoftwareCompany): programs="", ) self.game_memory.update_event(events) - + while n_round > 0: # self._save() if self.check_complete_round(): - n_round -= 1 self.update_round() round_id += 1 # add new task into env and continue # fixme: update self.task self.start(task=self.task, round=round_id) - + logger.info(f"{n_round=}") self._check_balance() await self.environment.run() diff --git a/metagpt/roles/minecraft/curriculum_agent.py b/metagpt/roles/minecraft/curriculum_agent.py index 831d54999..8b414aa7a 100644 --- a/metagpt/roles/minecraft/curriculum_agent.py +++ b/metagpt/roles/minecraft/curriculum_agent.py @@ -300,6 +300,7 @@ class CurriculumDesigner(Base): inventoryUsed = events[-1][1]["status"]["inventoryUsed"] task = self.game_memory.current_task qa_cache = self.game_memory.qa_cache + qa_cache_questions_vectordb = self.game_memory.qa_cache_questions_vectordb if self.game_memory.progress == 0: context = self.game_memory.context @@ -309,7 +310,7 @@ class CurriculumDesigner(Base): ) else: context = await DesignCurriculum().run( - task, qa_cache, human_msg, system_msg, *args, **kwargs + task, qa_cache, qa_cache_questions_vectordb, human_msg, system_msg, *args, **kwargs ) self.perform_game_info_callback(context, self.game_memory.update_context) return Message( diff --git a/metagpt/roles/minecraft/skill_manager.py b/metagpt/roles/minecraft/skill_manager.py index aec802d15..f70511c1c 100644 --- a/metagpt/roles/minecraft/skill_manager.py +++ b/metagpt/roles/minecraft/skill_manager.py @@ -19,40 +19,42 @@ from metagpt.utils.minecraft import load_prompt @agent_registry.register("skill_manager") class SkillManager(Base): def __init__( - self, - name: str = "John", - profile: str = "Skills Management Specialist", - goal: str = "To oversee and optimize the acquisition, development, and utilization of skills within the organization, ensuring workforce competence and efficiency.", - constraints: str = "Resource allocation, training budgets, and alignment with organizational goals.", + self, + name: str = "John", + profile: str = "Skills Management Specialist", + goal: str = "To oversee and optimize the acquisition, development, and utilization of skills within the organization, ensuring workforce competence and efficiency.", + constraints: str = "Resource allocation, training budgets, and alignment with organizational goals.", ) -> None: super().__init__(name, profile, goal, constraints) - + # Initialize actions specific to the SkillManager role self._init_actions([RetrieveSkills, GenerateSkillDescription, AddNewSkills]) - + # Set events or actions the SkillManager should watch or be aware of self._watch( [DesignCurriculum, GenerateActionCode, RetrieveSkills, GenerateSkillDescription] ) + self.finish_state = len(self._actions) - + def encapsule_message(self, program_code, program_name, *args, **kwargs): system_msg = self.render_system_message(load_prompt("skill")) human_msg = self.render_human_message( program_code + "\n\n" + f"The main function is `{program_name}`." ) return {"system_msg": [system_msg.content], "human_msg": human_msg.content} - + async def retrieve_skills(self, query, skills, *args, **kwargs): - retrieve_skills = await RetrieveSkills().run(query, skills) + vectordb = self.game_memory.vectordb + retrieve_skills = await RetrieveSkills().run(query, skills, vectordb) logger.info(f"Render Action Agent system message with {len(retrieve_skills)} skills") self.perform_game_info_callback(retrieve_skills, self.game_memory.update_retrieve_skills) - return Message(content=f"{retrieve_skills}", instruct_content="retrieve_skills", + return Message(content=f"{retrieve_skills}", instruct_content="retrieve_skills", role=self.profile, send_to=agent_registry.entries["action_developer"]()._setting.name) # return Message( # content=f"{skills}", instruct_content="retrieve_skills", role=self.profile # ) # Unit test only - + async def generate_skill_descp(self, human_msg, system_msg, *args, **kwargs): program_name = self.game_memory.program_name desp = await GenerateSkillDescription().run(program_name, human_msg, system_msg) @@ -62,20 +64,21 @@ class SkillManager(Base): instruct_content="generate_skill_descp", role=self.profile, ) - + async def handle_add_new_skills( - self, task, program_name, program_code, skills, *args, **kwargs + self, task, program_name, program_code, skills, *args, **kwargs ): if not self.game_memory.runtime_status: return Message( - content="", - instruct_content="handle_add_new_skills", - role=self.profile, - ) + content="", + instruct_content="handle_add_new_skills", + role=self.profile, + ) skill_desp = self.game_memory.skill_desp + vectordb = self.game_memory.vectordb new_skills_info = await AddNewSkills().run( - task, program_name, program_code, skills, skill_desp + task, program_name, program_code, skills, skill_desp, vectordb ) self.perform_game_info_callback(new_skills_info, self.game_memory.append_skill) return Message( @@ -83,13 +86,13 @@ class SkillManager(Base): instruct_content="handle_add_new_skills", role=self.profile, ) - + async def _act(self) -> Message: todo = self._rc.todo logger.debug(f"Todo is {todo}") self.maintain_actions(todo) + # 获取最新的游戏周边信息 - context = self.game_memory.context task = self.game_memory.current_task @@ -97,27 +100,27 @@ class SkillManager(Base): self.perform_game_info_callback(self.game_memory.event, self.game_memory.summarize_chatlog) event_summary = self.game_memory.event_summary program_code = self.game_memory.program_code - + program_name = self.game_memory.program_name skills = self.game_memory.skills - + # msg = self._rc.memory.get(k=1)[0] - + retrieve_skills_message_step1 = {"query": context, "skills": skills} logger.info(f"check query {context}") logger.info(f"check event summary {event_summary}") retrieve_skills_message_step2 = {"query": context + "\n\n" + event_summary, "skills": skills} - + generate_skill_message = self.encapsule_message(program_code, program_name) - + add_new_skills_message = { "task": task, "program_name": program_name, "program_code": program_code, "skills": skills, } - + handler_map = { DesignCurriculum: self.retrieve_skills, RetrieveSkills: self.retrieve_skills, @@ -136,10 +139,10 @@ class SkillManager(Base): msg = await handler(**generate_skill_message) else: msg = await handler(**add_new_skills_message) - + msg.cause_by = type(todo) msg.round_id = self.round_id self._publish_message(msg) return msg - + raise ValueError(f"Unknown todo type: {type(todo)}")