Files
ai-town/engine-python/app/opinions.py
empty 554d37fd4c feat: 添加观点传播系统
- 新增 Stance 数据结构 (optimism/fear)
- 情绪影响 stance (happy→乐观, anxious→恐惧)
- 实现 apply_social_influence 社交影响函数
- 确定性随机选择接触对象
- 单次变化限制 ±0.1

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

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

188 lines
5.5 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, Stance
# 情绪对 stance 的影响
EMOTION_STANCE_EFFECTS: Dict[str, Dict[str, float]] = {
"happy": {"optimism": 0.1, "fear": -0.05},
"calm": {"optimism": 0.0, "fear": 0.0},
"anxious": {"optimism": -0.05, "fear": 0.1},
}
# 观点模板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 update_stance_from_emotion(agent: AgentState) -> None:
"""根据情绪更新 stance单次变化不超过 ±0.1"""
emotion_key = agent.emotion.value
effects = EMOTION_STANCE_EFFECTS.get(emotion_key, {})
# 更新 optimism
new_optimism = agent.stance.optimism + effects.get("optimism", 0.0)
agent.stance.optimism = max(0.0, min(1.0, new_optimism))
# 更新 fear
new_fear = agent.stance.fear + effects.get("fear", 0.0)
agent.stance.fear = max(0.0, min(1.0, new_fear))
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
)
# 根据情绪更新 stance
update_stance_from_emotion(agent)
# 记录到 memory
memory_entry = f"[{agent_id}{active_effect.name}的看法] {text}"
agent.memory.append(memory_entry)
# 标记已评论
agent.commented_effects.append(effect_key)