From 5f521d42706a8c2b8ac909ebe47c5b03d0a03488 Mon Sep 17 00:00:00 2001 From: stellahsr Date: Sun, 24 Sep 2023 11:20:45 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9C=A8mc=20branch=E6=B7=BB=E5=8A=A0=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E7=9A=84actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metagpt/actions/minecraft/__init__.py | 35 ++++++ .../actions/minecraft/design_curriculumn.py | 84 +++++++++++++ metagpt/actions/minecraft/generate_actions.py | 57 +++++++++ metagpt/actions/minecraft/manage_skills.py | 53 +++++++++ metagpt/actions/minecraft/player_action.py | 10 ++ metagpt/actions/minecraft/review_task.py | 38 ++++++ metagpt/minecraft_team.py | 110 ++++++++++++++++++ metagpt/roles/minecraft/__init__.py | 4 + metagpt/roles/minecraft/action_developer.py | 78 +++++++++++++ metagpt/roles/minecraft/critic_agent.py | 27 +++++ metagpt/roles/minecraft/curriculum_agent.py | 93 +++++++++++++++ metagpt/roles/minecraft/event_handler.py | 68 +++++++++++ metagpt/roles/minecraft/minecraft_base.py | 105 +++++++++++++++++ metagpt/roles/minecraft/skill_manager.py | 71 +++++++++++ metagpt/utils/minecraft/__init__.py | 4 + metagpt/utils/minecraft/load_prompts.py | 9 ++ minecraft_run.py | 32 +++++ 17 files changed, 878 insertions(+) create mode 100644 metagpt/actions/minecraft/__init__.py create mode 100644 metagpt/actions/minecraft/design_curriculumn.py create mode 100644 metagpt/actions/minecraft/generate_actions.py create mode 100644 metagpt/actions/minecraft/manage_skills.py create mode 100644 metagpt/actions/minecraft/player_action.py create mode 100644 metagpt/actions/minecraft/review_task.py create mode 100644 metagpt/minecraft_team.py create mode 100644 metagpt/roles/minecraft/__init__.py create mode 100644 metagpt/roles/minecraft/action_developer.py create mode 100644 metagpt/roles/minecraft/critic_agent.py create mode 100644 metagpt/roles/minecraft/curriculum_agent.py create mode 100644 metagpt/roles/minecraft/event_handler.py create mode 100644 metagpt/roles/minecraft/minecraft_base.py create mode 100644 metagpt/roles/minecraft/skill_manager.py create mode 100644 metagpt/utils/minecraft/__init__.py create mode 100644 metagpt/utils/minecraft/load_prompts.py create mode 100644 minecraft_run.py diff --git a/metagpt/actions/minecraft/__init__.py b/metagpt/actions/minecraft/__init__.py new file mode 100644 index 000000000..d375b7439 --- /dev/null +++ b/metagpt/actions/minecraft/__init__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/23 14:26 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +from enum import Enum + +from metagpt.actions.action import Action +from metagpt.actions.action_output import ActionOutput +from metagpt.actions.minecraft.design_curriculumn import DesignTask, DesignCurriculum +from metagpt.actions.minecraft.generate_actions import GenerateActionCode, SummarizeLog +from metagpt.actions.minecraft.manage_skills import RetrieveSkills, GenerateSkillDescription, AddNewSkills +from metagpt.actions.minecraft.review_task import VerifyTask +from metagpt.actions.minecraft.player_action import PlayerActions + + +class ActionType(Enum): + """All types of Actions, used for indexing.""" + + Design_Task = DesignTask + Design_Curriculum = DesignCurriculum + Generate_Action_Code = GenerateActionCode + Summarize_Log = SummarizeLog + Retrieve_Skills = RetrieveSkills + Generate_Skill_Description = GenerateSkillDescription + Add_New_Skills = AddNewSkills + Verify_Task = VerifyTask + Player_Actions = PlayerActions + + + +__all__ = [ + "ActionType", + "Action", + "ActionOutput", +] diff --git a/metagpt/actions/minecraft/design_curriculumn.py b/metagpt/actions/minecraft/design_curriculumn.py new file mode 100644 index 000000000..1079ad4dd --- /dev/null +++ b/metagpt/actions/minecraft/design_curriculumn.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/23 14:56 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +from metagpt.logs import logger +from metagpt.actions import Action + + +class DesignTask(Action): + """ + Action class for decomposing a task. + Refer to the code in the voyager/agents/curriculum.py for implementation details. + """ + + def __init__(self, name="", context=None, llm=None): + super().__init__(name, context, llm) + + def decompose_task(self, query): + # Implement the logic to decompose a task here. + return "" + + async def propose_next_ai_task(self, prompts, system_msg): + """ + Refer to the code in the voyager/agents/curriculum.py propose_next_ai_task() for implementation details. + Returns: + + """ + curriculum = await self._aask(prompt=prompts, system_msgs=system_msg) + + logger.info(f"\033[31m****Curriculum Agent ai message****\n{curriculum}\033[0m") + + def parse_llm_response(self, llm_resp): + # Implement the logic to parse the LLM response here. + return "", "" + + async def run(self, human_msg, system_msg, *args, **kwargs): + logger.info(f"run {self.__repr__()}") + + # Call the language model to generate a response. + + llm_response = await self.propose_next_ai_task(prompts=human_msg, system_msg=system_msg) + + # Parse the response from the language model. + task, context = self.parse_llm_response(llm_response) + + return task, context + + +class DesignCurriculum(Action): + """ + Action class for designing curriculum-related questions. + Refer to the code in the voyager/agents/curriculum.py for implementation details. + """ + + def __init__(self, name="", context=None, llm=None): + super().__init__(name, context, llm) + self.vect_db = "" + + def get_task_context(self): + # Implement the logic for a specific task in generating context. + return "" + + def generate_qa(self): + # Implement the logic to generate curriculum-related questions and answers. + question = "" + answer = "" + context = f"Question: {question}\n{answer}" + return context + + def generate_qa_step1(self): + # Implement the logic for a specific step in generating questions and answers. + return "" + + def generate_qa_step2(self): + # Implement the logic for another specific step in generating questions and answers. + return "" + + async def run(self, *args, **kwargs): + logger.info(f"run {self.__repr__()}") + # Generate curriculum-related questions and answers. + curriculum_qa = self.generate_qa() + + # Return the generated questions and answers. + return curriculum_qa diff --git a/metagpt/actions/minecraft/generate_actions.py b/metagpt/actions/minecraft/generate_actions.py new file mode 100644 index 000000000..e32aab485 --- /dev/null +++ b/metagpt/actions/minecraft/generate_actions.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/23 15:44 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +from metagpt.logs import logger +from metagpt.actions import Action + + +class GenerateActionCode(Action): + """ + Action class for generating action code. + Refer to the code in the voyager/agents/action.py for implementation details. + """ + + def __init__(self, name="", context=None, llm=None): + super().__init__(name, context, llm) + + async def generate_code(self): + """ + Generate action code logic. + + Implement the logic for generating action code here. + """ + return "" + + async def run(self, human_msg, system_msg=[], *args, **kwargs): + logger.info(f"run {self.__repr__()}") + # Generate action code. + generated_code = await self.generate_code() + + # Return the generated code. + return generated_code + + +class SummarizeLog(Action): + """ + Action class for parsing and summarizing logs. + Refer to the code in the voyager/agents/action.py for implementation details. + """ + + def __init__(self, name="", context=None, llm=None): + super().__init__(name, context, llm) + + async def summarize_logs(self): + """ + Summarize chatlogs. + + Implement the logic for summarizing chatlogs here. + """ + return "" + + async def run(self, *args, **kwargs): + # Summarize chatlogs. + summary = await self.summarize_logs() + + # Return the summary. + return summary diff --git a/metagpt/actions/minecraft/manage_skills.py b/metagpt/actions/minecraft/manage_skills.py new file mode 100644 index 000000000..6ef7cdde8 --- /dev/null +++ b/metagpt/actions/minecraft/manage_skills.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/23 14:56 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : + +from metagpt.logs import logger +from metagpt.actions import Action + + +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.vect_db = "" + + async def run(self, *args, **kwargs): + # Implement the logic for retrieving skills here. + return [] + + +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) + + async def run(self, *args, **kwargs): + # Implement the logic for adding new skills here. + pass + + +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) + + async def run(self, *args, **kwargs): + # Implement the logic for generating skill descriptions here. + pass + + + \ No newline at end of file diff --git a/metagpt/actions/minecraft/player_action.py b/metagpt/actions/minecraft/player_action.py new file mode 100644 index 000000000..6597fc9a1 --- /dev/null +++ b/metagpt/actions/minecraft/player_action.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/23 17:06 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +from metagpt.actions import Action + +class PlayerActions(Action): + """Minecraft player info without any implementation details""" + async def run(self, *args, **kwargs): + raise NotImplementedError \ No newline at end of file diff --git a/metagpt/actions/minecraft/review_task.py b/metagpt/actions/minecraft/review_task.py new file mode 100644 index 000000000..9f757d9ee --- /dev/null +++ b/metagpt/actions/minecraft/review_task.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/23 14:56 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +from metagpt.logs import logger +from metagpt.actions import Action + +from metagpt.actions import Action + + +class VerifyTask(Action): + """ + Action class for verifying a task. + Refer to the code in the voyager/agents/critic.py for implementation details. + """ + + def __init__(self, name="", context=None, llm=None): + super().__init__(name, context, llm) + self.vect_db = "" + + async def run(self, *args, **kwargs): + task, status, review_info = None, False, "" + try: + # Implement the logic to verify the task here. + + # Example: Verify the completion of a task. + + # If verification is successful, return a success message. + logger.info("Task verified successfully.") + task, status, review_info = "", True, "Task verified successfully." + + # If verification fails, return an appropriate error message. + # return "Task verification failed due to [reason]." + except Exception as e: + # Handle any exceptions that may occur during verification. + logger.error(f"Error verifying the task: {str(e)}") + task, status, review_info = None, False, "Task verified failed." + return task, status, review_info diff --git a/metagpt/minecraft_team.py b/metagpt/minecraft_team.py new file mode 100644 index 000000000..ffc0357fb --- /dev/null +++ b/metagpt/minecraft_team.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/23 14:14 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +from typing import Iterable, Dict, Any +from pydantic import BaseModel, Field + +from metagpt.logs import logger +from metagpt.roles import Role +from metagpt.schema import Message +from metagpt.software_company import SoftwareCompany + +from metagpt.actions.minecraft.player_action import PlayerActions +from metagpt.roles.minecraft.minecraft_base import Minecraft +from metagpt.environment import Environment + + +class GameMemory(BaseModel): + """ + 游戏环境的记忆,用于多个agent进行信息的共享和缓存,而不需要重复在自己的角色内维护缓存 + """ + event: dict[str, Any] = Field(default_factory=dict) + current_task: str = Field(default="Craft 4 wooden planks") + task_execution_time: float = Field(default=float) + context: str = Field(default="") + + def register_roles(self, roles: Iterable[Minecraft]): + for role in roles: + role.set_memory(self) + + def update_event(self, event: Dict): + self.event = event + + def update_task(self, task: str): + self.current_task = task + + def update_context(self, context: str): + self.context = context + + async def on_event(self, *args): + """ + Retrieve Minecraft events. + + This function is used to obtain events from the Minecraft environment. Check the implementation in + the 'voyager/env/bridge.py step()' function to capture events generated within the game. + + Returns: + list: A list of Minecraft events. + + Raises: + Exception: If there is an issue retrieving events. + """ + try: + # Implement the logic to retrieve Minecraft events here. + events = { + "Biome": "river", + "Time": "night", + "Nearby blocks": "water, dirt, stone, coal_ore, sandstone, grass_block, sand, grass, oak_leaves, fern, seagrass, tall_seagrass", + "Nearby entities(nearest to farthest)": "turtle, salmon", + "Health": "20.0 / 20", + "Hunger": "20.0 / 20", + "Position": "x = -47.5, y = 63.0, z = -283.5", + "Equipment": [], + "Inventory(0 / 36)": "Empty", + "Chests": "" + } + # Example: events = minecraft_api.get_events() + + return events + except Exception as e: + logger.error(f"Failed to retrieve Minecraft events: {str(e)}") + raise {} + + +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: GameMemory = Field(default_factory=GameMemory) + investment: float = Field(default=50.0) + task: str = Field(default="") + game_info: dict = Field(default={}) + + def hire(self, roles: list[Role]): + self.environment.add_roles(roles) + self.game_memory.register_roles(roles) + + def start(self, task): + """Start a project from publishing boss requirement.""" + self.task = task + self.environment.publish_message(Message(role="Player", content=task, cause_by=PlayerActions)) + logger.info(self.game_info) + + def _save(self): + logger.info(self.json()) + + async def run(self, n_round=3): + """Run company until target round or no money""" + while n_round > 0: + # self._save() + n_round -= 1 + logger.debug(f"{n_round=}") + self._check_balance() + await self.environment.run() + + return self.environment.history + + diff --git a/metagpt/roles/minecraft/__init__.py b/metagpt/roles/minecraft/__init__.py new file mode 100644 index 000000000..f5c516efd --- /dev/null +++ b/metagpt/roles/minecraft/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/23 14:27 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : diff --git a/metagpt/roles/minecraft/action_developer.py b/metagpt/roles/minecraft/action_developer.py new file mode 100644 index 000000000..d89e823f3 --- /dev/null +++ b/metagpt/roles/minecraft/action_developer.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/23 12:45 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +from metagpt.logs import logger +from metagpt.roles.minecraft.minecraft_base import Minecraft as Base +from metagpt.schema import Message, HumanMessage, SystemMessage +from metagpt.roles.minecraft.minecraft_base import agent_registry +from metagpt.actions.minecraft.generate_actions import GenerateActionCode +from metagpt.actions.minecraft.design_curriculumn import DesignCurriculum +from metagpt.actions.minecraft.manage_skills import GenerateSkillDescription, RetrieveSkills, AddNewSkills + + +@agent_registry.register("action_developer") +class ActionDeveloper(Base): + """ + iterative prompting mechanism in paper. + generate action code based on environment observation and plan, as well as skills retrieval results + """ + + def __init__( + self, + name: str = "Bob", + profile: str = "Generate code for specified tasks", + goal: str = "Produce accurate and efficient code solutions in Python and JavaScript", + constraints: str = "Adhere to coding best practices and style guidelines", + ) -> None: + super().__init__(name, profile, goal, constraints) + # Initialize actions specific to the Action role + self._init_actions([GenerateActionCode]) + + # Set events or actions the ActionAgent should watch or be aware of + # 需要根据events进行自己chest_observation的更新 + self._watch([RetrieveSkills]) + + async def _observe(self) -> int: + await super()._observe() + for msg in self._rc.news: + logger.info(msg.send_to == self._setting.name) + self._rc.news = [ + msg for msg in self._rc.news if msg.send_to == self._setting.name + ] # only relevant msgs count as observed news + logger.info(len(self._rc.news)) + return len(self._rc.news) + + async def generate_action_code(self, human_msg, system_msg, *args, **kwargs): + code = await GenerateActionCode().run(human_msg) + logger.info(code) + msg = Message(content=f"test_action", instruct_content="generate_action_code", role=self.profile) + logger.info(msg) + return msg + + async def _act(self) -> Message: + todo = self._rc.todo + logger.debug(f"Todo is {todo}") + + # 获取最新的游戏周边信息 + context = self.game_memory.context + task = self.game_memory.current_task + + message = self.encapsule_message(task, context) + logger.info(todo) + handler_map = { + + GenerateActionCode: self.generate_action_code, + } + handler = handler_map.get(type(todo)) + logger.info(handler) + + if handler: + msg = await handler(**message) + logger.info(msg) + msg.cause_by = type(todo) + logger.info(msg.send_to) + self._publish_message(msg) + return msg + + raise ValueError(f"Unknown todo type: {type(todo)}") \ No newline at end of file diff --git a/metagpt/roles/minecraft/critic_agent.py b/metagpt/roles/minecraft/critic_agent.py new file mode 100644 index 000000000..668cba662 --- /dev/null +++ b/metagpt/roles/minecraft/critic_agent.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/23 12:46 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +from metagpt.roles.minecraft.minecraft_base import Minecraft as Base +from metagpt.actions.minecraft.review_task import VerifyTask +from metagpt.actions.minecraft.generate_actions import GenerateActionCode + +class CriticReviewer(Base): + """ + self-verification + """ + def __init__( + self, + name: str = "Simon", + profile: str = "Task Reviewer", + goal: str = "To provide insightful and constructive feedback on a wide range of content types, helping creators improve their work and maintaining high-quality standards.", + constraints: str = "Adherence to ethical reviewing practices, respectful communication, and confidentiality of sensitive information.", + ) -> None: + super().__init__(name, profile, goal, constraints) + # Initialize actions specific to the CriticReviewer role + self._init_actions([VerifyTask]) + + # Set events or actions the CriticReviewer should watch or be aware of + # 需要获取最新的events来进行评估 + self._watch([GenerateActionCode]) + \ No newline at end of file diff --git a/metagpt/roles/minecraft/curriculum_agent.py b/metagpt/roles/minecraft/curriculum_agent.py new file mode 100644 index 000000000..2a00cc45a --- /dev/null +++ b/metagpt/roles/minecraft/curriculum_agent.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/23 12:45 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +from metagpt.logs import logger +from metagpt.schema import Message, HumanMessage, SystemMessage +from metagpt.roles.minecraft.minecraft_base import Minecraft as Base +from metagpt.actions.minecraft.design_curriculumn import DesignCurriculum, DesignTask +from metagpt.actions.minecraft.player_action import PlayerActions + + +class CurriculumDesigner(Base): + """ + CurriculumDesigner is the automatic curriculum in paper, refer to the code voyager/agents/curriculum.py + """ + + def __init__( + self, + name: str = "David", + profile: str = "Expertise in minecraft task design and curriculum development.", + goal: str = " Collect and integrate learner feedback to improve and refine educational content and pathways", + constraints: str = "Limited budget and resources for the development of educational content and technology tools." + ) -> None: + super().__init__(name, profile, goal, constraints) + # Initialize actions specific to the Action role + self._init_actions([DesignTask, DesignCurriculum]) + + # Set events or actions the ActionAgent should watch or be aware of + self._watch([PlayerActions, DesignTask]) + + def render_human_message(self, msg, *args, **kwargs): + return HumanMessage(content=msg) + + def render_system_message(self, msg, *args, **kwargs): + return SystemMessage(content=msg) + + async def handle_task_design(self, human_msg, system_msg, *args, **kwargs): + """ + Args: + human_msg: + system_msg: + *args: + **kwargs: + + Returns: + """ + task = await DesignTask().run(human_msg, system_msg, *args, **kwargs) + self.perform_game_info_callback(task, self.game_memory.update_task) + return Message(content=f"{task}", instruct_content="task_design", role=self.profile) + + async def handle_curriculum_design(self, human_msg, system_msg, *args, **kwargs): + """ + refer to the context generation in voyager + Args: + human_msg: + system_msg: + *args: + **kwargs: + + Returns: + + """ + context = await DesignCurriculum().run(human_msg, system_msg, *args, **kwargs) + self.perform_game_info_callback(context, self.game_memory.update_context) + return Message(content=f"{context}", instruct_content="curriculum_design", role=self.profile) + + async def _act(self) -> Message: + todo = self._rc.todo + logger.debug(f"Todo is {todo}") + + # 获取最新的游戏周边环境信息 + event = await self._obtain_events() + task = self.game_memory.current_task + context = self.game_memory.context + + msg = self._rc.memory.get(k=1)[0] + query = msg.content + + message = self.encapsule_message(query, task, event) + + handler_map = { + + DesignTask: self.handle_task_design, + DesignCurriculum: self.handle_curriculum_design, + } + handler = handler_map.get(type(todo)) + if handler: + msg = await handler(**message) + msg.cause_by = type(todo) + self._publish_message(msg) + return msg + + raise ValueError(f"Unknown todo type: {type(todo)}") diff --git a/metagpt/roles/minecraft/event_handler.py b/metagpt/roles/minecraft/event_handler.py new file mode 100644 index 000000000..3840abd4a --- /dev/null +++ b/metagpt/roles/minecraft/event_handler.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/23 14:29 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +import json + +from metagpt.logs import logger + +from metagpt.roles.minecraft.minecraft_base import Minecraft as Base +from metagpt.schema import Message +from metagpt.actions.minecraft.player_action import PlayerActions +from metagpt.actions.minecraft.generate_actions import GenerateActionCode +from metagpt.actions.minecraft.process_event import HandleEvents + + +class EventHandler(Base): + def __init__( + self, + name: str = "Thompson", + profile: str = "Minecraft Event Handler", + goal: str = "To efficiently manage and respond to in-game events, providing information about the player.", + constraints: str = "Resource availability, server performance, adherence to server rules and regulations.", + ) -> None: + super().__init__(name, profile, goal, constraints) + # Initialize actions specific to the EventHandler role + self._init_actions([HandleEvents]) + + # Set events or actions the EventHandler should watch or be aware of + self._watch([PlayerActions, GenerateActionCode]) + self.last_events = {"env_events": {}, + "execute_results": {}} + + + async def _act(self) -> Message: + # 获取最新的消息 + + msg = self._rc.memory.get(k=1)[0] + query = msg.content if self._rc.state == 0 else msg.instruct_content + """ + todo: parse query info from message, e.g. test_round and action code + """ + test_round = 1 + logger.info(msg.cause_by) + + if msg.cause_by == GenerateActionCode: # 进行生成的代码执行, 获取的结果用于进行AI评估 + events = await HandleEvents().run(query) + result_msg = Message( + content=f"Round {test_round} of rollout done", + role=self.profile, + cause_by=HandleEvents, + sent_from=self.profile, + send_to="Task Reviewer", + ) + + self.perform_game_info_callback(events, self.game_memory.update_event) + + else: + events = await HandleEvents().run(query) + result_msg = Message( + content=events, + role=self.profile, + cause_by=HandleEvents, + sent_from=self.profile, + send_to="", + ) + + self.perform_game_info_callback(events, self.game_memory.update_event) + return result_msg diff --git a/metagpt/roles/minecraft/minecraft_base.py b/metagpt/roles/minecraft/minecraft_base.py new file mode 100644 index 000000000..ef3c58084 --- /dev/null +++ b/metagpt/roles/minecraft/minecraft_base.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/23 21:38 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +import contextlib +import json + +from metagpt.logs import logger +from metagpt.roles.role import Role +from metagpt.schema import HumanMessage, SystemMessage + +from typing import Dict + +from pydantic import BaseModel + + +class Registry(BaseModel): + """Registry for storing and building classes.""" + + name: str + entries: Dict = {} + + def register(self, key: str): + def decorator(class_builder): + self.entries[key] = class_builder + return class_builder + + return decorator + + def build(self, type: str, **kwargs): + if type not in self.entries: + raise ValueError( + f'{type} is not registered. Please register with the .register("{type}") method provided in {self.name} registry' + ) + return self.entries[type](**kwargs) + + def get_all_entries(self): + return self.entries + +class Minecraft(Role): + def __init__( + self, + name: str = "MC", + profile: str = "Minecraft Role", + goal: str = "", + constraints: str = "", + ) -> None: + super().__init__(name, profile, goal, constraints) + self.game_memory = None + self.event = {} + + + # + async def _think(self) -> None: + if len(self._actions) == 1: + # If there is only one action, then only this one can be performed + self._set_state(0) + return True + + if self._rc.todo is None: + logger.info("0") + self._set_state(0) + return True + + if self._rc.state + 1 < len(self._states): + self._set_state(self._rc.state + 1) + logger.info("1") + return True + else: + self._rc.todo = None + logger.info("2") + + return False + + async def _obtain_events(self): + return await self.game_memory.on_event() + + def set_memory(self, shared_memory: 'GameMemory'): + self.game_memory = shared_memory + + def render_human_message(self, msg, *args, **kwargs): + return HumanMessage(content=msg) + + def render_system_message(self, msg, *args, **kwargs): + return SystemMessage(content=msg) + + @staticmethod + def perform_game_info_callback(info: object, callback: object) -> object: + logger.debug(info) + callback(info) + + def encapsule_message(self, msg, *args, **kwargs): + human_msg = self.render_human_message(msg, *args, **kwargs) + system_msg = self.render_system_message(msg, *args, **kwargs) + return {"system_msg": [system_msg.content], + "human_msg": human_msg.content} + +agent_registry = Registry(name="Minecraft") + +if __name__ == "__main__": + mc = Minecraft() + result = "Async operation result" + # 调用回调函数,并传递结果 + # mc.perform_memory_callback(mc.my_callback) + print(mc.game_memory.current_task) diff --git a/metagpt/roles/minecraft/skill_manager.py b/metagpt/roles/minecraft/skill_manager.py new file mode 100644 index 000000000..3a618c2cc --- /dev/null +++ b/metagpt/roles/minecraft/skill_manager.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/23 12:46 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +from metagpt.logs import logger +from metagpt.roles.minecraft.minecraft_base import Minecraft as Base +from metagpt.roles.minecraft.minecraft_base import agent_registry +from metagpt.schema import Message, HumanMessage, SystemMessage +from metagpt.actions.minecraft.manage_skills import GenerateSkillDescription, RetrieveSkills, AddNewSkills +from metagpt.actions.minecraft.review_task import VerifyTask +from metagpt.actions.minecraft.design_curriculumn import DesignCurriculum + + + +@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.", + ) -> 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, VerifyTask, RetrieveSkills, GenerateSkillDescription]) + + async def retrieve_skills(self, human_msg, system_msg, *args, **kwargs): + skills = await RetrieveSkills().run(human_msg) + logger.info( + f"\033[33mRender Action Agent system message with {len(skills)} skills\033[0m" + ) + return Message(content=f"{skills}", instruct_content="retrieve_skills", role=self.profile, + send_to=agent_registry.entries["action_developer"]()._setting.name) + + async def generate_skill_descp(self, human_msg, system_msg, *args, **kwargs): + desp = await GenerateSkillDescription().run(human_msg) + return Message(content=f"{desp}", instruct_content="generate_skill_descp", role=self.profile) + + async def handle_add_new_skills(self, human_msg, system_msg, *args, **kwargs): + new_skills = await AddNewSkills().run(human_msg) + return Message(content=f"", instruct_content="generate_skill_descp", role=self.profile) + + async def _act(self) -> Message: + todo = self._rc.todo + logger.debug(f"Todo is {todo}") + + # 获取最新的游戏周边信息 + context = self.game_memory.context + + msg = self._rc.memory.get(k=1)[0] + + message = self.encapsule_message(context) + + handler_map = { + DesignCurriculum: self.retrieve_skills, + RetrieveSkills: self.retrieve_skills, + GenerateSkillDescription: self.generate_skill_descp, + AddNewSkills: self.handle_add_new_skills, + } + handler = handler_map.get(type(todo)) + if handler: + msg = await handler(**message) + msg.cause_by = type(todo) + self._publish_message(msg) + return msg + + raise ValueError(f"Unknown todo type: {type(todo)}") diff --git a/metagpt/utils/minecraft/__init__.py b/metagpt/utils/minecraft/__init__.py new file mode 100644 index 000000000..b6d3ec8fd --- /dev/null +++ b/metagpt/utils/minecraft/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/24 0:32 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : diff --git a/metagpt/utils/minecraft/load_prompts.py b/metagpt/utils/minecraft/load_prompts.py new file mode 100644 index 000000000..3af9c6c57 --- /dev/null +++ b/metagpt/utils/minecraft/load_prompts.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/24 11:03 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +import pkg_resources + + +def load_prompt(prompt): + pass \ No newline at end of file diff --git a/minecraft_run.py b/minecraft_run.py new file mode 100644 index 000000000..65db2b98d --- /dev/null +++ b/minecraft_run.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/9/24 11:09 +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +import asyncio + +from metagpt.roles.minecraft.curriculum_agent import CurriculumDesigner +from metagpt.roles.minecraft.skill_manager import SkillManager +from metagpt.roles.minecraft.action_developer import ActionDeveloper +from metagpt.roles.minecraft.critic_agent import CriticReviewer +from metagpt.minecraft_team import MinecraftPlayer + + +async def learn(task="Start", investment: float = 50.0, n_round: int = 3): + mc_player = MinecraftPlayer() + mc_player.hire( + [ + CurriculumDesigner(), + ActionDeveloper(), + CriticReviewer(), + SkillManager(), + + ] + ) + + mc_player.invest(investment) + mc_player.start(task) + await mc_player.run(n_round=n_round) + + +if __name__ == "__main__": + asyncio.run(learn())