update talk event

This commit is contained in:
bridge
2025-11-24 22:30:49 +08:00
parent 5dc137837b
commit 1215a2edce
8 changed files with 66 additions and 40 deletions

View File

@@ -16,7 +16,7 @@ class EventHelper:
def push_pair(event: Event, initiator: "Avatar", target: Optional["Avatar"], *, to_sidebar_once: bool = True) -> None:
initiator.add_event(event, to_sidebar=True)
if target is not None:
target.add_event(event, to_sidebar=(not to_sidebar_once), to_history=True)
target.add_event(event, to_sidebar=(not to_sidebar_once))
@staticmethod
def push_self(event: Event, avatar: "Avatar", *, to_sidebar: bool = True) -> None:

View File

@@ -405,9 +405,15 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin):
action_cls = ActionRegistry.get(action_name)
return action_cls(self, self.world)
def load_decide_result_chain(self, action_name_params_pairs: ACTION_NAME_PARAMS_PAIRS, avatar_thinking: str, short_term_objective: str):
def load_decide_result_chain(self, action_name_params_pairs: ACTION_NAME_PARAMS_PAIRS, avatar_thinking: str, short_term_objective: str, prepend: bool = False):
"""
加载AI的决策结果动作链立即设置第一个为当前动作其余进入队列。
Args:
action_name_params_pairs: 动作名和参数对列表
avatar_thinking: 思考内容
short_term_objective: 短期目标
prepend: 是否插队到最前面默认False即追加到末尾
"""
if not action_name_params_pairs:
return
@@ -415,7 +421,10 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin):
self.short_term_objective = short_term_objective
# 转为计划并入队(不立即提交,交由提交阶段统一触发开始事件)
plans: List[ActionPlan] = [ActionPlan(name, params) for name, params in action_name_params_pairs]
self.planned_actions.extend(plans)
if prepend:
self.planned_actions[0:0] = plans
else:
self.planned_actions.extend(plans)
def clear_plans(self) -> None:
self.planned_actions.clear()
@@ -607,11 +616,10 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin):
"""
return self.items.get(item, 0)
def add_event(self, event: Event, *, to_sidebar: bool = True, to_history: bool = True) -> None:
def add_event(self, event: Event, *, to_sidebar: bool = True) -> None:
"""
添加事件:
- to_sidebar: 是否进入全局侧边栏(通过 Avatar._pending_events 暂存)
- to_history: 兼容参数,已废弃(统一改为通过 World.event_manager 查询历史)
注意事件会先存入_pending_events统一由Simulator写入event_manager避免重复
"""

View File

@@ -1,8 +1,9 @@
"""
event class
"""
from dataclasses import dataclass
from dataclasses import dataclass, field
from typing import List, Optional
import uuid
from src.classes.calendar import Month, Year, MonthStamp
@@ -16,6 +17,8 @@ class Event:
is_major: bool = False
# 是否为故事事件不进入记忆索引默认False
is_story: bool = False
# 唯一ID用于去重
id: str = field(default_factory=lambda: str(uuid.uuid4()))
def __str__(self) -> str:
year = self.month_stamp.get_year()
@@ -29,7 +32,8 @@ class Event:
"content": self.content,
"related_avatars": self.related_avatars,
"is_major": self.is_major,
"is_story": self.is_story
"is_story": self.is_story,
"id": self.id
}
@classmethod
@@ -40,7 +44,8 @@ class Event:
content=data["content"],
related_avatars=data.get("related_avatars"),
is_major=data.get("is_major", False),
is_story=data.get("is_story", False)
is_story=data.get("is_story", False),
id=data.get("id", str(uuid.uuid4()))
)
class NullEvent:
@@ -66,4 +71,4 @@ NULL_EVENT = NullEvent()
def is_null_event(event) -> bool:
"""检查事件是否为空事件的便捷函数"""
return event is NULL_EVENT
return event is NULL_EVENT

View File

@@ -33,11 +33,16 @@ class EventManager:
dq.popleft()
def add_event(self, event: Event) -> None:
# 幂等:若已存在同 event_id
if getattr(event, "event_id", None) and event.event_id in self._by_id:
# 过滤掉空事件
from src.classes.event import is_null_event
if is_null_event(event):
return
if getattr(event, "event_id", None):
self._by_id[event.event_id] = event
# 幂等:若已存在同 id跳过
if getattr(event, "id", None) and event.id in self._by_id:
return
if getattr(event, "id", None):
self._by_id[event.id] = event
# 全局
self._events.append(event)

View File

@@ -11,7 +11,7 @@ from src.classes.relations import (
set_relation,
cancel_relation,
)
from src.classes.event import Event
from src.classes.event import Event, NULL_EVENT
from src.utils.config import CONFIG
from src.classes.action_runtime import ActionResult, ActionStatus
from src.classes.action.event_helper import EventHelper
@@ -72,16 +72,13 @@ class Conversation(MutualAction):
# 覆盖 start自定义事件消息
def start(self, target_avatar: "Avatar|str", **kwargs) -> Event:
target = self._get_target_avatar(target_avatar)
target_name = target.name if target is not None else str(target_avatar)
rel_ids = [self.avatar.id]
if target is not None:
rel_ids.append(target.id)
event = Event(self.world.month_stamp, f"{self.avatar.name}{target_name} 开始交谈", related_avatars=rel_ids)
self.avatar.add_event(event, to_sidebar=False)
if target is not None:
target.add_event(event, to_sidebar=False)
return event
# 记录开始时间
self._start_month_stamp = self.world.month_stamp
# Conversation 动作不仅返回 NULL_EVENT 以避免生成“开始交谈”的冗余事件(防止与对话内容事件时序显示混乱),
# 同时也无需手动 add_event因为我们希望侧边栏和历史记录都只显示最终的对话内容。
return NULL_EVENT
def _handle_feedback_result(self, target: "Avatar", result: dict) -> ActionResult:
"""
@@ -92,10 +89,13 @@ class Conversation(MutualAction):
new_relation_str = str(result.get("new_relation", "")).strip()
cancel_relation_str = str(result.get("cancal_relation", "")).strip() # 保持模板中的拼写
# 使用开始时间戳
month_stamp = self._start_month_stamp if self._start_month_stamp is not None else self.world.month_stamp
# 记录对话内容
if conversation_content:
content_event = Event(
self.world.month_stamp,
month_stamp,
f"{self.avatar.name}{target.name} 的交谈:{conversation_content}",
related_avatars=[self.avatar.id, target.id]
)
@@ -107,7 +107,7 @@ class Conversation(MutualAction):
if rel is not None:
set_relation(target, self.avatar, rel)
set_event = Event(
self.world.month_stamp,
month_stamp,
f"{target.name}{self.avatar.name} 的关系变为:{relation_display_names.get(rel, str(rel))}",
related_avatars=[self.avatar.id, target.id],
is_major=True
@@ -121,7 +121,7 @@ class Conversation(MutualAction):
success = cancel_relation(target, self.avatar, rel)
if success:
cancel_event = Event(
self.world.month_stamp,
month_stamp,
f"{target.name}{self.avatar.name} 取消了关系:{relation_display_names.get(rel, str(rel))}",
related_avatars=[self.avatar.id, target.id],
is_major=True

View File

@@ -53,6 +53,8 @@ class MutualAction(DefineAction, LLMAction, TargetingMixin):
# 异步反馈任务句柄与缓存结果
self._feedback_task: asyncio.Task | None = None
self._feedback_cached: dict | None = None
# 记录动作开始时间,用于生成事件的时间戳
self._start_month_stamp: int | None = None
def _get_template_path(self) -> Path:
return CONFIG.paths.templates / "mutual_action.txt"
@@ -170,6 +172,9 @@ class MutualAction(DefineAction, LLMAction, TargetingMixin):
"""
启动互动动作,返回开始事件
"""
# 记录开始时间
self._start_month_stamp = self.world.month_stamp
target = self._get_target_avatar(target_avatar)
target_name = target.name if target is not None else str(target_avatar)
action_name = self.ACTION_NAME
@@ -178,7 +183,7 @@ class MutualAction(DefineAction, LLMAction, TargetingMixin):
rel_ids.append(target.id)
# 根据IS_MAJOR类变量设置事件类型
is_major = self.__class__.IS_MAJOR if hasattr(self.__class__, 'IS_MAJOR') else False
event = Event(self.world.month_stamp, f"{self.avatar.name}{target_name} 发起 {action_name}", related_avatars=rel_ids, is_major=is_major)
event = Event(self._start_month_stamp, f"{self.avatar.name}{target_name} 发起 {action_name}", related_avatars=rel_ids, is_major=is_major)
# 仅写入历史,避免与提交阶段重复推送到侧边栏
self.avatar.add_event(event, to_sidebar=False)
if target is not None:
@@ -214,7 +219,9 @@ class MutualAction(DefineAction, LLMAction, TargetingMixin):
target.thinking = thinking
self._settle_feedback(target, feedback)
fb_label = self.FEEDBACK_LABELS.get(str(feedback).strip(), str(feedback))
feedback_event = Event(self.world.month_stamp, f"{target.name}{self.avatar.name} 的反馈:{fb_label}", related_avatars=[self.avatar.id, target.id])
# 使用开始时间戳
month_stamp = self._start_month_stamp if self._start_month_stamp is not None else self.world.month_stamp
feedback_event = Event(month_stamp, f"{target.name}{self.avatar.name} 的反馈:{fb_label}", related_avatars=[self.avatar.id, target.id])
EventHelper.push_pair(feedback_event, initiator=self.avatar, target=target, to_sidebar_once=True)
self._apply_feedback(target, feedback)
return ActionResult(status=ActionStatus.COMPLETED, events=[])

View File

@@ -56,11 +56,12 @@ class Talk(MutualAction):
)
EventHelper.push_pair(accept_event, initiator=self.avatar, target=target, to_sidebar_once=True)
# 将 Conversation 加入计划队列在Talk完成后立即执行
# 将 Conversation 加入计划队列在Talk完成后立即执行(插队到最前)
self.avatar.load_decide_result_chain(
[("Conversation", {"target_avatar": target.name})],
self.avatar.thinking,
self.avatar.short_term_objective
self.avatar.short_term_objective,
prepend=True
)
else:
# 拒绝攀谈