| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- # %% [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")
|