diff --git a/src/classes/action.py b/src/classes/action.py index b4b71c1..a8e4a86 100644 --- a/src/classes/action.py +++ b/src/classes/action.py @@ -233,6 +233,52 @@ class MoveToRegion(DefineAction, ActualActionMixin): """ return True + +class MoveToAvatar(DefineAction, ActualActionMixin): + """ + 朝另一个角色当前位置移动。 + """ + COMMENT = "移动到某个角色所在位置" + DOABLES_REQUIREMENTS = "任何时候都可以执行" + PARAMS = {"avatar_name": "str"} + + def _get_target(self, avatar_name: str): + """ + 根据名字查找目标角色;找不到返回 None。 + """ + for v in self.world.avatar_manager.avatars.values(): + if v.name == avatar_name: + return v + raise ValueError(f"未找到名为 {avatar_name} 的角色") + + def _execute(self, avatar_name: str) -> None: + target = self._get_target(avatar_name) + if target is None: + return + cur_loc = (self.avatar.pos_x, self.avatar.pos_y) + target_loc = (target.pos_x, target.pos_y) + delta_x = target_loc[0] - cur_loc[0] + delta_y = target_loc[1] - cur_loc[1] + step = getattr(self.avatar, "move_step_length", 1) + delta_x = max(-step, min(step, delta_x)) + delta_y = max(-step, min(step, delta_y)) + Move(self.avatar, self.world).execute(delta_x, delta_y) + + def is_finished(self, avatar_name: str) -> bool: + target = self._get_target(avatar_name) + if target is None: + return True + return self.avatar.pos_x == target.pos_x and self.avatar.pos_y == target.pos_y + + def get_event(self, avatar_name: str) -> Event: + target = self._get_target(avatar_name) + target_name = target.name if target is not None else avatar_name + return Event(self.world.month_stamp, f"{self.avatar.name} 开始移动向 {target_name}") + + @property + def is_doable(self) -> bool: + return True + @long_action(step_month=10) class Cultivate(DefineAction, ActualActionMixin): """ @@ -526,10 +572,10 @@ class Sold(DefineAction, ActualActionMixin): return isinstance(region, CityRegion) and bool(self.avatar.items) -ALL_ACTION_CLASSES = [Move, Cultivate, Breakthrough, MoveToRegion, Play, Hunt, Harvest, Sold] -ALL_ACTUAL_ACTION_CLASSES = [Cultivate, Breakthrough, MoveToRegion, Play, Hunt, Harvest, Sold] -ALL_ACTION_NAMES = ["Move", "Cultivate", "Breakthrough", "MoveToRegion", "Play", "Hunt", "Harvest", "Sold"] -ALL_ACTUAL_ACTION_NAMES = ["Cultivate", "Breakthrough", "MoveToRegion", "Play", "Hunt", "Harvest", "Sold"] +ALL_ACTION_CLASSES = [Move, Cultivate, Breakthrough, MoveToRegion, MoveToAvatar, Play, Hunt, Harvest, Sold] +ALL_ACTUAL_ACTION_CLASSES = [Cultivate, Breakthrough, MoveToRegion, MoveToAvatar, Play, Hunt, Harvest, Sold] +ALL_ACTION_NAMES = ["Move", "Cultivate", "Breakthrough", "MoveToRegion", "MoveToAvatar", "Play", "Hunt", "Harvest", "Sold"] +ALL_ACTUAL_ACTION_NAMES = ["Cultivate", "Breakthrough", "MoveToRegion", "MoveToAvatar", "Play", "Hunt", "Harvest", "Sold"] ACTION_INFOS = { action.__name__: { diff --git a/src/run/log.py b/src/run/log.py index 385aa3f..a2c56ea 100644 --- a/src/run/log.py +++ b/src/run/log.py @@ -97,11 +97,10 @@ class Logger: duration: 调用耗时(秒) additional_info: 额外信息 """ + # 机器可读的摘要(不包含大段文本,避免 JSON 转义导致 \ 混杂) log_data = { "timestamp": datetime.now().isoformat(), "model_name": model_name, - "prompt": prompt, - "response": response, "prompt_length": len(prompt), "response_length": len(response), "duration": duration @@ -110,11 +109,13 @@ class Logger: if additional_info: log_data.update(additional_info) - # 记录日志 + # 记录可解析的 JSON 摘要 log_message = f"LLM_INTERACTION: {json.dumps(log_data, ensure_ascii=False)}" - # 将JSON中的\n替换为真正的换行符,使日志更易读 - log_message = log_message.replace('\\n', '\n') self.logger.info(log_message) + + # 记录更友好的原始多行文本,避免引号被转义 + self.logger.info("LLM_PROMPT:\n%s", prompt) + self.logger.info("LLM_RESPONSE:\n%s", response) def log_error(self, error_message: str, prompt: str = None): """ @@ -124,16 +125,18 @@ class Logger: error_message: 错误信息 prompt: 相关的提示词(可选) """ + # 错误摘要(不含原始 prompt,避免转义干扰) log_data = { "timestamp": datetime.now().isoformat(), "error": error_message, - "prompt": prompt if prompt else None } - + log_message = f"LLM_ERROR: {json.dumps(log_data, ensure_ascii=False)}" - # 将JSON中的\n替换为真正的换行符,使日志更易读 - log_message = log_message.replace('\\n', '\n') self.logger.error(log_message) + + # 如提供 prompt,追加原始多行文本便于排查 + if prompt: + self.logger.error("LLM_ERROR_PROMPT:\n%s", prompt) def get_today_stats(self) -> dict: """