Преглед на файлове

Merge pull request #651 from nihaoshoum/feature/LoveAnalysisAgent

[毕业设计] LoveAnalysisAgent - 爱情分析师
Sizhou Chen преди 3 седмици
родител
ревизия
2f868bd188

+ 60 - 0
Co-creation-projects/nihaoshoum-LoveAnalysisAgent/data/1.txt

@@ -0,0 +1,60 @@
+男生:在吗?
+女生:有事?
+男生:你今天怎么一天都不回我消息?
+女生:忙。
+男生:忙到连回个表情包的时间都没有?
+女生:你觉得我回表情包的时间都没有吗?
+男生:我不是这个意思,我只是担心你。
+女生:担心我?你那是担心我还是查岗?
+男生:你能不能别每次说话都带刺?
+女生:我带刺?那你呢?
+男生:我怎么了?
+女生:昨天说好的今晚一起看电影,你人呢?
+男生:啊……我忘了,公司临时有点事。
+女生:忘了?这种话你说了第几次了?
+男生:我也不是故意的,下次一定补上。
+女生:没有下次了。
+男生:你别闹脾气行不行?
+女生:我闹脾气?你觉得我在无理取闹?
+男生:我没说你无理取闹,你别上纲上线。
+女生:行,我不说了,反正我说什么都是错。
+男生:你能不能理智一点?
+女生:我很理智,理智到不想再听你画大饼。
+男生:我怎么画大饼了?我对你是真心的。
+女生:真心?真心就是放我鸽子然后轻描淡写一句忘了?
+男生:我都道歉了你还想怎么样?
+女生:我要的不是道歉,是你的态度。
+男生:我的态度还不够好吗?
+女生:好?你现在的态度就是觉得我在找茬。
+男生:你自己看看聊天记录,是不是你一直在咄咄逼人?
+女生:我咄咄逼人?是你一直在逃避问题!
+男生:行行行,都是我的错,行了吧?
+女生:你这种敷衍的态度最让人恶心。
+男生:我怎么敷衍了?
+女生:每次吵架都是“行行行”、“我的错”,然后呢?改过吗?
+男生:那你要我怎么样?跪下给你道歉?
+女生:你看,你又急了。
+男生:我没急,是你不可理喻。
+女生:好,既然我不可理喻,那你去找个理喻的。
+男生:你别动不动就提分手行不行?
+女生:是你逼我的。
+男生:我逼你?我对你还不够好吗?
+女生:好?你所谓的“好”就是让我一直迁就你?
+男生:我什么时候让你迁就我了?
+女生:昨晚我发烧39度,你连句关心的话都没有,一直在打游戏。
+男生:我那不是打完游戏就给你发消息了吗?
+女生:那是第二天早上了!
+男生:我睡着了怎么回?
+女生:你心里根本就没有我。
+男生:又来了,能不能别总是否定我?
+女生:难道不是吗?
+男生:我不想吵了,大家都冷静一下吧。
+女生:冷静?每次一吵不过你就说冷静,其实就是冷暴力。
+男生:随你怎么想吧,我累了。
+女生:那就别回了。
+男生:……
+女生:呵,果然。
+男生:你想多了。
+女生:我想多了?是你做得太少。
+男生:随便你吧。
+女生:那就这样吧。

+ 527 - 0
Co-creation-projects/nihaoshoum-LoveAnalysisAgent/main.ipynb

@@ -0,0 +1,527 @@
+{
+ "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
+}

+ 13 - 0
Co-creation-projects/nihaoshoum-LoveAnalysisAgent/outputs/review_report.md

@@ -0,0 +1,13 @@
+# 自动审查报告
+
+**分析内容**:
+男生:在吗?
+女生:有事?
+男生:你今天怎么一天都不回我消息?
+女生:忙。
+男生:忙到连回个表情包的时间都没有?
+女生:你觉得我回表情包的时间都没有吗?
+男生:我不是这个意思,我只是担心你。
+女生:担...
+
+**结论**:内容正常

+ 31 - 0
Co-creation-projects/nihaoshoum-LoveAnalysisAgent/readme.md

@@ -0,0 +1,31 @@
+# 爱情分析师 (Love Analyst Agent)
+## 📖 项目简介
+这是一个基于大语言模型的智能体项目,旨在帮助处于暧昧期或恋爱中的用户解决情感困惑。
+- **解决什么问题?** 通过上传聊天记录,分析对方对你的喜欢程度、潜在意图以及情感状态,解决“Ta到底喜不喜欢我”、“这句话是什么意思”等情感难题。
+- **有什么特色功能?** 提供量化的“心动指数”评分、潜台词深度解读,并基于对方性格生成高情商的回复建议。
+- **适用于什么场景?** 暧昧期试探、恋爱矛盾分析、相亲后复盘、日常聊天话术优化。
+## ✨ 核心功能
+- [ ] **聊天记录深度解析**:支持上传微信/QQ等聊天记录文本,智能体自动清洗数据并提取关键情感交互节点。
+- [ ] **心动指数评估**:基于对话频率、用词亲密度、情绪价值等维度,给出0-100分的喜欢程度评分及雷达图分析。
+- [ ] **高情商回复生成**:针对当前聊天僵局或特定话题,生成多种风格(幽默、深情、推拉)的回复话术供用户选择。
+## 🛠️ 技术栈
+- **框架**:HelloAgents 框架
+- **智能体范式**:ReAct(推理+行动,用于结合心理学知识库进行分析)、Plan-and-Solve(规划并解决复杂的情感矛盾)
+- **工具和API**:
+    - LLM API 
+    - LangChain (用于文本处理与链式调用)
+    - Matplotlib/Seaborn (用于生成情感分析可视化图表)
+- **其他依赖库**:Pydantic, python-dotenv, JupyterLab
+
+## 🚀 快速开始
+### 环境要求
+- Python 3.10+
+- 有效的 LLM API Key
+### 安装依赖
+```bash
+pip install -r requirements.txt
+👤 作者
+GitHub: @nihaoshoum
+Email: 1601779293@qq.com
+🙏 致谢
+感谢Datawhale社区和Hello-Agents项目

+ 4 - 0
Co-creation-projects/nihaoshoum-LoveAnalysisAgent/requirements.txt

@@ -0,0 +1,4 @@
+# 核心依赖
+hello-agents[all]>=0.2.7
+paddlenlp>=2.6.1
+pandas

+ 375 - 0
Co-creation-projects/nihaoshoum-LoveAnalysisAgent/run.py

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