mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-06-17 15:35:21 +02:00
Fix some bugs, a runnable version remain some TODO
This commit is contained in:
parent
d45ddf58e4
commit
1047d698ae
7 changed files with 95 additions and 79 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue