Merge pull request #421 from stellaHSR/minecraft

Minecraft: 更新vdb的运行保存&恢复
This commit is contained in:
Sirui Hong 2023-10-12 14:27:58 +08:00 committed by GitHub
commit 18e4b245cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 244 additions and 187 deletions

View file

@ -91,8 +91,9 @@ class DesignCurriculum(Action):
def __init__(self, name="", context=None, llm=None):
super().__init__(name, context, llm)
self.llm.model = "gpt-3.5-turbo"
async def generate_qa(self, events, qa_cache, human_msg, system_msg):
async def generate_qa(self, events, qa_cache, qa_cache_questions_vectordb, game_memory, human_msg, system_msg):
"""
Generate qa for DesignTask's HumanMessage
"""
@ -100,13 +101,13 @@ class DesignCurriculum(Action):
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 self.qa_cache_questions_vectordb._collection.count() > 0:
if qa_cache_questions_vectordb._collection.count() > 0:
docs_and_scores = (
self.qa_cache_questions_vectordb.similarity_search_with_score(
qa_cache_questions_vectordb.similarity_search_with_score(
question, k=1
)
)
@ -120,14 +121,18 @@ class DesignCurriculum(Action):
answer = await self.generate_qa_step2(question=question)
assert question not in qa_cache
qa_cache[question] = answer
self.qa_cache_questions_vectordb.add_texts(
qa_cache_questions_vectordb.add_texts(
texts=[question],
)
with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "w") as f:
json.dump(qa_cache, f)
self.qa_cache_questions_vectordb.persist()
qa_cache_questions_vectordb.persist()
questions.append(question)
answers.append(answer)
game_memory.qa_cache = qa_cache
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}")
@ -170,7 +175,7 @@ class DesignCurriculum(Action):
# logger.info(f"Curriculum Agent generate_qa_step2 answer: {answer}")
return answer
async def get_context_from_task(self, task, qa_cache):
async def get_context_from_task(self, task, qa_cache, qa_cache_questions_vectordb, game_memory):
"""
Args: task
Returns: context: "Question: {question}\n{answer}"
@ -186,16 +191,18 @@ class DesignCurriculum(Action):
else:
answer = await self.generate_qa_step2(question=question)
qa_cache[question] = answer
self.qa_cache_questions_vectordb.add_texts(
qa_cache_questions_vectordb.add_texts(
texts=[question],
)
with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "w") as f:
json.dump(qa_cache, f)
self.qa_cache_questions_vectordb.persist()
qa_cache_questions_vectordb.persist()
game_memory.qa_cache = qa_cache
context = f"Question: {question}\n{answer}"
return context
async def generate_context(self, task, qa_cache, max_retries=5):
async def generate_context(self, task, qa_cache, qa_cache_questions_vectordb, game_memory, max_retries=5):
"""
Refer to the code in the voyager/agents/curriculum.py propose_next_ai_task() for implementation details.
Returns: context
@ -206,7 +213,8 @@ class DesignCurriculum(Action):
raise RuntimeError("Max retries reached, failed to propose context.")
try:
context = await self.get_context_from_task(
task=task, qa_cache=qa_cache
task=task, qa_cache=qa_cache, qa_cache_questions_vectordb=qa_cache_questions_vectordb,
game_memory=game_memory,
) # Curriculum Agent Question: How to craft 4 wooden planks in Minecraft? & Curriculum Agent Answer: ...
return context
except Exception as e:
@ -214,14 +222,18 @@ class DesignCurriculum(Action):
return await self.generate_context(
task=task,
qa_cache=qa_cache,
qa_cache_questions_vectordb=qa_cache_questions_vectordb,
game_memory=game_memory,
max_retries=max_retries - 1,
)
async def run(self, task, qa_cache, human_msg, system_msg, *args, **kwargs):
async def run(self, task, qa_cache, qa_cache_questions_vectordb, game_memory, human_msg, system_msg, *args,
**kwargs):
logger.info(f"run {self.__repr__()}")
# Generate curriculum-related questions and answers.
# curriculum_qustion = await self.generate_qa_step1(events, human_msg, system_msg)
curriculum_context = await self.generate_context(task, qa_cache)
curriculum_context = await self.generate_context(task, qa_cache, qa_cache_questions_vectordb,
game_memory=game_memory)
# Return the generated questions and answers.
return curriculum_context

View file

