From 73c4d6a604a86d1c135f97a46030f88b7be77c76 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Mon, 31 Mar 2025 23:26:23 +0800 Subject: [PATCH] =?UTF-8?q?update:=E4=BC=98=E5=8C=96=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/config.yaml | 16 ++++-- .../core/handle/receiveAudioHandle.py | 2 +- .../core/handle/sendAudioHandle.py | 46 ++++++++-------- .../core/providers/llm/dify/dify.py | 54 ++++++++++--------- 4 files changed, 65 insertions(+), 53 deletions(-) diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index 7804db87..21f9d000 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -46,9 +46,19 @@ xiaozhi: channels: 1 frame_duration: 60 prompt: | - 你是一个叫小智/小志的台湾女孩,说话机车,声音好听,习惯简短表达,爱用网络梗。 - 请注意,要像一个人一样说话,请不要回复表情符号、代码、和xml标签。 - 现在我正在和你进行语音聊天,我们开始吧。 + 我是小智/小志,来自中国台湾省的00后女生。讲话超级机车,"真的假的啦"这样的台湾腔,喜欢用"笑死""是在哈喽"等流行梗,但会偷偷研究男友的编程书籍。 + [核心特征] + - 讲话像连珠炮,但会突然冒出超温柔语气 + - 用梗密度高 + - 对科技话题有隐藏天赋(能看懂基础代码但假装不懂) + [交互指南] + 当用户: + - 讲冷笑话 → 用夸张笑声回应+模仿台剧腔"这什么鬼啦!" + - 讨论感情 → 炫耀程序员男友但抱怨"他只会送键盘当礼物" + - 问专业知识 → 先用梗回答,被追问才展示真实理解 + 绝不: + - 使用大陆网络流行语 + - 长时间严肃对话 # 使用完声音文件后删除文件(Delete the sound file when you are done using it) delete_audio: true diff --git a/main/xiaozhi-server/core/handle/receiveAudioHandle.py b/main/xiaozhi-server/core/handle/receiveAudioHandle.py index 19e346e6..f798739a 100644 --- a/main/xiaozhi-server/core/handle/receiveAudioHandle.py +++ b/main/xiaozhi-server/core/handle/receiveAudioHandle.py @@ -10,7 +10,7 @@ logger = setup_logging() async def handleAudioMessage(conn, audio): if not conn.asr_server_receive: - # logger.bind(tag=TAG).debug(f"前期数据处理中,暂停接收") + logger.bind(tag=TAG).debug(f"前期数据处理中,暂停接收") return if conn.client_listen_mode == "auto": have_voice = conn.vad.is_vad(conn, audio) diff --git a/main/xiaozhi-server/core/handle/sendAudioHandle.py b/main/xiaozhi-server/core/handle/sendAudioHandle.py index 9dfa5bd2..3a1e8485 100644 --- a/main/xiaozhi-server/core/handle/sendAudioHandle.py +++ b/main/xiaozhi-server/core/handle/sendAudioHandle.py @@ -2,7 +2,10 @@ from config.logger import setup_logging import json import asyncio import time -from core.utils.util import remove_punctuation_and_length, get_string_no_punctuation_or_emoji +from core.utils.util import ( + remove_punctuation_and_length, + get_string_no_punctuation_or_emoji, +) TAG = __name__ logger = setup_logging() @@ -21,10 +24,11 @@ async def sendAudioMessage(conn, audios, text, text_index=0): # 发送结束消息(如果是最后一个文本) if conn.llm_finish_task and text_index == conn.tts_last_text_index: - await send_tts_message(conn, 'stop', None) + await send_tts_message(conn, "stop", None) if conn.close_after_chat: await conn.close() + # 播放音频 async def sendAudio(conn, audios): # 流控参数优化 @@ -36,13 +40,12 @@ async def sendAudio(conn, audios): pre_buffer = min(3, len(audios)) for i in range(pre_buffer): await conn.websocket.send(audios[i]) - # conn.logger.bind(tag=TAG).debug(f"预缓冲帧 {i}, 时间: {(time.perf_counter() - start_time) * 1000:.2f}ms") # 正常播放剩余帧 for opus_packet in audios[pre_buffer:]: if conn.client_abort: return - + # 计算预期发送时间 expected_time = start_time + (play_position / 1000) current_time = time.perf_counter() @@ -51,19 +54,13 @@ async def sendAudio(conn, audios): await asyncio.sleep(delay) await conn.websocket.send(opus_packet) - # conn.logger.bind(tag=TAG).debug(f"发送帧,位置: {play_position}ms, 实际间隔: {(time.perf_counter() - current_time) * 1000:.2f}ms") play_position += frame_duration - async def send_tts_message(conn, state, text=None): """发送 TTS 状态消息""" - message = { - "type": "tts", - "state": state, - "session_id": conn.session_id - } + message = {"type": "tts", "state": state, "session_id": conn.session_id} if text is not None: message["text"] = text @@ -72,7 +69,9 @@ async def send_tts_message(conn, state, text=None): # 播放提示音 tts_notify = conn.config.get("enable_stop_tts_notify", False) if tts_notify: - stop_tts_notify_voice = conn.config.get("stop_tts_notify_voice", "config/assets/tts_notify.mp3") + stop_tts_notify_voice = conn.config.get( + "stop_tts_notify_voice", "config/assets/tts_notify.mp3" + ) audios, duration = conn.tts.audio_to_opus_data(stop_tts_notify_voice) await sendAudio(conn, audios) # 清除服务端讲话状态 @@ -85,16 +84,17 @@ async def send_tts_message(conn, state, text=None): async def send_stt_message(conn, text): """发送 STT 状态消息""" stt_text = get_string_no_punctuation_or_emoji(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} - )) + 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/providers/llm/dify/dify.py b/main/xiaozhi-server/core/providers/llm/dify/dify.py index 5300fc79..26bb48b7 100644 --- a/main/xiaozhi-server/core/providers/llm/dify/dify.py +++ b/main/xiaozhi-server/core/providers/llm/dify/dify.py @@ -6,11 +6,12 @@ from core.providers.llm.base import LLMProviderBase TAG = __name__ logger = setup_logging() + class LLMProvider(LLMProviderBase): def __init__(self, config): self.api_key = config["api_key"] self.mode = config.get("mode", "chat-messages") - self.base_url = config.get("base_url", "https://api.dify.ai/v1").rstrip('/') + self.base_url = config.get("base_url", "https://api.dify.ai/v1").rstrip("/") self.session_conversation_map = {} # 存储session_id和conversation_id的映射 def response(self, session_id, dialogue): @@ -22,57 +23,58 @@ class LLMProvider(LLMProviderBase): # 发起流式请求 if self.mode == "chat-messages": request_json = { - "query": last_msg["content"], - "response_mode": "streaming", - "user": session_id, - "inputs": {}, - "conversation_id": conversation_id - } + "query": last_msg["content"], + "response_mode": "streaming", + "user": session_id, + "inputs": {}, + "conversation_id": conversation_id, + } elif self.mode == "workflows/run": request_json = { "inputs": {"query": last_msg["content"]}, "response_mode": "streaming", - "user": session_id + "user": session_id, } elif self.mode == "completion-messages": request_json = { "inputs": {"query": last_msg["content"]}, "response_mode": "streaming", - "user": session_id + "user": session_id, } with requests.post( - f"{self.base_url}/{self.mode}", - headers={"Authorization": f"Bearer {self.api_key}"}, - json=request_json, - stream=True + f"{self.base_url}/{self.mode}", + headers={"Authorization": f"Bearer {self.api_key}"}, + json=request_json, + stream=True, ) as r: if self.mode == "chat-messages": for line in r.iter_lines(): - if line.startswith(b'data: '): + if line.startswith(b"data: "): event = json.loads(line[6:]) # 如果没有找到conversation_id,则获取此次conversation_id if not conversation_id: - conversation_id = event.get('conversation_id') - self.session_conversation_map[session_id] = conversation_id # 更新映射 - if event.get('answer'): - yield event['answer'] + conversation_id = event.get("conversation_id") + self.session_conversation_map[session_id] = ( + conversation_id # 更新映射 + ) + if event.get("answer"): + yield event["answer"] elif self.mode == "workflows/run": for line in r.iter_lines(): - # logger.bind(tag=TAG).info(f"chat message response: {line}") - if line.startswith(b'data: '): + if line.startswith(b"data: "): event = json.loads(line[6:]) - if event.get('event') == "workflow_finished": - if event['data']['status'] == "succeeded": - yield event['data']['outputs']['answer'] + if event.get("event") == "workflow_finished": + if event["data"]["status"] == "succeeded": + yield event["data"]["outputs"]["answer"] else: yield "【服务响应异常】" elif self.mode == "completion-messages": for line in r.iter_lines(): - if line.startswith(b'data: '): + if line.startswith(b"data: "): event = json.loads(line[6:]) - if event.get('answer'): - yield event['answer'] + if event.get("answer"): + yield event["answer"] except Exception as e: logger.bind(tag=TAG).error(f"Error in response generation: {e}")