From 57c669be47fb2c6841b9162afbcbdce8d8069534 Mon Sep 17 00:00:00 2001 From: bridge Date: Sat, 20 Dec 2025 21:16:09 +0800 Subject: [PATCH] fix bugs --- src/classes/avatar/action_mixin.py | 2 +- src/classes/avatar_manager.py | 6 +++ src/classes/relation_resolver.py | 65 +++++++++++++++++++--------- src/sim/simulator.py | 9 ++-- static/templates/relation_update.txt | 2 +- 5 files changed, 59 insertions(+), 25 deletions(-) diff --git a/src/classes/avatar/action_mixin.py b/src/classes/avatar/action_mixin.py index 6b0b485..5e3e51a 100644 --- a/src/classes/avatar/action_mixin.py +++ b/src/classes/avatar/action_mixin.py @@ -137,7 +137,7 @@ class ActionMixin: # 合并动作返回的事件(通常为空) if result.events: for e in result.events: - self._pending_events.append(e) + self.add_event(e) events, self._pending_events = self._pending_events, [] # 本轮已执行过,清除"新设动作"标记(但如果刚刚提交了新动作,commit_next_plan会重新设置为True) if self.current_action is None: diff --git a/src/classes/avatar_manager.py b/src/classes/avatar_manager.py index 06f5f04..26a770e 100644 --- a/src/classes/avatar_manager.py +++ b/src/classes/avatar_manager.py @@ -11,6 +11,12 @@ from src.classes.observe import get_observable_avatars class AvatarManager: avatars: Dict[str, "Avatar"] = field(default_factory=dict) + def get_avatar(self, avatar_id: str) -> "Avatar | None": + """ + 根据 ID 获取角色对象 + """ + return self.avatars.get(str(avatar_id)) + def get_avatars_in_same_region(self, avatar: "Avatar") -> List["Avatar"]: """ 返回与给定 avatar 处于同一区域的其他角色列表(不含自己)。 diff --git a/src/classes/relation_resolver.py b/src/classes/relation_resolver.py index dce21e0..fb3b394 100644 --- a/src/classes/relation_resolver.py +++ b/src/classes/relation_resolver.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import TYPE_CHECKING, List, Tuple +from typing import TYPE_CHECKING, List, Tuple, Optional from src.classes.relation import ( Relation, @@ -12,7 +12,6 @@ from src.classes.relations import ( ) from src.classes.calendar import get_date_str from src.classes.event import Event -from src.classes.action.event_helper import EventHelper from src.utils.llm import call_llm_with_template, LLMMode from src.utils.config import CONFIG @@ -50,8 +49,9 @@ class RelationResolver: return { "relation_rules_desc": get_relation_rules_desc(), - "avatar_a_info": str(avatar_a.get_info(detailed=True)), "avatar_a_name": avatar_a.name, + "avatar_a_info": str(avatar_a.get_info(detailed=True)), + "avatar_b_name": avatar_b.name, "avatar_b_info": str(avatar_b.get_info(detailed=True)), "current_relations": f"目前关系:{rel_desc}", "recent_events_text": recent_events_text, @@ -59,9 +59,9 @@ class RelationResolver: } @staticmethod - async def resolve_pair(avatar_a: "Avatar", avatar_b: "Avatar") -> None: + async def resolve_pair(avatar_a: "Avatar", avatar_b: "Avatar") -> Optional[Event]: """ - 处理一对角色的关系变化 + 处理一对角色的关系变化,返回产生的事件 """ infos = RelationResolver._build_prompt_data(avatar_a, avatar_b) @@ -69,7 +69,7 @@ class RelationResolver: changed = result.get("changed", False) if not changed: - return + return None month_stamp = avatar_a.world.month_stamp @@ -78,15 +78,16 @@ class RelationResolver: reason = result.get("reason", "") if not rel_name: - return + return None # 解析关系枚举 try: rel = Relation[rel_name] except KeyError: - return + return None display_name = relation_display_names.get(rel, rel_name) + event = None if c_type == "ADD": # 检查是否已有 @@ -106,31 +107,55 @@ class RelationResolver: current_rel = avatar_b.get_relation(avatar_a) if current_rel == rel: - return + return None set_relation(avatar_b, avatar_a, rel) - event_text = f"【关系新增】{reason},{avatar_a.name} 与 {avatar_b.name} 结为{display_name}。" + event_text = f"{avatar_a.name} 与 {avatar_b.name}因为{reason}成为{display_name}。" event = Event(month_stamp, event_text, related_avatars=[avatar_a.id, avatar_b.id], is_major=True) - EventHelper.push_pair(event, initiator=avatar_a, target=avatar_b, to_sidebar_once=True) elif c_type == "REMOVE": # 同样反转调用 success = cancel_relation(avatar_b, avatar_a, rel) if success: - event_text = f"【关系断绝】{reason},{avatar_a.name} 与 {avatar_b.name} 不再是{display_name}。" + event_text = f"{avatar_a.name} 与 {avatar_b.name} 因为{reason}不再是{display_name}。" event = Event(month_stamp, event_text, related_avatars=[avatar_a.id, avatar_b.id], is_major=True) - EventHelper.push_pair(event, initiator=avatar_a, target=avatar_b, to_sidebar_once=True) + + if event: + # 手动调用 add_event(to_sidebar=False) 来更新统计数据,但不加入 pending_events + # 因为事件将由 Simulator 统一处理 + avatar_a.add_event(event, to_sidebar=False) + avatar_b.add_event(event, to_sidebar=False) + return event + + return None @staticmethod - async def run_batch(pairs: List[Tuple["Avatar", "Avatar"]]) -> None: + async def run_batch(pairs: List[Tuple["Avatar", "Avatar"]]) -> List[Event]: """ - 批量并发处理 + 批量并发处理,返回产生的所有事件 """ if not pairs: - return + return [] - async with AITaskBatch() as batch: - for a, b in pairs: - batch.add(RelationResolver.resolve_pair(a, b)) - + events = [] + + # 使用 asyncio.gather 而不是 AITaskBatch.gather,因为 AITaskBatch 没有 gather 方法 + import asyncio + tasks = [] + for a, b in pairs: + # 创建协程任务但不立即 await + tasks.append(RelationResolver.resolve_pair(a, b)) + + if not tasks: + return [] + + # 并发执行所有任务 + results = await asyncio.gather(*tasks) + + # 收集结果 + for res in results: + if res and isinstance(res, Event): + events.append(res) + + return events diff --git a/src/sim/simulator.py b/src/sim/simulator.py index 79b500f..7275ffd 100644 --- a/src/sim/simulator.py +++ b/src/sim/simulator.py @@ -350,11 +350,14 @@ class Simulator: t_state["count"] = 0 t_state["checked_times"] += 1 + events = [] if pairs_to_resolve: - # 批量并发处理 - await RelationResolver.run_batch(pairs_to_resolve) + # 批量并发处理,并直接收集返回的事件 + relation_events = await RelationResolver.run_batch(pairs_to_resolve) + if relation_events: + events.extend(relation_events) - return [] + return events async def step(self): """ diff --git a/static/templates/relation_update.txt b/static/templates/relation_update.txt index 7eb64e7..40e1610 100644 --- a/static/templates/relation_update.txt +++ b/static/templates/relation_update.txt @@ -29,5 +29,5 @@ "changed": true | false, // 是否发生关系变更。如无必要,请填 false "change_type": "ADD" | "REMOVE", // 变更类型。changed为false时可忽略 "relation": "LOVERS" | "FRIEND" | "ENEMY" | "MASTER" ... (必须是大写枚举名), // 涉及的关系。changed为false时可忽略。注意是{avatar_a_name}相对于{avatar_b_name}的身份。如输出MASTER,即A变为B的师傅。 - "reason": "..." // 用于生成事件日志,如“经过多次生死与共,A与B结为道侣”。changed为false时可忽略 + "reason": "..." // 简述原因,名词,类似“意气相投”。changed为false时可忽略 }}