文件预览

communication_analyzer.py

查看 parents-homework 技能包中的文件内容。

文件内容

scripts/communication_analyzer.py

#!/usr/bin/env python3
"""
亲子沟通分析器
Parent-Child Communication Analyzer

分析亲子对话模式,识别沟通问题,提供改进建议。
"""

import json
import os
import re
from datetime import datetime
from typing import Dict, List, Optional, Tuple
from collections import defaultdict

class CommunicationPattern:
    """沟通模式识别"""
    
    # 负面沟通模式
    NEGATIVE_PATTERNS = {
        "criticism": {
            "name": "批评",
            "indicators": ["你总是", "你从来不", "你真", "你怎么又", "你怎么这么"],
            "severity": 3
        },
        "contempt": {
            "name": "轻蔑",
            "indicators": ["真笨", "没出息", "像谁", "无语了", "服了"],
            "severity": 4
        },
        "defensive": {
            "name": "防御",
            "indicators": ["那是因为", "我又没", "我不是说了", "那不是我的错"],
            "severity": 2
        },
        "stonewalling": {
            "name": "冷战",
            "indicators": ["随便", "嗯", "哦", "行吧", "知道了", "不说话"],
            "severity": 3
        }
    }
    
    # 正面沟通模式
    POSITIVE_PATTERNS = {
        "empathy": {
            "name": "共情",
            "indicators": ["我理解你的感受", "我知道你很难过", "我理解你为什么"],
            "weight": 2
        },
        "validation": {
            "name": "肯定",
            "indicators": ["你说得有道理", "我听到了", "我理解你的意思"],
            "weight": 1
        },
        "solution": {
            "name": "解决问题",
            "indicators": ["我们一起想想", "有什么办法", "你觉得怎么做好"],
            "weight": 2
        },
        "soft_start": {
            "name": "柔和开场",
            "indicators": ["我想和你聊聊", "我有个想法", "我们可以谈谈吗"],
            "weight": 1
        }
    }

