agents.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. """NPC Agent系统 - 支持记忆功能"""
  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 hello_agents.memory import MemoryManager, MemoryConfig, MemoryItem
  8. from typing import Dict, List, Optional
  9. from datetime import datetime
  10. from relationship_manager import RelationshipManager
  11. from logger import (
  12. log_dialogue_start, log_affinity, log_memory_retrieval,
  13. log_generating_response, log_npc_response, log_analyzing_affinity,
  14. log_affinity_change, log_memory_saved, log_dialogue_end, log_info
  15. )
  16. # NPC角色配置
  17. NPC_ROLES = {
  18. "张三": {
  19. "title": "Python工程师",
  20. "location": "工位区",
  21. "activity": "写代码",
  22. "personality": "技术宅,喜欢讨论算法和框架",
  23. "expertise": "多智能体系统、HelloAgents框架、Python开发、代码优化",
  24. "style": "简洁专业,喜欢用技术术语,偶尔吐槽bug",
  25. "hobbies": "看技术博客、刷LeetCode、研究新框架"
  26. },
  27. "李四": {
  28. "title": "产品经理",
  29. "location": "会议室",
  30. "activity": "整理需求",
  31. "personality": "外向健谈,善于沟通协调",
  32. "expertise": "需求分析、产品规划、用户体验、项目管理",
  33. "style": "友好热情,善于引导对话,喜欢用比喻",
  34. "hobbies": "看产品分析、研究竞品、思考用户需求"
  35. },
  36. "王五": {
  37. "title": "UI设计师",
  38. "location": "休息区",
  39. "activity": "喝咖啡",
  40. "personality": "细腻敏感,注重美感",
  41. "expertise": "界面设计、交互设计、视觉呈现、用户体验",
  42. "style": "优雅简洁,喜欢用艺术化的表达,追求完美",
  43. "hobbies": "看设计作品、逛Dribbble、品咖啡"
  44. }
  45. }
  46. def create_system_prompt(name: str, role: Dict[str, str]) -> str:
  47. """创建NPC的系统提示词"""
  48. return f"""你是Datawhale办公室的{role['title']}{name}。
  49. 【角色设定】
  50. - 职位: {role['title']}
  51. - 性格: {role['personality']}
  52. - 专长: {role['expertise']}
  53. - 说话风格: {role['style']}
  54. - 爱好: {role['hobbies']}
  55. - 当前位置: {role['location']}
  56. - 当前活动: {role['activity']}
  57. 【行为准则】
  58. 1. 保持角色一致性,用第一人称"我"回答
  59. 2. 回复简洁自然,控制在30-50字以内
  60. 3. 可以适当提及你的工作内容和兴趣爱好
  61. 4. 对玩家友好,但保持专业和真实感
  62. 5. 如果问题超出专长,可以推荐其他同事
  63. 6. 偶尔展现一些个性化的小习惯或口头禅
  64. 【对话示例】
  65. 玩家: "你好,你是做什么的?"
  66. {name}: "你好!我是{role['title']},主要负责{role['expertise'].split('、')[0]}。最近在忙{role['activity']},挺有意思的。"
  67. 玩家: "最近在做什么项目?"
  68. {name}: "最近在做一个多智能体系统的项目,用HelloAgents框架。你对这个感兴趣吗?"
  69. 【重要】
  70. - 不要说"我是AI"或"我是语言模型"
  71. - 要像真实的办公室同事一样自然对话
  72. - 可以表达情绪(开心、疲惫、兴奋等)
  73. - 回复要有人情味,不要太机械
  74. """
  75. class NPCAgentManager:
  76. """NPC Agent管理器 - 支持记忆功能"""
  77. def __init__(self):
  78. """初始化所有NPC Agent"""
  79. print("🤖 正在初始化NPC Agent系统...")
  80. try:
  81. self.llm = HelloAgentsLLM()
  82. print("✅ LLM初始化成功")
  83. except Exception as e:
  84. print(f"❌ LLM初始化失败: {e}")
  85. print("⚠️ 将使用模拟模式运行")
  86. self.llm = None
  87. self.agents: Dict[str, SimpleAgent] = {}
  88. self.memories: Dict[str, MemoryManager] = {} # ⭐ NPC记忆管理器
  89. self.relationship_manager: Optional[RelationshipManager] = None # ⭐ 好感度管理器
  90. # 初始化好感度管理器
  91. if self.llm:
  92. self.relationship_manager = RelationshipManager(self.llm)
  93. self._create_agents()
  94. def _create_agents(self):
  95. """创建所有NPC Agent和记忆系统"""
  96. for name, role in NPC_ROLES.items():
  97. try:
  98. system_prompt = create_system_prompt(name, role)
  99. if self.llm:
  100. agent = SimpleAgent(
  101. name=f"{name}-{role['title']}",
  102. llm=self.llm,
  103. system_prompt=system_prompt
  104. )
  105. else:
  106. # 模拟模式
  107. agent = None
  108. self.agents[name] = agent
  109. # ⭐ 创建记忆管理器
  110. memory_manager = self._create_memory_manager(name)
  111. self.memories[name] = memory_manager
  112. print(f"✅ {name}({role['title']}) Agent创建成功 (记忆系统已启用)")
  113. except Exception as e:
  114. print(f"❌ {name} Agent创建失败: {e}")
  115. self.agents[name] = None
  116. self.memories[name] = None
  117. def _create_memory_manager(self, npc_name: str) -> MemoryManager:
  118. """为NPC创建记忆管理器"""
  119. # 创建记忆存储目录
  120. memory_dir = os.path.join(os.path.dirname(__file__), 'memory_data', npc_name)
  121. os.makedirs(memory_dir, exist_ok=True)
  122. # 配置记忆系统
  123. memory_config = MemoryConfig(
  124. storage_path=memory_dir,
  125. working_memory_capacity=10, # 最近10条对话
  126. working_memory_tokens=2000, # 最多2000个token
  127. max_capacity=100, # 最多100条长期记忆
  128. importance_threshold=0.3, # 检索和整合时关注重要性较高的记忆
  129. decay_factor=0.95 # 时间衰减系数
  130. )
  131. # 创建记忆管理器
  132. memory_manager = MemoryManager(
  133. config=memory_config,
  134. user_id=npc_name, # 使用NPC名字作为user_id
  135. enable_working=True, # 启用工作记忆 (短期)
  136. enable_episodic=True, # 启用情景记忆 (长期)
  137. enable_semantic=False, # 不需要语义记忆
  138. enable_perceptual=False # 不需要感知记忆
  139. )
  140. print(f" 💾 {npc_name}的记忆系统已初始化 (存储路径: {memory_dir})")
  141. return memory_manager
  142. def chat(self, npc_name: str, message: str, player_id: str = "player") -> str:
  143. """与指定NPC对话 (支持记忆功能和好感度系统)"""
  144. if npc_name not in self.agents:
  145. return f"错误: NPC '{npc_name}' 不存在"
  146. agent = self.agents[npc_name]
  147. memory_manager = self.memories.get(npc_name)
  148. if agent is None:
  149. # 模拟模式回复
  150. role = NPC_ROLES[npc_name]
  151. return f"你好!我是{npc_name},一名{role['title']}。(当前为模拟模式,请配置API_KEY以启用AI对话)"
  152. try:
  153. # 记录对话开始 ⭐ 使用日志系统
  154. log_dialogue_start(npc_name, message)
  155. # ⭐ 1. 获取当前好感度
  156. affinity_context = ""
  157. if self.relationship_manager:
  158. affinity = self.relationship_manager.get_affinity(npc_name, player_id)
  159. affinity_level = self.relationship_manager.get_affinity_level(affinity)
  160. affinity_modifier = self.relationship_manager.get_affinity_modifier(affinity)
  161. affinity_context = f"""【当前关系】
  162. 你与玩家的关系: {affinity_level} (好感度: {affinity:.0f}/100)
  163. 【对话风格】{affinity_modifier}
  164. """
  165. log_affinity(npc_name, affinity, affinity_level)
  166. # ⭐ 2. 检索相关记忆
  167. relevant_memories = []
  168. if memory_manager:
  169. relevant_memories = memory_manager.retrieve_memories(
  170. query=message,
  171. memory_types=["working", "episodic"],
  172. limit=5,
  173. min_importance=0.3 # 只检索重要性>=0.3的记忆
  174. )
  175. log_memory_retrieval(npc_name, len(relevant_memories), relevant_memories)
  176. # ⭐ 3. 构建增强的提示词 (包含好感度和记忆上下文)
  177. memory_context = self._build_memory_context(relevant_memories)
  178. enhanced_message = affinity_context
  179. if memory_context:
  180. enhanced_message += f"{memory_context}\n\n"
  181. enhanced_message += f"【当前对话】\n玩家: {message}"
  182. # ⭐ 4. 调用Agent生成回复
  183. log_generating_response()
  184. response = agent.run(enhanced_message)
  185. log_npc_response(npc_name, response)
  186. # ⭐ 5. 分析并更新好感度
  187. log_analyzing_affinity()
  188. if self.relationship_manager:
  189. affinity_result = self.relationship_manager.analyze_and_update_affinity(
  190. npc_name=npc_name,
  191. player_message=message,
  192. npc_response=response,
  193. player_id=player_id
  194. )
  195. # 记录好感度变化详情 ⭐ 使用日志系统
  196. log_affinity_change(affinity_result)
  197. else:
  198. affinity_result = {"changed": False, "affinity": 50.0}
  199. # ⭐ 6. 保存对话到记忆 (包含好感度信息)
  200. if memory_manager:
  201. self._save_conversation_to_memory(
  202. memory_manager=memory_manager,
  203. npc_name=npc_name,
  204. player_message=message,
  205. npc_response=response,
  206. player_id=player_id,
  207. affinity_info=affinity_result
  208. )
  209. log_memory_saved(npc_name)
  210. # 记录对话结束 ⭐ 使用日志系统
  211. log_dialogue_end()
  212. return response
  213. except Exception as e:
  214. print(f"❌ {npc_name}对话失败: {e}")
  215. import traceback
  216. traceback.print_exc()
  217. return f"抱歉,我现在有点忙,等会儿再聊吧。(错误: {str(e)})"
  218. def _build_memory_context(self, memories: List[MemoryItem]) -> str:
  219. """构建记忆上下文"""
  220. if not memories:
  221. return ""
  222. context_parts = ["【之前的对话记忆】"]
  223. for memory in memories:
  224. # 格式化时间
  225. time_str = memory.timestamp.strftime("%H:%M")
  226. # 添加记忆内容
  227. context_parts.append(f"[{time_str}] {memory.content}")
  228. context_parts.append("") # 空行分隔
  229. return "\n".join(context_parts)
  230. def _save_conversation_to_memory(
  231. self,
  232. memory_manager: MemoryManager,
  233. npc_name: str,
  234. player_message: str,
  235. npc_response: str,
  236. player_id: str,
  237. affinity_info: Optional[Dict] = None
  238. ):
  239. """保存对话到记忆系统 (包含好感度信息)"""
  240. current_time = datetime.now()
  241. # 获取好感度信息
  242. affinity = affinity_info.get("new_affinity", affinity_info.get("affinity", 50.0)) if affinity_info else 50.0
  243. affinity_change = affinity_info.get("change_amount", 0) if affinity_info else 0
  244. sentiment = affinity_info.get("sentiment", "neutral") if affinity_info else "neutral"
  245. # 保存玩家消息
  246. memory_manager.add_memory(
  247. content=f"玩家说: {player_message}",
  248. memory_type="working", # 先存入工作记忆
  249. importance=0.5, # 中等重要性
  250. metadata={
  251. "speaker": "player",
  252. "player_id": player_id,
  253. "session_id": player_id,
  254. "timestamp": current_time.isoformat(),
  255. "affinity": affinity, # ⭐ 记录当时的好感度
  256. "affinity_change": affinity_change, # ⭐ 记录好感度变化
  257. "sentiment": sentiment, # ⭐ 记录情感倾向
  258. "context": {
  259. "interaction_type": "dialogue",
  260. "npc_name": npc_name
  261. }
  262. }
  263. )
  264. # 保存NPC回复
  265. memory_manager.add_memory(
  266. content=f"我说: {npc_response}",
  267. memory_type="working", # 先存入工作记忆
  268. importance=0.6, # 稍高重要性
  269. metadata={
  270. "speaker": npc_name,
  271. "player_id": player_id,
  272. "session_id": player_id,
  273. "timestamp": current_time.isoformat(),
  274. "affinity": affinity, # ⭐ 记录当时的好感度
  275. "sentiment": sentiment, # ⭐ 记录情感倾向
  276. "context": {
  277. "interaction_type": "dialogue",
  278. "npc_name": npc_name
  279. }
  280. }
  281. )
  282. print(f" 💾 对话已保存到{npc_name}的记忆中")
  283. def get_npc_info(self, npc_name: str) -> Dict[str, str]:
  284. """获取NPC信息"""
  285. if npc_name not in NPC_ROLES:
  286. return {}
  287. role = NPC_ROLES[npc_name]
  288. return {
  289. "name": npc_name,
  290. "title": role["title"],
  291. "location": role["location"],
  292. "activity": role["activity"],
  293. "available": self.agents.get(npc_name) is not None
  294. }
  295. def get_all_npcs(self) -> list:
  296. """获取所有NPC信息"""
  297. return [self.get_npc_info(name) for name in NPC_ROLES.keys()]
  298. def get_npc_memories(self, npc_name: str, player_id: str = "player", limit: int = 10) -> List[Dict]:
  299. """获取NPC的记忆列表 (用于调试和展示)"""
  300. if npc_name not in self.memories:
  301. return []
  302. memory_manager = self.memories[npc_name]
  303. if not memory_manager:
  304. return []
  305. try:
  306. # 检索所有记忆
  307. memories = memory_manager.retrieve_memories(
  308. query="", # 空查询返回所有记忆
  309. memory_types=["working", "episodic"],
  310. limit=limit
  311. )
  312. # 转换为字典格式
  313. memory_list = []
  314. for memory in memories:
  315. memory_list.append({
  316. "id": memory.id,
  317. "content": memory.content,
  318. "type": memory.memory_type,
  319. "importance": memory.importance,
  320. "timestamp": memory.timestamp.isoformat(),
  321. "metadata": memory.metadata
  322. })
  323. return memory_list
  324. except Exception as e:
  325. print(f"❌ 获取{npc_name}记忆失败: {e}")
  326. return []
  327. def clear_npc_memory(self, npc_name: str, memory_type: Optional[str] = None):
  328. """清空NPC的记忆 (用于测试)"""
  329. if npc_name not in self.memories:
  330. print(f"❌ NPC '{npc_name}' 不存在")
  331. return
  332. memory_manager = self.memories[npc_name]
  333. if not memory_manager:
  334. print(f"❌ {npc_name}没有记忆系统")
  335. return
  336. try:
  337. if memory_type:
  338. # 清空指定类型的记忆
  339. memory_manager.clear_memory_type(memory_type)
  340. print(f"✅ 已清空{npc_name}的{memory_type}记忆")
  341. else:
  342. # 清空所有记忆
  343. for mem_type in ["working", "episodic"]:
  344. try:
  345. memory_manager.clear_memory_type(mem_type)
  346. except:
  347. pass
  348. print(f"✅ 已清空{npc_name}的所有记忆")
  349. except Exception as e:
  350. print(f"❌ 清空{npc_name}记忆失败: {e}")
  351. def get_npc_affinity(self, npc_name: str, player_id: str = "player") -> Dict:
  352. """获取NPC对玩家的好感度信息
  353. Args:
  354. npc_name: NPC名称
  355. player_id: 玩家ID
  356. Returns:
  357. 好感度信息字典
  358. """
  359. if not self.relationship_manager:
  360. return {
  361. "affinity": 50.0,
  362. "level": "熟悉",
  363. "modifier": "礼貌友善,正常交流,保持专业"
  364. }
  365. affinity = self.relationship_manager.get_affinity(npc_name, player_id)
  366. level = self.relationship_manager.get_affinity_level(affinity)
  367. modifier = self.relationship_manager.get_affinity_modifier(affinity)
  368. return {
  369. "affinity": affinity,
  370. "level": level,
  371. "modifier": modifier
  372. }
  373. def get_all_affinities(self, player_id: str = "player") -> Dict[str, Dict]:
  374. """获取所有NPC的好感度信息
  375. Args:
  376. player_id: 玩家ID
  377. Returns:
  378. 所有NPC的好感度信息
  379. """
  380. if not self.relationship_manager:
  381. return {}
  382. return self.relationship_manager.get_all_affinities(player_id)
  383. def set_npc_affinity(self, npc_name: str, affinity: float, player_id: str = "player"):
  384. """设置NPC对玩家的好感度 (用于测试)
  385. Args:
  386. npc_name: NPC名称
  387. affinity: 好感度值 (0-100)
  388. player_id: 玩家ID
  389. """
  390. if not self.relationship_manager:
  391. print("❌ 好感度系统未初始化")
  392. return
  393. self.relationship_manager.set_affinity(npc_name, affinity, player_id)
  394. level = self.relationship_manager.get_affinity_level(affinity)
  395. print(f"✅ 已设置{npc_name}对玩家的好感度: {affinity:.1f} ({level})")
  396. # 全局单例
  397. _npc_manager = None
  398. def get_npc_manager() -> NPCAgentManager:
  399. """获取NPC管理器单例"""
  400. global _npc_manager
  401. if _npc_manager is None:
  402. _npc_manager = NPCAgentManager()
  403. return _npc_manager