From 37a7a96bbe0403c3719b8a1a8486f348f1d5fc4e Mon Sep 17 00:00:00 2001 From: stellahsr Date: Mon, 9 Oct 2023 19:32:50 +0800 Subject: [PATCH 1/8] update code --- Temp.md | 5 + config/config.yaml | 5 +- .../actions/minecraft/design_curriculumn.py | 78 +++++------ metagpt/actions/minecraft/generate_actions.py | 17 +-- metagpt/actions/minecraft/manage_skills.py | 25 ++-- metagpt/actions/minecraft/player_action.py | 74 ++++++----- metagpt/config.py | 1 + metagpt/minecraft_team.py | 55 +++++--- metagpt/mineflayer_env/mineflayer/index.js | 2 + metagpt/roles/minecraft/action_developer.py | 17 +-- metagpt/roles/minecraft/curriculum_agent.py | 125 +++++++++--------- metagpt/roles/minecraft/skill_manager.py | 61 ++++----- minecraft_run.py | 5 +- 13 files changed, 255 insertions(+), 215 deletions(-) diff --git a/Temp.md b/Temp.md index e5972438c..972860d64 100644 --- a/Temp.md +++ b/Temp.md @@ -31,6 +31,8 @@ ### 0926: 环境信息获取和更新 on_event()实际内容 mc_player.set_port(2465) # Modify this to your LAN port ``` +*注意世界游戏模式为生存,这样人物才会有血量,饥饿和库存概念;* + python minecraft_run.py @@ -67,3 +69,6 @@ ### 0930:Curriculum agent 更新 1. 若本地已克隆项目不好更改可尝试:删除 metagpt/mineflayer_env/mineflayer + 重新copy voyager/env/mineflayer到目录下 + (npm install...0926.B命令) 2. 重新拉取最新提交+重新配置 +### 第一人称视角 + +目前使用3007端口,可自行修改,代码运行以后,在浏览器输入127.0.0.1:3007访问 \ No newline at end of file diff --git a/config/config.yaml b/config/config.yaml index 2f16efa1b..b466feb2d 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -84,4 +84,7 @@ MODEL_FOR_RESEARCHER_REPORT: gpt-3.5-turbo-16k ### browser path for pyppeteer engine, support Chrome, Chromium,MS Edge #PYPPETEER_EXECUTABLE_PATH: "/usr/bin/google-chrome-stable" -PROMPT_FORMAT: json #json or markdown \ No newline at end of file +PROMPT_FORMAT: json #json or markdown + +### Minecraft +RESUME: false # If load json from ckpt dir(include chest_memory, skills, ...) \ No newline at end of file diff --git a/metagpt/actions/minecraft/design_curriculumn.py b/metagpt/actions/minecraft/design_curriculumn.py index a5e321326..eadb1b0cb 100644 --- a/metagpt/actions/minecraft/design_curriculumn.py +++ b/metagpt/actions/minecraft/design_curriculumn.py @@ -21,11 +21,11 @@ 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) self.llm.model = "gpt-3.5-turbo" - + async def decompose_task(self, query, events): system_msgs = SystemMessage( content=load_prompt("curriculum_task_decomposition") @@ -34,11 +34,11 @@ class DesignTask(Action): events=events, chest_observation="" ) + HumanMessage(content=f"Final task: {query}") logger.info(f"Curriculum Agent task decomposition\nFinal task: {query}") - + rsp = await self._aask(prompt=prompt, system_msgs=system_msgs) logger.info(f"Curriculum Agent task decomposition\n{rsp}") return fix_and_parse_json(rsp) - + def parse_llm_response(self, llm_resp): task = "" for line in llm_resp.split("\n"): @@ -46,14 +46,14 @@ class DesignTask(Action): task = line[5:].replace(".", "").strip() assert task, "Task not found in Curriculum Agent response" return {"next_task": task} - + async def generate_task(self, human_msg, system_msg, max_retries=5): """ Refer to the code in the voyager/agents/curriculum.py propose_next_ai_task() for implementation details. Returns: task & context """ - + if max_retries == 0: raise RuntimeError("Max retries reached, failed to propose task.") curriculum = await self._aask(prompt=human_msg, system_msgs=system_msg) @@ -72,14 +72,14 @@ class DesignTask(Action): system_msg=system_msg, max_retries=max_retries - 1, ) - + async def run(self, human_msg, system_msg, *args, **kwargs): logger.info(f"run {self.__repr__()}") - + # Call the language model to generate a response. - + task = await self.generate_task(human_msg=human_msg, system_msg=system_msg) - + return task @@ -88,49 +88,51 @@ 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) - # voyager vectordb using - - @classmethod - def generate_qa(cls, events, chest_observation): + + async def generate_qa(self, events, human_msg, system_msg): """ Generate qa for DesignTask's HumanMessage """ - questions_new, _ = cls.generate_qa_step1( - events=events, chest_observation=chest_observation + questions_new = await self.generate_qa_step1( + events=events, human_msg=human_msg, system_msg=system_msg ) + logger.debug(f"Generate_qa_step1 result list is HERE: {questions_new}") + questions = [] answers = [] for question in questions_new: - if cls.qa_cache_questions_vectordb._collection.count() > 0: + if self.qa_cache_questions_vectordb._collection.count() > 0: docs_and_scores = ( - cls.qa_cache_questions_vectordb.similarity_search_with_score( + self.qa_cache_questions_vectordb.similarity_search_with_score( question, k=1 ) ) if docs_and_scores and docs_and_scores[0][1] < 0.05: question_cached = docs_and_scores[0][0].page_content - assert question_cached in cls.qa_cache - answer_cached = cls.qa_cache[question_cached] + assert question_cached in self.qa_cache + answer_cached = self.qa_cache[question_cached] questions.append(question_cached) answers.append(answer_cached) continue - answer = cls.generate_qa_step2(question=question) - assert question not in cls.qa_cache - cls.qa_cache[question] = answer - cls.qa_cache_questions_vectordb.add_texts( + answer = await self.generate_qa_step2(question=question) + assert question not in self.qa_cache + self.qa_cache[question] = answer + self.qa_cache_questions_vectordb.add_texts( texts=[question], ) with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "w") as f: - json.dump(cls.qa_cache, f) - cls.qa_cache_questions_vectordb.persist() + json.dump(self.qa_cache, f) + self.qa_cache_questions_vectordb.persist() questions.append(question) answers.append(answer) assert len(questions_new) == len(questions) == len(answers) + logger.info(f"Curriculum Agent generate_qa Questions: {questions}") + logger.info(f"Curriculum Agent generate_qa Answers: {answers}") return questions, answers - + async def generate_qa_step1(self, events, human_msg, system_msg): biome = events[-1][1]["status"]["biome"].replace("_", " ") questions = [ @@ -139,7 +141,7 @@ class DesignCurriculum(Action): f"What are the mobs that I can find in the {biome} in Minecraft?", ] qa_response = await self._aask(prompt=human_msg, system_msgs=system_msg) - + try: # Regex pattern to extract question and concept pairs pattern = r"Question \d+: (.+)\nConcept \d+: (.+)" @@ -154,10 +156,10 @@ class DesignCurriculum(Action): f"QA step 1 ask questions: {e}." ) return questions - + async def generate_qa_step2(self, question): # Implement the logic for another specific step in generating questions and answers. - logger.info(f"Curriculum Agent Question: {question}") + # logger.info(f"Curriculum Agent generate_qa_step2 Question: {question}") human_msg = HumanMessage(content=f"Question: {question}").content system_msg = [ SystemMessage( @@ -165,16 +167,16 @@ class DesignCurriculum(Action): ).content ] answer = await self._aask(prompt=human_msg, system_msgs=system_msg) - logger.info(f"Curriculum Agent {answer}") + # logger.info(f"Curriculum Agent generate_qa_step2 answer: {answer}") return answer - + async def get_context_from_task(self, task): """ Args: task Returns: context: "Question: {question}\n{answer}" if include ore in question, gpt will try to use tool with skill touch enhancement to mine """ - + question = ( f"How to {task.replace('_', ' ').replace(' ore', '').replace(' ores', '').replace('.', '').strip().lower()}" f" in Minecraft?" @@ -192,14 +194,14 @@ class DesignCurriculum(Action): self.qa_cache_questions_vectordb.persist() context = f"Question: {question}\n{answer}" return context - + async def generate_context(self, task, max_retries=5): """ Refer to the code in the voyager/agents/curriculum.py propose_next_ai_task() for implementation details. Returns: context """ - + if max_retries == 0: raise RuntimeError("Max retries reached, failed to propose context.") try: @@ -213,12 +215,12 @@ class DesignCurriculum(Action): task=task, max_retries=max_retries - 1, ) - + async def run(self, task, 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) - + # Return the generated questions and answers. return curriculum_context diff --git a/metagpt/actions/minecraft/generate_actions.py b/metagpt/actions/minecraft/generate_actions.py index a669b5374..cc83c8abb 100644 --- a/metagpt/actions/minecraft/generate_actions.py +++ b/metagpt/actions/minecraft/generate_actions.py @@ -12,11 +12,11 @@ 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) self.llm.model = "gpt-4" - + async def generate_code(self, human_msg, system_msg=[]): """ Generate action code logic. @@ -27,22 +27,23 @@ class GenerateActionCode(Action): rsp = await self._aask(prompt=human_msg, system_msgs=system_msg) parsed_result = parse_action_response(rsp) # logger.info(f"parsed_result is HERE: {parsed_result}") - + try: return ( + parsed_result["program_code"], parsed_result["program_code"] + "\n" + parsed_result["exec_code"], parsed_result["program_name"], ) except: logger.error(f"Failed to parse response: {parsed_result}") - return None, None - + return None, None, None # TODO: midify to "", "", "" + async def run(self, human_msg, system_msg, *args, **kwargs): logger.info(f"run {self.__repr__()}") # Generate action code. - generated_code, program_name = await self.generate_code( + program_code, generated_code, program_name = await self.generate_code( human_msg=human_msg, system_msg=system_msg ) - + # Return the generated code. - return generated_code, program_name + return program_code, generated_code, program_name diff --git a/metagpt/actions/minecraft/manage_skills.py b/metagpt/actions/minecraft/manage_skills.py index 35d35e27b..208bb9360 100644 --- a/metagpt/actions/minecraft/manage_skills.py +++ b/metagpt/actions/minecraft/manage_skills.py @@ -5,6 +5,8 @@ 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 @@ -15,11 +17,11 @@ 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): # Implement the logic for retrieving skills here. k = min(self.vectordb._collection.count(), self.retrieval_top_k) @@ -42,13 +44,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, *args, **kwargs ): # Implement the logic for adding new skills here. # TODO: Fix this @@ -75,12 +77,7 @@ class AddNewSkills(Action): ids=[program_name], metadatas=[{"name": program_name}], ) - - # FIXME - # assert self.vectordb._collection.count() == len( - # skills - # ), "vectordb is not synced with skills.json" - + 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: @@ -99,13 +96,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 d83deff8f..80d995074 100644 --- a/metagpt/actions/minecraft/player_action.py +++ b/metagpt/actions/minecraft/player_action.py @@ -2,58 +2,70 @@ # @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.document_store import FaissStore + 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 = {} + self.skills = {} # for skills.json self.qa_cache = {} + 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 = json.load(f) + + with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "r") as f: + self.qa_cache = 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", ) - # TODO: change to FaissStore - # self.qa_cache_questions_vectordb = FaissStore( {CKPT_DIR}/ 'curriculum/vectordb' - @classmethod - def set_skills(cls, skills): - cls.skills = skills - # Check if Skill Manager's vectordb right using - assert cls.vectordb._collection.count() == len(cls.skills), ( - f"Skill Manager's vectordb is not synced with skills.json.\n" - f"There are {cls.vectordb._collection.count()} skills in vectordb but {len(cls.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." - ) + # FIXME + if self.check_init: + # 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." + ) + # Check if Skill Manager's vectordb right using + 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" + ) + self.check_init = False + # TODO: change to FaissStore + # self.qa_cache_questions_vectordb = FaissStore( {CKPT_DIR}/ 'curriculum/vectordb' - @classmethod - def set_qa_cache(cls, qa_cache): - cls.qa_cache = qa_cache - # Check if qa_cache right using - # Check if Skill Manager's vectordb right using - assert cls.qa_cache_questions_vectordb._collection.count() == len( - cls.qa_cache - ), ( - f"Curriculum Agent's qa cache question vectordb is not synced with qa_cache.json.\n" - f"There are {cls.qa_cache_questions_vectordb._collection.count()} questions in vectordb " - f"but {len(cls.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" - ) - """Minecraft player info without any implementation details""" + async def run(self, *args, **kwargs): - raise NotImplementedError \ No newline at end of file + raise NotImplementedError diff --git a/metagpt/config.py b/metagpt/config.py index 53271133b..8c86475c0 100644 --- a/metagpt/config.py +++ b/metagpt/config.py @@ -87,6 +87,7 @@ class Config(metaclass=Singleton): self.pyppeteer_executable_path = self._get("PYPPETEER_EXECUTABLE_PATH", "") self.prompt_format = self._get("PROMPT_FORMAT", "markdown") + self.resume = self._get("RESUME", False) def _init_with_config_files_and_env(self, configs: dict, yaml_file): """Load from config/key.yaml, config/config.yaml, and env in decreasing order of priority""" diff --git a/metagpt/minecraft_team.py b/metagpt/minecraft_team.py index 5d9243468..8194de735 100644 --- a/metagpt/minecraft_team.py +++ b/metagpt/minecraft_team.py @@ -4,7 +4,7 @@ # @Desc : from typing import Iterable, Dict, Any from pydantic import BaseModel, Field -import requests + import json import re @@ -18,6 +18,7 @@ from metagpt.roles.minecraft.minecraft_base import Minecraft from metagpt.environment import Environment from metagpt.mineflayer_environment import MineflayerEnv from metagpt.const import CKPT_DIR +from metagpt.config import CONFIG from metagpt.actions.minecraft.control_primitives import load_skills_code @@ -33,6 +34,7 @@ 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_name: str = Field(default="") critique: str = Field(default="") skills: dict = Field(default_factory=dict) # for skills.json @@ -76,12 +78,13 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): @property def core_inv_items_regex(self): return self.mf_instance.core_inv_items_regex - + def set_mc_port(self, mc_port): self.mf_instance.set_mc_port(mc_port) + self.set_mc_resume() - def set_mc_resume(self, resume: bool = False): # TODO: mv to config - if 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) @@ -91,16 +94,23 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): self.completed_tasks = json.load(f) with open(f"{CKPT_DIR}/curriculum/failed_tasks.json", "r") as f: self.failed_tasks = json.load(f) - with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "r") as f: - self.qa_cache = json.load(f) - + 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) def register_roles(self, roles: Iterable[Minecraft]): for role in roles: role.set_memory(self) + for act in role.vdb_actions: + logger.info(act) + act.set_qa_cache_questions_vectordb(self.qa_cache_questions_vectordb) + act.set_qa_cache(self.qa_cache) + act.set_vectordb(self.vectordb) def update_event(self, event: Dict): if self.event == event: @@ -114,7 +124,10 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): def update_context(self, context: str): self.context = context - + + def update_program_code(self, program_code: str): + self.program_code = program_code + def update_code(self, code: str): self.code = code # action_developer.gen_action_code to HERE @@ -335,9 +348,6 @@ class MinecraftPlayer(SoftwareCompany): def set_port(self, mc_port): self.game_memory.set_mc_port(mc_port) - def set_resume(self, resume: bool = False): - self.game_memory.set_mc_resume(resume=resume) - def check_complete_round(self): complete_round = [] for role in self.environment.roles.values(): @@ -380,12 +390,23 @@ class MinecraftPlayer(SoftwareCompany): async def run(self, n_round=3): """Run company until target round or no money""" round_id = 0 - self.game_memory.mf_instance.reset( - options={ - "mode": "soft", - "wait_ticks": 20, - } - ) + if CONFIG.resume: + # keep the inventory + self.game_memory.mf_instance.reset( + options={ + "mode": "soft", + "wait_ticks": 20, + } + ) + else: + # clear the inventory + self.game_memory.mf_instance.reset( + options={ + "mode": "hard", + "wait_ticks": 20, + } + ) + events = self.game_memory.mf_instance.step( code="", programs="", diff --git a/metagpt/mineflayer_env/mineflayer/index.js b/metagpt/mineflayer_env/mineflayer/index.js index 7fb0a8787..3887e124b 100644 --- a/metagpt/mineflayer_env/mineflayer/index.js +++ b/metagpt/mineflayer_env/mineflayer/index.js @@ -14,6 +14,7 @@ const Inventory = require("./lib/observation/inventory"); const OnSave = require("./lib/observation/onSave"); const Chests = require("./lib/observation/chests"); const { plugin: tool } = require("mineflayer-tool"); +const { mineflayer: mineflayerViewer } = require('prismarine-viewer') let bot = null; @@ -50,6 +51,7 @@ app.post("/start", (req, res) => { }); bot.once("spawn", async () => { + mineflayerViewer(bot, { port: 3007, firstPerson: false }) bot.removeListener("error", onConnectionFailed); let itemTicks = 1; if (req.body.reset === "hard") { diff --git a/metagpt/roles/minecraft/action_developer.py b/metagpt/roles/minecraft/action_developer.py index 5b088c86a..6d3d30a20 100644 --- a/metagpt/roles/minecraft/action_developer.py +++ b/metagpt/roles/minecraft/action_developer.py @@ -2,8 +2,6 @@ # @Date : 2023/9/23 12:45 # @Author : stellahong (stellahong@fuzhi.ai) # @Desc : -import copy - from metagpt.logs import logger from metagpt.roles.minecraft.minecraft_base import Minecraft as Base from metagpt.schema import Message, HumanMessage, SystemMessage @@ -40,14 +38,13 @@ class ActionDeveloper(Base): # 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]) self.rollout_num_iter = 0 self.task_max_retries = 4 self.finish_state = len(self._actions) - self.critic_reviewer = None # self._rc.env.roles["Task Reviewer"] + self.critic_reviewer = None # self._rc.env.roles["Task Reviewer"] def render_system_message(self, skills=[], *args, **kwargs): """ @@ -208,6 +205,7 @@ class ActionDeveloper(Base): async def handle_add_new_skills( self, task, program_name, program_code, skills, *args, **kwargs ): + skills = self.game_memory.skills skill_desp = self.game_memory.skill_desp new_skills_info = await AddNewSkills().run( task, program_name, program_code, skills, skill_desp @@ -231,11 +229,9 @@ class ActionDeveloper(Base): context = self.game_memory.context # 更新生成的代码和对应程序名称 - code, program_name = await GenerateActionCode().run( + program_code, code, program_name = await GenerateActionCode().run( human_msg, system_msg, *args, **kwargs ) - # logger.warning(type(code)) - # logger.info(f"Code is Here:{code}") if code is not None: # fixme:若有独立的mc code执行入口函数,使用独立的函数 @@ -298,6 +294,8 @@ class ActionDeveloper(Base): "success": self.game_memory.runtime_status, } logger.info(f"info is {info}") + + self.perform_game_info_callback(program_code, self.game_memory.update_program_code) self.perform_game_info_callback(code, self.game_memory.update_code) self.perform_game_info_callback( program_name, self.game_memory.update_program_name @@ -306,11 +304,10 @@ class ActionDeveloper(Base): return system_msg, human_msg, 0, done, info async def generate_action_code(self, human_msg, system_msg, *args, **kwargs): - code, program_name = await GenerateActionCode().run( + program_code, code, program_name = await GenerateActionCode().run( human_msg, system_msg, *args, **kwargs ) - # logger.warning(type(code)) - # logger.info(f"Code is Here:{code}") + self.perform_game_info_callback(program_code, self.game_memory.update_program_code) self.perform_game_info_callback(code, self.game_memory.update_code) self.perform_game_info_callback( program_name, self.game_memory.update_program_name diff --git a/metagpt/roles/minecraft/curriculum_agent.py b/metagpt/roles/minecraft/curriculum_agent.py index 1b2b12d2f..33d39a81f 100644 --- a/metagpt/roles/minecraft/curriculum_agent.py +++ b/metagpt/roles/minecraft/curriculum_agent.py @@ -18,29 +18,29 @@ 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.", + 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]) logger.info(self._actions) self.finish_state = len(self._actions) - + def render_curriculum_observation(self, *, events, chest_observation): """ Returns: observation for curriculum Refer to @ https://github.com/MineDojo/Voyager/blob/main/voyager/agents/curriculum.py """ - + assert events[-1][0] == "observe", "Last event must be observe" event = events[-1][1] biome = event["status"]["biome"] @@ -54,31 +54,31 @@ class CurriculumDesigner(Base): equipment = event["status"]["equipment"] inventory_used = event["status"]["inventoryUsed"] inventory = event["inventory"] - + if not any( - "dirt" in block - or "log" in block - or "grass" in block - or "sand" in block - or "snow" in block - for block in voxels + "dirt" in block + or "log" in block + or "grass" in block + or "sand" in block + or "snow" in block + for block in voxels ): biome = "underground" - + other_blocks = ", ".join( list( set(block_records).difference(set(voxels).union(set(inventory.keys()))) ) ) - + other_blocks = other_blocks if other_blocks else "None" - + nearby_entities = ( ", ".join([k for k, v in sorted(entities.items(), key=lambda x: x[1])]) if entities else "None" ) - + completed_tasks = ( ", ".join(self.game_memory.completed_tasks) if self.game_memory.completed_tasks @@ -89,18 +89,18 @@ class CurriculumDesigner(Base): if self.game_memory.failed_tasks else "None" ) - + # filter out optional inventory items if required if ( - self.game_memory.progress - < self.game_memory.warm_up["optional_inventory_items"] + self.game_memory.progress + < self.game_memory.warm_up["optional_inventory_items"] ): inventory = { k: v for k, v in inventory.items() if self.game_memory.core_inv_items_regex.search(k) is not None } - + observation = { "context": "", "biome": f"Biome: {biome}\n\n", @@ -118,25 +118,31 @@ class CurriculumDesigner(Base): "failed_tasks": f"Failed tasks that are too hard: {failed_tasks}\n\n", } return observation - + # --------------------------------Design Task Prepare--------------------------------------- - def render_design_task_human_message( - self, events, chest_observation, *args, **kwargs + async def render_design_task_human_message( + self, events, chest_observation, *args, **kwargs ): """ Returns: observation for curriculum Refer to @ https://github.com/MineDojo/Voyager/blob/main/voyager/agents/curriculum.py """ - + content = "" warm_up = self.game_memory.mf_instance.warm_up observation = self.render_curriculum_observation( events=events, chest_observation=chest_observation ) if self.game_memory.progress >= warm_up["context"]: - questions, answers = DesignCurriculum.generate_qa( + # if self.game_memory.progress >= 0: # TEST ONLY + human_msg = self.render_design_curriculum_human_message( events=events, chest_observation=chest_observation + ).content + system_msg = [self.render_design_curriculum_system_message().content] + questions, answers = await self.curriculum_design_action.generate_qa( + events=events, human_msg=human_msg, system_msg=system_msg ) + logger.debug(f"Generate_qa result is HERE: Ques: {questions}, Ans: {answers}") i = 1 for question, answer in zip(questions, answers): if "Answer: Unknown" in answer or "language model" in answer: @@ -146,7 +152,7 @@ class CurriculumDesigner(Base): i += 1 if i > 5: break - + for key in CURRICULUM_OB: if self.game_memory.progress >= warm_up[key]: if warm_up[key] != 0: @@ -155,20 +161,20 @@ class CurriculumDesigner(Base): should_include = True if should_include: content += observation[key] - + logger.info(f"Curriculum Agent human message\n{content}") return HumanMessage(content=content) - + def render_design_task_system_message(self, *args, **kwargs): return SystemMessage(content=load_prompt("curriculum")) - - def encapsule_design_task_message(self, events, chest_observation, *args, **kwargs): - human_msg = self.render_design_task_human_message( + + async def encapsule_design_task_message(self, events, chest_observation, *args, **kwargs): + human_msg = await self.render_design_task_human_message( events=events, chest_observation=chest_observation, *args, **kwargs ) system_msg = self.render_design_task_system_message(*args, **kwargs) return {"system_msg": [system_msg.content], "human_msg": human_msg.content} - + def generate_task_if_inventory_full(self, events, chest_observation): """ TODO: Try if this could be done with prompt @@ -187,15 +193,15 @@ class CurriculumDesigner(Base): else: task = "Craft 1 chest" return task - + # ----------------------------------------------------------------------------------------- - + # --------------------------------Design Curriculum Prepare-------------------------------- def render_design_curriculum_system_message(self, *args, **kwargs): return SystemMessage(content=load_prompt("curriculum_qa_step1_ask_questions")) - + def render_design_curriculum_human_message( - self, events, chest_observation, *args, **kwargs + self, events, chest_observation, *args, **kwargs ): observation = self.render_curriculum_observation( events=events, chest_observation=chest_observation @@ -204,16 +210,16 @@ class CurriculumDesigner(Base): for key in CURRICULUM_OB: content += observation[key] return HumanMessage(content=content) - + def encapsule_design_curriculum_message( - self, events, chest_observation, *args, **kwargs + self, events, chest_observation, *args, **kwargs ): human_msg = self.render_design_curriculum_human_message( events=events, chest_observation=chest_observation, *args, **kwargs ) system_msg = self.render_design_curriculum_system_message(*args, **kwargs) return {"system_msg": [system_msg.content], "human_msg": human_msg.content} - + def generate_context_if_inventory_full(self, events, chest_observation): """ TODO: Try if this could be done with prompt @@ -244,9 +250,9 @@ class CurriculumDesigner(Base): else: context = "Craft 1 chest with 8 planks of any kind of wood." return context - + # ----------------------------------------------------------------------------------------- - + async def handle_task_design(self, human_msg, system_msg, *args, **kwargs): """ Args: @@ -260,7 +266,7 @@ class CurriculumDesigner(Base): events = self.game_memory.event chest_observation = self.game_memory.chest_observation inventoryUsed = events[-1][1]["status"]["inventoryUsed"] - + if self.game_memory.progress == 0: task = self.game_memory.current_task elif inventoryUsed >= 33: @@ -270,12 +276,12 @@ class CurriculumDesigner(Base): else: task = await DesignTask().run(human_msg, system_msg, *args, **kwargs) logger.info(f"Handle_task_design result is Here: {task}") - + 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 @@ -292,7 +298,7 @@ class CurriculumDesigner(Base): chest_observation = self.game_memory.chest_observation inventoryUsed = events[-1][1]["status"]["inventoryUsed"] task = self.game_memory.current_task - + if self.game_memory.progress == 0: context = self.game_memory.context elif inventoryUsed >= 33: @@ -300,37 +306,34 @@ class CurriculumDesigner(Base): self, events=events, chest_observation=chest_observation ) else: - context = await DesignCurriculum().run( + context = await self.DesignCurriculum().run( task, 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}") - self.maintain_actions(todo) + self.maintain_actions(todo) + # 获取最新的游戏周边环境信息 # events = await self._obtain_events() events = self.game_memory.event chest_observation = self.game_memory.chest_observation - - # DesignCurriculum.set_qa_cache(self.game_memory.qa_cache) - - # msg = self._rc.memory.get(k=1)[0] - # query = msg.content - - design_task_message = self.encapsule_design_task_message( + + design_task_message = await self.encapsule_design_task_message( events, chest_observation ) design_curriculum_message = self.encapsule_design_curriculum_message( events, chest_observation ) - + handler_map = { DesignTask: self.handle_task_design, DesignCurriculum: self.handle_curriculum_design, @@ -345,5 +348,5 @@ class CurriculumDesigner(Base): msg.round_id = self.round_id self._publish_message(msg) return msg - + raise ValueError(f"Unknown todo type: {type(todo)}") diff --git a/metagpt/roles/minecraft/skill_manager.py b/metagpt/roles/minecraft/skill_manager.py index c86d0d2be..aec802d15 100644 --- a/metagpt/roles/minecraft/skill_manager.py +++ b/metagpt/roles/minecraft/skill_manager.py @@ -19,41 +19,40 @@ 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]) #AddNewSkills])#先去掉add + 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) 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) @@ -63,16 +62,16 @@ 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 new_skills_info = await AddNewSkills().run( @@ -84,43 +83,41 @@ 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) + self.maintain_actions(todo) # 获取最新的游戏周边信息 + context = self.game_memory.context task = self.game_memory.current_task code = self.game_memory.code self.perform_game_info_callback(self.game_memory.event, self.game_memory.summarize_chatlog) event_summary = self.game_memory.event_summary - try: - program_code = code["program_code"] # TODO: Handle code is None, cuz first round DesignCurriculum(code is None) trigger this - except (KeyError, TypeError): - program_code = "" - + 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, @@ -139,10 +136,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)}") diff --git a/minecraft_run.py b/minecraft_run.py index ed9276265..a43da3e38 100644 --- a/minecraft_run.py +++ b/minecraft_run.py @@ -11,10 +11,9 @@ 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): +async def learn(task="Start", investment: float = 50.0, n_round: int = 50): mc_player = MinecraftPlayer() - mc_player.set_port(33141) # Modify this to your Minecraft LAN port - # mc_player.set_resume(True) # If load json from ckpt dir(include chest_memory, skills, ...) + mc_player.set_port(30181) # Modify this to your Minecraft LAN port mc_player.hire( [ CurriculumDesigner(), From 21639ebe96f08ab2ab655e04311239d6b278d61e Mon Sep 17 00:00:00 2001 From: stellahsr Date: Mon, 9 Oct 2023 20:19:02 +0800 Subject: [PATCH 2/8] rm vdb init --- metagpt/minecraft_team.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/metagpt/minecraft_team.py b/metagpt/minecraft_team.py index 8194de735..949eba081 100644 --- a/metagpt/minecraft_team.py +++ b/metagpt/minecraft_team.py @@ -106,11 +106,6 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): def register_roles(self, roles: Iterable[Minecraft]): for role in roles: role.set_memory(self) - for act in role.vdb_actions: - logger.info(act) - act.set_qa_cache_questions_vectordb(self.qa_cache_questions_vectordb) - act.set_qa_cache(self.qa_cache) - act.set_vectordb(self.vectordb) def update_event(self, event: Dict): if self.event == event: From a0a64e7f3bd776b8cdcaa742516652e32821c229 Mon Sep 17 00:00:00 2001 From: stellahsr Date: Mon, 9 Oct 2023 21:46:46 +0800 Subject: [PATCH 3/8] bug fix: use action --- metagpt/roles/minecraft/curriculum_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/roles/minecraft/curriculum_agent.py b/metagpt/roles/minecraft/curriculum_agent.py index 33d39a81f..ebe9fc01b 100644 --- a/metagpt/roles/minecraft/curriculum_agent.py +++ b/metagpt/roles/minecraft/curriculum_agent.py @@ -306,7 +306,7 @@ class CurriculumDesigner(Base): self, events=events, chest_observation=chest_observation ) else: - context = await self.DesignCurriculum().run( + context = await DesignCurriculum().run( task, human_msg, system_msg, *args, **kwargs ) From e27d97ee3c2680a48db329404feb848890442d2a Mon Sep 17 00:00:00 2001 From: stellahsr Date: Tue, 10 Oct 2023 00:42:00 +0800 Subject: [PATCH 4/8] bug fix: add reset before code generation bug fix: parse js code using original code --- metagpt/roles/minecraft/action_developer.py | 2 ++ metagpt/roles/minecraft/critic_agent.py | 7 +++---- metagpt/utils/minecraft/action_rsp_parser.py | 8 ++++++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/metagpt/roles/minecraft/action_developer.py b/metagpt/roles/minecraft/action_developer.py index 6d3d30a20..07136a82c 100644 --- a/metagpt/roles/minecraft/action_developer.py +++ b/metagpt/roles/minecraft/action_developer.py @@ -187,6 +187,8 @@ class ActionDeveloper(Base): return len(self._rc.news) async def run_step(self, human_msg, system_msg, *args, **kwargs): + await self._obtain_events() + logger.info("reset before step()!") while True: logger.info(f"self.rollout_num_iter {self.rollout_num_iter}") system_msg, human_msg, reward, done, info = await self.runcode_and_evaluate(human_msg, system_msg, *args, diff --git a/metagpt/roles/minecraft/critic_agent.py b/metagpt/roles/minecraft/critic_agent.py index ba37689c7..53ca69fab 100644 --- a/metagpt/roles/minecraft/critic_agent.py +++ b/metagpt/roles/minecraft/critic_agent.py @@ -132,7 +132,7 @@ class CriticReviewer(Base): } async def verify_task(self, human_msg, system_msg, *args, **kwargs): - success, critique = await VerifyTask().run(human_msg, system_msg, max_retries=5) + success, critique = await VerifyTask().run(human_msg, system_msg, max_retries=1) self.perform_game_info_callback( success, self.game_memory.update_exploration_progress ) @@ -144,8 +144,7 @@ class CriticReviewer(Base): instruct_content="verify_task", role=self.profile, send_to=agent_registry.entries["skill_manager"]()._setting.name, - ) # addnewskill - # TODO:if not success + ) async def _act(self) -> Message: @@ -158,7 +157,7 @@ class CriticReviewer(Base): # 获取最新的游戏周边信息 events = await self._execute_events() self.perform_game_info_callback(events, self.game_memory.update_chest_memory) - # logger.info(f"Execute return event is {self.game_memory.event}") + context = self.game_memory.context task = self.game_memory.current_task chest_observation = self.game_memory.chest_observation diff --git a/metagpt/utils/minecraft/action_rsp_parser.py b/metagpt/utils/minecraft/action_rsp_parser.py index 91ccc141d..762da9e99 100644 --- a/metagpt/utils/minecraft/action_rsp_parser.py +++ b/metagpt/utils/minecraft/action_rsp_parser.py @@ -38,10 +38,14 @@ def parse_action_response(msg: str): retry = 3 error = None # 3 times failed return error - babel_generator = require("@babel/generator").default while retry > 0: try: - parsed = parse_js_code(msg) + babel = require("@babel/core") + babel_generator = require("@babel/generator").default + + code_pattern = re.compile(r"```(?:javascript|js)(.*?)```", re.DOTALL) + code = "\n".join(code_pattern.findall(msg)) + parsed = babel.parse(code) # Collect func list: check if func & async functions = [] assert len(list(parsed.program.body)) > 0, "No functions found" From 896932e02d90eaee4cb7246333fe64eb32923f67 Mon Sep 17 00:00:00 2001 From: stellahsr Date: Tue, 10 Oct 2023 19:25:30 +0800 Subject: [PATCH 5/8] bug fix: add exception handle --- metagpt/minecraft_team.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/metagpt/minecraft_team.py b/metagpt/minecraft_team.py index 949eba081..89a9e6fa9 100644 --- a/metagpt/minecraft_team.py +++ b/metagpt/minecraft_team.py @@ -7,6 +7,7 @@ from pydantic import BaseModel, Field import json import re +import time from metagpt.logs import logger from metagpt.roles import Role @@ -299,9 +300,30 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): ) self.update_event(events) return events + except Exception as e: + ### add: 异常的话结束当前流程,继续后续运行 logger.error(f"Failed to retrieve Minecraft events: {str(e)}") - raise {} + time.sleep(3) # wait for mineflayer to exit + info = { + "task": self.current_task, + "success": False, + } + # reset bot status here + events = self.mf_instance.reset( + options={ + "mode": "hard", + "wait_ticks": 20, + "inventory": self.events[-1][1]["inventory"], + "equipment": self.events[-1][1]["status"]["equipment"], + "position": self.events[-1][1]["status"]["position"], + } + ) + self.update_event(events) + # use red color background to print the error + logger.info("Your last round rollout terminated due to error:") + logger.info(f"\033[41m{e}\033[0m") + async def on_event_execute(self, *args): """ From 4bb69f45034ab0b42065ceb294c36e114e2c3e18 Mon Sep 17 00:00:00 2001 From: stellahsr Date: Tue, 10 Oct 2023 19:46:59 +0800 Subject: [PATCH 6/8] update code: add exception in execution --- metagpt/minecraft_team.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/metagpt/minecraft_team.py b/metagpt/minecraft_team.py index 89a9e6fa9..8d426261f 100644 --- a/metagpt/minecraft_team.py +++ b/metagpt/minecraft_team.py @@ -346,8 +346,27 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): self.update_event(events) return events except Exception as e: - logger.error(f"Failed to execute Minecraft events: {str(e)}") - raise {} + ### add: 异常的话结束当前流程,继续后续运行 + logger.error(f"Failed to retrieve Minecraft events: {str(e)}") + time.sleep(3) # wait for mineflayer to exit + info = { + "task": self.current_task, + "success": False, + } + # reset bot status here + events = self.mf_instance.reset( + options={ + "mode": "hard", + "wait_ticks": 20, + "inventory": self.events[-1][1]["inventory"], + "equipment": self.events[-1][1]["status"]["equipment"], + "position": self.events[-1][1]["status"]["position"], + } + ) + self.update_event(events) + # use red color background to print the error + logger.info("Your last round rollout terminated due to error:") + logger.info(f"\033[41m{e}\033[0m") class MinecraftPlayer(SoftwareCompany): From dd9983983645a0e7022be13bf56088661392beee Mon Sep 17 00:00:00 2001 From: stellahsr Date: Wed, 11 Oct 2023 16:59:19 +0800 Subject: [PATCH 7/8] update code --- .../actions/minecraft/design_curriculumn.py | 73 +++++------ metagpt/actions/minecraft/generate_actions.py | 12 +- metagpt/actions/minecraft/player_action.py | 4 +- metagpt/minecraft_team.py | 40 ++---- metagpt/mineflayer_env/mineflayer/index.js | 4 +- metagpt/roles/minecraft/critic_agent.py | 5 +- metagpt/roles/minecraft/curriculum_agent.py | 116 +++++++++--------- metagpt/utils/minecraft/action_rsp_parser.py | 6 +- 8 files changed, 124 insertions(+), 136 deletions(-) diff --git a/metagpt/actions/minecraft/design_curriculumn.py b/metagpt/actions/minecraft/design_curriculumn.py index eadb1b0cb..3e2f33705 100644 --- a/metagpt/actions/minecraft/design_curriculumn.py +++ b/metagpt/actions/minecraft/design_curriculumn.py @@ -21,11 +21,11 @@ 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) self.llm.model = "gpt-3.5-turbo" - + async def decompose_task(self, query, events): system_msgs = SystemMessage( content=load_prompt("curriculum_task_decomposition") @@ -34,11 +34,11 @@ class DesignTask(Action): events=events, chest_observation="" ) + HumanMessage(content=f"Final task: {query}") logger.info(f"Curriculum Agent task decomposition\nFinal task: {query}") - + rsp = await self._aask(prompt=prompt, system_msgs=system_msgs) logger.info(f"Curriculum Agent task decomposition\n{rsp}") return fix_and_parse_json(rsp) - + def parse_llm_response(self, llm_resp): task = "" for line in llm_resp.split("\n"): @@ -46,14 +46,14 @@ class DesignTask(Action): task = line[5:].replace(".", "").strip() assert task, "Task not found in Curriculum Agent response" return {"next_task": task} - + async def generate_task(self, human_msg, system_msg, max_retries=5): """ Refer to the code in the voyager/agents/curriculum.py propose_next_ai_task() for implementation details. Returns: task & context """ - + if max_retries == 0: raise RuntimeError("Max retries reached, failed to propose task.") curriculum = await self._aask(prompt=human_msg, system_msgs=system_msg) @@ -72,14 +72,14 @@ class DesignTask(Action): system_msg=system_msg, max_retries=max_retries - 1, ) - + async def run(self, human_msg, system_msg, *args, **kwargs): logger.info(f"run {self.__repr__()}") - + # Call the language model to generate a response. - + task = await self.generate_task(human_msg=human_msg, system_msg=system_msg) - + return task @@ -88,11 +88,11 @@ 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) - - async def generate_qa(self, events, human_msg, system_msg): + + async def generate_qa(self, events, qa_cache, human_msg, system_msg): """ Generate qa for DesignTask's HumanMessage """ @@ -112,19 +112,19 @@ class DesignCurriculum(Action): ) if docs_and_scores and docs_and_scores[0][1] < 0.05: question_cached = docs_and_scores[0][0].page_content - assert question_cached in self.qa_cache - answer_cached = self.qa_cache[question_cached] + assert question_cached in qa_cache + answer_cached = qa_cache[question_cached] questions.append(question_cached) answers.append(answer_cached) continue answer = await self.generate_qa_step2(question=question) - assert question not in self.qa_cache - self.qa_cache[question] = answer + assert question not in qa_cache + qa_cache[question] = answer self.qa_cache_questions_vectordb.add_texts( texts=[question], ) with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "w") as f: - json.dump(self.qa_cache, f) + json.dump(qa_cache, f) self.qa_cache_questions_vectordb.persist() questions.append(question) answers.append(answer) @@ -132,7 +132,7 @@ class DesignCurriculum(Action): logger.info(f"Curriculum Agent generate_qa Questions: {questions}") logger.info(f"Curriculum Agent generate_qa Answers: {answers}") return questions, answers - + async def generate_qa_step1(self, events, human_msg, system_msg): biome = events[-1][1]["status"]["biome"].replace("_", " ") questions = [ @@ -141,7 +141,7 @@ class DesignCurriculum(Action): f"What are the mobs that I can find in the {biome} in Minecraft?", ] qa_response = await self._aask(prompt=human_msg, system_msgs=system_msg) - + try: # Regex pattern to extract question and concept pairs pattern = r"Question \d+: (.+)\nConcept \d+: (.+)" @@ -156,7 +156,7 @@ class DesignCurriculum(Action): f"QA step 1 ask questions: {e}." ) return questions - + async def generate_qa_step2(self, question): # Implement the logic for another specific step in generating questions and answers. # logger.info(f"Curriculum Agent generate_qa_step2 Question: {question}") @@ -169,58 +169,59 @@ class DesignCurriculum(Action): answer = await self._aask(prompt=human_msg, system_msgs=system_msg) # logger.info(f"Curriculum Agent generate_qa_step2 answer: {answer}") return answer - - async def get_context_from_task(self, task): + + async def get_context_from_task(self, task, qa_cache): """ Args: task Returns: context: "Question: {question}\n{answer}" if include ore in question, gpt will try to use tool with skill touch enhancement to mine """ - + question = ( f"How to {task.replace('_', ' ').replace(' ore', '').replace(' ores', '').replace('.', '').strip().lower()}" f" in Minecraft?" ) - if question in self.qa_cache: - answer = self.qa_cache[question] + if question in qa_cache: + answer = qa_cache[question] else: answer = await self.generate_qa_step2(question=question) - self.qa_cache[question] = answer + qa_cache[question] = answer self.qa_cache_questions_vectordb.add_texts( texts=[question], ) with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "w") as f: - json.dump(self.qa_cache, f) + json.dump(qa_cache, f) self.qa_cache_questions_vectordb.persist() context = f"Question: {question}\n{answer}" return context - - async def generate_context(self, task, max_retries=5): + + async def generate_context(self, task, qa_cache, max_retries=5): """ Refer to the code in the voyager/agents/curriculum.py propose_next_ai_task() for implementation details. Returns: context """ - + if max_retries == 0: raise RuntimeError("Max retries reached, failed to propose context.") try: context = await self.get_context_from_task( - task=task + task=task, qa_cache=qa_cache ) # Curriculum Agent Question: How to craft 4 wooden planks in Minecraft? & Curriculum Agent Answer: ... return context except Exception as e: logger.info(f"Error parsing curriculum response: {e}. Trying again!") return await self.generate_context( task=task, + qa_cache=qa_cache, max_retries=max_retries - 1, ) - - async def run(self, task, human_msg, system_msg, *args, **kwargs): + + async def run(self, task, qa_cache, 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) - + curriculum_context = await self.generate_context(task, qa_cache) + # Return the generated questions and answers. return curriculum_context diff --git a/metagpt/actions/minecraft/generate_actions.py b/metagpt/actions/minecraft/generate_actions.py index cc83c8abb..ca9fba391 100644 --- a/metagpt/actions/minecraft/generate_actions.py +++ b/metagpt/actions/minecraft/generate_actions.py @@ -12,11 +12,11 @@ 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) self.llm.model = "gpt-4" - + async def generate_code(self, human_msg, system_msg=[]): """ Generate action code logic. @@ -27,7 +27,7 @@ class GenerateActionCode(Action): rsp = await self._aask(prompt=human_msg, system_msgs=system_msg) parsed_result = parse_action_response(rsp) # logger.info(f"parsed_result is HERE: {parsed_result}") - + try: return ( parsed_result["program_code"], @@ -36,14 +36,14 @@ class GenerateActionCode(Action): ) except: logger.error(f"Failed to parse response: {parsed_result}") - return None, None, None # TODO: midify to "", "", "" - + return "", "", "" + async def run(self, human_msg, system_msg, *args, **kwargs): logger.info(f"run {self.__repr__()}") # Generate action code. program_code, generated_code, program_name = await self.generate_code( human_msg=human_msg, system_msg=system_msg ) - + # Return the generated code. return program_code, generated_code, program_name diff --git a/metagpt/actions/minecraft/player_action.py b/metagpt/actions/minecraft/player_action.py index 80d995074..4a90b84da 100644 --- a/metagpt/actions/minecraft/player_action.py +++ b/metagpt/actions/minecraft/player_action.py @@ -7,9 +7,11 @@ import json from metagpt.actions import Action from langchain.vectorstores import Chroma from langchain.embeddings.openai import OpenAIEmbeddings - +from metagpt.document_store import FaissStore from metagpt.const import CKPT_DIR from metagpt.config import CONFIG +from metagpt.logs import logger +from metagpt.actions.minecraft.control_primitives import load_skills_code class PlayerActions(Action): diff --git a/metagpt/minecraft_team.py b/metagpt/minecraft_team.py index 8d426261f..e86f6dfe8 100644 --- a/metagpt/minecraft_team.py +++ b/metagpt/minecraft_team.py @@ -4,11 +4,15 @@ # @Desc : from typing import Iterable, Dict, Any from pydantic import BaseModel, Field - +import requests import json import re import time +from langchain.vectorstores import Chroma +from langchain.embeddings.openai import OpenAIEmbeddings +from metagpt.document_store import FaissStore + from metagpt.logs import logger from metagpt.roles import Role from metagpt.schema import Message @@ -300,30 +304,20 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): ) self.update_event(events) return events - except Exception as e: - ### add: 异常的话结束当前流程,继续后续运行 - logger.error(f"Failed to retrieve Minecraft events: {str(e)}") time.sleep(3) # wait for mineflayer to exit - info = { - "task": self.current_task, - "success": False, - } # reset bot status here events = self.mf_instance.reset( options={ "mode": "hard", "wait_ticks": 20, - "inventory": self.events[-1][1]["inventory"], - "equipment": self.events[-1][1]["status"]["equipment"], - "position": self.events[-1][1]["status"]["position"], + "inventory": self.event[-1][1]["inventory"], + "equipment": self.event[-1][1]["status"]["equipment"], + "position": self.event[-1][1]["status"]["position"], } ) self.update_event(events) - # use red color background to print the error - logger.info("Your last round rollout terminated due to error:") - logger.info(f"\033[41m{e}\033[0m") - + logger.error(f"Failed to retrieve Minecraft events: {str(e)}") async def on_event_execute(self, *args): """ @@ -346,27 +340,19 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True): self.update_event(events) return events except Exception as e: - ### add: 异常的话结束当前流程,继续后续运行 - logger.error(f"Failed to retrieve Minecraft events: {str(e)}") time.sleep(3) # wait for mineflayer to exit - info = { - "task": self.current_task, - "success": False, - } # reset bot status here events = self.mf_instance.reset( options={ "mode": "hard", "wait_ticks": 20, - "inventory": self.events[-1][1]["inventory"], - "equipment": self.events[-1][1]["status"]["equipment"], - "position": self.events[-1][1]["status"]["position"], + "inventory": self.event[-1][1]["inventory"], + "equipment": self.event[-1][1]["status"]["equipment"], + "position": self.event[-1][1]["status"]["position"], } ) self.update_event(events) - # use red color background to print the error - logger.info("Your last round rollout terminated due to error:") - logger.info(f"\033[41m{e}\033[0m") + logger.error(f"Failed to execute Minecraft events: {str(e)}") class MinecraftPlayer(SoftwareCompany): diff --git a/metagpt/mineflayer_env/mineflayer/index.js b/metagpt/mineflayer_env/mineflayer/index.js index 3887e124b..02b64ba5a 100644 --- a/metagpt/mineflayer_env/mineflayer/index.js +++ b/metagpt/mineflayer_env/mineflayer/index.js @@ -224,7 +224,7 @@ app.post("/step", async (req, res) => { } } - bot.on("physicTick", onTick); + bot.on("physicsTick", onTick); // initialize fail count let _craftItemFailCount = 0; @@ -250,7 +250,7 @@ app.post("/step", async (req, res) => { response_sent = true; res.json(bot.observe()); } - bot.removeListener("physicTick", onTick); + bot.removeListener("physicsTick", onTick); async function evaluateCode(code, programs) { // Echo the code produced for players to see it. Don't echo when the bot code is already producing dialog or it will double echo diff --git a/metagpt/roles/minecraft/critic_agent.py b/metagpt/roles/minecraft/critic_agent.py index 53ca69fab..9bc34cbcc 100644 --- a/metagpt/roles/minecraft/critic_agent.py +++ b/metagpt/roles/minecraft/critic_agent.py @@ -144,7 +144,8 @@ class CriticReviewer(Base): instruct_content="verify_task", role=self.profile, send_to=agent_registry.entries["skill_manager"]()._setting.name, - ) + ) # addnewskill + # TODO:if not success async def _act(self) -> Message: @@ -157,7 +158,7 @@ class CriticReviewer(Base): # 获取最新的游戏周边信息 events = await self._execute_events() self.perform_game_info_callback(events, self.game_memory.update_chest_memory) - + # logger.info(f"Execute return event is {self.game_memory.event}") context = self.game_memory.context task = self.game_memory.current_task chest_observation = self.game_memory.chest_observation diff --git a/metagpt/roles/minecraft/curriculum_agent.py b/metagpt/roles/minecraft/curriculum_agent.py index ebe9fc01b..831d54999 100644 --- a/metagpt/roles/minecraft/curriculum_agent.py +++ b/metagpt/roles/minecraft/curriculum_agent.py @@ -18,29 +18,29 @@ 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.", + 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]) logger.info(self._actions) self.finish_state = len(self._actions) - + def render_curriculum_observation(self, *, events, chest_observation): """ Returns: observation for curriculum Refer to @ https://github.com/MineDojo/Voyager/blob/main/voyager/agents/curriculum.py """ - + assert events[-1][0] == "observe", "Last event must be observe" event = events[-1][1] biome = event["status"]["biome"] @@ -54,31 +54,31 @@ class CurriculumDesigner(Base): equipment = event["status"]["equipment"] inventory_used = event["status"]["inventoryUsed"] inventory = event["inventory"] - + if not any( - "dirt" in block - or "log" in block - or "grass" in block - or "sand" in block - or "snow" in block - for block in voxels + "dirt" in block + or "log" in block + or "grass" in block + or "sand" in block + or "snow" in block + for block in voxels ): biome = "underground" - + other_blocks = ", ".join( list( set(block_records).difference(set(voxels).union(set(inventory.keys()))) ) ) - + other_blocks = other_blocks if other_blocks else "None" - + nearby_entities = ( ", ".join([k for k, v in sorted(entities.items(), key=lambda x: x[1])]) if entities else "None" ) - + completed_tasks = ( ", ".join(self.game_memory.completed_tasks) if self.game_memory.completed_tasks @@ -89,18 +89,18 @@ class CurriculumDesigner(Base): if self.game_memory.failed_tasks else "None" ) - + # filter out optional inventory items if required if ( - self.game_memory.progress - < self.game_memory.warm_up["optional_inventory_items"] + self.game_memory.progress + < self.game_memory.warm_up["optional_inventory_items"] ): inventory = { k: v for k, v in inventory.items() if self.game_memory.core_inv_items_regex.search(k) is not None } - + observation = { "context": "", "biome": f"Biome: {biome}\n\n", @@ -118,29 +118,30 @@ class CurriculumDesigner(Base): "failed_tasks": f"Failed tasks that are too hard: {failed_tasks}\n\n", } return observation - + # --------------------------------Design Task Prepare--------------------------------------- async def render_design_task_human_message( - self, events, chest_observation, *args, **kwargs + self, events, chest_observation, *args, **kwargs ): """ Returns: observation for curriculum Refer to @ https://github.com/MineDojo/Voyager/blob/main/voyager/agents/curriculum.py """ - + content = "" warm_up = self.game_memory.mf_instance.warm_up + qa_cache = self.game_memory.qa_cache observation = self.render_curriculum_observation( events=events, chest_observation=chest_observation ) if self.game_memory.progress >= warm_up["context"]: - # if self.game_memory.progress >= 0: # TEST ONLY + # if self.game_memory.progress >= 0: # TEST ONLY human_msg = self.render_design_curriculum_human_message( events=events, chest_observation=chest_observation ).content system_msg = [self.render_design_curriculum_system_message().content] - questions, answers = await self.curriculum_design_action.generate_qa( - events=events, human_msg=human_msg, system_msg=system_msg + questions, answers = await DesignCurriculum().generate_qa( + events=events, qa_cache=qa_cache, human_msg=human_msg, system_msg=system_msg ) logger.debug(f"Generate_qa result is HERE: Ques: {questions}, Ans: {answers}") i = 1 @@ -152,7 +153,7 @@ class CurriculumDesigner(Base): i += 1 if i > 5: break - + for key in CURRICULUM_OB: if self.game_memory.progress >= warm_up[key]: if warm_up[key] != 0: @@ -161,20 +162,20 @@ class CurriculumDesigner(Base): should_include = True if should_include: content += observation[key] - + logger.info(f"Curriculum Agent human message\n{content}") return HumanMessage(content=content) - + def render_design_task_system_message(self, *args, **kwargs): return SystemMessage(content=load_prompt("curriculum")) - + async def encapsule_design_task_message(self, events, chest_observation, *args, **kwargs): human_msg = await self.render_design_task_human_message( events=events, chest_observation=chest_observation, *args, **kwargs ) system_msg = self.render_design_task_system_message(*args, **kwargs) return {"system_msg": [system_msg.content], "human_msg": human_msg.content} - + def generate_task_if_inventory_full(self, events, chest_observation): """ TODO: Try if this could be done with prompt @@ -193,15 +194,15 @@ class CurriculumDesigner(Base): else: task = "Craft 1 chest" return task - + # ----------------------------------------------------------------------------------------- - + # --------------------------------Design Curriculum Prepare-------------------------------- def render_design_curriculum_system_message(self, *args, **kwargs): return SystemMessage(content=load_prompt("curriculum_qa_step1_ask_questions")) - + def render_design_curriculum_human_message( - self, events, chest_observation, *args, **kwargs + self, events, chest_observation, *args, **kwargs ): observation = self.render_curriculum_observation( events=events, chest_observation=chest_observation @@ -210,16 +211,16 @@ class CurriculumDesigner(Base): for key in CURRICULUM_OB: content += observation[key] return HumanMessage(content=content) - + def encapsule_design_curriculum_message( - self, events, chest_observation, *args, **kwargs + self, events, chest_observation, *args, **kwargs ): human_msg = self.render_design_curriculum_human_message( events=events, chest_observation=chest_observation, *args, **kwargs ) system_msg = self.render_design_curriculum_system_message(*args, **kwargs) return {"system_msg": [system_msg.content], "human_msg": human_msg.content} - + def generate_context_if_inventory_full(self, events, chest_observation): """ TODO: Try if this could be done with prompt @@ -250,9 +251,9 @@ class CurriculumDesigner(Base): else: context = "Craft 1 chest with 8 planks of any kind of wood." return context - + # ----------------------------------------------------------------------------------------- - + async def handle_task_design(self, human_msg, system_msg, *args, **kwargs): """ Args: @@ -266,7 +267,7 @@ class CurriculumDesigner(Base): events = self.game_memory.event chest_observation = self.game_memory.chest_observation inventoryUsed = events[-1][1]["status"]["inventoryUsed"] - + if self.game_memory.progress == 0: task = self.game_memory.current_task elif inventoryUsed >= 33: @@ -276,12 +277,12 @@ class CurriculumDesigner(Base): else: task = await DesignTask().run(human_msg, system_msg, *args, **kwargs) logger.info(f"Handle_task_design result is Here: {task}") - + 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 @@ -298,7 +299,8 @@ class CurriculumDesigner(Base): chest_observation = self.game_memory.chest_observation inventoryUsed = events[-1][1]["status"]["inventoryUsed"] task = self.game_memory.current_task - + qa_cache = self.game_memory.qa_cache + if self.game_memory.progress == 0: context = self.game_memory.context elif inventoryUsed >= 33: @@ -307,33 +309,33 @@ class CurriculumDesigner(Base): ) else: context = await DesignCurriculum().run( - task, human_msg, system_msg, *args, **kwargs + task, qa_cache, 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}") self.maintain_actions(todo) - + # 获取最新的游戏周边环境信息 - # events = await self._obtain_events() - events = self.game_memory.event + events = await self._obtain_events() + logger.info(f"Retrieve event is HERE: {events}, current task is {self.game_memory.current_task}") + # events = self.game_memory.event chest_observation = self.game_memory.chest_observation - + design_task_message = await self.encapsule_design_task_message( events, chest_observation ) design_curriculum_message = self.encapsule_design_curriculum_message( events, chest_observation ) - + handler_map = { DesignTask: self.handle_task_design, DesignCurriculum: self.handle_curriculum_design, @@ -348,5 +350,5 @@ class CurriculumDesigner(Base): msg.round_id = self.round_id self._publish_message(msg) return msg - + raise ValueError(f"Unknown todo type: {type(todo)}") diff --git a/metagpt/utils/minecraft/action_rsp_parser.py b/metagpt/utils/minecraft/action_rsp_parser.py index 762da9e99..ba1ba00ba 100644 --- a/metagpt/utils/minecraft/action_rsp_parser.py +++ b/metagpt/utils/minecraft/action_rsp_parser.py @@ -40,12 +40,8 @@ def parse_action_response(msg: str): error = None # 3 times failed return error while retry > 0: try: - babel = require("@babel/core") + parsed = parse_js_code(msg) babel_generator = require("@babel/generator").default - - code_pattern = re.compile(r"```(?:javascript|js)(.*?)```", re.DOTALL) - code = "\n".join(code_pattern.findall(msg)) - parsed = babel.parse(code) # Collect func list: check if func & async functions = [] assert len(list(parsed.program.body)) > 0, "No functions found" From d731fd85cd6ee311a79c88a732316f7c555aa502 Mon Sep 17 00:00:00 2001 From: stellahsr Date: Wed, 11 Oct 2023 17:05:01 +0800 Subject: [PATCH 8/8] update player_action.py, change to _check --- metagpt/actions/minecraft/player_action.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/metagpt/actions/minecraft/player_action.py b/metagpt/actions/minecraft/player_action.py index 4a90b84da..58f121878 100644 --- a/metagpt/actions/minecraft/player_action.py +++ b/metagpt/actions/minecraft/player_action.py @@ -7,28 +7,25 @@ import json from metagpt.actions import Action from langchain.vectorstores import Chroma from langchain.embeddings.openai import OpenAIEmbeddings -from metagpt.document_store import FaissStore from metagpt.const import CKPT_DIR from metagpt.config import CONFIG -from metagpt.logs import logger -from metagpt.actions.minecraft.control_primitives import load_skills_code class PlayerActions(Action): def __init__(self, name="", context=None, llm=None): super().__init__(name, context, llm) - self.skills = {} # for skills.json - self.qa_cache = {} + 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 = json.load(f) + self.skills_check = json.load(f) with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "r") as f: - self.qa_cache = json.load(f) + self.qa_cache_check = json.load(f) self.retrieval_top_k = 5 @@ -47,19 +44,19 @@ class PlayerActions(Action): # FIXME if self.check_init: # Check if Skill Manager's vectordb right using - assert self.vectordb._collection.count() >= len(self.skills), ( + 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)} skills in 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 + 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)} questions in qa_cache.json.\n" + 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" )