Files
ai-town/engine-python/app/opinions.py
empty af279bedd9 feat: 添加角色事件评论系统
- 新增 Opinion 模型,记录角色对事件的观点
- 新增 opinions.py,基于规则生成观点(支持5种事件×3种情绪)
- 同一事件生命周期内每个 agent 只生成一次观点
- 观点同时记录到 agent.memory
- 新增 3 个测试用例

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 10:42:59 +08:00

164 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""角色事件评论系统 - 基于规则生成观点"""
from typing import Dict, List, Optional
from .models import WorldState, AgentState, Opinion, Emotion, WorldEffect
# 观点模板effect_type -> emotion -> 观点列表
OPINION_TEMPLATES: Dict[str, Dict[str, List[str]]] = {
"festival": {
"happy": [
"这真是个让人开心的日子!",
"节日的气氛太棒了!",
"大家都在庆祝,真好!",
],
"calm": [
"节日挺热闹的。",
"看来大家都很高兴。",
"难得的庆典活动。",
],
"anxious": [
"我不太确定这是不是好事...",
"人太多了,有点不安。",
"希望一切顺利吧。",
],
},
"storm": {
"happy": [
"暴风雨也有它的美!",
"待在室内也挺好的。",
"雨声其实很治愈。",
],
"calm": [
"暴风雨来了,注意安全。",
"等雨停了再出门吧。",
"天气变化无常啊。",
],
"anxious": [
"这暴风雨让我很担心...",
"情况看起来不太妙。",
"希望大家都平安。",
],
},
"scandal": {
"happy": [
"不管怎样,生活还要继续!",
"真相总会大白的。",
"保持乐观吧。",
],
"calm": [
"这件事确实令人意外。",
"让我们静观其变。",
"事情总会解决的。",
],
"anxious": [
"这消息太令人震惊了...",
"不知道接下来会怎样。",
"小镇的未来让人担忧。",
],
},
"miracle": {
"happy": [
"太神奇了!这是奇迹!",
"彩虹真的太美了!",
"今天是幸运的一天!",
],
"calm": [
"难得一见的景象。",
"大自然真是神奇。",
"值得记住的时刻。",
],
"anxious": [
"希望这是好兆头...",
"美丽的景象,但愿能持续。",
"也许事情会好转吧。",
],
},
"fire": {
"happy": [
"消防队很快就会控制住的!",
"大家齐心协力,一定没问题!",
"相信我们能度过难关!",
],
"calm": [
"火灾很危险,大家要小心。",
"希望没有人受伤。",
"消防队正在处理。",
],
"anxious": [
"情况变得不妙,我得小心...",
"火灾太可怕了...",
"希望大家都能平安。",
],
},
}
# 默认观点(未知事件类型)
DEFAULT_OPINIONS: Dict[str, List[str]] = {
"happy": ["不管发生什么,保持乐观!"],
"calm": ["让我们看看会怎样。"],
"anxious": ["不知道这意味着什么..."],
}
def get_opinion_text(
effect_type: str,
emotion: Emotion,
agent_id: str,
tick: int
) -> str:
"""根据事件类型和情绪生成观点文本"""
emotion_key = emotion.value
# 获取对应模板
if effect_type in OPINION_TEMPLATES:
templates = OPINION_TEMPLATES[effect_type].get(
emotion_key,
DEFAULT_OPINIONS[emotion_key]
)
else:
templates = DEFAULT_OPINIONS[emotion_key]
# 基于 agent_id 和 tick 选择模板(确保可复现)
index = (hash(agent_id) + tick) % len(templates)
return templates[index]
def generate_opinions(state: WorldState) -> None:
"""为所有 agent 生成对当前活跃事件的观点"""
# 无活跃效果时,清空 opinion
if not state.world_effects:
for agent in state.agents.values():
agent.opinion = None
return
# 取第一个活跃效果(优先级最高)
active_effect = state.world_effects[0]
effect_key = f"{active_effect.type}_{active_effect.name}"
for agent_id, agent in state.agents.items():
# 检查是否已对此事件发表过观点
if effect_key in agent.commented_effects:
continue
# 生成观点
text = get_opinion_text(
active_effect.type,
agent.emotion,
agent_id,
state.tick
)
# 更新 agent.opinion
agent.opinion = Opinion(
about=active_effect.type,
text=text,
tick=state.tick
)
# 记录到 memory
memory_entry = f"[{agent_id}{active_effect.name}的看法] {text}"
agent.memory.append(memory_entry)
# 标记已评论
agent.commented_effects.append(effect_key)