update code

This commit is contained in:
stellahsr 2023-10-09 19:32:50 +08:00
parent f70ba27d35
commit 37a7a96bbe
13 changed files with 255 additions and 215 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

@ -21,11 +21,11 @@ class DesignTask(Action):
Action class for decomposing a task.
Refer to the code in the voyager/agents/curriculum.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 decompose_task(self, query, events):
system_msgs = SystemMessage(
content=load_prompt("curriculum_task_decomposition")
@ -34,11 +34,11 @@ class DesignTask(Action):
events=events, chest_observation=""
) + HumanMessage(content=f"Final task: {query}")
logger.info(f"Curriculum Agent task decomposition\nFinal task: {query}")
rsp = await self._aask(prompt=prompt, system_msgs=system_msgs)
logger.info(f"Curriculum Agent task decomposition\n{rsp}")
return fix_and_parse_json(rsp)
def parse_llm_response(self, llm_resp):
task = ""
for line in llm_resp.split("\n"):
@ -46,14 +46,14 @@ class DesignTask(Action):
task = line[5:].replace(".", "").strip()
assert task, "Task not found in Curriculum Agent response"
return {"next_task": task}
async def generate_task(self, human_msg, system_msg, max_retries=5):
"""
Refer to the code in the voyager/agents/curriculum.py propose_next_ai_task() for implementation details.
Returns: task & context
"""
if max_retries == 0:
raise RuntimeError("Max retries reached, failed to propose task.")
curriculum = await self._aask(prompt=human_msg, system_msgs=system_msg)
@ -72,14 +72,14 @@ class DesignTask(Action):
system_msg=system_msg,
max_retries=max_retries - 1,
)
async def run(self, human_msg, system_msg, *args, **kwargs):
logger.info(f"run {self.__repr__()}")
# Call the language model to generate a response.
task = await self.generate_task(human_msg=human_msg, system_msg=system_msg)
return task
@ -88,49 +88,51 @@ class DesignCurriculum(Action):
Action class for designing curriculum-related questions.
Refer to the code in the voyager/agents/curriculum.py for implementation details.
"""
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, 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 self.qa_cache
answer_cached = self.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 self.qa_cache
self.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(self.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):
biome = events[-1][1]["status"]["biome"].replace("_", " ")
questions = [
@ -139,7 +141,7 @@ class DesignCurriculum(Action):
f"What are the mobs that I can find in the {biome} in Minecraft?",
]
qa_response = await self._aask(prompt=human_msg, system_msgs=system_msg)
try:
# Regex pattern to extract question and concept pairs
pattern = r"Question \d+: (.+)\nConcept \d+: (.+)"
@ -154,10 +156,10 @@ class DesignCurriculum(Action):
f"QA step 1 ask questions: {e}."
)
return questions
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,16 +167,16 @@ 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):
"""
Args: task
Returns: context: "Question: {question}\n{answer}"
if include ore in question, gpt will try to use tool with skill touch enhancement to mine
"""
question = (
f"How to {task.replace('_', ' ').replace(' ore', '').replace(' ores', '').replace('.', '').strip().lower()}"
f" in Minecraft?"
@ -192,14 +194,14 @@ class DesignCurriculum(Action):
self.qa_cache_questions_vectordb.persist()
context = f"Question: {question}\n{answer}"
return context
async def generate_context(self, task, max_retries=5):
"""
Refer to the code in the voyager/agents/curriculum.py propose_next_ai_task() for implementation details.
Returns: context
"""
if max_retries == 0:
raise RuntimeError("Max retries reached, failed to propose context.")
try:
@ -213,12 +215,12 @@ class DesignCurriculum(Action):
task=task,
max_retries=max_retries - 1,
)
async def run(self, task, 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)
# Return the generated questions and answers.
return curriculum_context

View file

@ -12,11 +12,11 @@ class GenerateActionCode(Action):
Action class for generating action code.
Refer to the code in the voyager/agents/action.py for implementation details.
"""
def __init__(self, name="", context=None, llm=None):
super().__init__(name, context, llm)
self.llm.model = "gpt-4"
async def generate_code(self, human_msg, system_msg=[]):
"""
Generate action code logic.
@ -27,22 +27,23 @@ class GenerateActionCode(Action):
rsp = await self._aask(prompt=human_msg, system_msgs=system_msg)
parsed_result = parse_action_response(rsp)
# logger.info(f"parsed_result is HERE: {parsed_result}")
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 None, None, None # TODO: midify to "", "", ""
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,70 @@
# @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.skills = {} # for skills.json
self.qa_cache = {}
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 = json.load(f)
with open(f"{CKPT_DIR}/curriculum/qa_cache.json", "r") as f:
self.qa_cache = 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), (
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."
)
# Check if Skill Manager's vectordb right using
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"
)
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

@ -4,7 +4,7 @@
# @Desc :
from typing import Iterable, Dict, Any
from pydantic import BaseModel, Field
import requests
import json
import re
@ -18,6 +18,7 @@ from metagpt.roles.minecraft.minecraft_base import Minecraft
from metagpt.environment import Environment
from metagpt.mineflayer_environment import MineflayerEnv
from metagpt.const import CKPT_DIR
from metagpt.config import CONFIG
from metagpt.actions.minecraft.control_primitives import load_skills_code
@ -33,6 +34,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 +78,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,16 +94,23 @@ 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:
role.set_memory(self)
for act in role.vdb_actions:
logger.info(act)
act.set_qa_cache_questions_vectordb(self.qa_cache_questions_vectordb)
act.set_qa_cache(self.qa_cache)
act.set_vectordb(self.vectordb)
def update_event(self, event: Dict):
if self.event == event:
@ -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
@ -335,9 +348,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 +390,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") {

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):
"""
@ -208,6 +205,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 +229,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 +294,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 +304,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

