| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- """NPC好感度管理系统"""
- import sys
- import os
- # 添加HelloAgents到Python路径
- sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'HelloAgents'))
- from hello_agents import SimpleAgent, HelloAgentsLLM
- from typing import Dict, Optional, Tuple
- import json
- import re
- class RelationshipManager:
- """NPC好感度管理器
-
- 功能:
- - 管理NPC与玩家的好感度 (0-100)
- - 使用LLM分析对话情感
- - 自动更新好感度
- - 提供好感度等级和修饰词
- """
-
- def __init__(self, llm: HelloAgentsLLM):
- """初始化好感度管理器
-
- Args:
- llm: HelloAgentsLLM实例
- """
- self.llm = llm
-
- # 存储每个NPC与玩家的好感度
- # 格式: {npc_name: {player_id: affinity_score}}
- self.affinity_scores: Dict[str, Dict[str, float]] = {}
-
- # 创建好感度分析Agent
- self.analyzer_agent = SimpleAgent(
- name="AffinityAnalyzer",
- llm=llm,
- system_prompt=self._create_analyzer_prompt()
- )
-
- print("💖 好感度管理系统已初始化")
-
- def _create_analyzer_prompt(self) -> str:
- """创建情感分析Agent的系统提示词"""
- return """你是一个情感分析专家,负责分析对话中的情感倾向,判断是否应该改变NPC对玩家的好感度。
- 【任务】
- 分析玩家与NPC的对话,判断是否应该改变好感度,以及改变的幅度。
- 【分析维度】
- 1. **玩家态度**: 友好/中立/不友好
- 2. **对话内容**: 积极/中立/消极
- 3. **互动质量**: 深入/一般/敷衍
- 4. **情感倾向**: 赞美/批评/中性
- 【好感度变化规则】
- - 赞美、感谢、请教: +3 到 +8
- - 友好问候、正常交流: +1 到 +3
- - 普通闲聊、中性话题: 0
- - 批评、质疑、不耐烦: -3 到 -8
- - 侮辱、攻击、恶意: -8 到 -15
- 【输出格式】(严格遵守JSON格式,不要添加任何其他文字)
- {
- "should_change": true/false,
- "change_amount": -15到+10之间的整数,
- "reason": "简短说明原因(10字以内)",
- "sentiment": "positive/neutral/negative"
- }
- 【示例1】
- 玩家: "你好,很高兴认识你!"
- NPC: "你好!我也很高兴认识你。"
- 输出: {"should_change": true, "change_amount": 5, "reason": "友好问候", "sentiment": "positive"}
- 【示例2】
- 玩家: "你这个设计太丑了!"
- NPC: "抱歉,我会改进的..."
- 输出: {"should_change": true, "change_amount": -8, "reason": "批评工作", "sentiment": "negative"}
- 【示例3】
- 玩家: "今天天气不错"
- NPC: "是啊,挺好的。"
- 输出: {"should_change": false, "change_amount": 0, "reason": "普通闲聊", "sentiment": "neutral"}
- 【示例4】
- 玩家: "你的代码写得真棒!"
- NPC: "谢谢!我最近在研究新技术。"
- 输出: {"should_change": true, "change_amount": 8, "reason": "赞美工作", "sentiment": "positive"}
- 【示例5】
- 玩家: "能教教我吗?"
- NPC: "当然可以!我很乐意分享。"
- 输出: {"should_change": true, "change_amount": 6, "reason": "请教学习", "sentiment": "positive"}
- 【重要】
- - 只输出JSON,不要添加任何解释或其他文字
- - change_amount必须是整数
- - reason必须简短(10字以内)
- - sentiment必须是positive/neutral/negative之一
- """
-
- def get_affinity(self, npc_name: str, player_id: str = "player") -> float:
- """获取好感度 (0-100)
-
- Args:
- npc_name: NPC名称
- player_id: 玩家ID
-
- Returns:
- 好感度值 (0-100)
- """
- if npc_name not in self.affinity_scores:
- self.affinity_scores[npc_name] = {}
-
- if player_id not in self.affinity_scores[npc_name]:
- self.affinity_scores[npc_name][player_id] = 50.0 # 初始好感度50
-
- return self.affinity_scores[npc_name][player_id]
-
- def set_affinity(self, npc_name: str, affinity: float, player_id: str = "player"):
- """设置好感度
-
- Args:
- npc_name: NPC名称
- affinity: 好感度值 (0-100)
- player_id: 玩家ID
- """
- if npc_name not in self.affinity_scores:
- self.affinity_scores[npc_name] = {}
-
- # 限制在0-100范围内
- affinity = max(0.0, min(100.0, affinity))
- self.affinity_scores[npc_name][player_id] = affinity
-
- def analyze_and_update_affinity(
- self,
- npc_name: str,
- player_message: str,
- npc_response: str,
- player_id: str = "player"
- ) -> Dict:
- """分析对话并更新好感度
-
- Args:
- npc_name: NPC名称
- player_message: 玩家消息
- npc_response: NPC回复
- player_id: 玩家ID
-
- Returns:
- 分析结果字典
- """
- # 构建分析提示
- prompt = f"""请分析以下对话:
- 玩家: {player_message}
- {npc_name}: {npc_response}
- 请判断是否应该改变好感度,并给出变化量。
- """
-
- try:
- # 调用分析Agent
- response = self.analyzer_agent.run(prompt)
-
- # 解析JSON响应
- analysis = self._parse_analysis(response)
-
- if analysis["should_change"]:
- # 更新好感度
- current_affinity = self.get_affinity(npc_name, player_id)
- new_affinity = current_affinity + analysis["change_amount"]
- new_affinity = max(0.0, min(100.0, new_affinity)) # 限制在0-100
- self.set_affinity(npc_name, new_affinity, player_id)
- # 获取好感度等级
- old_level = self.get_affinity_level(current_affinity)
- new_level = self.get_affinity_level(new_affinity)
- # 注意: 打印日志已移到agents.py中,避免重复输出
- return {
- "changed": True,
- "old_affinity": current_affinity,
- "new_affinity": new_affinity,
- "change_amount": analysis["change_amount"],
- "reason": analysis["reason"],
- "sentiment": analysis.get("sentiment", "neutral"),
- "old_level": old_level,
- "new_level": new_level
- }
- else:
- return {
- "changed": False,
- "affinity": self.get_affinity(npc_name, player_id),
- "reason": analysis["reason"],
- "sentiment": analysis.get("sentiment", "neutral")
- }
-
- except Exception as e:
- print(f"❌ 好感度分析失败: {e}")
- import traceback
- traceback.print_exc()
- return {
- "changed": False,
- "affinity": self.get_affinity(npc_name, player_id),
- "reason": "分析失败",
- "sentiment": "neutral"
- }
-
- def _parse_analysis(self, response: str) -> Dict:
- """解析分析结果
-
- Args:
- response: LLM响应
-
- Returns:
- 解析后的字典
- """
- try:
- # 尝试直接解析JSON
- analysis = json.loads(response)
- return analysis
- except json.JSONDecodeError:
- # 尝试提取JSON部分
- # 查找第一个 { 和最后一个 }
- start = response.find('{')
- end = response.rfind('}') + 1
-
- if start != -1 and end > start:
- json_str = response[start:end]
- try:
- analysis = json.loads(json_str)
- return analysis
- except json.JSONDecodeError:
- pass
-
- # 尝试使用正则表达式提取
- # 匹配 "should_change": true/false
- should_change_match = re.search(r'"should_change"\s*:\s*(true|false)', response, re.IGNORECASE)
- change_amount_match = re.search(r'"change_amount"\s*:\s*(-?\d+)', response)
- reason_match = re.search(r'"reason"\s*:\s*"([^"]+)"', response)
- sentiment_match = re.search(r'"sentiment"\s*:\s*"([^"]+)"', response)
-
- if should_change_match and change_amount_match:
- return {
- "should_change": should_change_match.group(1).lower() == "true",
- "change_amount": int(change_amount_match.group(1)),
- "reason": reason_match.group(1) if reason_match else "未知",
- "sentiment": sentiment_match.group(1) if sentiment_match else "neutral"
- }
-
- # 解析失败,返回默认值
- print(f"⚠️ JSON解析失败,使用默认值。原始响应: {response[:100]}...")
- return {
- "should_change": False,
- "change_amount": 0,
- "reason": "解析失败",
- "sentiment": "neutral"
- }
-
- def get_affinity_level(self, affinity: float) -> str:
- """获取好感度等级
-
- Args:
- affinity: 好感度值 (0-100)
-
- Returns:
- 好感度等级名称
- """
- if affinity >= 80:
- return "挚友"
- elif affinity >= 60:
- return "亲密"
- elif affinity >= 40:
- return "友好"
- elif affinity >= 20:
- return "熟悉"
- else:
- return "陌生"
-
- def get_affinity_modifier(self, affinity: float) -> str:
- """获取好感度修饰词 (用于调整对话风格)
-
- Args:
- affinity: 好感度值 (0-100)
-
- Returns:
- 对话风格修饰词
- """
- if affinity >= 80:
- return "非常热情友好,像老朋友一样亲切,愿意分享私人话题"
- elif affinity >= 60:
- return "友好热情,愿意多聊,会主动关心对方"
- elif affinity >= 40:
- return "礼貌友善,正常交流,保持专业"
- elif affinity >= 20:
- return "礼貌但略显生疏,回答简洁"
- else:
- return "冷淡疏离,不太愿意多说,回答简短"
-
- def get_all_affinities(self, player_id: str = "player") -> Dict[str, Dict]:
- """获取所有NPC的好感度信息
-
- Args:
- player_id: 玩家ID
-
- Returns:
- 所有NPC的好感度信息
- """
- result = {}
- for npc_name in self.affinity_scores:
- affinity = self.get_affinity(npc_name, player_id)
- result[npc_name] = {
- "affinity": affinity,
- "level": self.get_affinity_level(affinity),
- "modifier": self.get_affinity_modifier(affinity)
- }
- return result
|