fix long events calling
This commit is contained in:
@@ -63,7 +63,7 @@ class LLMAI(AI):
|
||||
avatar_infos = {}
|
||||
for avatar in avatars_to_decide:
|
||||
observed = world.get_observable_avatars(avatar)
|
||||
avatar_infos[avatar.name] = avatar.get_prompt_info(observed)
|
||||
avatar_infos[avatar.name] = avatar.get_expanded_info(observed)
|
||||
general_action_infos = ACTION_INFOS_STR
|
||||
info = {
|
||||
"avatar_infos": avatar_infos,
|
||||
|
||||
@@ -547,11 +547,21 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin):
|
||||
action_space = [action.name for action in doable_actions]
|
||||
return action_space
|
||||
|
||||
def get_prompt_info(self, co_region_avatars: Optional[List["Avatar"]] = None) -> dict:
|
||||
def get_expanded_info(
|
||||
self,
|
||||
co_region_avatars: Optional[List["Avatar"]] = None,
|
||||
other_avatar: Optional["Avatar"] = None,
|
||||
detailed: bool = False
|
||||
) -> dict:
|
||||
"""
|
||||
获取角色提示词信息,返回 dict。
|
||||
获取角色的扩展信息,包含基础信息、观察到的角色和事件历史。
|
||||
|
||||
Args:
|
||||
co_region_avatars: 同区域的其他角色列表,用于"观察到的角色"字段
|
||||
other_avatar: 另一个角色,如果提供则返回两人共同经历的事件,否则返回单人事件
|
||||
detailed: 是否返回详细信息
|
||||
"""
|
||||
info = self.get_info(detailed=False)
|
||||
info = self.get_info(detailed=detailed)
|
||||
|
||||
observed: list[str] = []
|
||||
if co_region_avatars:
|
||||
@@ -562,15 +572,21 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin):
|
||||
em = self.world.event_manager
|
||||
major_limit = CONFIG.social.major_event_context_num
|
||||
minor_limit = CONFIG.social.minor_event_context_num
|
||||
major_events = em.get_major_events_by_avatar(self.id, limit=major_limit)
|
||||
minor_events = em.get_minor_events_by_avatar(self.id, limit=minor_limit)
|
||||
|
||||
# 根据是否提供 other_avatar 决定获取单人事件还是双人共同事件
|
||||
if other_avatar is not None:
|
||||
major_events = em.get_major_events_between(self.id, other_avatar.id, limit=major_limit)
|
||||
minor_events = em.get_minor_events_between(self.id, other_avatar.id, limit=minor_limit)
|
||||
else:
|
||||
major_events = em.get_major_events_by_avatar(self.id, limit=major_limit)
|
||||
minor_events = em.get_minor_events_by_avatar(self.id, limit=minor_limit)
|
||||
|
||||
major_list = [str(e) for e in major_events]
|
||||
minor_list = [str(e) for e in minor_events]
|
||||
|
||||
info["观察到的角色"] = observed
|
||||
info["长期记忆"] = major_list
|
||||
info["短期记忆"] = minor_list
|
||||
info["重大事件"] = major_list
|
||||
info["短期事件"] = minor_list
|
||||
info["长期目标"] = self.long_term_objective.content if self.long_term_objective else "无"
|
||||
return info
|
||||
|
||||
|
||||
@@ -77,31 +77,17 @@ async def generate_long_term_objective(avatar: "Avatar") -> Optional[LongTermObj
|
||||
Returns:
|
||||
生成的LongTermObjective对象,失败则返回None
|
||||
"""
|
||||
|
||||
# 准备世界信息
|
||||
world_info = avatar.world.get_info()
|
||||
|
||||
# 准备角色信息
|
||||
avatar_info = avatar.get_info(detailed=True)
|
||||
avatar_info_str = "\n".join([f"{k}: {v}" for k, v in avatar_info.items()])
|
||||
|
||||
# 获取事件历史
|
||||
em = avatar.world.event_manager
|
||||
major_limit = CONFIG.social.major_event_context_num
|
||||
minor_limit = CONFIG.social.minor_event_context_num
|
||||
major_events = em.get_major_events_by_avatar(avatar.id, limit=major_limit)
|
||||
minor_events = em.get_minor_events_by_avatar(avatar.id, limit=minor_limit)
|
||||
|
||||
major_events_str = "\n".join([f"- {str(e)}" for e in major_events]) if major_events else "无"
|
||||
minor_events_str = "\n".join([f"- {str(e)}" for e in minor_events]) if minor_events else "无"
|
||||
# 获取 expanded_info(包含详细信息和事件历史)
|
||||
expanded_info = avatar.get_expanded_info(detailed=True)
|
||||
|
||||
# 准备模板参数
|
||||
template_path = CONFIG.paths.templates / "long_term_objective.txt"
|
||||
infos = {
|
||||
"world_info": world_info,
|
||||
"avatar_info": avatar_info_str,
|
||||
"major_events": major_events_str,
|
||||
"minor_events": minor_events_str
|
||||
"avatar_info": expanded_info,
|
||||
}
|
||||
|
||||
# 调用LLM并自动解析JSON(使用fast模型)
|
||||
|
||||
@@ -43,31 +43,27 @@ class Conversation(MutualAction):
|
||||
def _build_prompt_infos(self, target_avatar: "Avatar") -> dict:
|
||||
avatar_name_1 = self.avatar.name
|
||||
avatar_name_2 = target_avatar.name
|
||||
# 交谈:使用详细信息,便于生成更丰富对话
|
||||
|
||||
# avatar1 使用 expanded_info(包含详细信息和共同事件),避免重复
|
||||
expanded_info = self.avatar.get_expanded_info(other_avatar=target_avatar, detailed=True)
|
||||
|
||||
avatar_infos = {
|
||||
avatar_name_1: self.avatar.get_info(detailed=True),
|
||||
avatar_name_1: expanded_info,
|
||||
avatar_name_2: target_avatar.get_info(detailed=True),
|
||||
}
|
||||
|
||||
# 可能的后天关系(转中文名,给模板阅读)
|
||||
# 注意:这里计算的是 target 相对于 avatar 的可能关系
|
||||
possible_new_relations = [relation_display_names[r] for r in get_possible_new_relations(self.avatar, target_avatar)]
|
||||
# 可能取消的关系
|
||||
possible_cancel_relations = [relation_display_names[r] for r in get_possible_cancel_relations(target_avatar, self.avatar)]
|
||||
|
||||
# 历史上下文:仅双方共同经历的大事和小事
|
||||
major_limit = CONFIG.social.major_event_context_num
|
||||
minor_limit = CONFIG.social.minor_event_context_num
|
||||
em = self.world.event_manager
|
||||
major_events = em.get_major_events_between(self.avatar.id, target_avatar.id, limit=major_limit)
|
||||
minor_events = em.get_minor_events_between(self.avatar.id, target_avatar.id, limit=minor_limit)
|
||||
pair_recent_events = [str(e) for e in major_events + minor_events]
|
||||
return {
|
||||
"avatar_infos": avatar_infos,
|
||||
"avatar_name_1": avatar_name_1,
|
||||
"avatar_name_2": avatar_name_2,
|
||||
"possible_new_relations": possible_new_relations,
|
||||
"possible_cancal_relations": possible_cancel_relations, # 保持模板中的拼写
|
||||
"recent_events": pair_recent_events,
|
||||
}
|
||||
|
||||
def _can_start(self, target: "Avatar") -> tuple[bool, str]:
|
||||
|
||||
@@ -60,19 +60,15 @@ class MutualAction(DefineAction, LLMAction, TargetingMixin):
|
||||
def _build_prompt_infos(self, target_avatar: "Avatar") -> dict:
|
||||
avatar_name_1 = self.avatar.name
|
||||
avatar_name_2 = target_avatar.name
|
||||
# avatar infos 仅放入与两人相关的提示,避免超长
|
||||
|
||||
# avatar1 使用 expanded_info(包含非详细信息和共同事件),避免重复
|
||||
expanded_info = self.avatar.get_expanded_info(other_avatar=target_avatar, detailed=False)
|
||||
|
||||
avatar_infos = {
|
||||
# 决策:使用非详细信息
|
||||
avatar_name_1: self.avatar.get_info(detailed=False),
|
||||
avatar_name_1: expanded_info,
|
||||
avatar_name_2: target_avatar.get_info(detailed=False),
|
||||
}
|
||||
# 历史上下文:仅双方共同经历的大事和小事
|
||||
major_limit = CONFIG.social.major_event_context_num
|
||||
minor_limit = CONFIG.social.minor_event_context_num
|
||||
em = self.world.event_manager
|
||||
major_events = em.get_major_events_between(self.avatar.id, target_avatar.id, limit=major_limit)
|
||||
minor_events = em.get_minor_events_between(self.avatar.id, target_avatar.id, limit=minor_limit)
|
||||
pair_recent_events = [str(e) for e in major_events + minor_events]
|
||||
|
||||
feedback_actions = self.FEEDBACK_ACTIONS
|
||||
comment = self.COMMENT
|
||||
action_name = self.ACTION_NAME
|
||||
@@ -83,7 +79,6 @@ class MutualAction(DefineAction, LLMAction, TargetingMixin):
|
||||
"action_name": action_name,
|
||||
"action_info": comment,
|
||||
"feedback_actions": feedback_actions,
|
||||
"recent_events": pair_recent_events,
|
||||
}
|
||||
|
||||
def _call_llm_feedback(self, infos: dict) -> dict:
|
||||
|
||||
@@ -63,26 +63,13 @@ async def generate_nickname(avatar: "Avatar") -> Optional[str]:
|
||||
生成的绰号,失败则返回None
|
||||
"""
|
||||
try:
|
||||
# 准备角色信息
|
||||
avatar_info = avatar.get_info(detailed=True)
|
||||
avatar_info_str = "\n".join([f"{k}: {v}" for k, v in avatar_info.items()])
|
||||
|
||||
# 获取事件历史(根据配置的阈值获取对应数量的事件)
|
||||
em = avatar.world.event_manager
|
||||
major_threshold = CONFIG.nickname.major_event_threshold
|
||||
minor_threshold = CONFIG.nickname.minor_event_threshold
|
||||
major_events = em.get_major_events_by_avatar(avatar.id, limit=major_threshold)
|
||||
minor_events = em.get_minor_events_by_avatar(avatar.id, limit=minor_threshold)
|
||||
|
||||
major_events_str = "\n".join([f"- {str(e)}" for e in major_events]) if major_events else "无"
|
||||
minor_events_str = "\n".join([f"- {str(e)}" for e in minor_events]) if minor_events else "无"
|
||||
# 获取 expanded_info(包含详细信息和事件历史)
|
||||
expanded_info = avatar.get_expanded_info(detailed=True)
|
||||
|
||||
# 准备模板参数
|
||||
template_path = CONFIG.paths.templates / "nickname.txt"
|
||||
infos = {
|
||||
"avatar_info": avatar_info_str,
|
||||
"major_events": major_events_str,
|
||||
"minor_events": minor_events_str
|
||||
"avatar_info": expanded_info,
|
||||
}
|
||||
|
||||
# 调用LLM并自动解析JSON
|
||||
|
||||
@@ -45,48 +45,37 @@ class StoryTeller:
|
||||
return infos
|
||||
|
||||
@staticmethod
|
||||
def _collect_recent_events(*actors: "Avatar") -> list[str]:
|
||||
from src.utils.config import CONFIG as _CONFIG
|
||||
major_limit = _CONFIG.social.major_event_context_num
|
||||
minor_limit = _CONFIG.social.minor_event_context_num
|
||||
world = None
|
||||
for av in actors:
|
||||
if av is not None:
|
||||
world = av.world
|
||||
break
|
||||
if world is None:
|
||||
return []
|
||||
em = world.event_manager
|
||||
non_null = [a for a in actors if a is not None]
|
||||
if len(non_null) >= 2:
|
||||
# 两人故事:获取两人的大事和小事
|
||||
a1, a2 = non_null[0], non_null[1]
|
||||
major_events = em.get_major_events_between(a1.id, a2.id, limit=major_limit)
|
||||
minor_events = em.get_minor_events_between(a1.id, a2.id, limit=minor_limit)
|
||||
return [str(e) for e in major_events + minor_events]
|
||||
if non_null:
|
||||
# 单人故事:获取单人的大事和小事
|
||||
a = non_null[0]
|
||||
major_events = em.get_major_events_by_avatar(a.id, limit=major_limit)
|
||||
minor_events = em.get_minor_events_by_avatar(a.id, limit=minor_limit)
|
||||
return [str(e) for e in major_events + minor_events]
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def tell_story(avatar_infos: Dict[str, dict], event: str, res: str, STORY_PROMPT: str = "", *, recent_events: list[str] | None = None) -> str:
|
||||
def tell_story(event: str, res: str, *actors: "Avatar", prompt: str = "") -> str:
|
||||
"""
|
||||
基于 `static/templates/story.txt` 模板生成小故事。
|
||||
始终使用 fast 模式以提升速度。
|
||||
失败时返回降级版文案,避免中断流程。
|
||||
|
||||
Args:
|
||||
event: 事件描述
|
||||
res: 结果描述
|
||||
*actors: 参与的角色(1-2个)
|
||||
prompt: 可选的故事提示词
|
||||
"""
|
||||
# 构建 avatar_infos,第一个 avatar 使用 expanded_info
|
||||
non_null = [a for a in actors if a is not None]
|
||||
avatar_infos: Dict[str, dict] = {}
|
||||
|
||||
if len(non_null) >= 2:
|
||||
# 双人故事:第一个用 expanded_info(包含共同事件),第二个用 detailed info
|
||||
avatar_infos[non_null[0].name] = non_null[0].get_expanded_info(other_avatar=non_null[1], detailed=True)
|
||||
avatar_infos[non_null[1].name] = non_null[1].get_info(detailed=True)
|
||||
elif non_null:
|
||||
# 单人故事:直接用 expanded_info
|
||||
avatar_infos[non_null[0].name] = non_null[0].get_expanded_info(detailed=True)
|
||||
|
||||
template_path = CONFIG.paths.templates / "story.txt"
|
||||
infos = {
|
||||
"avatar_infos": avatar_infos,
|
||||
"event": event,
|
||||
"res": res,
|
||||
"style": random.choice(story_styles),
|
||||
"story_prompt": STORY_PROMPT or "",
|
||||
"recent_events": (recent_events or []),
|
||||
"story_prompt": prompt,
|
||||
}
|
||||
try:
|
||||
data = get_prompt_and_call_llm(template_path, infos, mode="fast")
|
||||
@@ -100,18 +89,35 @@ class StoryTeller:
|
||||
return f"{event}。{res}。{style}"
|
||||
|
||||
@staticmethod
|
||||
async def tell_story_async(avatar_infos: Dict[str, dict], event: str, res: str, STORY_PROMPT: str = "", *, recent_events: list[str] | None = None) -> str:
|
||||
async def tell_story_async(event: str, res: str, *actors: "Avatar", prompt: str = "") -> str:
|
||||
"""
|
||||
异步版本:生成小故事,失败时返回降级文案。
|
||||
|
||||
Args:
|
||||
event: 事件描述
|
||||
res: 结果描述
|
||||
*actors: 参与的角色(1-2个)
|
||||
prompt: 可选的故事提示词
|
||||
"""
|
||||
# 构建 avatar_infos,第一个 avatar 使用 expanded_info
|
||||
non_null = [a for a in actors if a is not None]
|
||||
avatar_infos: Dict[str, dict] = {}
|
||||
|
||||
if len(non_null) >= 2:
|
||||
# 双人故事:第一个用 expanded_info(包含共同事件),第二个用 detailed info
|
||||
avatar_infos[non_null[0].name] = non_null[0].get_expanded_info(other_avatar=non_null[1], detailed=True)
|
||||
avatar_infos[non_null[1].name] = non_null[1].get_info(detailed=True)
|
||||
elif non_null:
|
||||
# 单人故事:直接用 expanded_info
|
||||
avatar_infos[non_null[0].name] = non_null[0].get_expanded_info(detailed=True)
|
||||
|
||||
template_path = CONFIG.paths.templates / "story.txt"
|
||||
infos = {
|
||||
"avatar_infos": avatar_infos,
|
||||
"event": event,
|
||||
"res": res,
|
||||
"style": random.choice(story_styles),
|
||||
"story_prompt": STORY_PROMPT or "",
|
||||
"recent_events": (recent_events or []),
|
||||
"story_prompt": prompt,
|
||||
}
|
||||
try:
|
||||
data = await get_prompt_and_call_llm_async(template_path, infos, mode="fast")
|
||||
@@ -126,17 +132,16 @@ class StoryTeller:
|
||||
@staticmethod
|
||||
def tell_from_actors(event: str, res: str, *actors: "Avatar", prompt: str | None = None) -> str:
|
||||
"""
|
||||
便捷方法:直接从参与者对象生成 avatar_infos 并讲述故事。
|
||||
便捷方法别名,保持向后兼容。直接调用 tell_story。
|
||||
"""
|
||||
avatar_infos = StoryTeller.build_avatar_infos(*actors)
|
||||
recent_events = StoryTeller._collect_recent_events(*actors)
|
||||
return StoryTeller.tell_story(avatar_infos, event, res, prompt or "", recent_events=recent_events)
|
||||
return StoryTeller.tell_story(event, res, *actors, prompt=prompt or "")
|
||||
|
||||
@staticmethod
|
||||
async def tell_from_actors_async(event: str, res: str, *actors: "Avatar", prompt: str | None = None) -> str:
|
||||
avatar_infos = StoryTeller.build_avatar_infos(*actors)
|
||||
recent_events = StoryTeller._collect_recent_events(*actors)
|
||||
return await StoryTeller.tell_story_async(avatar_infos, event, res, prompt or "", recent_events=recent_events)
|
||||
"""
|
||||
便捷方法别名,保持向后兼容。直接调用 tell_story_async。
|
||||
"""
|
||||
return await StoryTeller.tell_story_async(event, res, *actors, prompt=prompt or "")
|
||||
|
||||
|
||||
__all__ = ["StoryTeller"]
|
||||
|
||||
@@ -20,5 +20,7 @@ def intentify_prompt_infos(infos: dict) -> dict:
|
||||
processed["global_info"] = to_json_str_with_intent(processed["global_info"])
|
||||
if "general_action_infos" in processed:
|
||||
processed["general_action_infos"] = to_json_str_with_intent(processed["general_action_infos"])
|
||||
if "expanded_info" in processed:
|
||||
processed["expanded_info"] = to_json_str_with_intent(processed["expanded_info"])
|
||||
return processed
|
||||
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
|
||||
你需要进行决策的NPC的dict[AvatarName, info]为
|
||||
{avatar_infos}
|
||||
|
||||
正在进行的动作为:{avatar_name_1}和{avatar_name_2}正在对话。这个对话可能是善意的,也可能是恶意的,也可能是闲聊。内容和性质取决于NPC特质(性格、天赋等)、正邪、关系等因素。
|
||||
|
||||
两者可能进入的关系:{possible_new_relations}
|
||||
两者可能取消的关系:{possible_cancal_relations}
|
||||
注意:进入/取消关系不是必须的,完全由你根据对话情况、双方性格、历史事件等判断决定。
|
||||
|
||||
最近事件:
|
||||
{recent_events}
|
||||
|
||||
注意,只返回json格式的结果。
|
||||
格式为:
|
||||
{{
|
||||
|
||||
@@ -8,12 +8,6 @@
|
||||
角色信息:
|
||||
{avatar_info}
|
||||
|
||||
角色的重大事迹:
|
||||
{major_events}
|
||||
|
||||
角色的近期经历:
|
||||
{minor_events}
|
||||
|
||||
基于以上信息,为该角色设定一个符合其身份、性格、境遇的长期目标。
|
||||
|
||||
返回JSON格式:
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
|
||||
你需要进行决策的NPC的dict[AvatarName, info]为
|
||||
{avatar_infos}
|
||||
|
||||
正在进行的动作为:{avatar_name_1}向{avatar_name_2}发起了动作:{action_name}。这个动作的意味为{action_info}
|
||||
{avatar_name_2}可以进行的选择为:
|
||||
{feedback_actions}
|
||||
|
||||
最近事件:
|
||||
{recent_events}
|
||||
|
||||
注意,只返回json格式的结果。
|
||||
只返回{avatar_name_2}的行动,格式为:
|
||||
{{
|
||||
|
||||
@@ -9,12 +9,6 @@
|
||||
角色信息:
|
||||
{avatar_info}
|
||||
|
||||
角色的长期事件(重大事迹):
|
||||
{major_events}
|
||||
|
||||
角色的短期事件(近期经历):
|
||||
{minor_events}
|
||||
|
||||
基于以上信息,为该角色起一个合适的修仙界绰号。
|
||||
|
||||
返回JSON格式:
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
|
||||
你需要进行决策的NPC的dict[AvatarName, info]为
|
||||
{avatar_infos}
|
||||
|
||||
发生的事件为:
|
||||
{event}
|
||||
结果为:
|
||||
{res}
|
||||
|
||||
最近事件:
|
||||
{recent_events}
|
||||
|
||||
注意,只返回json格式的结果,格式为:
|
||||
{{
|
||||
"story": "", // 第三人称的故事正文,仙侠语言风格
|
||||
|
||||
Reference in New Issue
Block a user