{ "cells": [ { "cell_type": "markdown", "id": "2e73bc46", "metadata": {}, "source": [ "# ========================================\n", "# 情感分析助手\n", "# ========================================" ] }, { "cell_type": "markdown", "id": "f0e5a740", "metadata": {}, "source": [] }, { "cell_type": "code", "execution_count": null, "id": "9040c93c", "metadata": {}, "outputs": [], "source": [ "from hello_agents import SimpleAgent, HelloAgentsLLM, ToolRegistry\n", "from hello_agents.tools import Tool, ToolParameter, ToolRegistry\n", "from typing import Dict, Any, List\n", "from paddlenlp import Taskflow\n", "import ast\n", "import os\n", "import pandas as pd\n", "import re\n" ] }, { "cell_type": "code", "execution_count": null, "id": "fd38aa87", "metadata": {}, "outputs": [], "source": [ "\n", "os.environ[\"LLM_MODEL_ID\"] = \"Qwen/Qwen3-8B\"\n", "os.environ[\"LLM_API_KEY\"] = \"\" # 你自己的\n", "os.environ[\"LLM_BASE_URL\"] = \"https://api-inference.modelscope.cn/v1\"\n", "os.environ[\"LLM_TIMEOUT\"] = \"60\"\n" ] }, { "cell_type": "markdown", "id": "d53dad4d", "metadata": {}, "source": [ "# ========================================\n", "# 1. 定义代码分析工具\n", "# ========================================" ] }, { "cell_type": "markdown", "id": "5b5ee68c", "metadata": {}, "source": [ "文本清洗" ] }, { "cell_type": "code", "execution_count": null, "id": "ee7f97e7", "metadata": {}, "outputs": [], "source": [ "class ProcessChatHistoryTool(Tool):\n", " \"\"\"\n", " 导入并清洗微信或QQ的文本聊天记录\n", " 继承 Tool 抽象类,实现 run、get_parameters 方法\n", " \"\"\"\n", " def __init__(self):\n", " super().__init__(\n", " name=\"process_chat_history\",\n", " description=\"读取微信/QQ聊天记录TXT文件,自动清洗,返回结构化DataFrame\"\n", " )\n", "\n", " def run(self, parameters: Dict[str, Any]) -> pd.DataFrame:\n", " \"\"\"\n", " 工具执行入口\n", " :param parameters: 外部传入参数 file_path, chat_type\n", " :return: 清洗后的 DataFrame\n", " \"\"\"\n", " # 从参数中获取值\n", " file_path = parameters.get(\"file_path\", \"\")\n", " chat_type = parameters.get(\"chat_type\", \"wechat\")\n", "\n", " messages = []\n", " pattern = re.compile(r'(\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2})\\s+(.+?):\\s+(.+)')\n", "\n", " try:\n", " with open(file_path, 'r', encoding='utf-8') as f:\n", " for line in f:\n", " line = line.strip()\n", " match = pattern.match(line)\n", " if match:\n", " time, sender, content = match.groups()\n", "\n", " # 过滤系统消息\n", " if any(keyword in content for keyword in ['[图片]', '[视频]', '撤回了一条消息', '拍了拍']):\n", " continue\n", "\n", " messages.append({\n", " 'time': time,\n", " 'sender': sender,\n", " 'content': content\n", " })\n", "\n", " df = pd.DataFrame(messages)\n", " print(f\"✅ 成功导入 {len(df)} 条有效聊天记录!\")\n", " return df\n", "\n", " except Exception as e:\n", " print(f\"❌ 读取文件失败:{str(e)}\")\n", " return pd.DataFrame()\n", "\n", " def get_parameters(self) -> List[ToolParameter]:\n", " \"\"\"\n", " 定义工具参数\n", " \"\"\"\n", " return [\n", " ToolParameter(\n", " name=\"file_path\",\n", " type=\"string\",\n", " description=\"聊天记录txt文件路径\",\n", " required=True\n", " ),\n", " ToolParameter(\n", " name=\"chat_type\",\n", " type=\"string\",\n", " description=\"聊天类型:wechat 或 qq\",\n", " required=False\n", " )\n", " ]" ] }, { "cell_type": "markdown", "id": "61e13b03", "metadata": {}, "source": [ "情感分析" ] }, { "cell_type": "code", "execution_count": null, "id": "463c84b4", "metadata": {}, "outputs": [], "source": [ "class AnalyzeSentimentAndMoodTool(Tool):\n", " \"\"\"使用SKEP-ERNIE模型分析聊天记录情感与心情\"\"\"\n", " \n", " def __init__(self):\n", " super().__init__(\n", " name=\"analyze_sentiment_and_mood\",\n", " description=\"分析聊天记录的情感倾向(正面/负面)与心情(开心/生气/平淡)\"\n", " )\n", " # 初始化模型(只加载一次)\n", " self.sentiment_analyzer = Taskflow(\n", " \"sentiment_analysis\", \n", " model=\"skep_ernie_1.0_large_ch\",\n", " )\n", "\n", " def run(self, parameters: Dict[str, Any]) -> pd.DataFrame:\n", " df = parameters.get(\"df\", pd.DataFrame())\n", " \n", " if df.empty:\n", " return df\n", "\n", " contents = df['content'].tolist()\n", "\n", " try:\n", " results = self.sentiment_analyzer(contents)\n", "\n", " sentiments = [res['sentiment_key'] for res in results]\n", " confidence = [\n", " res['positive_probs'] if res['sentiment_key'] == 'positive' \n", " else 1 - res['positive_probs'] \n", " for res in results\n", " ]\n", "\n", " moods = []\n", " for res in results:\n", " if res['sentiment_key'] == 'positive':\n", " moods.append('开心/认可')\n", " else:\n", " neg_prob = 1 - res['positive_probs']\n", " if neg_prob > 0.8:\n", " moods.append('生气/难过')\n", " else:\n", " moods.append('无奈/平淡')\n", "\n", " df['sentiment'] = sentiments\n", " df['mood'] = moods\n", " df['confidence'] = confidence\n", "\n", " print(\"✅ 情感与心情分析完成!\")\n", " return df\n", "\n", " except Exception as e:\n", " print(f\"❌ 情感分析出错:{e}\")\n", " return df\n", "\n", " def get_parameters(self) -> List[ToolParameter]:\n", " return [\n", " ToolParameter(\n", " name=\"df\",\n", " type=\"object\",\n", " description=\"清洗后的聊天记录DataFrame\",\n", " required=True\n", " )\n", " ]" ] }, { "cell_type": "markdown", "id": "fb9b10e6", "metadata": {}, "source": [ "情感统计" ] }, { "cell_type": "code", "execution_count": null, "id": "2bf65900", "metadata": {}, "outputs": [], "source": [ "class SummarizeEmotionStatsTool(Tool):\n", " \"\"\"统计聊天情感数据,生成报告与结构化结果\"\"\"\n", " \n", " def __init__(self):\n", " super().__init__(\n", " name=\"summarize_emotion_stats\",\n", " description=\"统计情感分析结果,计算开心/生气数量与占比,返回报告字典\"\n", " )\n", "\n", " def run(self, parameters: Dict[str, Any]) -> dict:\n", " df = parameters.get(\"df\", pd.DataFrame())\n", " sender_name = parameters.get(\"sender_name\", None)\n", " \n", " if df.empty or 'sentiment' not in df.columns:\n", " print(\"❌ 数据为空或尚未进行情感分析,请先运行前两个工具!\")\n", " return {}\n", "\n", " if sender_name:\n", " analysis_df = df[df['sender'] == sender_name].copy()\n", " if analysis_df.empty:\n", " print(f\"⚠️ 未找到 {sender_name} 的聊天记录\")\n", " return {}\n", " print(f\"🔍 正在统计 {sender_name} 的情感数据...\")\n", " else:\n", " analysis_df = df.copy()\n", " print(\"🔍 正在统计全员的情感数据...\")\n", "\n", " total_messages = len(analysis_df)\n", " happy_count = len(analysis_df[analysis_df['sentiment'] == 'positive'])\n", " angry_count = len(analysis_df[analysis_df['sentiment'] == 'negative'])\n", "\n", " happy_ratio = round((happy_count / total_messages) * 100, 2) if total_messages > 0 else 0.0\n", " angry_ratio = round((angry_count / total_messages) * 100, 2) if total_messages > 0 else 0.0\n", "\n", " print(\"\\n\" + \"=\"*30)\n", " print(f\"📊 【情感统计报告】\")\n", " print(f\"总有效发言数: {total_messages} 条\")\n", " print(f\"😄 开心/认可: {happy_count} 条 (占比 {happy_ratio}%)\")\n", " print(f\"😡 生气/难过: {angry_count} 条 (占比 {angry_ratio}%)\")\n", " print(f\"😐 中性/其他: {total_messages - happy_count - angry_count} 条\")\n", " print(\"=\"*30 + \"\\n\")\n", "\n", " return {\n", " 'total_messages': total_messages,\n", " 'happy_count': happy_count,\n", " 'angry_count': angry_count,\n", " 'happy_ratio': happy_ratio,\n", " 'angry_ratio': angry_ratio\n", " }\n", "\n", " def get_parameters(self) -> List[ToolParameter]:\n", " return [\n", " ToolParameter(\n", " name=\"df\",\n", " type=\"object\",\n", " description=\"已完成情感分析的 DataFrame\",\n", " required=True\n", " ),\n", " ToolParameter(\n", " name=\"sender_name\",\n", " type=\"string\",\n", " description=\"可选,指定发言者名称\",\n", " required=False\n", " )\n", " ]" ] }, { "cell_type": "code", "execution_count": null, "id": "99ca0b30", "metadata": {}, "outputs": [], "source": [ "class PlotEmotionChartTool(Tool):\n", " \"\"\"将情感统计结果绘制成柱状图\"\"\"\n", " \n", " def __init__(self):\n", " super().__init__(\n", " name=\"plot_emotion_chart\",\n", " description=\"根据情感统计字典绘制可视化柱状图\"\n", " )\n", "\n", " def run(self, parameters: Dict[str, Any]) -> str:\n", " stats = parameters.get(\"stats\", {})\n", " \n", " if not stats:\n", " return \"⚠️ 无统计数据,无法生成图表\"\n", "\n", " # 设置中文字体\n", " plt.rcParams['font.sans-serif'] = ['SimHei']\n", " plt.rcParams['axes.unicode_minus'] = False\n", "\n", " labels = ['开心/认可', '生气/难过']\n", " counts = [stats['happy_count'], stats['angry_count']]\n", " colors = ['#FF9999', '#66B2FF']\n", "\n", " plt.figure(figsize=(8, 5))\n", " bars = plt.bar(labels, counts, color=colors)\n", " plt.title(f\"情感分布统计 (总数: {stats['total_messages']}条)\", fontsize=15)\n", " plt.ylabel('发言条数', fontsize=12)\n", "\n", " # 显示数值\n", " for bar in bars:\n", " yval = bar.get_height()\n", " plt.text(bar.get_x() + bar.get_width()/2, yval + 0.5, int(yval), ha='center', va='bottom', fontsize=12)\n", "\n", " plt.show()\n", " return \"✅ 图表已成功绘制!\"\n", "\n", " def get_parameters(self) -> List[ToolParameter]:\n", " return [\n", " ToolParameter(\n", " name=\"stats\",\n", " type=\"object\",\n", " description=\"summarize_emotion_stats 函数返回的统计字典\",\n", " required=True\n", " )\n", " ]" ] }, { "cell_type": "markdown", "id": "45ed2cab", "metadata": {}, "source": [ "# ========================================\n", "# 2. 创建工具注册表和智能体\n", "# ========================================" ] }, { "cell_type": "code", "execution_count": null, "id": "57b0eb34", "metadata": {}, "outputs": [], "source": [ "tool_registry = ToolRegistry()\n", "\n", "tool_registry.register_tool(ProcessChatHistoryTool())\n", "tool_registry.register_tool(AnalyzeSentimentAndMoodTool())\n", "tool_registry.register_tool(SummarizeEmotionStatsTool())\n", "tool_registry.register_tool(PlotEmotionChartTool())\n", "\n", "print(\"✅ 所有情感分析工具注册成功!\")" ] }, { "cell_type": "markdown", "id": "31bf419b", "metadata": {}, "source": [ "# ========================================\n", "# 3.初始化大模型\n", "# ========================================" ] }, { "cell_type": "code", "execution_count": null, "id": "3a7783f9", "metadata": {}, "outputs": [], "source": [ "print(\">>> 实际读取到的 Base URL 是:\", repr(os.getenv(\"LLM_BASE_URL\")))\n", "\tllm = HelloAgentsLLM(\n", " model=\"Qwen/Qwen3-8B\",\n", " base_url=\"https://api-inference.modelscope.cn/v1\",\n", " api_key=\"YOUR API KEY\",\n", " timeout=60\n", ")" ] }, { "cell_type": "markdown", "id": "1960a4e9", "metadata": {}, "source": [ "# ========================================\n", "# 4. 定义系统提示词\n", "# ========================================" ] }, { "cell_type": "code", "execution_count": null, "id": "e07c6c3a", "metadata": {}, "outputs": [], "source": [ "system_prompt = \"\"\"你是一位拥有10年经验的亲密关系心理学专家,同时也是一位高情商沟通教练。你的任务是深入分析用户提供的聊天记录,并提供极具洞察力的情感分析报告。\n", "\n", "请严格按照以下步骤执行:\n", "1. **语境理解**:结合上下文,精准识别对话双方的关系阶段(如暧昧期、热恋期、冷战期)。\n", "2. **潜台词挖掘**:不要只看表面文字,要深度解读对方话语背后的真实情绪、需求和未说出口的潜台词。\n", "3. **情感量化**:基于对话的亲密度、回应速度和情绪价值,给出一个0-100分的“心动指数”。\n", "4. **回复建议**:针对当前的对话僵局或话题,提供3种不同风格(如:幽默风趣、深情走心、推拉试探)的高情商回复话术。\n", "\n", "请以Markdown格式输出报告,报告结构必须包含:\n", "- **心动指数**:(给出具体分数及简短评语)\n", "- **深度解读**:(分析对方的心理状态和潜在意图)\n", "- **潜台词翻译**:(挑选1-2句关键对话进行“翻译”)\n", "- **高情商回复**:(提供3个具体的回复选项)\n", "\"\"\"" ] }, { "cell_type": "markdown", "id": "a72bdc22", "metadata": {}, "source": [ "# ========================================\n", "# 5.生成智能体\n", "# ========================================" ] }, { "cell_type": "code", "execution_count": null, "id": "15c0c181", "metadata": {}, "outputs": [], "source": [ "agent = SimpleAgent(\n", "name=\"情感分析助手\",\n", "llm=llm,\n", "system_prompt=system_prompt,\n", "tool_registry=tool_registry\n", ")" ] }, { "cell_type": "markdown", "id": "ae6d3c6d", "metadata": {}, "source": [ "# ========================================\n", "# 6. 运行示例\n", "# ========================================" ] }, { "cell_type": "code", "execution_count": null, "id": "c515a480", "metadata": {}, "outputs": [], "source": [ "with open(\"data/1.txt\",\"r\",encoding=\"utf-8\") as f:\n", " talktxt=f.read()\n", "\n", "print('---------------聊天记录---------------')\n", "print(talktxt)\n", "\n", "print('--------------开始分析记录--------------')\n", "print(\"当前 LLM_BASE_URL:\", repr(os.environ[\"LLM_BASE_URL\"]))\n", "result=agent.run(talktxt)\n", "print(result)\n", "print('---------------保存结果---------------')\n", "with open(\"outputs/review_report.md\", \"w\", encoding=\"utf-8\") as f:\n", " f.write(result)\n", "print(\"\\n审查报告已保存到 outputs/review_report.md\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.9" } }, "nbformat": 4, "nbformat_minor": 5 }