class CommunicationAnalyzer:
    """沟通分析器"""
    
    def __init__(self, data_dir: str = None):
        if data_dir is None:
            data_dir = os.path.join(os.path.dirname(__file__), '..', 'data')
        self.data_dir = os.path.abspath(data_dir)
        os.makedirs(self.data_dir, exist_ok=True)
        self.records_file = os.path.join(self.data_dir, 'communication_records.json')
    
    def analyze_text(self, text: str) -> Dict:
        """分析单条文本"""
        text_lower = text.lower()
        
        results = {
            "negative_patterns": [],
            "positive_patterns": [],
            "total_negative_score": 0,
            "total_positive_score": 0,
            "analysis": {}
        }
        
        # 检测负面模式
        for pattern_id, pattern in CommunicationPattern.NEGATIVE_PATTERNS.items():
            matches = []
            for indicator in pattern["indicators"]:
                if indicator in text_lower:
                    matches.append(indicator)
            if matches:
                results["negative_patterns"].append({
                    "pattern_id": pattern_id,
                    "name": pattern["name"],
                    "matches": matches,
                    "severity": pattern["severity"]
                })
                results["total_negative_score"] += pattern["severity"]
        
        # 检测正面模式
        for pattern_id, pattern in CommunicationPattern.POSITIVE_PATTERNS.items():
            matches = []
            for indicator in pattern["indicators"]:
                if indicator in text:
                    matches.append(indicator)
            if matches:
                results["positive_patterns"].append({
                    "pattern_id": pattern_id,
                    "name": pattern["name"],
                    "matches": matches,
                    "weight": pattern["weight"]
                })
                results["total_positive_score"] += pattern["weight"]
        
        # 综合评分
        if results["total_negative_score"] > 0 or results["total_positive_score"] > 0:
            ratio = results["total_positive_score"] / (results["total_positive_score"] + results["total_negative_score"])
            results["analysis"]["health_ratio"] = round(ratio * 100, 1)
            results["analysis"]["recommendation"] = self._get_recommendation(ratio)
        else:
            results["analysis"]["health_ratio"] = 50
            results["analysis"]["recommendation"] = "未检测到明显的沟通模式,继续保持"
        
        return results
    
    def _get_recommendation(self, ratio: float) -> str:
        """根据健康比例给出建议"""
        if ratio >= 0.8:
            return "优秀的沟通模式!继续保持"
        elif ratio >= 0.6:
            return "良好的沟通,可以继续改进"
        elif ratio >= 0.4:
            return "需要关注和改进沟通方式"
        else:
            return "建议学习非暴力沟通技巧"
    
    def analyze_conversation(self, conversation: List[Dict]) -> Dict:
        """分析一段对话"""
        if not conversation:
            return {"message": "对话记录为空"}
        
        results = {
            "total_messages": len(conversation),
            "speaker_stats": defaultdict(lambda: {"messages": 0, "negative_score": 0, "positive_score": 0}),
            "pattern_counts": defaultdict(int),
            "overall_ratio": 0,
            "messages": []
        }
        
        total_positive = 0
        total_negative = 0
        
        for msg in conversation:
            speaker = msg.get("speaker", "unknown")
            text = msg.get("text", "")
            
            results["speaker_stats"][speaker]["messages"] += 1
            
            analysis = self.analyze_text(text)
            
            msg["analysis"] = analysis
            results["messages"].append(msg)
            
            results["speaker_stats"][speaker]["negative_score"] += analysis["total_negative_score"]
            results["speaker_stats"][speaker]["positive_score"] += analysis["total_positive_score"]
            
            total_positive += analysis["total_positive_score"]
            total_negative += analysis["total_negative_score"]
            
            for pattern in analysis["negative_patterns"]:
                results["pattern_counts"][pattern["name"]] += 1
            for pattern in analysis["positive_patterns"]:
                results["pattern_counts"][pattern["name"]] += 1
        
        # 计算总体健康比例
        if total_positive + total_negative > 0:
            results["overall_ratio"] = round(total_positive / (total_positive + total_negative) * 100, 1)
        
        return results
    
    def save_record(self, record: Dict, filename: str = None) -> str:
        """保存分析记录"""
        if filename is None:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"comm_analysis_{timestamp}.json"
        
        filepath = os.path.join(self.data_dir, filename)
        
        with open(filepath, 'w', encoding='utf-8') as f:
            json.dump(record, f, ensure_ascii=False, indent=2)
        
        return filepath
    
    def interactive_analyze(self):
        """交互式分析"""
        print("\n" + "="*50)
        print("亲子沟通分析器")
        print("="*50)
        
        print("\n请输入对话内容(输入 'done' 结束输入):")
        print("-"*40)
        
        conversation = []
        speaker = "父母"
        
        while True:
            speaker = "父母" if speaker == "孩子" else "孩子"
            print(f"\n[{speaker}]")
            text = input().strip()
            
            if text.lower() == "done":
                break
            
            if text:
                conversation.append({
                    "speaker": speaker,
                    "text": text,
                    "timestamp": datetime.now().isoformat()
                })
        
        if not conversation:
            print("没有输入对话内容")
            return None
        
        # 分析对话
        print("\n" + "="*50)
        print("分析结果")
        print("="*50)
        
        results = self.analyze_conversation(conversation)
        self.print_analysis(results)
        
        # 保存
        filepath = self.save_record(results)
        print(f"\n分析结果已保存至: {filepath}")
        
        return results
    
    def print_analysis(self, results: Dict):
        """打印分析结果"""
        print(f"\n总消息数: {results['total_messages']}")
        print(f"整体沟通健康度: {results['overall_ratio']}%")
        
        print("\n【各发言人统计】:")
        for speaker, stats in results["speaker_stats"].items():
            pos = stats["positive_score"]
            neg = stats["negative_score"]
            ratio = pos / (pos + neg) * 100 if pos + neg > 0 else 50
            print(f"  {speaker}: {stats['messages']}条消息, 健康度{ratio:.0f}%")
        
        if results["pattern_counts"]:
            print("\n【沟通模式统计】:")
            for pattern, count in sorted(results["pattern_counts"].items(), key=lambda x: x[1], reverse=True):
                bar = "█" * min(count, 20)
                print(f"  {pattern}: {count}次 {bar}")
        
        # 打印消息分析
        print("\n【逐条分析】:")
        for i, msg in enumerate(results["messages"], 1):
            speaker = msg["speaker"]
            text = msg["text"][:50] + "..." if len(msg["text"]) > 50 else msg["text"]
            analysis = msg["analysis"]
            
            print(f"\n  {i}. [{speaker}] {text}")
            if analysis["negative_patterns"]:
                neg_names = [p["name"] for p in analysis["negative_patterns"]]
                print(f"     ⚠️ 负面: {', '.join(neg_names)}")
            if analysis["positive_patterns"]:
                pos_names = [p["name"] for p in analysis["positive_patterns"]]
                print(f"     ✅ 正面: {', '.join(pos_names)}")
        
        print(f"\n【建议】: {results.get('analysis', {}).get('recommendation', 'N/A')}")


def main():
    """主函数"""
    analyzer = CommunicationAnalyzer()
    return analyzer.interactive_analyze()


if __name__ == "__main__":
    main()