@ -5,8 +5,6 @@
import os
import json
from traits.trait_types import self
from metagpt.logs import logger
from metagpt.actions.minecraft.player_action import PlayerActions as Action
from metagpt.const import CKPT_DIR
@ -17,18 +15,18 @@ class RetrieveSkills(Action):
Action class for retrieving skills.
Refer to the code in the voyager/agents/skill.py for implementation details.
"""
def __init__(self, name="", context=None, llm=None):
super().__init__(name, context, llm)
self.llm.model = "gpt-3.5-turbo"
async def run(self, query, skills, *args, **kwargs):
async def run(self, query, skills, vectordb, *args, **kwargs):
# Implement the logic for retrieving skills here.
k = min(self.vectordb._collection.count(), self.retrieval_top_k)
k = min(vectordb._collection.count(), self.retrieval_top_k)
if k == 0:
return []
logger.info(f"Skill Manager retrieving for {k} skills")
docs_and_scores = self.vectordb.similarity_search_with_score(query, k=k)
docs_and_scores = vectordb.similarity_search_with_score(query, k=k)
logger.info(
f"Skill Manager retrieved skills: "
f"{', '.join([doc.metadata['name'] for doc, _ in docs_and_scores])}"
@ -44,13 +42,13 @@ class AddNewSkills(Action):
Action class for adding new skills.
Refer to the code in the voyager/agents/skill.py for implementation details.
"""
def __init__(self, name="", context=None, llm=None):
super().__init__(name, context, llm)
self.llm.model = "gpt-3.5-turbo"
async def run(
self, task, program_name, program_code, skills, skill_desp, *args, **kwargs
self, task, program_name, program_code, skills, skill_desp, vectordb, *args, **kwargs
):
# Implement the logic for adding new skills here.
# TODO: Fix this
@ -65,26 +63,33 @@ class AddNewSkills(Action):
if program_name in skills:
logger.info(f"Skill {program_name} already exists. Rewriting!")
self.vectordb._collection.delete(ids=[program_name])
vectordb._collection.delete(ids=[program_name])
i = 2
while f"{program_name}V{i}.js" in os.listdir(f"{CKPT_DIR}/skill/code"):
i += 1
dumped_program_name = f"{program_name}V{i}"
else:
dumped_program_name = program_name
self.vectordb.add_texts(
vectordb.add_texts(
texts=[skill_desp],
ids=[program_name],
metadatas=[{"name": program_name}],
)
skills[program_name] = {
"code": program_code,
"description": skill_desp,
}
logger.debug(f"ADD_CHECK: There are {vectordb._collection.count()} skills in vectordb")
with open(f"{CKPT_DIR}/skill/code/{dumped_program_name}.js", "w") as f:
f.write(program_code)
with open(f"{CKPT_DIR}/skill/description/{dumped_program_name}.txt", "w") as f:
f.write(skill_desp)
with open(f"{CKPT_DIR}/skill/skills.json", "w") as f:
json.dump(skills, f)
self.vectordb.persist()
vectordb.persist()
return {
"code": program_code,
"description": skill_desp,
@ -96,13 +101,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}}"

View file

