1
0

run.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. # %% [markdown]
  2. # # ========================================
  3. # # 情感分析助手
  4. # # ========================================
  5. # %% [markdown]
  6. #
  7. # %%
  8. from hello_agents import SimpleAgent, HelloAgentsLLM, ToolRegistry
  9. from hello_agents.tools import Tool, ToolParameter, ToolRegistry
  10. from typing import Dict, Any, List
  11. import os
  12. import pandas as pd
  13. import re
  14. from paddlenlp import Taskflow
  15. # %%
  16. os.environ["LLM_API_KEY"] = "" # 你自己的
  17. os.environ["LLM_BASE_URL"] = "https://api-inference.modelscope.cn/v1"
  18. os.environ["LLM_TIMEOUT"] = "60"
  19. # %% [markdown]
  20. # # ========================================
  21. # # 1. 定义代码分析工具
  22. # # ========================================
  23. # %% [markdown]
  24. # 文本清洗
  25. # %%
  26. class ProcessChatHistoryTool(Tool):
  27. """
  28. 导入并清洗微信或QQ的文本聊天记录
  29. 继承 Tool 抽象类,实现 run、get_parameters 方法
  30. """
  31. def __init__(self):
  32. super().__init__(
  33. name="process_chat_history",
  34. description="读取微信/QQ聊天记录TXT文件,自动清洗,返回结构化DataFrame"
  35. )
  36. def run(self, parameters: Dict[str, Any]) -> pd.DataFrame:
  37. """
  38. 工具执行入口
  39. :param parameters: 外部传入参数 file_path, chat_type
  40. :return: 清洗后的 DataFrame
  41. """
  42. # 从参数中获取值
  43. file_path = parameters.get("file_path", "")
  44. chat_type = parameters.get("chat_type", "wechat")
  45. messages = []
  46. pattern = re.compile(r'(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})\s+(.+?):\s+(.+)')
  47. try:
  48. with open(file_path, 'r', encoding='utf-8') as f:
  49. for line in f:
  50. line = line.strip()
  51. match = pattern.match(line)
  52. if match:
  53. time, sender, content = match.groups()
  54. # 过滤系统消息
  55. if any(keyword in content for keyword in ['[图片]', '[视频]', '撤回了一条消息', '拍了拍']):
  56. continue
  57. messages.append({
  58. 'time': time,
  59. 'sender': sender,
  60. 'content': content
  61. })
  62. df = pd.DataFrame(messages)
  63. print(f"✅ 成功导入 {len(df)} 条有效聊天记录!")
  64. return df
  65. except Exception as e:
  66. print(f"❌ 读取文件失败:{str(e)}")
  67. return pd.DataFrame()
  68. def get_parameters(self) -> List[ToolParameter]:
  69. """
  70. 定义工具参数
  71. """
  72. return [
  73. ToolParameter(
  74. name="file_path",
  75. type="string",
  76. description="聊天记录txt文件路径",
  77. required=True
  78. ),
  79. ToolParameter(
  80. name="chat_type",
  81. type="string",
  82. description="聊天类型:wechat 或 qq",
  83. required=False
  84. )
  85. ]
  86. # %% [markdown]
  87. # 情感分析
  88. # %%
  89. class AnalyzeSentimentAndMoodTool(Tool):
  90. """使用SKEP-ERNIE模型分析聊天记录情感与心情"""
  91. def __init__(self):
  92. super().__init__(
  93. name="analyze_sentiment_and_mood",
  94. description="分析聊天记录的情感倾向(正面/负面)与心情(开心/生气/平淡)"
  95. )
  96. # 初始化模型(只加载一次)
  97. self.sentiment_analyzer = Taskflow(
  98. "sentiment_analysis",
  99. model="skep_ernie_1.0_large_ch",
  100. )
  101. def run(self, parameters: Dict[str, Any]) -> pd.DataFrame:
  102. df = parameters.get("df", pd.DataFrame())
  103. if df.empty:
  104. return df
  105. contents = df['content'].tolist()
  106. try:
  107. results = self.sentiment_analyzer(contents)
  108. sentiments = [res['sentiment_key'] for res in results]
  109. confidence = [
  110. res['positive_probs'] if res['sentiment_key'] == 'positive'
  111. else 1 - res['positive_probs']
  112. for res in results
  113. ]
  114. moods = []
  115. for res in results:
  116. if res['sentiment_key'] == 'positive':
  117. moods.append('开心/认可')
  118. else:
  119. neg_prob = 1 - res['positive_probs']
  120. if neg_prob > 0.8:
  121. moods.append('生气/难过')
  122. else:
  123. moods.append('无奈/平淡')
  124. df['sentiment'] = sentiments
  125. df['mood'] = moods
  126. df['confidence'] = confidence
  127. print("✅ 情感与心情分析完成!")
  128. return df
  129. except Exception as e:
  130. print(f"❌ 情感分析出错:{e}")
  131. return df
  132. def get_parameters(self) -> List[ToolParameter]:
  133. return [
  134. ToolParameter(
  135. name="df",
  136. type="object",
  137. description="清洗后的聊天记录DataFrame",
  138. required=True
  139. )
  140. ]
  141. # %% [markdown]
  142. # 情感统计
  143. # %%
  144. class SummarizeEmotionStatsTool(Tool):
  145. """统计聊天情感数据,生成报告与结构化结果"""
  146. def __init__(self):
  147. super().__init__(
  148. name="summarize_emotion_stats",
  149. description="统计情感分析结果,计算开心/生气数量与占比,返回报告字典"
  150. )
  151. def run(self, parameters: Dict[str, Any]) -> dict:
  152. df = parameters.get("df", pd.DataFrame())
  153. sender_name = parameters.get("sender_name", None)
  154. if df.empty or 'sentiment' not in df.columns:
  155. print("❌ 数据为空或尚未进行情感分析,请先运行前两个工具!")
  156. return {}
  157. if sender_name:
  158. analysis_df = df[df['sender'] == sender_name].copy()
  159. if analysis_df.empty:
  160. print(f"⚠️ 未找到 {sender_name} 的聊天记录")
  161. return {}
  162. print(f"🔍 正在统计 {sender_name} 的情感数据...")
  163. else:
  164. analysis_df = df.copy()
  165. print("🔍 正在统计全员的情感数据...")
  166. total_messages = len(analysis_df)
  167. happy_count = len(analysis_df[analysis_df['sentiment'] == 'positive'])
  168. angry_count = len(analysis_df[analysis_df['sentiment'] == 'negative'])
  169. happy_ratio = round((happy_count / total_messages) * 100, 2) if total_messages > 0 else 0.0
  170. angry_ratio = round((angry_count / total_messages) * 100, 2) if total_messages > 0 else 0.0
  171. print("\n" + "="*30)
  172. print(f"📊 【情感统计报告】")
  173. print(f"总有效发言数: {total_messages} 条")
  174. print(f"😄 开心/认可: {happy_count} 条 (占比 {happy_ratio}%)")
  175. print(f"😡 生气/难过: {angry_count} 条 (占比 {angry_ratio}%)")
  176. print(f"😐 中性/其他: {total_messages - happy_count - angry_count} 条")
  177. print("="*30 + "\n")
  178. return {
  179. 'total_messages': total_messages,
  180. 'happy_count': happy_count,
  181. 'angry_count': angry_count,
  182. 'happy_ratio': happy_ratio,
  183. 'angry_ratio': angry_ratio
  184. }
  185. def get_parameters(self) -> List[ToolParameter]:
  186. return [
  187. ToolParameter(
  188. name="df",
  189. type="object",
  190. description="已完成情感分析的 DataFrame",
  191. required=True
  192. ),
  193. ToolParameter(
  194. name="sender_name",
  195. type="string",
  196. description="可选,指定发言者名称",
  197. required=False
  198. )
  199. ]
  200. # %%
  201. class PlotEmotionChartTool(Tool):
  202. """将情感统计结果绘制成柱状图"""
  203. def __init__(self):
  204. super().__init__(
  205. name="plot_emotion_chart",
  206. description="根据情感统计字典绘制可视化柱状图"
  207. )
  208. def run(self, parameters: Dict[str, Any]) -> str:
  209. stats = parameters.get("stats", {})
  210. if not stats:
  211. return "⚠️ 无统计数据,无法生成图表"
  212. # 设置中文字体
  213. plt.rcParams['font.sans-serif'] = ['SimHei']
  214. plt.rcParams['axes.unicode_minus'] = False
  215. labels = ['开心/认可', '生气/难过']
  216. counts = [stats['happy_count'], stats['angry_count']]
  217. colors = ['#FF9999', '#66B2FF']
  218. plt.figure(figsize=(8, 5))
  219. bars = plt.bar(labels, counts, color=colors)
  220. plt.title(f"情感分布统计 (总数: {stats['total_messages']}条)", fontsize=15)
  221. plt.ylabel('发言条数', fontsize=12)
  222. # 显示数值
  223. for bar in bars:
  224. yval = bar.get_height()
  225. plt.text(bar.get_x() + bar.get_width()/2, yval + 0.5, int(yval), ha='center', va='bottom', fontsize=12)
  226. plt.show()
  227. return "✅ 图表已成功绘制!"
  228. def get_parameters(self) -> List[ToolParameter]:
  229. return [
  230. ToolParameter(
  231. name="stats",
  232. type="object",
  233. description="summarize_emotion_stats 函数返回的统计字典",
  234. required=True
  235. )
  236. ]
  237. # %% [markdown]
  238. # # ========================================
  239. # # 2. 创建工具注册表和智能体
  240. # # ========================================
  241. # %%
  242. tool_registry = ToolRegistry()
  243. tool_registry.register_tool(ProcessChatHistoryTool())
  244. tool_registry.register_tool(AnalyzeSentimentAndMoodTool())
  245. tool_registry.register_tool(SummarizeEmotionStatsTool())
  246. tool_registry.register_tool(PlotEmotionChartTool())
  247. print("✅ 所有情感分析工具注册成功!")
  248. # %% [markdown]
  249. # # ========================================
  250. # # 3.初始化大模型
  251. # # ========================================
  252. # %%
  253. print(">>> 实际读取到的 Base URL 是:", repr(os.getenv("LLM_BASE_URL")))
  254. llm = HelloAgentsLLM(
  255. model_id="Qwen/Qwen2.5-72B-Instruct", # ✅ 使用支持的 72B 模型,注意大写 Q
  256. api_key="",
  257. base_url="https://api-inference.modelscope.cn/v1"
  258. )
  259. # 打印底层客户端的真实 URL,确认是否生效
  260. print("底层客户端 Base URL:", llm._client.base_url)
  261. # %% [markdown]
  262. # # ========================================
  263. # # 4. 定义系统提示词
  264. # # ========================================
  265. # %%
  266. system_prompt = """你是一位拥有10年经验的亲密关系心理学专家,同时也是一位高情商沟通教练。你的任务是深入分析用户提供的聊天记录,并提供极具洞察力的情感分析报告。
  267. 请严格按照以下步骤执行:
  268. 1. **语境理解**:结合上下文,精准识别对话双方的关系阶段(如暧昧期、热恋期、冷战期)。
  269. 2. **潜台词挖掘**:不要只看表面文字,要深度解读对方话语背后的真实情绪、需求和未说出口的潜台词。
  270. 3. **情感量化**:基于对话的亲密度、回应速度和情绪价值,给出一个0-100分的“心动指数”。
  271. 4. **回复建议**:针对当前的对话僵局或话题,提供3种不同风格(如:幽默风趣、深情走心、推拉试探)的高情商回复话术。
  272. 请以Markdown格式输出报告,报告结构必须包含:
  273. - **心动指数**:(给出具体分数及简短评语)
  274. - **深度解读**:(分析对方的心理状态和潜在意图)
  275. - **潜台词翻译**:(挑选1-2句关键对话进行“翻译”)
  276. - **高情商回复**:(提供3个具体的回复选项)
  277. """
  278. # %% [markdown]
  279. # # ========================================
  280. # # 5.生成智能体
  281. # # ========================================
  282. # %%
  283. agent = SimpleAgent(
  284. name="情感分析助手",
  285. llm=llm,
  286. system_prompt=system_prompt,
  287. tool_registry=tool_registry
  288. )
  289. # %% [markdown]
  290. # # ========================================
  291. # # 6. 运行示例
  292. # # ========================================
  293. # %%
  294. with open("data/1.txt","r",encoding="utf-8") as f:
  295. talktxt=f.read()
  296. print('---------------聊天记录---------------')
  297. print(talktxt)
  298. print('--------------开始分析记录--------------')
  299. print("当前 LLM_BASE_URL:", repr(os.environ["LLM_BASE_URL"]))
  300. result=agent.run(talktxt)
  301. print(result)
  302. print('---------------保存结果---------------')
  303. with open("outputs/review_report.md", "w", encoding="utf-8") as f:
  304. f.write(result)
  305. print("\n审查报告已保存到 outputs/review_report.md")