mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-06-17 15:35:21 +02:00
Merge pull request #421 from stellaHSR/minecraft
Minecraft: 更新vdb的运行保存&恢复
This commit is contained in:
commit
18e4b245cb
7 changed files with 244 additions and 187 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}}"
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)}")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue