update code

This commit is contained in:
stellahsr 2023-10-11 16:59:19 +08:00
parent 4bb69f4503
commit dd99839836
8 changed files with 124 additions and 136 deletions

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,11 +88,11 @@ 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)
async def generate_qa(self, events, human_msg, system_msg):
async def generate_qa(self, events, qa_cache, human_msg, system_msg):
"""
Generate qa for DesignTask's HumanMessage
"""
@ -112,19 +112,19 @@ class DesignCurriculum(Action):
)
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 self.qa_cache
answer_cached = self.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 = await self.generate_qa_step2(question=question)
assert question not in self.qa_cache
self.qa_cache[question] = answer
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(self.qa_cache, f)
json.dump(qa_cache, f)
self.qa_cache_questions_vectordb.persist()
questions.append(question)
answers.append(answer)
@ -132,7 +132,7 @@ class DesignCurriculum(Action):
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 = [
@ -141,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+: (.+)"
@ -156,7 +156,7 @@ 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 generate_qa_step2 Question: {question}")
@ -169,58 +169,59 @@ class DesignCurriculum(Action):
answer = await self._aask(prompt=human_msg, system_msgs=system_msg)
# 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}"
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?"
)
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
"""
if max_retries == 0:
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

@ -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,7 +27,7 @@ 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"],
@ -36,14 +36,14 @@ class GenerateActionCode(Action):
)
except:
logger.error(f"Failed to parse response: {parsed_result}")
return None, None, None # TODO: midify to "", "", ""
return "", "", ""
async def run(self, human_msg, system_msg, *args, **kwargs):
logger.info(f"run {self.__repr__()}")
# Generate action code.
program_code, generated_code, program_name = await self.generate_code(
human_msg=human_msg, system_msg=system_msg
)
# Return the generated code.
return program_code, generated_code, program_name

View file

@ -7,9 +7,11 @@ 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
from metagpt.logs import logger
from metagpt.actions.minecraft.control_primitives import load_skills_code
class PlayerActions(Action):

View file

@ -4,11 +4,15 @@
# @Desc :
from typing import Iterable, Dict, Any
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
from metagpt.schema import Message
@ -300,30 +304,20 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
)
self.update_event(events)
return events
except Exception as e:
### add: 异常的话结束当前流程,继续后续运行
logger.error(f"Failed to retrieve Minecraft events: {str(e)}")
time.sleep(3) # wait for mineflayer to exit
info = {
"task": self.current_task,
"success": False,
}
# reset bot status here
events = self.mf_instance.reset(
options={
"mode": "hard",
"wait_ticks": 20,
"inventory": self.events[-1][1]["inventory"],
"equipment": self.events[-1][1]["status"]["equipment"],
"position": self.events[-1][1]["status"]["position"],
"inventory": self.event[-1][1]["inventory"],
"equipment": self.event[-1][1]["status"]["equipment"],
"position": self.event[-1][1]["status"]["position"],
}
)
self.update_event(events)
# use red color background to print the error
logger.info("Your last round rollout terminated due to error:")
logger.info(f"\033[41m{e}\033[0m")
logger.error(f"Failed to retrieve Minecraft events: {str(e)}")
async def on_event_execute(self, *args):
"""
@ -346,27 +340,19 @@ class GameEnvironment(BaseModel, arbitrary_types_allowed=True):
self.update_event(events)
return events
except Exception as e:
### add: 异常的话结束当前流程,继续后续运行
logger.error(f"Failed to retrieve Minecraft events: {str(e)}")
time.sleep(3) # wait for mineflayer to exit
info = {
"task": self.current_task,
"success": False,
}
# reset bot status here
events = self.mf_instance.reset(
options={
"mode": "hard",
"wait_ticks": 20,
"inventory": self.events[-1][1]["inventory"],
"equipment": self.events[-1][1]["status"]["equipment"],
"position": self.events[-1][1]["status"]["position"],
"inventory": self.event[-1][1]["inventory"],
"equipment": self.event[-1][1]["status"]["equipment"],
"position": self.event[-1][1]["status"]["position"],
}
)
self.update_event(events)
# use red color background to print the error
logger.info("Your last round rollout terminated due to error:")
logger.info(f"\033[41m{e}\033[0m")
logger.error(f"Failed to execute Minecraft events: {str(e)}")
class MinecraftPlayer(SoftwareCompany):

View file

@ -224,7 +224,7 @@ app.post("/step", async (req, res) => {
}
}
bot.on("physicTick", onTick);
bot.on("physicsTick", onTick);
// initialize fail count
let _craftItemFailCount = 0;
@ -250,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

@ -144,7 +144,8 @@ class CriticReviewer(Base):
instruct_content="verify_task",
role=self.profile,
send_to=agent_registry.entries["skill_manager"]()._setting.name,
)
) # addnewskill
# TODO:if not success
async def _act(self) -> Message:
@ -157,7 +158,7 @@ class CriticReviewer(Base):
# 获取最新的游戏周边信息
events = await self._execute_events()
self.perform_game_info_callback(events, self.game_memory.update_chest_memory)
# logger.info(f"Execute return event is {self.game_memory.event}")
context = self.game_memory.context
task = self.game_memory.current_task
chest_observation = self.game_memory.chest_observation

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,29 +118,30 @@ class CurriculumDesigner(Base):
"failed_tasks": f"Failed tasks that are too hard: {failed_tasks}\n\n",
}
return observation
# --------------------------------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
Refer to @ https://github.com/MineDojo/Voyager/blob/main/voyager/agents/curriculum.py
"""
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"]:
# 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 self.curriculum_design_action.generate_qa(
events=events, human_msg=human_msg, system_msg=system_msg
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
@ -152,7 +153,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:
@ -161,20 +162,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"))
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
@ -193,15 +194,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
@ -210,16 +211,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
@ -250,9 +251,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:
@ -266,7 +267,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:
@ -276,12 +277,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
@ -298,7 +299,8 @@ 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
elif inventoryUsed >= 33:
@ -307,33 +309,33 @@ 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(
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)
# 获取最新的游戏周边环境信息
# 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
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,
@ -348,5 +350,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

@ -40,12 +40,8 @@ def parse_action_response(msg: str):
error = None # 3 times failed return error
while retry > 0:
try:
babel = require("@babel/core")
parsed = parse_js_code(msg)
babel_generator = require("@babel/generator").default
code_pattern = re.compile(r"```(?:javascript|js)(.*?)```", re.DOTALL)
code = "\n".join(code_pattern.findall(msg))
parsed = babel.parse(code)
# Collect func list: check if func & async
functions = []
assert len(list(parsed.program.body)) > 0, "No functions found"