From a37be1bd335d5916840db2254890703105ce68f7 Mon Sep 17 00:00:00 2001 From: better629 Date: Sun, 8 Oct 2023 17:57:50 +0800 Subject: [PATCH] save environment/step.json after movement and add more logs --- .../st_game/actions/gen_hourly_schedule.py | 2 +- examples/st_game/actions/st_action.py | 4 +- examples/st_game/actions/task_decomp.py | 2 +- examples/st_game/plan/st_plan.py | 1 + examples/st_game/reflect/reflect.py | 6 +- examples/st_game/roles/st_role.py | 95 ++++++++++--------- examples/st_game/utils/mg_ga_transform.py | 21 +++- metagpt/provider/openai_api.py | 6 +- 8 files changed, 81 insertions(+), 56 deletions(-) diff --git a/examples/st_game/actions/gen_hourly_schedule.py b/examples/st_game/actions/gen_hourly_schedule.py index f4289d6ef..665b6fb3b 100644 --- a/examples/st_game/actions/gen_hourly_schedule.py +++ b/examples/st_game/actions/gen_hourly_schedule.py @@ -121,7 +121,7 @@ class GenHourlySchedule(STAction): output = self._run_v1(prompt) CONFIG.max_tokens_rsp = raw_max_tokens_rsp - logger.info(f"max_tokens_rsp: {CONFIG.max_tokens_rsp}") + logger.debug(f"max_tokens_rsp: {CONFIG.max_tokens_rsp}") logger.info(f"Role: {role.name} _generate_schedule_for_given_hour prompt_input: {prompt_input_str}, " f"output: {output}") return output diff --git a/examples/st_game/actions/st_action.py b/examples/st_game/actions/st_action.py index e5c048b4e..1c997836f 100644 --- a/examples/st_game/actions/st_action.py +++ b/examples/st_game/actions/st_action.py @@ -8,6 +8,7 @@ import json from metagpt.actions.action import Action from metagpt.schema import Message +from metagpt.logs import logger from examples.st_game.utils.const import PROMPTS_DIR @@ -63,6 +64,7 @@ class STAction(Action): """ for idx in range(retry): llm_resp = self._ask(prompt) + logger.info(f"_run_v1 llm raw resp: {llm_resp}") if self._func_validate(llm_resp, prompt): return self._func_cleanup(llm_resp, prompt) return self.fail_default_resp @@ -81,7 +83,7 @@ class STAction(Action): for idx in range(retry): try: llm_resp = self._ask(prompt) - print("llm_resp ", llm_resp) + logger.info(f"_run_v2 llm raw resp: {llm_resp}") end_idx = llm_resp.strip().rfind("}") + 1 llm_resp = llm_resp[:end_idx] llm_resp = json.loads(llm_resp)["output"] diff --git a/examples/st_game/actions/task_decomp.py b/examples/st_game/actions/task_decomp.py index 3a4ffc810..838130740 100644 --- a/examples/st_game/actions/task_decomp.py +++ b/examples/st_game/actions/task_decomp.py @@ -71,7 +71,7 @@ class TaskDecomp(STAction): def _func_validate(self, llm_resp: str, prompt: str) -> bool: # TODO -- this sometimes generates error try: - self._func_cleanup(llm_resp) + self._func_cleanup(llm_resp, prompt) except Exception as exp: return False return True diff --git a/examples/st_game/plan/st_plan.py b/examples/st_game/plan/st_plan.py index 39173c600..dfdf484fb 100644 --- a/examples/st_game/plan/st_plan.py +++ b/examples/st_game/plan/st_plan.py @@ -54,6 +54,7 @@ def plan(role: "STRole", maze: Maze, roles: list["STRole"], new_day: bool, retri # c) False if focused_event: reaction_mode = _should_react(role, focused_event, roles) + logger.info(f"Role: {role.name} reaction_mode: {reaction_mode}") if reaction_mode: # If we do want to chat, then we generate conversation if reaction_mode[:9] == "chat with": diff --git a/examples/st_game/reflect/reflect.py b/examples/st_game/reflect/reflect.py index 62f835e1a..7b5f05851 100644 --- a/examples/st_game/reflect/reflect.py +++ b/examples/st_game/reflect/reflect.py @@ -6,6 +6,7 @@ import datetime from metagpt.logs import logger from examples.st_game.utils.utils import get_embedding +from examples.st_game.memory.retrieve import new_agent_retrieve from examples.st_game.actions.run_reflect_action import ( AgentFocusPt, AgentInsightAndGuidance, AgentEventTriple, AgentEventPoignancy, AgentChatPoignancy, AgentPlanThoughtOnConvo, @@ -102,13 +103,14 @@ def run_reflect(role: "STRole"): focal_points = generate_focal_points(role, 3) # Retrieve the relevant Nodesobject for each of the focal points. # has keys of focal points, and values of the associated Nodes. - retrieved = role.retrieve(focal_points) + retrieved = new_agent_retrieve(role, focal_points) # For each of the focal points, generate thoughts and save it in the # agent's memory. for focal_pt, nodes in retrieved.items(): xx = [i.embedding_key for i in nodes] - for xxx in xx: logger.info(f"Nodes retrieved for {focal_pt} are {xxx}.") + for xxx in xx: + logger.info(f"Nodes retrieved for `{focal_pt}` are `{xxx}`.") thoughts = generate_insights_and_evidence(role, nodes, 5) # 生成的是字典类型 diff --git a/examples/st_game/roles/st_role.py b/examples/st_game/roles/st_role.py index 8b86e8c93..62b74d453 100644 --- a/examples/st_game/roles/st_role.py +++ b/examples/st_game/roles/st_role.py @@ -33,7 +33,7 @@ from examples.st_game.memory.scratch import Scratch from examples.st_game.utils.utils import get_embedding, path_finder from examples.st_game.utils.const import collision_block_id, STORAGE_PATH from examples.st_game.reflect.reflect import generate_poig_score -from examples.st_game.utils.mg_ga_transform import save_movement, get_role_environment +from examples.st_game.utils.mg_ga_transform import save_movement, get_role_environment, save_environment from examples.st_game.actions.inner_voice_action import AgentWhisperThoughtAction from examples.st_game.actions.run_reflect_action import AgentEventTriple from examples.st_game.reflect.reflect import role_reflect @@ -261,51 +261,52 @@ class STRole(Role): obj = p_event[2].split(":")[-1] keywords.update([sub, obj]) - # Get event embedding - desc_embedding_in = desc - if "(" in desc: - desc_embedding_in = (desc_embedding_in.split("(")[1] - .split(")")[0] - .strip()) - if desc_embedding_in in self._rc.memory.embeddings: - event_embedding = self._rc.memory.embeddings[desc_embedding_in] - else: - event_embedding = get_embedding(desc_embedding_in) - event_embedding_pair = (desc_embedding_in, event_embedding) - - # Get event poignancy. - event_poignancy = generate_poig_score(self, - "action", - desc_embedding_in) - - # If we observe the persona's self chat, we include that in the memory - # of the persona here. - chat_node_ids = [] - if p_event[0] == f"{self.name}" and p_event[1] == "chat with": - curr_event = self._rc.scratch.act_event - if self._rc.scratch.act_description in self._rc.memory.embeddings: - chat_embedding = self._rc.memory.embeddings[ - self._rc.scratch.act_description] + # Get event embedding + desc_embedding_in = desc + if "(" in desc: + desc_embedding_in = (desc_embedding_in.split("(")[1] + .split(")")[0] + .strip()) + if desc_embedding_in in self._rc.memory.embeddings: + event_embedding = self._rc.memory.embeddings[desc_embedding_in] else: - chat_embedding = get_embedding(self._rc.scratch - .act_description) - chat_embedding_pair = (self._rc.scratch.act_description, - chat_embedding) - chat_poignancy = generate_poig_score(self._rc.scratch, "chat", - self._rc.scratch.act_description) - chat_node = self._rc.memory.add_chat(self._rc.scratch.curr_time, None, - curr_event[0], curr_event[1], curr_event[2], - self._rc.scratch.act_description, keywords, - chat_poignancy, chat_embedding_pair, - self._rc.scratch.chat) - chat_node_ids = [chat_node.node_id] + event_embedding = get_embedding(desc_embedding_in) + event_embedding_pair = (desc_embedding_in, event_embedding) - # Finally, we add the current event to the agent's memory. - ret_events += [self._rc.memory.add_event(self._rc.scratch.curr_time, None, - s, p, o, desc, keywords, event_poignancy, - event_embedding_pair, chat_node_ids)] - self._rc.scratch.importance_trigger_curr -= event_poignancy - self._rc.scratch.importance_ele_n += 1 + # Get event poignancy. + event_poignancy = generate_poig_score(self, + "event", + desc_embedding_in) + logger.info(f"Role {self.name} event_poignancy: {event_poignancy}") + + # If we observe the persona's self chat, we include that in the memory + # of the persona here. + chat_node_ids = [] + if p_event[0] == f"{self.name}" and p_event[1] == "chat with": + curr_event = self._rc.scratch.act_event + if self._rc.scratch.act_description in self._rc.memory.embeddings: + chat_embedding = self._rc.memory.embeddings[ + self._rc.scratch.act_description] + else: + chat_embedding = get_embedding(self._rc.scratch + .act_description) + chat_embedding_pair = (self._rc.scratch.act_description, + chat_embedding) + chat_poignancy = generate_poig_score(self._rc.scratch, "chat", + self._rc.scratch.act_description) + chat_node = self._rc.memory.add_chat(self._rc.scratch.curr_time, None, + curr_event[0], curr_event[1], curr_event[2], + self._rc.scratch.act_description, keywords, + chat_poignancy, chat_embedding_pair, + self._rc.scratch.chat) + chat_node_ids = [chat_node.node_id] + + # Finally, we add the current event to the agent's memory. + ret_events += [self._rc.memory.add_event(self._rc.scratch.curr_time, None, + s, p, o, desc, keywords, event_poignancy, + event_embedding_pair, chat_node_ids)] + self._rc.scratch.importance_trigger_curr -= event_poignancy + self._rc.scratch.importance_ele_n += 1 return ret_events @@ -353,7 +354,7 @@ class STRole(Role): # is a list of tile coordinates where the persona may go # to execute the current action. The goal is to pick one of them. target_tiles = None - logger.info("plan: ", plan) + logger.info(f"Role {self.name} plan: {plan}") if "" in plan: # Executing persona-persona interaction. @@ -510,7 +511,7 @@ class STRole(Role): elif (self.scratch.curr_time.strftime('%A %B %d') != self.curr_time.strftime('%A %B %d')): new_day = "New day" - logger.info(f"Role: {self.name} {new_day}") + logger.info(f"Role: {self.name} new_day: {new_day}") self._rc.scratch.curr_time = self.curr_time # get maze_env from self._rc.env, and observe env info @@ -536,7 +537,9 @@ class STRole(Role): # step update logger.info(f"Role: {self.name} run at {self.step} step on {self.curr_time}") self.step += 1 + save_environment(self.name, self.step, self.sim_code, next_tile) self.curr_time += datetime.timedelta(seconds=self.sec_per_step) self.inner_voice = False + time.sleep(0.5) return DummyMessage() diff --git a/examples/st_game/utils/mg_ga_transform.py b/examples/st_game/utils/mg_ga_transform.py index 9b445fb54..70f49ee83 100644 --- a/examples/st_game/utils/mg_ga_transform.py +++ b/examples/st_game/utils/mg_ga_transform.py @@ -21,8 +21,7 @@ def save_movement(role_name: str, role_move: dict, step: int, sim_code: str, cur if not movement_path.parent.exists(): movement_path.parent.mkdir(exist_ok=True) if movement_path.exists(): - with open(movement_path, "r") as fin: - movement = json.load(fin) + movement = read_json_file(movement_path) else: movement = { "persona": dict(), @@ -35,6 +34,24 @@ def save_movement(role_name: str, role_move: dict, step: int, sim_code: str, cur logger.info(f"save_movement at step: {step}, curr_time: {movement['meta']['curr_time']}") +def save_environment(role_name: str, step: int, sim_code: str, movement: list[int]): + environment_path = STORAGE_PATH.joinpath(f"{sim_code}/environment/{step}.json") + if not environment_path.parent.exists(): + environment_path.parent.mkdir(exist_ok=True) + if environment_path.exists(): + environment = read_json_file(environment_path) + else: + environment = {} + + environment[role_name] = { + "maze": "the_ville", + "x": movement[0], + "y": movement[1] + } + write_json_file(environment_path, environment) + logger.info(f"save_environment at step: {step}") + + def get_role_environment(sim_code: str, role_name: str, step: int = 0) -> dict: env_path = STORAGE_PATH.joinpath(f"{sim_code}/environment/{step}.json") role_env = None diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 7e865f288..461986f16 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -212,7 +212,7 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): def _chat_completion(self, messages: list[dict]) -> dict: rsp = self.llm.ChatCompletion.create(**self._cons_kwargs(messages)) - self._update_costs(rsp) + self._update_costs(rsp.get("usage")) return rsp def completion(self, messages: list[dict]) -> dict: @@ -249,7 +249,7 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): usage["completion_tokens"] = completion_tokens return usage except Exception as e: - logger.error("usage calculation failed!", e) + logger.error("usage calculation failed! {e}") else: return usage @@ -286,7 +286,7 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): completion_tokens = int(usage["completion_tokens"]) self._cost_manager.update_cost(prompt_tokens, completion_tokens, self.model) except Exception as e: - logger.error("updating costs failed!", e) + logger.error("updating costs failed! {e}") def get_costs(self) -> Costs: return self._cost_manager.get_costs()