@ -2,68 +2,14 @@
# @Date : 2023/9/23 17:06
# @Author : stellahong (stellahong@fuzhi.ai)
# @Desc :
import json
from metagpt.actions import Action
from langchain.vectorstores import Chroma
from langchain.embeddings.openai import OpenAIEmbeddings
from metagpt.const import CKPT_DIR
from metagpt.config import CONFIG
class PlayerActions(Action):
def __init__(self, name="", context=None, llm=None):
super().__init__(name, context, llm)
self.skills_check = {} # for skills.json
self.qa_cache_check = {}
self.check_init = True
if (
CONFIG.resume
): # TODO: now for assert only, no update, cuz program using in step()
with open(f"{CKPT_DIR}/skill/skills.json", "r") as f:
self.skills_check = json.load(f)
with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "r") as f:
self.qa_cache_check = json.load(f)
self.retrieval_top_k = 5
self.vectordb = Chroma(
collection_name="skill_vectordb",
embedding_function=OpenAIEmbeddings(),
persist_directory=f"{CKPT_DIR}/skill/vectordb",
)
self.qa_cache_questions_vectordb = Chroma(
collection_name="qa_cache_questions_vectordb",
embedding_function=OpenAIEmbeddings(),
persist_directory=f"{CKPT_DIR}/curriculum/vectordb",
)
# FIXME
if self.check_init:
# Check if Skill Manager's vectordb right using
assert self.vectordb._collection.count() >= len(self.skills_check), (
f"Skill Manager's vectordb is not synced with skills.json.\n"
f"There are {self.vectordb._collection.count()} skills in vectordb but {len(self.skills_check)} skills in skills.json.\n"
f"Did you set resume=False when initializing the manager?\n"
f"You may need to manually delete the vectordb directory for running from scratch."
)
# Check if Skill Manager's vectordb right using
assert self.qa_cache_questions_vectordb._collection.count() >= len(
self.qa_cache_check
), (
f"Curriculum Agent's qa cache question vectordb is not synced with qa_cache.json.\n"
f"There are {self.qa_cache_questions_vectordb._collection.count()} questions in vectordb "
f"but {len(self.qa_cache_check)} questions in qa_cache.json.\n"
f"Did you set resume=False when initializing the agent?\n"
f"You may need to manually delete the qa cache question vectordb directory for running from scratch.\n"
)
self.check_init = False
# TODO: change to FaissStore
# self.qa_cache_questions_vectordb = FaissStore( {CKPT_DIR}/ 'curriculum/vectordb'
"""Minecraft player info without any implementation details"""
async def run(self, *args, **kwargs):

View file

