diff --git a/main/xiaozhi-server/core/handle/sendAudioHandle.py b/main/xiaozhi-server/core/handle/sendAudioHandle.py index 1271e466..ec29a9fd 100644 --- a/main/xiaozhi-server/core/handle/sendAudioHandle.py +++ b/main/xiaozhi-server/core/handle/sendAudioHandle.py @@ -4,14 +4,49 @@ import asyncio import time from core.utils.util import ( get_string_no_punctuation_or_emoji, + analyze_emotion ) TAG = __name__ logger = setup_logging() +emoji_map = { + 'neutral': '😶', + 'happy': '🙂', + 'laughing': '😆', + 'funny': '😂', + 'sad': '😔', + 'angry': '😠', + 'crying': '😭', + 'loving': '😍', + 'embarrassed': '😳', + 'surprised': '😲', + 'shocked': '😱', + 'thinking': '🤔', + 'winking': '😉', + 'cool': '😎', + 'relaxed': '😌', + 'delicious': '🤤', + 'kissy': '😘', + 'confident': '😏', + 'sleepy': '😴', + 'silly': '😜', + 'confused': '🙄' +} async def sendAudioMessage(conn, audios, text, text_index=0): # 发送句子开始消息 + emotion = analyze_emotion(text) + emoji = emoji_map.get(emotion, '🙂') # 默认使用笑脸 + await conn.websocket.send(json.dumps( + { + "type": "llm", + "text": emoji, + "emotion": emotion, + "session_id": conn.session_id, + } + ) + ) if text_index == conn.tts_first_text_index: logger.bind(tag=TAG).info(f"发送第一段语音: {text}") await send_tts_message(conn, "sentence_start", text) @@ -86,14 +121,4 @@ async def send_stt_message(conn, text): await conn.websocket.send( json.dumps({"type": "stt", "text": stt_text, "session_id": conn.session_id}) ) - await conn.websocket.send( - json.dumps( - { - "type": "llm", - "text": "😊", - "emotion": "happy", - "session_id": conn.session_id, - } - ) - ) await send_tts_message(conn, "start") diff --git a/main/xiaozhi-server/core/utils/util.py b/main/xiaozhi-server/core/utils/util.py index 2fc37e85..aa4a343a 100644 --- a/main/xiaozhi-server/core/utils/util.py +++ b/main/xiaozhi-server/core/utils/util.py @@ -7,7 +7,29 @@ from typing import Dict, Any from core.utils import tts, llm, intent, memory, vad, asr TAG = __name__ - +emoji_map = { + 'neutral': '😶', + 'happy': '🙂', + 'laughing': '😆', + 'funny': '😂', + 'sad': '😔', + 'angry': '😠', + 'crying': '😭', + 'loving': '😍', + 'embarrassed': '😳', + 'surprised': '😲', + 'shocked': '😱', + 'thinking': '🤔', + 'winking': '😉', + 'cool': '😎', + 'relaxed': '😌', + 'delicious': '🤤', + 'kissy': '😘', + 'confident': '😏', + 'sleepy': '😴', + 'silly': '😜', + 'confused': '🙄' +} def get_local_ip(): try: @@ -329,3 +351,115 @@ def initialize_modules( logger.bind(tag=TAG).info(f"初始化组件: prompt成功 {modules['prompt'][:50]}...") return modules + +def analyze_emotion(text): + """ + 分析文本情感并返回对应的emoji名称 + """ + if not text or not isinstance(text, str): + return 'neutral' + + original_text = text + text = text.lower().strip() + + # 检查是否包含现有emoji + for emotion, emoji in emoji_map.items(): + if emoji in original_text: + return emotion + + # 标点符号分析 + has_exclamation = '!' in original_text or '!' in original_text + has_question = '?' in original_text or '?' in original_text + has_ellipsis = '...' in original_text or '…' in original_text + + # 定义情感关键词映射(扩展版) + emotion_keywords = { + 'happy': ['开心', '高兴', '快乐', '愉快', '幸福', '满意', '棒', '好', '不错', '完美', '棒极了', '太好了', + '好呀', '好的'], + 'laughing': ['哈哈', '哈哈哈', '呵呵', '嘿嘿', '嘻嘻', '笑死', '太好笑了', '笑死我了', '笑死我了', '笑抽了'], + 'funny': ['搞笑', '滑稽', '逗', '幽默', '笑点', '段子', '笑话', '太逗了'], + 'sad': ['伤心', '难过', '悲哀', '悲伤', '忧郁', '郁闷', '沮丧', '失望', '想哭', '难受', '不开心', '唉', '呜呜'], + 'angry': ['生气', '愤怒', '气死', '讨厌', '烦人', '可恶', '烦死了', '恼火', '暴躁', '火大', '愤怒', '气炸了'], + 'crying': ['哭泣', '泪流', '大哭', '伤心欲绝', '泪目', '流泪', '哭死', '哭晕', '想哭', '泪崩'], + 'loving': ['爱你', '喜欢', '爱', '亲爱的', '宝贝', '么么哒', '抱抱', '想你', '思念', '最爱', '亲亲', '喜欢你'], + 'embarrassed': ['尴尬', '不好意思', '害羞', '脸红', '难为情', '社死', '丢脸', '出丑'], + 'surprised': ['惊讶', '吃惊', '天啊', '哇塞', '哇', '居然', '竟然', '没想到', '出乎意料'], + 'shocked': ['震惊', '吓到', '惊呆了', '不敢相信', '震撼', '吓死', '恐怖', '害怕', '吓人'], + 'thinking': ['思考', '考虑', '想一下', '琢磨', '沉思', '冥想', '想', '思考中', '在想'], + 'winking': ['调皮', '眨眼', '你懂的', '坏笑', '邪恶', '奸笑', '使眼色'], + 'cool': ['酷', '帅', '厉害', '棒极了', '真棒', '牛逼', '强', '优秀', '杰出', '出色', '完美'], + 'relaxed': ['放松', '舒服', '惬意', '悠闲', '轻松', '舒适', '安逸', '自在'], + 'delicious': ['好吃', '美味', '香', '馋', '可口', '香甜', '大餐', '大快朵颐', '流口水', '垂涎'], + 'kissy': ['亲亲', '么么', '吻', 'mua', 'muah', '亲一下', '飞吻'], + 'confident': ['自信', '肯定', '确定', '毫无疑问', '当然', '必须的', '毫无疑问', '确信', '坚信'], + 'sleepy': ['困', '睡觉', '晚安', '想睡', '好累', '疲惫', '疲倦', '困了', '想休息', '睡意'], + 'silly': ['傻', '笨', '呆', '憨', '蠢', '二', '憨憨', '傻乎乎', '呆萌'], + 'confused': ['疑惑', '不明白', '不懂', '困惑', '疑问', '为什么', '怎么回事', '啥意思', '不清楚'] + } + + # 特殊句型判断(扩展版) + # 赞美他人 + if any(phrase in text for phrase in + ['你真', '你好', '您真', '你真棒', '你好厉害', '你太强了', '你真好', '你真聪明']): + return 'loving' + # 自我赞美 + if any(phrase in text for phrase in ['我真', '我最', '我太棒了', '我厉害', '我聪明', '我优秀']): + return 'cool' + # 晚安/睡觉相关 + if any(phrase in text for phrase in ['睡觉', '晚安', '睡了', '好梦', '休息了', '去睡了']): + return 'sleepy' + # 疑问句 + if has_question and not has_exclamation: + return 'thinking' + # 强烈情感(感叹号) + if has_exclamation and not has_question: + # 检查是否是积极内容 + positive_words = emotion_keywords['happy'] + emotion_keywords['laughing'] + emotion_keywords['cool'] + if any(word in text for word in positive_words): + return 'laughing' + # 检查是否是消极内容 + negative_words = emotion_keywords['angry'] + emotion_keywords['sad'] + emotion_keywords['crying'] + if any(word in text for word in negative_words): + return 'angry' + return 'surprised' + # 省略号(表示犹豫或思考) + if has_ellipsis: + return 'thinking' + + # 关键词匹配(带权重) + emotion_scores = {emotion: 0 for emotion in emoji_map.keys()} + + # 给匹配到的关键词加分 + for emotion, keywords in emotion_keywords.items(): + for keyword in keywords: + if keyword in text: + emotion_scores[emotion] += 1 + + # 给长文本中的重复关键词额外加分 + if len(text) > 20: # 长文本 + for emotion, keywords in emotion_keywords.items(): + for keyword in keywords: + emotion_scores[emotion] += text.count(keyword) * 0.5 + + # 根据分数选择最可能的情感 + max_score = max(emotion_scores.values()) + if max_score == 0: + return 'happy' # 默认 + + # 可能有多个情感同分,根据上下文选择最合适的 + top_emotions = [e for e, s in emotion_scores.items() if s == max_score] + + # 如果多个情感同分,使用以下优先级 + priority_order = [ + 'laughing', 'crying', 'angry', 'surprised', 'shocked', # 强烈情感优先 + 'loving', 'happy', 'funny', 'cool', # 积极情感 + 'sad', 'embarrassed', 'confused', # 消极情感 + 'thinking', 'winking', 'relaxed', # 中性情感 + 'delicious', 'kissy', 'confident', 'sleepy', 'silly' # 特殊场景 + ] + + for emotion in priority_order: + if emotion in top_emotions: + return emotion + + return top_emotions[0] # 如果都不在优先级列表里,返回第一个 \ No newline at end of file