1
0

relationship_manager.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. """NPC好感度管理系统"""
  2. import sys
  3. import os
  4. # 添加HelloAgents到Python路径
  5. sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'HelloAgents'))
  6. from hello_agents import SimpleAgent, HelloAgentsLLM
  7. from typing import Dict, Optional, Tuple
  8. import json
  9. import re
  10. class RelationshipManager:
  11. """NPC好感度管理器
  12. 功能:
  13. - 管理NPC与玩家的好感度 (0-100)
  14. - 使用LLM分析对话情感
  15. - 自动更新好感度
  16. - 提供好感度等级和修饰词
  17. """
  18. def __init__(self, llm: HelloAgentsLLM):
  19. """初始化好感度管理器
  20. Args:
  21. llm: HelloAgentsLLM实例
  22. """
  23. self.llm = llm
  24. # 存储每个NPC与玩家的好感度
  25. # 格式: {npc_name: {player_id: affinity_score}}
  26. self.affinity_scores: Dict[str, Dict[str, float]] = {}
  27. # 创建好感度分析Agent
  28. self.analyzer_agent = SimpleAgent(
  29. name="AffinityAnalyzer",
  30. llm=llm,
  31. system_prompt=self._create_analyzer_prompt()
  32. )
  33. print("💖 好感度管理系统已初始化")
  34. def _create_analyzer_prompt(self) -> str:
  35. """创建情感分析Agent的系统提示词"""
  36. return """你是一个情感分析专家,负责分析对话中的情感倾向,判断是否应该改变NPC对玩家的好感度。
  37. 【任务】
  38. 分析玩家与NPC的对话,判断是否应该改变好感度,以及改变的幅度。
  39. 【分析维度】
  40. 1. **玩家态度**: 友好/中立/不友好
  41. 2. **对话内容**: 积极/中立/消极
  42. 3. **互动质量**: 深入/一般/敷衍
  43. 4. **情感倾向**: 赞美/批评/中性
  44. 【好感度变化规则】
  45. - 赞美、感谢、请教: +3 到 +8
  46. - 友好问候、正常交流: +1 到 +3
  47. - 普通闲聊、中性话题: 0
  48. - 批评、质疑、不耐烦: -3 到 -8
  49. - 侮辱、攻击、恶意: -8 到 -15
  50. 【输出格式】(严格遵守JSON格式,不要添加任何其他文字)
  51. {
  52. "should_change": true/false,
  53. "change_amount": -15到+10之间的整数,
  54. "reason": "简短说明原因(10字以内)",
  55. "sentiment": "positive/neutral/negative"
  56. }
  57. 【示例1】
  58. 玩家: "你好,很高兴认识你!"
  59. NPC: "你好!我也很高兴认识你。"
  60. 输出: {"should_change": true, "change_amount": 5, "reason": "友好问候", "sentiment": "positive"}
  61. 【示例2】
  62. 玩家: "你这个设计太丑了!"
  63. NPC: "抱歉,我会改进的..."
  64. 输出: {"should_change": true, "change_amount": -8, "reason": "批评工作", "sentiment": "negative"}
  65. 【示例3】
  66. 玩家: "今天天气不错"
  67. NPC: "是啊,挺好的。"
  68. 输出: {"should_change": false, "change_amount": 0, "reason": "普通闲聊", "sentiment": "neutral"}
  69. 【示例4】
  70. 玩家: "你的代码写得真棒!"
  71. NPC: "谢谢!我最近在研究新技术。"
  72. 输出: {"should_change": true, "change_amount": 8, "reason": "赞美工作", "sentiment": "positive"}
  73. 【示例5】
  74. 玩家: "能教教我吗?"
  75. NPC: "当然可以!我很乐意分享。"
  76. 输出: {"should_change": true, "change_amount": 6, "reason": "请教学习", "sentiment": "positive"}
  77. 【重要】
  78. - 只输出JSON,不要添加任何解释或其他文字
  79. - change_amount必须是整数
  80. - reason必须简短(10字以内)
  81. - sentiment必须是positive/neutral/negative之一
  82. """
  83. def get_affinity(self, npc_name: str, player_id: str = "player") -> float:
  84. """获取好感度 (0-100)
  85. Args:
  86. npc_name: NPC名称
  87. player_id: 玩家ID
  88. Returns:
  89. 好感度值 (0-100)
  90. """
  91. if npc_name not in self.affinity_scores:
  92. self.affinity_scores[npc_name] = {}
  93. if player_id not in self.affinity_scores[npc_name]:
  94. self.affinity_scores[npc_name][player_id] = 50.0 # 初始好感度50
  95. return self.affinity_scores[npc_name][player_id]
  96. def set_affinity(self, npc_name: str, affinity: float, player_id: str = "player"):
  97. """设置好感度
  98. Args:
  99. npc_name: NPC名称
  100. affinity: 好感度值 (0-100)
  101. player_id: 玩家ID
  102. """
  103. if npc_name not in self.affinity_scores:
  104. self.affinity_scores[npc_name] = {}
  105. # 限制在0-100范围内
  106. affinity = max(0.0, min(100.0, affinity))
  107. self.affinity_scores[npc_name][player_id] = affinity
  108. def analyze_and_update_affinity(
  109. self,
  110. npc_name: str,
  111. player_message: str,
  112. npc_response: str,
  113. player_id: str = "player"
  114. ) -> Dict:
  115. """分析对话并更新好感度
  116. Args:
  117. npc_name: NPC名称
  118. player_message: 玩家消息
  119. npc_response: NPC回复
  120. player_id: 玩家ID
  121. Returns:
  122. 分析结果字典
  123. """
  124. # 构建分析提示
  125. prompt = f"""请分析以下对话:
  126. 玩家: {player_message}
  127. {npc_name}: {npc_response}
  128. 请判断是否应该改变好感度,并给出变化量。
  129. """
  130. try:
  131. # 调用分析Agent
  132. response = self.analyzer_agent.run(prompt)
  133. # 解析JSON响应
  134. analysis = self._parse_analysis(response)
  135. if analysis["should_change"]:
  136. # 更新好感度
  137. current_affinity = self.get_affinity(npc_name, player_id)
  138. new_affinity = current_affinity + analysis["change_amount"]
  139. new_affinity = max(0.0, min(100.0, new_affinity)) # 限制在0-100
  140. self.set_affinity(npc_name, new_affinity, player_id)
  141. # 获取好感度等级
  142. old_level = self.get_affinity_level(current_affinity)
  143. new_level = self.get_affinity_level(new_affinity)
  144. # 注意: 打印日志已移到agents.py中,避免重复输出
  145. return {
  146. "changed": True,
  147. "old_affinity": current_affinity,
  148. "new_affinity": new_affinity,
  149. "change_amount": analysis["change_amount"],
  150. "reason": analysis["reason"],
  151. "sentiment": analysis.get("sentiment", "neutral"),
  152. "old_level": old_level,
  153. "new_level": new_level
  154. }
  155. else:
  156. return {
  157. "changed": False,
  158. "affinity": self.get_affinity(npc_name, player_id),
  159. "reason": analysis["reason"],
  160. "sentiment": analysis.get("sentiment", "neutral")
  161. }
  162. except Exception as e:
  163. print(f"❌ 好感度分析失败: {e}")
  164. import traceback
  165. traceback.print_exc()
  166. return {
  167. "changed": False,
  168. "affinity": self.get_affinity(npc_name, player_id),
  169. "reason": "分析失败",
  170. "sentiment": "neutral"
  171. }
  172. def _parse_analysis(self, response: str) -> Dict:
  173. """解析分析结果
  174. Args:
  175. response: LLM响应
  176. Returns:
  177. 解析后的字典
  178. """
  179. try:
  180. # 尝试直接解析JSON
  181. analysis = json.loads(response)
  182. return analysis
  183. except json.JSONDecodeError:
  184. # 尝试提取JSON部分
  185. # 查找第一个 { 和最后一个 }
  186. start = response.find('{')
  187. end = response.rfind('}') + 1
  188. if start != -1 and end > start:
  189. json_str = response[start:end]
  190. try:
  191. analysis = json.loads(json_str)
  192. return analysis
  193. except json.JSONDecodeError:
  194. pass
  195. # 尝试使用正则表达式提取
  196. # 匹配 "should_change": true/false
  197. should_change_match = re.search(r'"should_change"\s*:\s*(true|false)', response, re.IGNORECASE)
  198. change_amount_match = re.search(r'"change_amount"\s*:\s*(-?\d+)', response)
  199. reason_match = re.search(r'"reason"\s*:\s*"([^"]+)"', response)
  200. sentiment_match = re.search(r'"sentiment"\s*:\s*"([^"]+)"', response)
  201. if should_change_match and change_amount_match:
  202. return {
  203. "should_change": should_change_match.group(1).lower() == "true",
  204. "change_amount": int(change_amount_match.group(1)),
  205. "reason": reason_match.group(1) if reason_match else "未知",
  206. "sentiment": sentiment_match.group(1) if sentiment_match else "neutral"
  207. }
  208. # 解析失败,返回默认值
  209. print(f"⚠️ JSON解析失败,使用默认值。原始响应: {response[:100]}...")
  210. return {
  211. "should_change": False,
  212. "change_amount": 0,
  213. "reason": "解析失败",
  214. "sentiment": "neutral"
  215. }
  216. def get_affinity_level(self, affinity: float) -> str:
  217. """获取好感度等级
  218. Args:
  219. affinity: 好感度值 (0-100)
  220. Returns:
  221. 好感度等级名称
  222. """
  223. if affinity >= 80:
  224. return "挚友"
  225. elif affinity >= 60:
  226. return "亲密"
  227. elif affinity >= 40:
  228. return "友好"
  229. elif affinity >= 20:
  230. return "熟悉"
  231. else:
  232. return "陌生"
  233. def get_affinity_modifier(self, affinity: float) -> str:
  234. """获取好感度修饰词 (用于调整对话风格)
  235. Args:
  236. affinity: 好感度值 (0-100)
  237. Returns:
  238. 对话风格修饰词
  239. """
  240. if affinity >= 80:
  241. return "非常热情友好,像老朋友一样亲切,愿意分享私人话题"
  242. elif affinity >= 60:
  243. return "友好热情,愿意多聊,会主动关心对方"
  244. elif affinity >= 40:
  245. return "礼貌友善,正常交流,保持专业"
  246. elif affinity >= 20:
  247. return "礼貌但略显生疏,回答简洁"
  248. else:
  249. return "冷淡疏离,不太愿意多说,回答简短"
  250. def get_all_affinities(self, player_id: str = "player") -> Dict[str, Dict]:
  251. """获取所有NPC的好感度信息
  252. Args:
  253. player_id: 玩家ID
  254. Returns:
  255. 所有NPC的好感度信息
  256. """
  257. result = {}
  258. for npc_name in self.affinity_scores:
  259. affinity = self.get_affinity(npc_name, player_id)
  260. result[npc_name] = {
  261. "affinity": affinity,
  262. "level": self.get_affinity_level(affinity),
  263. "modifier": self.get_affinity_modifier(affinity)
  264. }
  265. return result