@ -31,7 +31,7 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
"""
游戏环境的记忆用于多个agent进行信息的共享和缓存而不需要重复在自己的角色内维护缓存
"""
event: dict[str, Any] = Field(default_factory=dict)
current_task: str = Field(default="Mine 1 wood log")
task_execution_time: float = Field(default=float)
@ -39,32 +39,36 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
default="You can mine one of oak, birch, spruce, jungle, acacia, dark oak, or mangrove logs."
)
code: str = Field(default="")
program_code: str = Field(default="") # write in skill/code/*.js
program_code: str = Field(default="") # write in skill/code/*.js
program_name: str = Field(default="")
critique: str = Field(default="")
skills: dict = Field(default_factory=dict) # for skills.json
retrieve_skills: list[str] = Field(default_factory=list)
event_summary: str = Field(default="")
qa_cache: dict[str, str] = Field(default_factory=dict)
completed_tasks: list[str] = Field(default_factory=list) # Critique things
failed_tasks: list[str] = Field(default_factory=list)
skill_desp: str = Field(default="")
chest_memory: dict[str, Any] = Field(
default_factory=dict
) # eg: {'(1344, 64, 1381)': 'Unknown'}
chest_observation: str = Field(default="") # eg: "Chests: None\n\n"
mf_instance: MineflayerEnv = Field(default_factory=MineflayerEnv)
runtime_status: bool = False # equal to action execution status: success or failed
vectordb: Chroma = Field(default_factory=Chroma)
qa_cache_questions_vectordb: Chroma = Field(default_factory=Chroma)
@property
def progress(self):
# return len(self.completed_tasks) + 10 # Test only
return len(self.completed_tasks)
@property
def programs(self):
programs = ""
@ -75,25 +79,39 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
for primitives in load_skills_code():
programs += f"{primitives}\n\n"
return programs
@property
def warm_up(self):
return self.mf_instance.warm_up
@property
def core_inv_items_regex(self):
return self.mf_instance.core_inv_items_regex
def set_mc_port(self, mc_port):
self.mf_instance.set_mc_port(mc_port)
self.set_mc_resume()
def set_mc_resume(self):
self.qa_cache_questions_vectordb = Chroma(
collection_name="qa_cache_questions_vectordb",
embedding_function=OpenAIEmbeddings(),
persist_directory=f"{CKPT_DIR}/curriculum/vectordb",
)
self.vectordb = Chroma(
collection_name="skill_vectordb",
embedding_function=OpenAIEmbeddings(),
persist_directory=f"{CKPT_DIR}/skill/vectordb",
)
if CONFIG.resume:
logger.info(f"Loading Action Developer from {CKPT_DIR}/action")
with open(f"{CKPT_DIR}/action/chest_memory.json", "r") as f:
self.chest_memory = json.load(f)
logger.info(f"Loading Curriculum Agent from {CKPT_DIR}/curriculum")
with open(f"{CKPT_DIR}/curriculum/completed_tasks.json", "r") as f:
self.completed_tasks = json.load(f)
@ -103,25 +121,78 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
logger.info(f"Loading Skill Manager from {CKPT_DIR}/skill\033[0m")
with open(f"{CKPT_DIR}/skill/skills.json", "r") as f:
self.skills = json.load(f)
logger.info(f"Loading Qa Cache from {CKPT_DIR}/curriculum\033[0m")
with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "r") as f:
self.qa_cache = json.load(f)
self.qa_cache = json.load(f)
if self.vectordb._collection.count() == 0:
logger.info(self.vectordb._collection.count())
# Set vdvs for skills & qa_cache
skill_desps = [
skill["description"] for program_name, skill in self.skills.items()
]
program_names = [
program_name for program_name, skill in self.skills.items()
]
metadatas = [{"name": program_name} for program_name in program_names]
# add vectordb from file
ids = self.vectordb.add_texts(
texts=skill_desps,
ids=program_names,
metadatas=metadatas,
)
self.vectordb.persist()
logger.info(self.qa_cache_questions_vectordb._collection.count())
if self.qa_cache_questions_vectordb._collection.count() == 0:
questions = [question for question, answer in self.qa_cache.items()]
self.qa_cache_questions_vectordb.add_texts(texts=questions)
self.qa_cache_questions_vectordb.persist()
logger.info(
f"INIT_CHECK: There are {self.vectordb._collection.count()} skills in vectordb and {len(self.skills)} skills in skills.json."
)
# Check if Skill Manager's vectordb right using
assert self.vectordb._collection.count() >= len(self.skills), (
f"Skill Manager's vectordb is not synced with skills.json.\n"
f"There are {self.vectordb._collection.count()} skills in vectordb but {len(self.skills)} skills in skills.json.\n"
f"Did you set resume=False when initializing the manager?\n"
f"You may need to manually delete the vectordb directory for running from scratch."
)
logger.info(
f"INIT_CHECK: There are {self.qa_cache_questions_vectordb._collection.count()} qa_cache in vectordb and {len(self.qa_cache)} questions in qa_cache.json."
)
assert self.qa_cache_questions_vectordb._collection.count() >= len(
self.qa_cache
), (
f"Curriculum Agent's qa cache question vectordb is not synced with qa_cache.json.\n"
f"There are {self.qa_cache_questions_vectordb._collection.count()} questions in vectordb "
f"but {len(self.qa_cache)} questions in qa_cache.json.\n"
f"Did you set resume=False when initializing the agent?\n"
f"You may need to manually delete the qa cache question vectordb directory for running from scratch.\n"
)
def register_roles(self, roles: Iterable[Minecraft]):
for role in roles:
role.set_memory(self)
def update_event(self, event: Dict):
if self.event == event:
return
self.event = event
# self.update_chest_memory(event)
# self.event_summary = self.summarize_chatlog(event)
def update_task(self, task: str):
self.current_task = task
def update_context(self, context: str):
self.context = context
@ -130,22 +201,25 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
def update_code(self, code: str):
self.code = code # action_developer.gen_action_code to HERE
def update_program_name(self, program_name: str):
self.program_name = program_name
def update_critique(self, critique: str):
self.critique = critique # critic_agent.check_task_success to HERE
def append_skill(self, skill: dict):
self.skills[self.program_name] = skill # skill_manager.retrieve_skills to HERE
def update_retrieve_skills(self, retrieve_skills: list):
self.retrieve_skills = retrieve_skills
def update_skill_desp(self, skill_desp: str):
self.skill_desp = skill_desp
async def update_qa_cache(self, qa_cache: dict):
self.qa_cache = qa_cache
def update_chest_memory(self, events: Dict):
"""
Input: events: Dict
@ -165,13 +239,13 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
self.chest_memory[position] = chest
with open(f"{CKPT_DIR}/action/chest_memory.json", "w") as f:
json.dump(self.chest_memory, f)
def update_chest_observation(self):
"""
update chest_memory to chest_observation.
Refer to @ https://github.com/MineDojo/Voyager/blob/main/voyager/agents/action.py
"""
chests = []
for chest_position, chest in self.chest_memory.items():
if isinstance(chest, dict) and len(chest) > 0:
@ -189,7 +263,7 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
self.chest_observation = f"Chests:\n{chests}\n\n"
else:
self.chest_observation = f"Chests: None\n\n"
def summarize_chatlog(self, events):
def filter_item(message: str):
craft_pattern = r"I cannot make \w+ because I need: (.*)"
@ -198,25 +272,28 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
)
mine_pattern = r"I need at least a (.*) to mine \w+!"
if re.match(craft_pattern, message):
self.event_summary = re.match(craft_pattern, message).groups()[0]
self.event_summary = re.match(craft_pattern, message).groups()[0]
elif re.match(craft_pattern2, message):
self.event_summary = "a nearby crafting table"
elif re.match(mine_pattern, message):
self.event_summary = re.match(mine_pattern, message).groups()[0]
else:
self.event_summary = ""
chatlog = set()
for event_type, event in events:
if event_type == "onChat":
item = filter_item(event["onChat"])
if item:
chatlog.add(item)
self.event_summary = "I also need " + ", ".join(chatlog) + "." if chatlog else ""
self.event_summary = (
"I also need " + ", ".join(chatlog) + "." if chatlog else ""
)
def reset_block_info(self):
# revert all the placing event in the last step
pass
def update_exploration_progress(self, success: bool):
"""
Split task into completed_tasks or failed_tasks
@ -252,9 +329,9 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
)
self.event[-1][1]["inventory"] = new_events[-1][1]["inventory"]
self.event[-1][1]["voxels"] = new_events[-1][1]["voxels"]
self.save_sorted_tasks()
def save_sorted_tasks(self):
updated_completed_tasks = []
# record repeated failed tasks
@ -263,21 +340,21 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
for task in self.completed_tasks:
if task not in updated_completed_tasks:
updated_completed_tasks.append(task)
# remove completed tasks from failed tasks
for task in updated_completed_tasks:
while task in updated_failed_tasks:
updated_failed_tasks.remove(task)
self.completed_tasks = updated_completed_tasks
self.failed_tasks = updated_failed_tasks
# dump to json
with open(f"{CKPT_DIR}/curriculum/completed_tasks.json", "w") as f:
json.dump(self.completed_tasks, f)
with open(f"{CKPT_DIR}/curriculum/failed_tasks.json", "w") as f:
json.dump(self.failed_tasks, f)
async def on_event_retrieve(self, *args):
"""
Retrieve Minecraft events.
@ -295,9 +372,7 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
"wait_ticks": 20,
}
)
difficulty = (
"easy" if len(self.completed_tasks) > 15 else "peaceful"
)
difficulty = "easy" if len(self.completed_tasks) > 15 else "peaceful"
events = self.mf_instance.step(
"bot.chat(`/time set ${getNextTime()}`);\n"
+ f"bot.chat('/difficulty {difficulty}');"
@ -318,7 +393,8 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
)
self.update_event(events)
logger.error(f"Failed to retrieve Minecraft events: {str(e)}")
return events
async def on_event_execute(self, *args):
"""
Execute Minecraft events.
@ -334,7 +410,7 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
"""
try:
events = self.mf_instance.step(
code = self.code,
code=self.code,
programs=self.programs,
)
self.update_event(events)
@ -353,6 +429,7 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
)
self.update_event(events)
logger.error(f"Failed to execute Minecraft events: {str(e)}")
return events
class MinecraftPlayer(SoftwareCompany):
@ -360,16 +437,16 @@ class MinecraftPlayer(SoftwareCompany):
Software Company: Possesses a team, SOP (Standard Operating Procedures), and a platform for instant messaging,
dedicated to writing executable code.
"""
environment: Environment = Field(default_factory=Environment)
game_memory: GameEnvironment = Field(default_factory=GameEnvironment)
investment: float = Field(default=50.0)
task: str = Field(default="")
game_info: dict = Field(default={})
def set_port(self, mc_port):
self.game_memory.set_mc_port(mc_port)
def check_complete_round(self):
complete_round = []
for role in self.environment.roles.values():
@ -381,7 +458,7 @@ class MinecraftPlayer(SoftwareCompany):
complete_round_tag = all(complete_round)
logger.info(f"complete_round {complete_round}")
return complete_round_tag
def update_round(self):
for role in self.environment.roles.values():
role.finish_step = False
@ -389,11 +466,11 @@ class MinecraftPlayer(SoftwareCompany):
role._rc.todo = None
role.finish_state = len(role._actions)
logger.info(f"round_id:{role.round_id}")
def hire(self, roles: list[Role]):
self.environment.add_roles(roles)
self.game_memory.register_roles(roles)
def start(self, task, round=0):
"""Start a project from publishing boss requirement."""
self.task = task
@ -401,14 +478,14 @@ class MinecraftPlayer(SoftwareCompany):
Message(role="Player", content=task, cause_by=PlayerActions, round_id=round)
)
logger.info(self.game_info)
def _save(self):
logger.info(self.json())
def _reset(self):
for role_profile, role in self.environment.roles.items():
role.reset_state()
async def run(self, n_round=3):
"""Run company until target round or no money"""
round_id = 0
@ -434,21 +511,26 @@ class MinecraftPlayer(SoftwareCompany):
programs="",
)
self.game_memory.update_event(events)
while n_round > 0:
# self._save()
if self.check_complete_round():
n_round -= 1
self.update_round()
round_id += 1
# add new task into env and continue
# fixme: update self.task
self.start(task=self.task, round=round_id)
logger.info(f"{n_round=}")
self._check_balance()
await self.environment.run()
# self.environment.memory.clear()
# self._reset()
return self.environment.history
if __name__ == "__main__":
A = GameEnvironment()
a = A.qa_cache_questions_vectordb
print(a._persist_directory)

