Merge branch 'knowledge-base-fix' of https://github.com/xinnan-tech/xiaozhi-esp32-server into Knowledge-Base

This commit is contained in:
rainv123
2025-11-12 15:57:51 +08:00
12 changed files with 56 additions and 53 deletions
+3 -3
View File
@@ -11,12 +11,12 @@
1、如果你是源码部署,你的`mqtt-websocket`地址是:
```
ws://127.0.0.1:8000/xiaozhi/v1?from=mqtt_gateway
ws://127.0.0.1:8000/xiaozhi/v1/?from=mqtt_gateway
```
2、如果你是docker部署,你的`mqtt-websocket`地址是
```
ws://你宿主机局域网IP:8000/xiaozhi/v1?from=mqtt_gateway
ws://你宿主机局域网IP:8000/xiaozhi/v1/?from=mqtt_gateway
```
## 重要提示
@@ -53,7 +53,7 @@ cp config/mqtt.json.example config/mqtt.json
{
"production": {
"chat_servers": [
"ws://127.0.0.1:8000/xiaozhi/v1?from=mqtt_gateway"
"ws://127.0.0.1:8000/xiaozhi/v1/?from=mqtt_gateway"
]
},
"debug": false,
+1 -1
View File
@@ -446,7 +446,7 @@ class ConnectionHandler:
)
if enhanced_prompt:
self.change_system_prompt(enhanced_prompt)
self.logger.bind(tag=TAG).info("系统提示词已增强更新")
self.logger.bind(tag=TAG).debug("系统提示词已增强更新")
def _init_report_threads(self):
"""初始化ASR和TTS上报线程"""
@@ -43,15 +43,15 @@ async def handleHelloMessage(conn, msg_json):
audio_params = msg_json.get("audio_params")
if audio_params:
format = audio_params.get("format")
conn.logger.bind(tag=TAG).info(f"客户端音频格式: {format}")
conn.logger.bind(tag=TAG).debug(f"客户端音频格式: {format}")
conn.audio_format = format
conn.welcome_msg["audio_params"] = audio_params
features = msg_json.get("features")
if features:
conn.logger.bind(tag=TAG).info(f"客户端特性: {features}")
conn.logger.bind(tag=TAG).debug(f"客户端特性: {features}")
conn.features = features
if features.get("mcp"):
conn.logger.bind(tag=TAG).info("客户端支持MCP")
conn.logger.bind(tag=TAG).debug("客户端支持MCP")
conn.mcp_client = MCPClient()
# 发送初始化
asyncio.create_task(send_mcp_initialize_message(conn))
@@ -174,7 +174,7 @@ class ASRProvider(ASRProviderBase):
self.task_id = uuid.uuid4().hex
logger.bind(tag=TAG).info(f"WebSocket连接建立成功, task_id: {self.task_id}")
logger.bind(tag=TAG).debug(f"WebSocket连接建立成功, task_id: {self.task_id}")
self.is_processing = True
self.server_ready = False # 重置服务器准备状态
@@ -202,7 +202,7 @@ class ASRProvider(ASRProviderBase):
}
}
await self.asr_ws.send(json.dumps(start_request, ensure_ascii=False))
logger.bind(tag=TAG).info("已发送开始请求,等待服务器准备...")
logger.bind(tag=TAG).debug("已发送开始请求,等待服务器准备...")
async def _forward_results(self, conn):
"""转发识别结果"""
@@ -231,7 +231,7 @@ class ASRProvider(ASRProviderBase):
# 收到TranscriptionStarted表示服务器准备好接收音频数据
if message_name == "TranscriptionStarted":
self.server_ready = True
logger.bind(tag=TAG).info("服务器已准备,开始发送缓存音频...")
logger.bind(tag=TAG).debug("服务器已准备,开始发送缓存音频...")
# 发送缓存音频
if conn.asr_audio:
@@ -281,7 +281,7 @@ class ASRProvider(ASRProviderBase):
async def _cleanup(self, conn):
"""清理资源"""
logger.bind(tag=TAG).info(f"开始ASR会话清理 | 当前状态: processing={self.is_processing}, server_ready={self.server_ready}")
logger.bind(tag=TAG).debug(f"开始ASR会话清理 | 当前状态: processing={self.is_processing}, server_ready={self.server_ready}")
# 清理连接的音频缓存
if conn and hasattr(conn, 'asr_audio_for_voiceprint'):
@@ -304,17 +304,17 @@ class ASRProvider(ASRProviderBase):
"appkey": self.appkey
}
}
logger.bind(tag=TAG).info("正在发送ASR终止请求")
logger.bind(tag=TAG).debug("正在发送ASR终止请求")
await self.asr_ws.send(json.dumps(stop_msg, ensure_ascii=False))
await asyncio.sleep(0.1)
logger.bind(tag=TAG).info("ASR终止请求已发送")
logger.bind(tag=TAG).debug("ASR终止请求已发送")
except Exception as e:
logger.bind(tag=TAG).error(f"ASR终止请求发送失败: {e}")
# 状态重置(在终止请求发送后)
self.is_processing = False
self.server_ready = False
logger.bind(tag=TAG).info("ASR状态已重置")
logger.bind(tag=TAG).debug("ASR状态已重置")
# 清理任务
if self.forward_task and not self.forward_task.done():
@@ -337,7 +337,7 @@ class ASRProvider(ASRProviderBase):
finally:
self.asr_ws = None
logger.bind(tag=TAG).info("ASR会话清理完成")
logger.bind(tag=TAG).debug("ASR会话清理完成")
async def speech_to_text(self, opus_data, session_id, audio_format):
"""获取识别结果"""
@@ -101,7 +101,7 @@ class ASRProviderBase(ABC):
self.speech_to_text(asr_audio_task, conn.session_id, conn.audio_format)
)
end_time = time.monotonic()
logger.bind(tag=TAG).info(f"ASR耗时: {end_time - start_time:.3f}s")
logger.bind(tag=TAG).debug(f"ASR耗时: {end_time - start_time:.3f}s")
return result
finally:
loop.close()
@@ -158,7 +158,7 @@ class ASRProviderBase(ABC):
# 性能监控
total_time = time.monotonic() - total_start_time
logger.bind(tag=TAG).info(f"总处理耗时: {total_time:.3f}s")
logger.bind(tag=TAG).debug(f"总处理耗时: {total_time:.3f}s")
# 检查文本长度
text_len, _ = remove_punctuation_and_length(raw_text)
@@ -42,7 +42,7 @@ class MemoryProvider(MemoryProviderBase):
if message.role != "system"
]
result = self.client.add(
messages, user_id=self.role_id, output_format=self.api_version
messages, user_id=self.role_id
)
logger.bind(tag=TAG).debug(f"Save memory result: {result}")
except Exception as e:
@@ -53,9 +53,12 @@ class MemoryProvider(MemoryProviderBase):
if not self.use_mem0:
return ""
try:
results = self.client.search(
query, user_id=self.role_id, output_format=self.api_version
)
if not getattr(self, "role_id", None):
return ""
filters = {"user_id": self.role_id}
results = self.client.search(query, filters=filters)
if not results or "results" not in results:
return ""
@@ -106,14 +106,14 @@ async def send_mcp_message(conn, payload: dict):
try:
await conn.websocket.send(message)
logger.bind(tag=TAG).info(f"成功发送MCP消息: {message}")
logger.bind(tag=TAG).debug(f"成功发送MCP消息: {message}")
except Exception as e:
logger.bind(tag=TAG).error(f"发送MCP消息失败: {e}")
async def handle_mcp_message(conn, mcp_client: MCPClient, payload: dict):
"""处理MCP消息,包括初始化、工具列表和工具调用响应等"""
logger.bind(tag=TAG).info(f"处理MCP消息: {str(payload)[:100]}")
logger.bind(tag=TAG).debug(f"处理MCP消息: {str(payload)[:100]}")
if not isinstance(payload, dict):
logger.bind(tag=TAG).error("MCP消息缺少payload字段或格式错误")
@@ -138,7 +138,7 @@ async def handle_mcp_message(conn, mcp_client: MCPClient, payload: dict):
if isinstance(server_info, dict):
name = server_info.get("name")
version = server_info.get("version")
logger.bind(tag=TAG).info(
logger.bind(tag=TAG).debug(
f"客户端MCP服务器信息: name={name}, version={version}"
)
return
@@ -195,11 +195,11 @@ async def handle_mcp_message(conn, mcp_client: MCPClient, payload: dict):
next_cursor = result.get("nextCursor", "")
if next_cursor:
logger.bind(tag=TAG).info(f"有更多工具,nextCursor: {next_cursor}")
logger.bind(tag=TAG).debug(f"有更多工具,nextCursor: {next_cursor}")
await send_mcp_tools_list_continue_request(conn, next_cursor)
else:
await mcp_client.set_ready(True)
logger.bind(tag=TAG).info("所有工具已获取,MCP客户端准备就绪")
logger.bind(tag=TAG).debug("所有工具已获取,MCP客户端准备就绪")
# 刷新工具缓存,确保MCP工具被包含在函数列表中
if hasattr(conn, "func_handler") and conn.func_handler:
@@ -255,7 +255,7 @@ async def send_mcp_initialize_message(conn):
},
},
}
logger.bind(tag=TAG).info("发送MCP初始化消息")
logger.bind(tag=TAG).debug("发送MCP初始化消息")
await send_mcp_message(conn, payload)
@@ -69,7 +69,7 @@ class UnifiedToolHandler:
self._initialize_home_assistant()
self.finish_init = True
self.logger.info("统一工具处理器初始化完成")
self.logger.debug("统一工具处理器初始化完成")
# 输出当前支持的所有工具列表
self.current_support_functions()
@@ -20,7 +20,7 @@ class ToolManager:
"""注册工具执行器"""
self.executors[tool_type] = executor
self._invalidate_cache()
self.logger.info(f"注册工具执行器: {tool_type.value}")
self.logger.debug(f"注册工具执行器: {tool_type.value}")
def _invalidate_cache(self):
"""使缓存失效"""
@@ -109,7 +109,7 @@ class ToolManager:
def refresh_tools(self):
"""刷新工具缓存"""
self._invalidate_cache()
self.logger.info("工具缓存已刷新")
self.logger.debug("工具缓存已刷新")
def get_tool_statistics(self) -> Dict[str, int]:
"""获取工具统计信息"""
@@ -189,7 +189,7 @@ class TTSProvider(TTSProviderBase):
self.task_id = uuid.uuid4().hex
logger.bind(tag=TAG).info(f"使用已有链接..., task_id: {self.task_id}")
return self.ws
logger.bind(tag=TAG).info("开始建立新连接...")
logger.bind(tag=TAG).debug("开始建立新连接...")
self.ws = await websockets.connect(
self.ws_url,
@@ -199,7 +199,7 @@ class TTSProvider(TTSProviderBase):
close_timeout=10,
)
self.task_id = uuid.uuid4().hex
logger.bind(tag=TAG).info(f"WebSocket连接建立成功, task_id: {self.task_id}")
logger.bind(tag=TAG).debug(f"WebSocket连接建立成功, task_id: {self.task_id}")
self.last_active_time = time.time()
return self.ws
except Exception as e:
@@ -227,14 +227,14 @@ class TTSProvider(TTSProviderBase):
if message.sentence_type == SentenceType.FIRST:
# 初始化参数
try:
logger.bind(tag=TAG).info("开始启动TTS会话...")
logger.bind(tag=TAG).debug("开始启动TTS会话...")
future = asyncio.run_coroutine_threadsafe(
self.start_session(self.task_id),
loop=self.conn.loop,
)
future.result()
self.before_stop_play_files.clear()
logger.bind(tag=TAG).info("TTS会话启动成功")
logger.bind(tag=TAG).debug("TTS会话启动成功")
except Exception as e:
logger.bind(tag=TAG).error(f"启动TTS会话失败: {str(e)}")
@@ -265,7 +265,7 @@ class TTSProvider(TTSProviderBase):
self._process_audio_file_stream(message.content_file, callback=lambda audio_data: self.handle_audio_file(audio_data, message.content_detail))
if message.sentence_type == SentenceType.LAST:
try:
logger.bind(tag=TAG).info("开始结束TTS会话...")
logger.bind(tag=TAG).debug("开始结束TTS会话...")
future = asyncio.run_coroutine_threadsafe(
self.finish_session(self.task_id),
loop=self.conn.loop,
@@ -313,7 +313,7 @@ class TTSProvider(TTSProviderBase):
raise
async def start_session(self, task_id):
logger.bind(tag=TAG).info("开始会话~~")
logger.bind(tag=TAG).debug("开始会话~~")
try:
# 会话开始时检测上个会话的监听状态
if (
@@ -352,7 +352,7 @@ class TTSProvider(TTSProviderBase):
}
await self.ws.send(json.dumps(start_request))
self.last_active_time = time.time()
logger.bind(tag=TAG).info("会话启动请求已发送")
logger.bind(tag=TAG).debug("会话启动请求已发送")
except Exception as e:
logger.bind(tag=TAG).error(f"启动会话失败: {str(e)}")
# 确保清理资源
@@ -360,7 +360,7 @@ class TTSProvider(TTSProviderBase):
raise
async def finish_session(self, task_id):
logger.bind(tag=TAG).info(f"关闭会话~~{task_id}")
logger.bind(tag=TAG).debug(f"关闭会话~~{task_id}")
try:
if self.ws:
stop_request = {
@@ -373,7 +373,7 @@ class TTSProvider(TTSProviderBase):
}
}
await self.ws.send(json.dumps(stop_request))
logger.bind(tag=TAG).info("会话结束请求已发送")
logger.bind(tag=TAG).debug("会话结束请求已发送")
self.last_active_time = time.time()
if self._monitor_task:
try:
@@ -186,7 +186,7 @@ class TTSProvider(TTSProviderBase):
if self.ws:
logger.bind(tag=TAG).info(f"使用已有链接...")
return self.ws
logger.bind(tag=TAG).info("开始建立新连接...")
logger.bind(tag=TAG).debug("开始建立新连接...")
ws_header = {
"X-Api-App-Key": self.appId,
"X-Api-Access-Key": self.access_token,
@@ -196,11 +196,11 @@ class TTSProvider(TTSProviderBase):
self.ws = await websockets.connect(
self.ws_url, additional_headers=ws_header, max_size=1000000000
)
logger.bind(tag=TAG).info("WebSocket连接建立成功")
logger.bind(tag=TAG).debug("WebSocket连接建立成功")
# 连接建立成功后,启动监听任务
if self._monitor_task is None or self._monitor_task.done():
logger.bind(tag=TAG).info("启动监听任务...")
logger.bind(tag=TAG).debug("启动监听任务...")
self._monitor_task = asyncio.create_task(self._start_monitor_tts_response())
return self.ws
@@ -238,16 +238,16 @@ class TTSProvider(TTSProviderBase):
try:
if not getattr(self.conn, "sentence_id", None):
self.conn.sentence_id = uuid.uuid4().hex
logger.bind(tag=TAG).info(f"自动生成新的 会话ID: {self.conn.sentence_id}")
logger.bind(tag=TAG).debug(f"自动生成新的 会话ID: {self.conn.sentence_id}")
logger.bind(tag=TAG).info("开始启动TTS会话...")
logger.bind(tag=TAG).debu("开始启动TTS会话...")
future = asyncio.run_coroutine_threadsafe(
self.start_session(self.conn.sentence_id),
loop=self.conn.loop,
)
future.result()
self.before_stop_play_files.clear()
logger.bind(tag=TAG).info("TTS会话启动成功")
logger.bind(tag=TAG).debug("TTS会话启动成功")
except Exception as e:
logger.bind(tag=TAG).error(f"启动TTS会话失败: {str(e)}")
continue
@@ -277,7 +277,7 @@ class TTSProvider(TTSProviderBase):
self._process_audio_file_stream(message.content_file, callback=lambda audio_data: self.handle_audio_file(audio_data, message.content_detail))
if message.sentence_type == SentenceType.LAST:
try:
logger.bind(tag=TAG).info("开始结束TTS会话...")
logger.bind(tag=TAG).debug("开始结束TTS会话...")
future = asyncio.run_coroutine_threadsafe(
self.finish_session(self.conn.sentence_id),
loop=self.conn.loop,
@@ -320,7 +320,7 @@ class TTSProvider(TTSProviderBase):
raise
async def start_session(self, session_id):
logger.bind(tag=TAG).info(f"开始会话~~{session_id}")
logger.bind(tag=TAG).debug(f"开始会话~~{session_id}")
try:
# 等待上一个会话结束,最多等待3次
for _ in range(3):
@@ -351,7 +351,7 @@ class TTSProvider(TTSProviderBase):
event=EVENT_StartSession, speaker=self.voice
)
await self.send_event(self.ws, header, optional, payload)
logger.bind(tag=TAG).info("会话启动请求已发送")
logger.bind(tag=TAG).debug("会话启动请求已发送")
except Exception as e:
logger.bind(tag=TAG).error(f"启动会话失败: {str(e)}")
# 确保清理资源
@@ -359,7 +359,7 @@ class TTSProvider(TTSProviderBase):
raise
async def finish_session(self, session_id):
logger.bind(tag=TAG).info(f"关闭会话~~{session_id}")
logger.bind(tag=TAG).debug(f"关闭会话~~{session_id}")
try:
if self.ws:
header = Header(
@@ -372,7 +372,7 @@ class TTSProvider(TTSProviderBase):
).as_bytes()
payload = str.encode("{}")
await self.send_event(self.ws, header, optional, payload)
logger.bind(tag=TAG).info("会话结束请求已发送")
logger.bind(tag=TAG).debug("会话结束请求已发送")
except Exception as e:
logger.bind(tag=TAG).error(f"关闭会话失败: {str(e)}")
@@ -381,7 +381,7 @@ class TTSProvider(TTSProviderBase):
raise
async def cancel_session(self,session_id):
logger.bind(tag=TAG).info(f"取消会话,释放服务端资源~~{session_id}")
logger.bind(tag=TAG).debug(f"取消会话,释放服务端资源~~{session_id}")
try:
if self.ws:
header = Header(
@@ -394,7 +394,7 @@ class TTSProvider(TTSProviderBase):
).as_bytes()
payload = str.encode("{}")
await self.send_event(self.ws, header, optional, payload)
logger.bind(tag=TAG).info("会话取消请求已发送")
logger.bind(tag=TAG).debug("会话取消请求已发送")
except Exception as e:
logger.bind(tag=TAG).error(f"取消会话失败: {str(e)}")
# 确保清理资源
@@ -184,7 +184,7 @@ class PromptManager:
local_address = self._get_location_info(client_ip)
# 获取天气信息(使用全局缓存)
self._get_weather_info(conn, local_address)
self.logger.bind(tag=TAG).info(f"上下文信息更新完成")
self.logger.bind(tag=TAG).debug(f"上下文信息更新完成")
except Exception as e:
self.logger.bind(tag=TAG).error(f"更新上下文信息失败: {e}")