@ -18,29 +18,29 @@ class CurriculumDesigner(Base):
"""
CurriculumDesigner is the automatic curriculum in paper, refer to the code voyager/agents/curriculum.py
"""
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
self._init_actions([DesignTask, DesignCurriculum])
# Set events or actions the ActionAgent should watch or be aware of
self._watch([PlayerActions, DesignTask])
logger.info(self._actions)
self.finish_state = len(self._actions)
def render_curriculum_observation(self, *, events, chest_observation):
"""
Returns: observation for curriculum
Refer to @ https://github.com/MineDojo/Voyager/blob/main/voyager/agents/curriculum.py
"""
assert events[-1][0] == "observe", "Last event must be observe"
event = events[-1][1]
biome = event["status"]["biome"]
@ -54,31 +54,31 @@ class CurriculumDesigner(Base):
equipment = event["status"]["equipment"]
inventory_used = event["status"]["inventoryUsed"]
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"
other_blocks = ", ".join(
list(
set(block_records).difference(set(voxels).union(set(inventory.keys())))
)
)
other_blocks = other_blocks if other_blocks else "None"
nearby_entities = (
", ".join([k for k, v in sorted(entities.items(), key=lambda x: x[1])])
if entities
else "None"
)
completed_tasks = (
", ".join(self.game_memory.completed_tasks)
if self.game_memory.completed_tasks
@ -89,18 +89,18 @@ class CurriculumDesigner(Base):
if self.game_memory.failed_tasks
else "None"
)
# 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
for k, v in inventory.items()
if self.game_memory.core_inv_items_regex.search(k) is not None
}
observation = {
"context": "",
"biome": f"Biome: {biome}\n\n",
@ -118,25 +118,31 @@ class CurriculumDesigner(Base):
"failed_tasks": f"Failed tasks that are too hard: {failed_tasks}\n\n",
}
return observation
# --------------------------------Design Task Prepare---------------------------------------
def render_design_task_human_message(
self, events, chest_observation, *args, **kwargs
async def render_design_task_human_message(
self, events, chest_observation, *args, **kwargs
):
"""
Returns: observation for curriculum
Refer to @ https://github.com/MineDojo/Voyager/blob/main/voyager/agents/curriculum.py
"""
content = ""
warm_up = self.game_memory.mf_instance.warm_up
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 self.curriculum_design_action.generate_qa(
events=events, 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:
@ -146,7 +152,7 @@ class CurriculumDesigner(Base):
i += 1
if i > 5:
break
for key in CURRICULUM_OB:
if self.game_memory.progress >= warm_up[key]:
if warm_up[key] != 0:
@ -155,20 +161,20 @@ class CurriculumDesigner(Base):
should_include = True
if should_include:
content += observation[key]
logger.info(f"Curriculum Agent human message\n{content}")
return HumanMessage(content=content)
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)
return {"system_msg": [system_msg.content], "human_msg": human_msg.content}
def generate_task_if_inventory_full(self, events, chest_observation):
"""
TODO: Try if this could be done with prompt
@ -187,15 +193,15 @@ class CurriculumDesigner(Base):
else:
task = "Craft 1 chest"
return task
# -----------------------------------------------------------------------------------------
# --------------------------------Design Curriculum Prepare--------------------------------
def render_design_curriculum_system_message(self, *args, **kwargs):
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
@ -204,16 +210,16 @@ class CurriculumDesigner(Base):
for key in CURRICULUM_OB:
content += observation[key]
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
)
system_msg = self.render_design_curriculum_system_message(*args, **kwargs)
return {"system_msg": [system_msg.content], "human_msg": human_msg.content}
def generate_context_if_inventory_full(self, events, chest_observation):
"""
TODO: Try if this could be done with prompt
@ -244,9 +250,9 @@ class CurriculumDesigner(Base):
else:
context = "Craft 1 chest with 8 planks of any kind of wood."
return context
# -----------------------------------------------------------------------------------------
async def handle_task_design(self, human_msg, system_msg, *args, **kwargs):
"""
Args:
@ -260,7 +266,7 @@ class CurriculumDesigner(Base):
events = self.game_memory.event
chest_observation = self.game_memory.chest_observation
inventoryUsed = events[-1][1]["status"]["inventoryUsed"]
if self.game_memory.progress == 0:
task = self.game_memory.current_task
elif inventoryUsed >= 33:
@ -270,12 +276,12 @@ class CurriculumDesigner(Base):
else:
task = await DesignTask().run(human_msg, system_msg, *args, **kwargs)
logger.info(f"Handle_task_design result is Here: {task}")
self.perform_game_info_callback(task, self.game_memory.update_task)
return Message(
content=f"{task}", instruct_content="task_design", role=self.profile
)
async def handle_curriculum_design(self, human_msg, system_msg, *args, **kwargs):
"""
refer to the context generation in voyager
@ -292,7 +298,7 @@ class CurriculumDesigner(Base):
chest_observation = self.game_memory.chest_observation
inventoryUsed = events[-1][1]["status"]["inventoryUsed"]
task = self.game_memory.current_task
if self.game_memory.progress == 0:
context = self.game_memory.context
elif inventoryUsed >= 33:
@ -300,37 +306,34 @@ class CurriculumDesigner(Base):
self, events=events, chest_observation=chest_observation
)
else:
context = await DesignCurriculum().run(
context = await self.DesignCurriculum().run(
task, human_msg, system_msg, *args, **kwargs
)
self.perform_game_info_callback(context, self.game_memory.update_context)
return Message(
content=f"{context}",
instruct_content="curriculum_design",
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)
# 获取最新的游戏周边环境信息
# events = await self._obtain_events()
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(
events, chest_observation
)
handler_map = {
DesignTask: self.handle_task_design,
DesignCurriculum: self.handle_curriculum_design,
@ -345,5 +348,5 @@ class CurriculumDesigner(Base):
msg.round_id = self.round_id
self._publish_message(msg)
return msg
raise ValueError(f"Unknown todo type: {type(todo)}")

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

@ -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(),