refactor nickname

This commit is contained in:
bridge
2025-11-26 15:22:48 +08:00
parent 37b51b7650
commit bb8614407d
8 changed files with 147 additions and 19 deletions

View File

@@ -45,6 +45,7 @@ from src.classes.appearance import Appearance, get_random_appearance
from src.classes.battle import get_base_strength
from src.classes.spirit_animal import SpiritAnimal
from src.classes.long_term_objective import LongTermObjective
from src.classes.nickname_data import Nickname
persona_num = CONFIG.avatar.persona_num
@@ -110,7 +111,7 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin):
# 灵兽:最多一个;若再次捕捉则覆盖
spirit_animal: Optional[SpiritAnimal] = None
# 绰号:江湖中对该角色的称谓,满足条件后生成,永久不变
nickname: Optional[str] = None
nickname: Optional[Nickname] = None
# 自定义头像ID如果设置优先使用此ID显示头像
custom_pic_id: Optional[int] = None
# 当月/当步新设动作标记:在 commit_next_plan 设为 True首次 tick_action 后清为 False
@@ -264,7 +265,7 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin):
}
# 绰号:仅在存在时显示
if self.nickname is not None:
info_dict["绰号"] = self.nickname
info_dict["绰号"] = self.nickname.value
# 灵兽:仅在存在时显示
if self.spirit_animal is not None:
info_dict["灵兽"] = spirit_animal_info
@@ -295,7 +296,8 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin):
"thinking": self.thinking,
"short_term_objective": self.short_term_objective,
"long_term_objective": self.long_term_objective.content if self.long_term_objective else "",
"nickname": self.nickname,
"nickname": self.nickname.value if self.nickname else None,
"nickname_reason": self.nickname.reason if self.nickname else None,
}
# 复杂对象结构化
@@ -687,7 +689,7 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin):
lines: list[str] = []
# 基础信息
if self.nickname:
add_kv(lines, "绰号", f"{self.nickname}")
add_kv(lines, "绰号", f"{self.nickname.value}")
add_kv(lines, "性别", self.gender)
add_kv(lines, "年龄", self.age)

View File

@@ -13,6 +13,8 @@ from src.utils.config import CONFIG
from src.utils.llm import call_llm_with_template, LLMMode
from src.run.log import get_logger
from src.classes.nickname_data import Nickname
logger = get_logger().logger
@@ -50,7 +52,7 @@ def can_get_nickname(avatar: "Avatar") -> bool:
return major_count >= major_threshold and minor_count >= minor_threshold
async def generate_nickname(avatar: "Avatar") -> Optional[str]:
async def generate_nickname(avatar: "Avatar") -> Optional[dict]:
"""
为角色生成绰号
@@ -60,7 +62,7 @@ async def generate_nickname(avatar: "Avatar") -> Optional[str]:
avatar: 要生成绰号的角色
Returns:
生成的绰号失败则返回None
包含 nickname 和 reason 的字典失败则返回None
"""
try:
# 获取 expanded_info包含详细信息和事件历史
@@ -77,15 +79,19 @@ async def generate_nickname(avatar: "Avatar") -> Optional[str]:
nickname = response_data.get("nickname", "").strip()
thinking = response_data.get("thinking", "")
reason = response_data.get("reason", "").strip()
if not nickname:
logger.warning(f"为角色 {avatar.name} 生成绰号失败:返回空绰号")
return None
logger.info(f"为角色 {avatar.name} 生成绰号:{nickname}")
logger.info(f"为角色 {avatar.name} 生成绰号:{nickname} (原因:{reason})")
logger.debug(f"绰号生成思考过程:{thinking}")
return nickname
return {
"nickname": nickname,
"reason": reason
}
except Exception as e:
logger.error(f"生成绰号时出错:{e}")
@@ -107,15 +113,19 @@ async def process_avatar_nickname(avatar: "Avatar") -> Optional[Event]:
if not can_get_nickname(avatar):
return None
nickname = await generate_nickname(avatar)
if not nickname:
result = await generate_nickname(avatar)
if not result:
return None
avatar.nickname = nickname
nickname_str = result["nickname"]
reason = result["reason"]
avatar.nickname = Nickname(value=nickname_str, reason=reason)
# 生成事件:角色获得绰号
event = Event(
avatar.world.month_stamp,
f"{avatar.name}在修仙界中闯出名号,被人称为「{nickname}」。",
f"{avatar.name}在修仙界中闯出名号,被人称为「{nickname_str}」。",
related_avatars=[avatar.id],
is_major=True
)

View File

@@ -0,0 +1,30 @@
"""
Nickname 数据类
"""
from dataclasses import dataclass, asdict
from typing import Optional
@dataclass
class Nickname:
"""
绰号数据类
包含绰号本身及其来源原因
"""
value: str
reason: str
def to_dict(self) -> dict:
return asdict(self)
@classmethod
def from_dict(cls, data: dict) -> "Nickname":
if not data:
return None
return cls(
value=data["value"],
reason=data["reason"]
)
def __str__(self) -> str:
return self.value

View File

@@ -144,10 +144,14 @@ class AvatarLoadMixin:
# 设置外貌通过level获取完整的Appearance对象
avatar.appearance = get_appearance_by_level(data.get("appearance", 5))
# 恢复绰号
from src.classes.nickname_data import Nickname
avatar.nickname = Nickname.from_dict(data.get("nickname"))
# 设置行动与AI
avatar.thinking = data.get("thinking", "")
avatar.short_term_objective = data.get("short_term_objective", data.get("objective", "")) # 兼容旧存档
avatar.short_term_objective = data.get("short_term_objective", "")
avatar._action_cd_last_months = data.get("_action_cd_last_months", {})
# 加载长期目标

View File

@@ -89,6 +89,7 @@ class AvatarSaveMixin:
"alignment": self.alignment.name if self.alignment else None,
"persona_ids": [p.id for p in self.personas] if self.personas else [],
"appearance": self.appearance.level,
"nickname": self.nickname.to_dict() if self.nickname else None,
# 行动与AI
"current_action": current_action_dict,