update talk event
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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,避免重复
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=[])
|
||||
|
||||
@@ -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:
|
||||
# 拒绝攀谈
|
||||
|
||||
Reference in New Issue
Block a user