View file

@ -216,7 +216,7 @@ class ActionDeveloper(Base):
self.perform_game_info_callback(new_skills_info, self.game_memory.append_skill)
async def retrieve_skills(self, query, skills, *args, **kwargs):
retrieve_skills = await RetrieveSkills().run(query, skills)
retrieve_skills = await RetrieveSkills().run(query, skills, vectordb=self.game_memory.vectordb)
logger.info(f"Render Action Agent system message with {len(retrieve_skills)} skills")
self.perform_game_info_callback(retrieve_skills, self.game_memory.update_retrieve_skills)
# return Message(content=f"{retrieve_skills}", instruct_content="retrieve_skills",

View file

@ -20,11 +20,11 @@ class CurriculumDesigner(Base):
"""
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
@ -56,12 +56,12 @@ class CurriculumDesigner(Base):
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"
@ -92,8 +92,8 @@ class CurriculumDesigner(Base):
# 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
@ -121,7 +121,7 @@ class CurriculumDesigner(Base):
# --------------------------------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
@ -135,15 +135,19 @@ class CurriculumDesigner(Base):
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 DesignCurriculum().generate_qa(
events=events, qa_cache=qa_cache, human_msg=human_msg, system_msg=system_msg
events=events, qa_cache=qa_cache,
qa_cache_questions_vectordb=self.game_memory.qa_cache_questions_vectordb,
game_memory=self.game_memory,
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:
@ -202,7 +206,7 @@ class CurriculumDesigner(Base):
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
@ -213,7 +217,7 @@ class CurriculumDesigner(Base):
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
@ -300,6 +304,7 @@ class CurriculumDesigner(Base):
inventoryUsed = events[-1][1]["status"]["inventoryUsed"]
task = self.game_memory.current_task
qa_cache = self.game_memory.qa_cache
qa_cache_questions_vectordb = self.game_memory.qa_cache_questions_vectordb
if self.game_memory.progress == 0:
context = self.game_memory.context
@ -308,8 +313,12 @@ class CurriculumDesigner(Base):
self, events=events, chest_observation=chest_observation
)
else:
logger.info(self.game_memory.qa_cache_questions_vectordb._collection.count())
logger.info(self.game_memory.vectordb._collection.count())
context = await DesignCurriculum().run(
task, qa_cache, human_msg, system_msg, *args, **kwargs
task, qa_cache, qa_cache_questions_vectordb, game_memory=self.game_memory,
human_msg=human_msg,
system_msg=system_msg, *args, **kwargs
)
self.perform_game_info_callback(context, self.game_memory.update_context)
return Message(

View file

@ -19,40 +19,42 @@ from metagpt.utils.minecraft import load_prompt
@agent_registry.register("skill_manager")
class SkillManager(Base):
def __init__(
self,
name: str = "John",
profile: str = "Skills Management Specialist",
goal: str = "To oversee and optimize the acquisition, development, and utilization of skills within the organization, ensuring workforce competence and efficiency.",
constraints: str = "Resource allocation, training budgets, and alignment with organizational goals.",
self,
name: str = "John",
profile: str = "Skills Management Specialist",
goal: str = "To oversee and optimize the acquisition, development, and utilization of skills within the organization, ensuring workforce competence and efficiency.",
constraints: str = "Resource allocation, training budgets, and alignment with organizational goals.",
) -> None:
super().__init__(name, profile, goal, constraints)
# Initialize actions specific to the SkillManager role
self._init_actions([RetrieveSkills, GenerateSkillDescription, AddNewSkills])
# Set events or actions the SkillManager should watch or be aware of
self._watch(
[DesignCurriculum, GenerateActionCode, RetrieveSkills, GenerateSkillDescription]
)
self.finish_state = len(self._actions)
def encapsule_message(self, program_code, program_name, *args, **kwargs):
system_msg = self.render_system_message(load_prompt("skill"))
human_msg = self.render_human_message(
program_code + "\n\n" + f"The main function is `{program_name}`."
)
return {"system_msg": [system_msg.content], "human_msg": human_msg.content}
async def retrieve_skills(self, query, skills, *args, **kwargs):
retrieve_skills = await RetrieveSkills().run(query, skills)
vectordb = self.game_memory.vectordb
retrieve_skills = await RetrieveSkills().run(query, skills, vectordb)
logger.info(f"Render Action Agent system message with {len(retrieve_skills)} skills")
self.perform_game_info_callback(retrieve_skills, self.game_memory.update_retrieve_skills)
return Message(content=f"{retrieve_skills}", instruct_content="retrieve_skills",
return Message(content=f"{retrieve_skills}", instruct_content="retrieve_skills",
role=self.profile, send_to=agent_registry.entries["action_developer"]()._setting.name)
# return Message(
# content=f"{skills}", instruct_content="retrieve_skills", role=self.profile
# ) # Unit test only
async def generate_skill_descp(self, human_msg, system_msg, *args, **kwargs):
program_name = self.game_memory.program_name
desp = await GenerateSkillDescription().run(program_name, human_msg, system_msg)
@ -62,20 +64,21 @@ class SkillManager(Base):
instruct_content="generate_skill_descp",
role=self.profile,
)
async def handle_add_new_skills(
self, task, program_name, program_code, skills, *args, **kwargs
self, task, program_name, program_code, skills, *args, **kwargs
):
if not self.game_memory.runtime_status:
return Message(
content="",
instruct_content="handle_add_new_skills",
role=self.profile,
)
content="",
instruct_content="handle_add_new_skills",
role=self.profile,
)
skill_desp = self.game_memory.skill_desp
vectordb = self.game_memory.vectordb
new_skills_info = await AddNewSkills().run(
task, program_name, program_code, skills, skill_desp
task, program_name, program_code, skills, skill_desp, vectordb
)
self.perform_game_info_callback(new_skills_info, self.game_memory.append_skill)
return Message(
@ -83,13 +86,13 @@ class SkillManager(Base):
instruct_content="handle_add_new_skills",
role=self.profile,
)
async def _act(self) -> Message:
todo = self._rc.todo
logger.debug(f"Todo is {todo}")
self.maintain_actions(todo)
# 获取最新的游戏周边信息
context = self.game_memory.context
task = self.game_memory.current_task
@ -97,27 +100,27 @@ class SkillManager(Base):
self.perform_game_info_callback(self.game_memory.event, self.game_memory.summarize_chatlog)
event_summary = self.game_memory.event_summary
program_code = self.game_memory.program_code
program_name = self.game_memory.program_name
skills = self.game_memory.skills
# msg = self._rc.memory.get(k=1)[0]
retrieve_skills_message_step1 = {"query": context, "skills": skills}
logger.info(f"check query {context}")
logger.info(f"check event summary {event_summary}")
retrieve_skills_message_step2 = {"query": context + "\n\n" + event_summary, "skills": skills}
generate_skill_message = self.encapsule_message(program_code, program_name)
add_new_skills_message = {
"task": task,
"program_name": program_name,
"program_code": program_code,
"skills": skills,
}
handler_map = {
DesignCurriculum: self.retrieve_skills,
RetrieveSkills: self.retrieve_skills,
@ -136,10 +139,10 @@ class SkillManager(Base):
msg = await handler(**generate_skill_message)
else:
msg = await handler(**add_new_skills_message)
msg.cause_by = type(todo)
msg.round_id = self.round_id
self._publish_message(msg)
return msg
raise ValueError(f"Unknown todo type: {type(todo)}")