197 lines
6.0 KiB
Python
197 lines
6.0 KiB
Python
"""
|
||
长期目标模块
|
||
为角色生成和管理长期目标(5-10年)
|
||
"""
|
||
from __future__ import annotations
|
||
from dataclasses import dataclass
|
||
from typing import Optional, TYPE_CHECKING
|
||
import random
|
||
|
||
if TYPE_CHECKING:
|
||
from src.classes.avatar import Avatar
|
||
|
||
from src.classes.event import Event
|
||
from src.utils.config import CONFIG
|
||
from src.utils.llm import call_llm_with_task_name
|
||
from src.run.log import get_logger
|
||
from src.classes.actions import ACTION_INFOS_STR
|
||
|
||
logger = get_logger().logger
|
||
|
||
|
||
@dataclass
|
||
class LongTermObjective:
|
||
"""长期目标类"""
|
||
content: str # 目标内容
|
||
origin: str # "llm" 或 "user"
|
||
set_year: int # 设定时的年份
|
||
|
||
|
||
def can_generate_long_term_objective(avatar: "Avatar") -> bool:
|
||
"""
|
||
检查角色是否需要生成/更新长期目标
|
||
|
||
规则:
|
||
1. 已有用户设定的目标,永不自动生成
|
||
2. 无目标时,可以生成
|
||
3. 距离上次设定 <5年,不生成
|
||
4. 距离上次设定 ≥10年,必定生成
|
||
5. 距离上次设定 5-10年,按概率生成(渐进概率)
|
||
|
||
Args:
|
||
avatar: 要检查的角色
|
||
|
||
Returns:
|
||
是否应该生成长期目标
|
||
"""
|
||
# 已有用户设定的目标,不再自动生成
|
||
if avatar.long_term_objective and avatar.long_term_objective.origin == "user":
|
||
return False
|
||
|
||
current_year = avatar.world.month_stamp.get_year()
|
||
|
||
# 首次设定(无目标)
|
||
if not avatar.long_term_objective:
|
||
return True
|
||
|
||
years_passed = current_year - avatar.long_term_objective.set_year
|
||
|
||
if years_passed < 5:
|
||
return False
|
||
elif years_passed >= 10:
|
||
return True
|
||
else: # 5-10年之间
|
||
# 渐进概率:5年时10%,随时间推移逐渐增加,接近10年时接近100%
|
||
probability = (years_passed - 5) / 5 * 0.9 + 0.1
|
||
return random.random() < probability
|
||
|
||
|
||
async def generate_long_term_objective(avatar: "Avatar") -> Optional[LongTermObjective]:
|
||
"""
|
||
为角色生成长期目标
|
||
|
||
调用LLM基于角色信息和事件历史生成合适的长期目标
|
||
|
||
Args:
|
||
avatar: 要生成长期目标的角色
|
||
|
||
Returns:
|
||
生成的LongTermObjective对象,失败则返回None
|
||
"""
|
||
# 准备世界信息(仅获取已知区域 + 距离信息)
|
||
world_info = avatar.world.get_info(avatar=avatar)
|
||
|
||
# 获取 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": expanded_info,
|
||
"general_action_infos": ACTION_INFOS_STR,
|
||
}
|
||
|
||
# 调用LLM并自动解析JSON(使用配置的模型模式)
|
||
response_data = await call_llm_with_task_name("long_term_objective", template_path, infos)
|
||
|
||
content = response_data.get("long_term_objective", "").strip()
|
||
|
||
if not content:
|
||
logger.warning(f"为角色 {avatar.name} 生成长期目标失败:返回空内容")
|
||
return None
|
||
|
||
current_year = avatar.world.month_stamp.get_year()
|
||
objective = LongTermObjective(
|
||
content=content,
|
||
origin="llm",
|
||
set_year=current_year
|
||
)
|
||
|
||
logger.info(f"为角色 {avatar.name} 生成长期目标:{content}")
|
||
|
||
return objective
|
||
|
||
|
||
|
||
async def process_avatar_long_term_objective(avatar: "Avatar") -> Optional[Event]:
|
||
"""
|
||
处理单个角色的长期目标生成/更新
|
||
|
||
检查角色是否需要生成目标,需要则生成并返回对应事件
|
||
|
||
Args:
|
||
avatar: 要处理的角色
|
||
|
||
Returns:
|
||
生成的事件,如果不需要生成或生成失败则返回None
|
||
"""
|
||
if not can_generate_long_term_objective(avatar):
|
||
return None
|
||
|
||
old_objective = avatar.long_term_objective
|
||
new_objective = await generate_long_term_objective(avatar)
|
||
|
||
if not new_objective:
|
||
return None
|
||
|
||
avatar.long_term_objective = new_objective
|
||
|
||
# 生成事件
|
||
if old_objective:
|
||
# 更新目标
|
||
event = Event(
|
||
avatar.world.month_stamp,
|
||
f"{avatar.name}经过深思熟虑,重新确定了自己的长期目标:{new_objective.content}",
|
||
related_avatars=[avatar.id],
|
||
is_major=False
|
||
)
|
||
else:
|
||
# 首次设定目标
|
||
event = Event(
|
||
avatar.world.month_stamp,
|
||
f"{avatar.name}确定了自己的长期目标:{new_objective.content}",
|
||
related_avatars=[avatar.id],
|
||
is_major=False
|
||
)
|
||
|
||
return event
|
||
|
||
|
||
def set_user_long_term_objective(avatar: "Avatar", objective_content: str) -> None:
|
||
"""
|
||
玩家设定角色的长期目标
|
||
|
||
用户设定后,origin标记为"user",系统将不再自动调用LLM更新该目标
|
||
但允许玩家再次调用此函数修改
|
||
|
||
Args:
|
||
avatar: 要设定目标的角色
|
||
objective_content: 目标内容
|
||
"""
|
||
current_year = avatar.world.month_stamp.get_year()
|
||
avatar.long_term_objective = LongTermObjective(
|
||
content=objective_content,
|
||
origin="user",
|
||
set_year=current_year
|
||
)
|
||
|
||
# 用户手动设定长期目标时,清空短期目标和后续计划,以便AI重新规划
|
||
avatar.short_term_objective = ""
|
||
avatar.clear_plans()
|
||
|
||
logger.info(f"玩家为角色 {avatar.name} 设定长期目标:{objective_content},并已清空短期目标和后续计划")
|
||
|
||
|
||
def clear_user_long_term_objective(avatar: "Avatar") -> bool:
|
||
"""
|
||
清空玩家设定的长期目标
|
||
如果当前目标是 system/llm 生成的,则不清除并返回 False
|
||
如果是 user 生成的,清除并返回 True
|
||
"""
|
||
if avatar.long_term_objective and avatar.long_term_objective.origin == "user":
|
||
avatar.long_term_objective = None
|
||
logger.info(f"玩家清空了角色 {avatar.name} 的长期目标")
|
||
return True
|
||
return False
|