Merge pull request #408 from stellaHSR/minecraft

Minecraft: bug fix for code save and skill add.
This commit is contained in:
Sirui Hong 2023-10-11 17:15:16 +08:00 committed by GitHub
commit f62d22de4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 223 additions and 157 deletions

View file

@ -31,6 +31,8 @@ ### 0926 环境信息获取和更新 on_event()实际内容
mc_player.set_port(2465) # Modify this to your LAN port
```
*注意世界游戏模式为生存,这样人物才会有血量,饥饿和库存概念;*
python minecraft_run.py
<img src="docs/resources/workspace/minecraft_tests/on_event.jpeg" style="zoom:67%;" />
@ -67,3 +69,6 @@ ### 0930Curriculum agent 更新
1. 若本地已克隆项目不好更改可尝试:删除 metagpt/mineflayer_env/mineflayer + 重新copy voyager/env/mineflayer到目录下 + npm install...0926.B命令
2. 重新拉取最新提交+重新配置
### 第一人称视角
目前使用3007端口可自行修改代码运行以后在浏览器输入127.0.0.1:3007访问

View file

@ -84,4 +84,7 @@ MODEL_FOR_RESEARCHER_REPORT: gpt-3.5-turbo-16k
### browser path for pyppeteer engine, support Chrome, Chromium,MS Edge
#PYPPETEER_EXECUTABLE_PATH: "/usr/bin/google-chrome-stable"
PROMPT_FORMAT: json #json or markdown
PROMPT_FORMAT: json #json or markdown
### Minecraft
RESUME: false # If load json from ckpt dir(include chest_memory, skills, ...)

View file

@ -91,44 +91,46 @@ class DesignCurriculum(Action):
def __init__(self, name="", context=None, llm=None):
super().__init__(name, context, llm)
# voyager vectordb using
@classmethod
def generate_qa(cls, events, chest_observation):
async def generate_qa(self, events, qa_cache, human_msg, system_msg):
"""
Generate qa for DesignTask's HumanMessage
"""
questions_new, _ = cls.generate_qa_step1(
events=events, chest_observation=chest_observation
questions_new = await self.generate_qa_step1(
events=events, human_msg=human_msg, system_msg=system_msg
)
logger.debug(f"Generate_qa_step1 result list is HERE: {questions_new}")
questions = []
answers = []
for question in questions_new:
if cls.qa_cache_questions_vectordb._collection.count() > 0:
if self.qa_cache_questions_vectordb._collection.count() > 0:
docs_and_scores = (
cls.qa_cache_questions_vectordb.similarity_search_with_score(
self.qa_cache_questions_vectordb.similarity_search_with_score(
question, k=1
)
)
if docs_and_scores and docs_and_scores[0][1] < 0.05:
question_cached = docs_and_scores[0][0].page_content
assert question_cached in cls.qa_cache
answer_cached = cls.qa_cache[question_cached]
assert question_cached in qa_cache
answer_cached = qa_cache[question_cached]
questions.append(question_cached)
answers.append(answer_cached)
continue
answer = cls.generate_qa_step2(question=question)
assert question not in cls.qa_cache
cls.qa_cache[question] = answer
cls.qa_cache_questions_vectordb.add_texts(
answer = await self.generate_qa_step2(question=question)
assert question not in qa_cache
qa_cache[question] = answer
self.qa_cache_questions_vectordb.add_texts(
texts=[question],
)
with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "w") as f:
json.dump(cls.qa_cache, f)
cls.qa_cache_questions_vectordb.persist()
json.dump(qa_cache, f)
self.qa_cache_questions_vectordb.persist()
questions.append(question)
answers.append(answer)
assert len(questions_new) == len(questions) == len(answers)
logger.info(f"Curriculum Agent generate_qa Questions: {questions}")
logger.info(f"Curriculum Agent generate_qa Answers: {answers}")
return questions, answers
async def generate_qa_step1(self, events, human_msg, system_msg):
@ -157,7 +159,7 @@ class DesignCurriculum(Action):
async def generate_qa_step2(self, question):
# Implement the logic for another specific step in generating questions and answers.
logger.info(f"Curriculum Agent Question: {question}")
# logger.info(f"Curriculum Agent generate_qa_step2 Question: {question}")
human_msg = HumanMessage(content=f"Question: {question}").content
system_msg = [
SystemMessage(
@ -165,10 +167,10 @@ class DesignCurriculum(Action):
).content
]
answer = await self._aask(prompt=human_msg, system_msgs=system_msg)
logger.info(f"Curriculum Agent {answer}")
# logger.info(f"Curriculum Agent generate_qa_step2 answer: {answer}")
return answer
async def get_context_from_task(self, task):
async def get_context_from_task(self, task, qa_cache):
"""
Args: task
Returns: context: "Question: {question}\n{answer}"
@ -179,21 +181,21 @@ class DesignCurriculum(Action):
f"How to {task.replace('_', ' ').replace(' ore', '').replace(' ores', '').replace('.', '').strip().lower()}"
f" in Minecraft?"
)
if question in self.qa_cache:
answer = self.qa_cache[question]
if question in qa_cache:
answer = qa_cache[question]
else:
answer = await self.generate_qa_step2(question=question)
self.qa_cache[question] = answer
qa_cache[question] = answer
self.qa_cache_questions_vectordb.add_texts(
texts=[question],
)
with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "w") as f:
json.dump(self.qa_cache, f)
json.dump(qa_cache, f)
self.qa_cache_questions_vectordb.persist()
context = f"Question: {question}\n{answer}"
return context
async def generate_context(self, task, max_retries=5):
async def generate_context(self, task, qa_cache, max_retries=5):
"""
Refer to the code in the voyager/agents/curriculum.py propose_next_ai_task() for implementation details.
Returns: context
@ -204,21 +206,22 @@ class DesignCurriculum(Action):
raise RuntimeError("Max retries reached, failed to propose context.")
try:
context = await self.get_context_from_task(
task=task
task=task, qa_cache=qa_cache
) # Curriculum Agent Question: How to craft 4 wooden planks in Minecraft? & Curriculum Agent Answer: ...
return context
except Exception as e:
logger.info(f"Error parsing curriculum response: {e}. Trying again!")
return await self.generate_context(
task=task,
qa_cache=qa_cache,
max_retries=max_retries - 1,
)
async def run(self, task, human_msg, system_msg, *args, **kwargs):
async def run(self, task, qa_cache, human_msg, system_msg, *args, **kwargs):
logger.info(f"run {self.__repr__()}")
# Generate curriculum-related questions and answers.
# curriculum_qustion = await self.generate_qa_step1(events, human_msg, system_msg)
curriculum_context = await self.generate_context(task)
curriculum_context = await self.generate_context(task, qa_cache)
# Return the generated questions and answers.
return curriculum_context

View file

@ -30,19 +30,20 @@ class GenerateActionCode(Action):
try:
return (
parsed_result["program_code"],
parsed_result["program_code"] + "\n" + parsed_result["exec_code"],
parsed_result["program_name"],
)
except:
logger.error(f"Failed to parse response: {parsed_result}")
return None, None
return "", "", ""
async def run(self, human_msg, system_msg, *args, **kwargs):
logger.info(f"run {self.__repr__()}")
# Generate action code.
generated_code, program_name = await self.generate_code(
program_code, generated_code, program_name = await self.generate_code(
human_msg=human_msg, system_msg=system_msg
)
# Return the generated code.
return generated_code, program_name
return program_code, generated_code, program_name

View file

@ -5,6 +5,8 @@
import os
import json
from traits.trait_types import self
from metagpt.logs import logger
from metagpt.actions.minecraft.player_action import PlayerActions as Action
from metagpt.const import CKPT_DIR
@ -15,11 +17,11 @@ class RetrieveSkills(Action):
Action class for retrieving skills.
Refer to the code in the voyager/agents/skill.py for implementation details.
"""
def __init__(self, name="", context=None, llm=None):
super().__init__(name, context, llm)
self.llm.model = "gpt-3.5-turbo"
async def run(self, query, skills, *args, **kwargs):
# Implement the logic for retrieving skills here.
k = min(self.vectordb._collection.count(), self.retrieval_top_k)
@ -42,13 +44,13 @@ class AddNewSkills(Action):
Action class for adding new skills.
Refer to the code in the voyager/agents/skill.py for implementation details.
"""
def __init__(self, name="", context=None, llm=None):
super().__init__(name, context, llm)
self.llm.model = "gpt-3.5-turbo"
async def run(
self, task, program_name, program_code, skills, skill_desp, *args, **kwargs
self, task, program_name, program_code, skills, skill_desp, *args, **kwargs
):
# Implement the logic for adding new skills here.
# TODO: Fix this
@ -75,12 +77,7 @@ class AddNewSkills(Action):
ids=[program_name],
metadatas=[{"name": program_name}],
)
# FIXME
# assert self.vectordb._collection.count() == len(
# skills
# ), "vectordb is not synced with skills.json"
with open(f"{CKPT_DIR}/skill/code/{dumped_program_name}.js", "w") as f:
f.write(program_code)
with open(f"{CKPT_DIR}/skill/description/{dumped_program_name}.txt", "w") as f:
@ -99,13 +96,13 @@ class GenerateSkillDescription(Action):
Action class for generating skill descriptions.
Refer to the code in the voyager/agents/skill.py for implementation details.
"""
def __init__(self, name="", context=None, llm=None):
super().__init__(name, context, llm)
self.llm.model = "gpt-3.5-turbo"
async def run(self, program_name, human_message, system_message, *args, **kwargs):
# Implement the logic for generating skill descriptions here.
rsp = await self._aask(prompt=human_message, system_msgs=system_message)
skill_description = f" // { rsp}"
skill_description = f" // {rsp}"
return f"async function {program_name}(bot) {{\n{skill_description}\n}}"

View file

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

View file

@ -87,6 +87,7 @@ class Config(metaclass=Singleton):
self.pyppeteer_executable_path = self._get("PYPPETEER_EXECUTABLE_PATH", "")
self.prompt_format = self._get("PROMPT_FORMAT", "markdown")
self.resume = self._get("RESUME", False)
def _init_with_config_files_and_env(self, configs: dict, yaml_file):
"""Load from config/key.yaml, config/config.yaml, and env in decreasing order of priority"""

View file

@ -7,6 +7,11 @@ from pydantic import BaseModel, Field
import requests
import json
import re
import time
from langchain.vectorstores import Chroma
from langchain.embeddings.openai import OpenAIEmbeddings
from metagpt.document_store import FaissStore
from metagpt.logs import logger
from metagpt.roles import Role
@ -18,6 +23,7 @@ from metagpt.roles.minecraft.minecraft_base import Minecraft
from metagpt.environment import Environment
from metagpt.mineflayer_environment import MineflayerEnv
from metagpt.const import CKPT_DIR
from metagpt.config import CONFIG
from metagpt.actions.minecraft.control_primitives import load_skills_code
@ -33,6 +39,7 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
default="You can mine one of oak, birch, spruce, jungle, acacia, dark oak, or mangrove logs."
)
code: str = Field(default="")
program_code: str = Field(default="") # write in skill/code/*.js
program_name: str = Field(default="")
critique: str = Field(default="")
skills: dict = Field(default_factory=dict) # for skills.json
@ -76,12 +83,13 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
@property
def core_inv_items_regex(self):
return self.mf_instance.core_inv_items_regex
def set_mc_port(self, mc_port):
self.mf_instance.set_mc_port(mc_port)
self.set_mc_resume()
def set_mc_resume(self, resume: bool = False): # TODO: mv to config
if resume:
def set_mc_resume(self):
if CONFIG.resume:
logger.info(f"Loading Action Developer from {CKPT_DIR}/action")
with open(f"{CKPT_DIR}/action/chest_memory.json", "r") as f:
self.chest_memory = json.load(f)
@ -91,12 +99,14 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
self.completed_tasks = json.load(f)
with open(f"{CKPT_DIR}/curriculum/failed_tasks.json", "r") as f:
self.failed_tasks = json.load(f)
with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "r") as f:
self.qa_cache = json.load(f)
logger.info(f"Loading Skill Manager from {CKPT_DIR}/skill\033[0m")
with open(f"{CKPT_DIR}/skill/skills.json", "r") as f:
self.skills = json.load(f)
logger.info(f"Loading Qa Cache from {CKPT_DIR}/curriculum\033[0m")
with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "r") as f:
self.qa_cache = json.load(f)
def register_roles(self, roles: Iterable[Minecraft]):
for role in roles:
@ -114,7 +124,10 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
def update_context(self, context: str):
self.context = context
def update_program_code(self, program_code: str):
self.program_code = program_code
def update_code(self, code: str):
self.code = code # action_developer.gen_action_code to HERE
@ -292,8 +305,19 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
self.update_event(events)
return events
except Exception as e:
time.sleep(3) # wait for mineflayer to exit
# reset bot status here
events = self.mf_instance.reset(
options={
"mode": "hard",
"wait_ticks": 20,
"inventory": self.event[-1][1]["inventory"],
"equipment": self.event[-1][1]["status"]["equipment"],
"position": self.event[-1][1]["status"]["position"],
}
)
self.update_event(events)
logger.error(f"Failed to retrieve Minecraft events: {str(e)}")
raise {}
async def on_event_execute(self, *args):
"""
@ -316,8 +340,19 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
self.update_event(events)
return events
except Exception as e:
time.sleep(3) # wait for mineflayer to exit
# reset bot status here
events = self.mf_instance.reset(
options={
"mode": "hard",
"wait_ticks": 20,
"inventory": self.event[-1][1]["inventory"],
"equipment": self.event[-1][1]["status"]["equipment"],
"position": self.event[-1][1]["status"]["position"],
}
)
self.update_event(events)
logger.error(f"Failed to execute Minecraft events: {str(e)}")
raise {}
class MinecraftPlayer(SoftwareCompany):
@ -335,9 +370,6 @@ class MinecraftPlayer(SoftwareCompany):
def set_port(self, mc_port):
self.game_memory.set_mc_port(mc_port)
def set_resume(self, resume: bool = False):
self.game_memory.set_mc_resume(resume=resume)
def check_complete_round(self):
complete_round = []
for role in self.environment.roles.values():
@ -380,12 +412,23 @@ class MinecraftPlayer(SoftwareCompany):
async def run(self, n_round=3):
"""Run company until target round or no money"""
round_id = 0
self.game_memory.mf_instance.reset(
options={
"mode": "soft",
"wait_ticks": 20,
}
)
if CONFIG.resume:
# keep the inventory
self.game_memory.mf_instance.reset(
options={
"mode": "soft",
"wait_ticks": 20,
}
)
else:
# clear the inventory
self.game_memory.mf_instance.reset(
options={
"mode": "hard",
"wait_ticks": 20,
}
)
events = self.game_memory.mf_instance.step(
code="",
programs="",

View file

@ -14,6 +14,7 @@ const Inventory = require("./lib/observation/inventory");
const OnSave = require("./lib/observation/onSave");
const Chests = require("./lib/observation/chests");
const { plugin: tool } = require("mineflayer-tool");
const { mineflayer: mineflayerViewer } = require('prismarine-viewer')
let bot = null;
@ -50,6 +51,7 @@ app.post("/start", (req, res) => {
});
bot.once("spawn", async () => {
mineflayerViewer(bot, { port: 3007, firstPerson: false })
bot.removeListener("error", onConnectionFailed);
let itemTicks = 1;
if (req.body.reset === "hard") {
@ -222,7 +224,7 @@ app.post("/step", async (req, res) => {
}
}
bot.on("physicTick", onTick);
bot.on("physicsTick", onTick);
// initialize fail count
let _craftItemFailCount = 0;
@ -248,7 +250,7 @@ app.post("/step", async (req, res) => {
response_sent = true;
res.json(bot.observe());
}
bot.removeListener("physicTick", onTick);
bot.removeListener("physicsTick", onTick);
async function evaluateCode(code, programs) {
// Echo the code produced for players to see it. Don't echo when the bot code is already producing dialog or it will double echo

View file

@ -2,8 +2,6 @@
# @Date : 2023/9/23 12:45
# @Author : stellahong (stellahong@fuzhi.ai)
# @Desc :
import copy
from metagpt.logs import logger
from metagpt.roles.minecraft.minecraft_base import Minecraft as Base
from metagpt.schema import Message, HumanMessage, SystemMessage
@ -40,14 +38,13 @@ class ActionDeveloper(Base):
# Initialize actions specific to the Action role
self._init_actions([GenerateActionCode])
# Set events or actions the ActionAgent should watch or be aware of
# 需要根据events进行自己chest_observation的更新
self._watch([RetrieveSkills])
self.rollout_num_iter = 0
self.task_max_retries = 4
self.finish_state = len(self._actions)
self.critic_reviewer = None # self._rc.env.roles["Task Reviewer"]
self.critic_reviewer = None # self._rc.env.roles["Task Reviewer"]
def render_system_message(self, skills=[], *args, **kwargs):
"""
@ -190,6 +187,8 @@ class ActionDeveloper(Base):
return len(self._rc.news)
async def run_step(self, human_msg, system_msg, *args, **kwargs):
await self._obtain_events()
logger.info("reset before step()!")
while True:
logger.info(f"self.rollout_num_iter {self.rollout_num_iter}")
system_msg, human_msg, reward, done, info = await self.runcode_and_evaluate(human_msg, system_msg, *args,
@ -208,6 +207,7 @@ class ActionDeveloper(Base):
async def handle_add_new_skills(
self, task, program_name, program_code, skills, *args, **kwargs
):
skills = self.game_memory.skills
skill_desp = self.game_memory.skill_desp
new_skills_info = await AddNewSkills().run(
task, program_name, program_code, skills, skill_desp
@ -231,11 +231,9 @@ class ActionDeveloper(Base):
context = self.game_memory.context
# 更新生成的代码和对应程序名称
code, program_name = await GenerateActionCode().run(
program_code, code, program_name = await GenerateActionCode().run(
human_msg, system_msg, *args, **kwargs
)
# logger.warning(type(code))
# logger.info(f"Code is Here:{code}")
if code is not None:
# fixme若有独立的mc code执行入口函数使用独立的函数
@ -298,6 +296,8 @@ class ActionDeveloper(Base):
"success": self.game_memory.runtime_status,
}
logger.info(f"info is {info}")
self.perform_game_info_callback(program_code, self.game_memory.update_program_code)
self.perform_game_info_callback(code, self.game_memory.update_code)
self.perform_game_info_callback(
program_name, self.game_memory.update_program_name
@ -306,11 +306,10 @@ class ActionDeveloper(Base):
return system_msg, human_msg, 0, done, info
async def generate_action_code(self, human_msg, system_msg, *args, **kwargs):
code, program_name = await GenerateActionCode().run(
program_code, code, program_name = await GenerateActionCode().run(
human_msg, system_msg, *args, **kwargs
)
# logger.warning(type(code))
# logger.info(f"Code is Here:{code}")
self.perform_game_info_callback(program_code, self.game_memory.update_program_code)
self.perform_game_info_callback(code, self.game_memory.update_code)
self.perform_game_info_callback(
program_name, self.game_memory.update_program_name

View file

@ -132,7 +132,7 @@ class CriticReviewer(Base):
}
async def verify_task(self, human_msg, system_msg, *args, **kwargs):
success, critique = await VerifyTask().run(human_msg, system_msg, max_retries=5)
success, critique = await VerifyTask().run(human_msg, system_msg, max_retries=1)
self.perform_game_info_callback(
success, self.game_memory.update_exploration_progress
)

View file

@ -120,7 +120,7 @@ class CurriculumDesigner(Base):
return observation
# --------------------------------Design Task Prepare---------------------------------------
def render_design_task_human_message(
async def render_design_task_human_message(
self, events, chest_observation, *args, **kwargs
):
"""
@ -130,13 +130,20 @@ class CurriculumDesigner(Base):
content = ""
warm_up = self.game_memory.mf_instance.warm_up
qa_cache = self.game_memory.qa_cache
observation = self.render_curriculum_observation(
events=events, chest_observation=chest_observation
)
if self.game_memory.progress >= warm_up["context"]:
questions, answers = DesignCurriculum.generate_qa(
# if self.game_memory.progress >= 0: # TEST ONLY
human_msg = self.render_design_curriculum_human_message(
events=events, chest_observation=chest_observation
).content
system_msg = [self.render_design_curriculum_system_message().content]
questions, answers = await DesignCurriculum().generate_qa(
events=events, qa_cache=qa_cache, human_msg=human_msg, system_msg=system_msg
)
logger.debug(f"Generate_qa result is HERE: Ques: {questions}, Ans: {answers}")
i = 1
for question, answer in zip(questions, answers):
if "Answer: Unknown" in answer or "language model" in answer:
@ -162,8 +169,8 @@ class CurriculumDesigner(Base):
def render_design_task_system_message(self, *args, **kwargs):
return SystemMessage(content=load_prompt("curriculum"))
def encapsule_design_task_message(self, events, chest_observation, *args, **kwargs):
human_msg = self.render_design_task_human_message(
async def encapsule_design_task_message(self, events, chest_observation, *args, **kwargs):
human_msg = await self.render_design_task_human_message(
events=events, chest_observation=chest_observation, *args, **kwargs
)
system_msg = self.render_design_task_system_message(*args, **kwargs)
@ -292,6 +299,7 @@ class CurriculumDesigner(Base):
chest_observation = self.game_memory.chest_observation
inventoryUsed = events[-1][1]["status"]["inventoryUsed"]
task = self.game_memory.current_task
qa_cache = self.game_memory.qa_cache
if self.game_memory.progress == 0:
context = self.game_memory.context
@ -301,7 +309,7 @@ class CurriculumDesigner(Base):
)
else:
context = await DesignCurriculum().run(
task, human_msg, system_msg, *args, **kwargs
task, qa_cache, human_msg, system_msg, *args, **kwargs
)
self.perform_game_info_callback(context, self.game_memory.update_context)
return Message(
@ -313,18 +321,15 @@ class CurriculumDesigner(Base):
async def _act(self) -> Message:
todo = self._rc.todo
logger.debug(f"Todo is {todo}")
self.maintain_actions(todo)
self.maintain_actions(todo)
# 获取最新的游戏周边环境信息
# events = await self._obtain_events()
events = self.game_memory.event
events = await self._obtain_events()
logger.info(f"Retrieve event is HERE: {events}, current task is {self.game_memory.current_task}")
# events = self.game_memory.event
chest_observation = self.game_memory.chest_observation
# DesignCurriculum.set_qa_cache(self.game_memory.qa_cache)
# msg = self._rc.memory.get(k=1)[0]
# query = msg.content
design_task_message = self.encapsule_design_task_message(
design_task_message = await self.encapsule_design_task_message(
events, chest_observation
)
design_curriculum_message = self.encapsule_design_curriculum_message(

View file

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

View file

@ -38,10 +38,10 @@ def parse_action_response(msg: str):
retry = 3
error = None # 3 times failed return error
babel_generator = require("@babel/generator").default
while retry > 0:
try:
parsed = parse_js_code(msg)
babel_generator = require("@babel/generator").default
# Collect func list: check if func & async
functions = []
assert len(list(parsed.program.body)) > 0, "No functions found"

View file

@ -11,10 +11,9 @@ from metagpt.roles.minecraft.critic_agent import CriticReviewer
from metagpt.minecraft_team import MinecraftPlayer
async def learn(task="Start", investment: float = 50.0, n_round: int = 3):
async def learn(task="Start", investment: float = 50.0, n_round: int = 50):
mc_player = MinecraftPlayer()
mc_player.set_port(33141) # Modify this to your Minecraft LAN port
# mc_player.set_resume(True) # If load json from ckpt dir(include chest_memory, skills, ...)
mc_player.set_port(30181) # Modify this to your Minecraft LAN port
mc_player.hire(
[
CurriculumDesigner(),