Fix some bugs, a runnable version remain some TODO

This commit is contained in:
yuymf 2023-10-03 05:37:38 +08:00
parent d45ddf58e4
commit 1047d698ae
7 changed files with 95 additions and 79 deletions

View file

@ -1,18 +1,16 @@
import pkg_resources
import os
import voyager.utils as U
import metagpt.utils.minecraft as utils
from metagpt.logs import logger
def load_control_primitives(primitive_names=None):
package_path = pkg_resources.resource_filename("metagpt", "")
if primitive_names is None:
primitive_names = [
primitives[:-3]
for primitives in os.listdir(f"{package_path}/actions/minecraft/control_primitives")
if primitives.endswith(".js")
def load_skills_code(skill_names=None):
skills_dir = os.path.dirname(os.path.abspath(__file__))
if skill_names is None:
skill_names = [
skill[:-3] for skill in os.listdir(f"{skills_dir}") if skill.endswith(".js")
]
primitives = [
U.load_text(f"{package_path}/actions/minecraft/control_primitives/{primitive_name}.js")
for primitive_name in primitive_names
skills = [
utils.load_text(os.path.join(skills_dir, f"{skill_name}.js"))
for skill_name in skill_names
]
return primitives
return skills

View file

@ -100,20 +100,21 @@ class DesignCurriculum(Action):
)
# TODO: change to FaissStore
# self.qa_cache_questions_vectordb = FaissStore( {CKPT_DIR}/ 'curriculum/vectordb')
# TODO:
# 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"
# )
@classmethod
def set_qa_cache(cls, qa_cache):
cls.qa_cache = qa_cache
# Check if qa_cache 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"
)
@classmethod
def generate_qa(cls, events, chest_observation):

View file

@ -23,25 +23,21 @@ class RetrieveSkills(Action):
super().__init__(name, context, llm)
# TODO: mv to PlayerAction
self.retrieval_top_k = 5
self.skills = {}
self.vectordb = Chroma(
collection_name="skill_vectordb",
embedding_function=OpenAIEmbeddings(),
persist_directory=f"{CKPT_DIR}/skill/vectordb",
)
@classmethod
def set_skills(cls, skills):
cls.skills = skills
# Check if skills 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."
)
# TODO:
# 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."
# )
async def run(self, query, *args, **kwargs):
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)
if k == 0:
@ -52,10 +48,10 @@ class RetrieveSkills(Action):
f"Skill Manager retrieved skills: "
f"{', '.join([doc.metadata['name'] for doc, _ in docs_and_scores])}"
)
skills = []
retrieve_skills = []
for doc, _ in docs_and_scores:
skills.append(self.skills[doc.metadata["name"]]["code"])
return skills
retrieve_skills.append(skills[doc.metadata["name"]]["code"])
return retrieve_skills
class AddNewSkills(Action):
@ -74,26 +70,23 @@ class AddNewSkills(Action):
)
# TODO: change to FaissStore
# self.qa_cache_questions_vectordb = FaissStore( {CKPT_DIR}/ 'skill/vectordb')
@classmethod
def set_skills(cls, skills):
cls.skills = skills
# TODO:
# Check if skills 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."
)
# 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."
# )
async def run(
self, task, program_name, program_code, skills, skill_desp, *args, **kwargs
):
# Implement the logic for adding new skills here.
# TODO: Fix this
if task.startswith("Deposit useless items into the chest at"):
# No need to reuse the deposit skill
return {}
# TODO: Fix this
logger.info(
f"Skill Manager generated description for {program_name}:\n{skill_desp}\033[0m"
)

View file

@ -41,6 +41,6 @@ class VerifyTask(Action):
return response["success"], response["critique"]
except Exception as e:
logger.error(f"Error verifying the task: {str(e)}")
return self.run(human_msg, system_msg, max_retries=max_retries-1)
return await self.run(human_msg, system_msg, max_retries=max_retries-1)

View file

@ -18,7 +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.actions.minecraft.control_primitives_context import load_skills_code_context
from metagpt.actions.minecraft.control_primitives import load_skills_code
class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
@ -32,10 +32,11 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
context: str = Field(
default="You can mine one of oak, birch, spruce, jungle, acacia, dark oak, or mangrove logs."
)
code: str = Field(default=None)
code: str = Field(default="")
program_name: str = Field(default="")
critique: str = Field(default=None)
skills: dict = Field(default_factory=dict)
critique: str = Field(default="")
skills: dict = Field(default_factory=dict) # for skills.json
retrieve_skills: list[str] = Field(default_factory=list)
event_summary: str = Field(default="")
qa_cache: dict[str, str] = Field(default_factory=dict)
@ -59,11 +60,13 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
@property
def programs(self):
programs = ""
if self.code == "":
return programs # TODO: maybe fix 10054 now, a better way is isolating env.step() like voyager
for skill_name, entry in self.skills.items():
programs += f"{entry['code']}\n\n"
for primitives in load_skills_code_context():
for primitives in load_skills_code():
programs += f"{primitives}\n\n"
return programs
return programs
@property
def warm_up(self):
@ -123,6 +126,9 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
def append_skill(self, skill: dict):
self.skills[self.program_name] = skill # skill_manager.retrieve_skills to HERE
def update_retrieve_skills(self, retrieve_skills: list):
self.retrieve_skills = retrieve_skills
def update_skill_desp(self, skill_desp: str):
self.skill_desp = skill_desp
@ -208,19 +214,36 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
return
if success:
logger.info(f"Completed task {task}.")
self.game_memory.completed_tasks.append(task)
self.completed_tasks.append(task)
else:
logger.info(f"Failed to complete task {task}. Skipping to next task.")
self.game_memory.failed_tasks.append(task)
self.failed_tasks.append(task)
# TODO: when not success, transform code below to update event!(isolate step soon!)
# if self.reset_placed_if_failed and not success:
# # revert all the placing event in the last step
# blocks = []
# positions = []
# for event_type, event in events:
# if event_type == "onSave" and event["onSave"].endswith("_placed"):
# block = event["onSave"].split("_placed")[0]
# position = event["status"]["position"]
# blocks.append(block)
# positions.append(position)
# new_events = self.env.step(
# f"await givePlacedItemBack(bot, {U.json_dumps(blocks)}, {U.json_dumps(positions)})",
# programs=self.skill_manager.programs,
# )
# events[-1][1]["inventory"] = new_events[-1][1]["inventory"]
# events[-1][1]["voxels"] = new_events[-1][1]["voxels"]
self.save_sorted_tasks()
def save_sorted_tasks(self):
updated_completed_tasks = []
# record repeated failed tasks
updated_failed_tasks = self.game_memory.failed_tasks
updated_failed_tasks = self.failed_tasks
# dedup but keep order
for task in self.game_memory.completed_tasks:
for task in self.completed_tasks:
if task not in updated_completed_tasks:
updated_completed_tasks.append(task)
@ -229,14 +252,14 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
while task in updated_failed_tasks:
updated_failed_tasks.remove(task)
self.game_memory.completed_tasks = updated_completed_tasks
self.game_memory.failed_tasks = updated_failed_tasks
self.completed_tasks = updated_completed_tasks
self.failed_tasks = updated_failed_tasks
# dump to json
with open(f"{CKPT_DIR}/curriculum/completed_tasks.json", "w") as f:
json.dump(self.game_memory.completed_tasks, f)
json.dump(self.completed_tasks, f)
with open(f"{CKPT_DIR}/curriculum/failed_tasks.json", "w") as f:
json.dump(self.game_memory.failed_tasks, f)
json.dump(self.failed_tasks, f)
async def on_event(self, *args):
"""

View file

@ -210,7 +210,7 @@ class ActionDeveloper(Base):
task = self.game_memory.current_task
code = self.game_memory.code
critique = self.game_memory.critique
skills = self.game_memory.skills
retrieve_skills = self.game_memory.retrieve_skills
message = self.encapsule_message(
events=events,
@ -218,7 +218,7 @@ class ActionDeveloper(Base):
task=task,
context=context,
critique=critique,
skills=skills,
skills=retrieve_skills,
)
logger.info(todo)
handler_map = {

View file

@ -42,10 +42,11 @@ class SkillManager(Base):
)
return {"system_msg": [system_msg.content], "human_msg": human_msg.content}
async def retrieve_skills(self, query, *args, **kwargs):
skills = await RetrieveSkills().run(query)
logger.info(f"Render Action Agent system message with {len(skills)} skills")
return Message(content=f"{skills}", instruct_content="retrieve_skills",
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",
role=self.profile, send_to=agent_registry.entries["action_developer"]()._setting.name)
# return Message(
# content=f"{skills}", instruct_content="retrieve_skills", role=self.profile
@ -84,19 +85,19 @@ class SkillManager(Base):
task = self.game_memory.current_task
event_summary = self.game_memory.event_summary
code = self.game_memory.code
program_code = code["program_code"]
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_name = self.game_memory.program_name
skills = self.game_memory.skills
# TODO: mv to PlayerAction
RetrieveSkills.set_skills(skills)
AddNewSkills.set_skills(skills)
# msg = self._rc.memory.get(k=1)[0]
retrieve_skills_message_step1 = {"query": context}
retrieve_skills_message_step1 = {"query": context, "skills": skills}
retrieve_skills_message_step2 = {"query": context + "\n\n" + event_summary}
retrieve_skills_message_step2 = {"query": context + "\n\n" + event_summary, "skills": skills}
generate_skill_message = self.encapsule_message(program_code, program_name)
@ -115,11 +116,11 @@ class SkillManager(Base):
}
handler = handler_map.get(type(todo))
if handler:
if type(todo) == "DesignCurriculum":
if type(todo) == DesignCurriculum:
msg = await handler(**retrieve_skills_message_step1)
elif type(todo) == "RetrieveSkills":
elif type(todo) == RetrieveSkills:
msg = await handler(**retrieve_skills_message_step2)
elif type(todo) == "GenerateSkillDescription":
elif type(todo) == GenerateSkillDescription:
msg = await handler(**generate_skill_message)
else:
msg = await handler(**add_new_skills_message)