mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-31 03:43:57 +08:00
feat: 统一LLM错误处理并添加系统错误回复配置
在多个LLM提供者中移除try-catch块,将错误处理统一到connection.py的流处理层 添加system_error_response配置项,支持自定义系统错误时的回复内容 在意图识别和流处理中捕获异常时返回配置的错误回复,避免硬编码错误信息 Fixes #2075
This commit is contained in:
@@ -202,6 +202,9 @@ prompt: |
|
||||
# 默认系统提示词模板文件
|
||||
prompt_template: agent-base-prompt.txt
|
||||
|
||||
# 系统错误时的回复
|
||||
system_error_response: "主人,小智现在有点忙,我们稍后再试吧。"
|
||||
|
||||
# 结束语prompt
|
||||
end_prompt:
|
||||
enable: true # 是否开启结束语
|
||||
|
||||
@@ -40,8 +40,10 @@ from config.logger import setup_logging, build_module_string, create_connection_
|
||||
from config.manage_api_client import DeviceNotFoundException, DeviceBindException
|
||||
from core.utils.prompt_manager import PromptManager
|
||||
from core.utils.voiceprint_provider import VoiceprintProvider
|
||||
from core.utils.util import get_system_error_response
|
||||
from core.utils import textUtils
|
||||
|
||||
|
||||
TAG = __name__
|
||||
|
||||
auto_import_modules("plugins_func.functions")
|
||||
@@ -869,6 +871,7 @@ class ConnectionHandler:
|
||||
content_arguments = ""
|
||||
self.client_abort = False
|
||||
emotion_flag = True
|
||||
try:
|
||||
for response in llm_responses:
|
||||
if self.client_abort:
|
||||
break
|
||||
@@ -909,6 +912,26 @@ class ConnectionHandler:
|
||||
content_detail=content,
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
self.logger.bind(tag=TAG).error(f"LLM stream processing error: {e}")
|
||||
self.tts.tts_text_queue.put(
|
||||
TTSMessageDTO(
|
||||
sentence_id=self.sentence_id,
|
||||
sentence_type=SentenceType.LAST,
|
||||
content_type=ContentType.TEXT,
|
||||
content_detail=get_system_error_response(self.config),
|
||||
)
|
||||
)
|
||||
if depth == 0:
|
||||
self.tts.tts_text_queue.put(
|
||||
TTSMessageDTO(
|
||||
sentence_id=self.sentence_id,
|
||||
sentence_type=SentenceType.LAST,
|
||||
content_type=ContentType.ACTION,
|
||||
)
|
||||
)
|
||||
self.llm_finish_task = True
|
||||
return
|
||||
# 处理function call
|
||||
if tool_call_flag:
|
||||
bHasError = False
|
||||
|
||||
@@ -2,11 +2,14 @@ from typing import List, Dict
|
||||
from ..base import IntentProviderBase
|
||||
from plugins_func.functions.play_music import initialize_music_handler
|
||||
from config.logger import setup_logging
|
||||
from core.utils.util import get_system_error_response
|
||||
import re
|
||||
import json
|
||||
import hashlib
|
||||
import time
|
||||
|
||||
|
||||
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
@@ -115,12 +118,16 @@ class IntentProvider(IntentProviderBase):
|
||||
return prompt
|
||||
|
||||
def replyResult(self, text: str, original_text: str):
|
||||
try:
|
||||
llm_result = self.llm.response_no_stream(
|
||||
system_prompt=text,
|
||||
user_prompt="请根据以上内容,像人类一样说话的口吻回复用户,要求简洁,请直接返回结果。用户现在说:"
|
||||
+ original_text,
|
||||
)
|
||||
return llm_result
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"Error in generating reply result: {e}")
|
||||
return get_system_error_response(self.config)
|
||||
|
||||
async def detect_intent(self, conn, dialogue_history: List[Dict], text: str) -> str:
|
||||
if not self.llm:
|
||||
@@ -194,9 +201,13 @@ class IntentProvider(IntentProviderBase):
|
||||
llm_start_time = time.time()
|
||||
logger.bind(tag=TAG).debug(f"开始LLM意图识别调用, 模型: {model_info}")
|
||||
|
||||
try:
|
||||
intent = self.llm.response_no_stream(
|
||||
system_prompt=prompt_music, user_prompt=user_prompt
|
||||
)
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"Error in intent detection LLM call: {e}")
|
||||
return '{"function_call": {"name": "continue_chat"}}'
|
||||
|
||||
# 记录LLM调用完成时间
|
||||
llm_time = time.time() - llm_start_time
|
||||
|
||||
@@ -21,7 +21,6 @@ class LLMProvider(LLMProviderBase):
|
||||
check_model_key("AliBLLLM", self.api_key)
|
||||
|
||||
def response(self, session_id, dialogue):
|
||||
try:
|
||||
# 处理dialogue
|
||||
if self.is_No_prompt:
|
||||
dialogue.pop(0)
|
||||
@@ -95,10 +94,6 @@ class LLMProvider(LLMProviderBase):
|
||||
if chunk:
|
||||
yield chunk
|
||||
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"【阿里百练API服务】响应异常: {e}")
|
||||
yield "【LLM服务响应异常】"
|
||||
|
||||
def response_with_functions(self, session_id, dialogue, functions=None):
|
||||
# 阿里百练当前未支持原生的 function call。为保持兼容,这里回退到普通文本流式输出。
|
||||
# 上层会按 (content, tool_calls) 的形式消费,这里始终返回 (token, None)
|
||||
|
||||
@@ -11,7 +11,6 @@ class LLMProviderBase(ABC):
|
||||
pass
|
||||
|
||||
def response_no_stream(self, system_prompt, user_prompt, **kwargs):
|
||||
try:
|
||||
# 构造对话格式
|
||||
dialogue = [
|
||||
{"role": "system", "content": system_prompt},
|
||||
@@ -22,10 +21,6 @@ class LLMProviderBase(ABC):
|
||||
result += part
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"Error in Ollama response generation: {e}")
|
||||
return "【LLM服务响应异常】"
|
||||
|
||||
def response_with_functions(self, session_id, dialogue, functions=None):
|
||||
"""
|
||||
Default implementation for function calling (streaming)
|
||||
|
||||
@@ -20,7 +20,6 @@ class LLMProvider(LLMProviderBase):
|
||||
logger.bind(tag=TAG).error(model_key_msg)
|
||||
|
||||
def response(self, session_id, dialogue, **kwargs):
|
||||
try:
|
||||
# 取最后一条用户消息
|
||||
last_msg = next(m for m in reversed(dialogue) if m["role"] == "user")
|
||||
conversation_id = self.session_conversation_map.get(session_id)
|
||||
@@ -87,10 +86,6 @@ class LLMProvider(LLMProviderBase):
|
||||
):
|
||||
yield event["answer"]
|
||||
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"Error in response generation: {e}")
|
||||
yield "【服务响应异常】"
|
||||
|
||||
def response_with_functions(self, session_id, dialogue, functions=None):
|
||||
if len(dialogue) == 2 and functions is not None and len(functions) > 0:
|
||||
# 第一次调用llm, 取最后一条用户消息,附加tool提示词
|
||||
|
||||
@@ -19,7 +19,6 @@ class LLMProvider(LLMProviderBase):
|
||||
logger.bind(tag=TAG).error(model_key_msg)
|
||||
|
||||
def response(self, session_id, dialogue, **kwargs):
|
||||
try:
|
||||
# 取最后一条用户消息
|
||||
last_msg = next(m for m in reversed(dialogue) if m["role"] == "user")
|
||||
|
||||
@@ -63,10 +62,6 @@ class LLMProvider(LLMProviderBase):
|
||||
except Exception as e:
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"Error in response generation: {e}")
|
||||
yield "【服务响应异常】"
|
||||
|
||||
def response_with_functions(self, session_id, dialogue, functions=None):
|
||||
logger.bind(tag=TAG).error(
|
||||
f"fastgpt暂未实现完整的工具调用(function call),建议使用其他意图识别"
|
||||
|
||||
@@ -15,7 +15,6 @@ class LLMProvider(LLMProviderBase):
|
||||
self.api_url = f"{self.base_url}/api/conversation/process" # 拼接完整的 API URL
|
||||
|
||||
def response(self, session_id, dialogue, **kwargs):
|
||||
try:
|
||||
# home assistant语音助手自带意图,无需使用xiaozhi ai自带的,只需要把用户说的话传递给home assistant即可
|
||||
|
||||
# 提取最后一个 role 为 'user' 的 content
|
||||
@@ -60,11 +59,6 @@ class LLMProvider(LLMProviderBase):
|
||||
else:
|
||||
logger.bind(tag=TAG).warning("API 返回数据中没有 speech 内容")
|
||||
|
||||
except RequestException as e:
|
||||
logger.bind(tag=TAG).error(f"HTTP 请求错误: {e}")
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"生成响应时出错: {e}")
|
||||
|
||||
def response_with_functions(self, session_id, dialogue, functions=None):
|
||||
logger.bind(tag=TAG).error(
|
||||
f"homeassistant不支持(function call),建议使用其他意图识别"
|
||||
|
||||
@@ -25,7 +25,6 @@ class LLMProvider(LLMProviderBase):
|
||||
self.is_qwen3 = self.model_name and self.model_name.lower().startswith("qwen3")
|
||||
|
||||
def response(self, session_id, dialogue, **kwargs):
|
||||
try:
|
||||
# 如果是qwen3模型,在用户最后一条消息中添加/no_think指令
|
||||
if self.is_qwen3:
|
||||
# 复制对话列表,避免修改原始对话
|
||||
@@ -89,12 +88,7 @@ class LLMProvider(LLMProviderBase):
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"Error processing chunk: {e}")
|
||||
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"Error in Ollama response generation: {e}")
|
||||
yield "【Ollama服务响应异常】"
|
||||
|
||||
def response_with_functions(self, session_id, dialogue, functions=None):
|
||||
try:
|
||||
# 如果是qwen3模型,在用户最后一条消息中添加/no_think指令
|
||||
if self.is_qwen3:
|
||||
# 复制对话列表,避免修改原始对话
|
||||
@@ -169,7 +163,3 @@ class LLMProvider(LLMProviderBase):
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"Error processing function chunk: {e}")
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"Error in Ollama function call: {e}")
|
||||
yield f"【Ollama服务响应异常: {str(e)}】", None
|
||||
|
||||
@@ -56,7 +56,6 @@ class LLMProvider(LLMProviderBase):
|
||||
return dialogue
|
||||
|
||||
def response(self, session_id, dialogue, **kwargs):
|
||||
try:
|
||||
dialogue = self.normalize_dialogue(dialogue)
|
||||
|
||||
request_params = {
|
||||
@@ -77,6 +76,7 @@ class LLMProvider(LLMProviderBase):
|
||||
if value is not None:
|
||||
request_params[key] = value
|
||||
|
||||
# raise ValueError("model_name is required")
|
||||
responses = self.client.chat.completions.create(**request_params)
|
||||
|
||||
is_active = True
|
||||
@@ -96,11 +96,7 @@ class LLMProvider(LLMProviderBase):
|
||||
if is_active:
|
||||
yield content
|
||||
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"Error in response generation: {e}")
|
||||
|
||||
def response_with_functions(self, session_id, dialogue, functions=None, **kwargs):
|
||||
try:
|
||||
dialogue = self.normalize_dialogue(dialogue)
|
||||
|
||||
request_params = {
|
||||
@@ -136,7 +132,3 @@ class LLMProvider(LLMProviderBase):
|
||||
f"输出 {getattr(usage_info, 'completion_tokens', '未知')},"
|
||||
f"共计 {getattr(usage_info, 'total_tokens', '未知')}"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"Error in function call streaming: {e}")
|
||||
yield f"【OpenAI服务响应异常: {e}】", None
|
||||
|
||||
@@ -31,7 +31,6 @@ class LLMProvider(LLMProviderBase):
|
||||
raise
|
||||
|
||||
def response(self, session_id, dialogue, **kwargs):
|
||||
try:
|
||||
logger.bind(tag=TAG).debug(
|
||||
f"Sending request to Xinference with model: {self.model_name}, dialogue length: {len(dialogue)}"
|
||||
)
|
||||
@@ -59,12 +58,7 @@ class LLMProvider(LLMProviderBase):
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"Error processing chunk: {e}")
|
||||
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"Error in Xinference response generation: {e}")
|
||||
yield "【Xinference服务响应异常】"
|
||||
|
||||
def response_with_functions(self, session_id, dialogue, functions=None):
|
||||
try:
|
||||
logger.bind(tag=TAG).debug(
|
||||
f"Sending function call request to Xinference with model: {self.model_name}, dialogue length: {len(dialogue)}"
|
||||
)
|
||||
@@ -89,10 +83,3 @@ class LLMProvider(LLMProviderBase):
|
||||
yield content, tool_calls
|
||||
elif tool_calls:
|
||||
yield None, tool_calls
|
||||
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"Error in Xinference function call: {e}")
|
||||
yield {
|
||||
"type": "content",
|
||||
"content": f"【Xinference服务响应异常: {str(e)}】",
|
||||
}
|
||||
|
||||
@@ -162,6 +162,7 @@ class MemoryProvider(MemoryProviderBase):
|
||||
msgStr += f"当前时间:{time_str}"
|
||||
|
||||
if self.save_to_file:
|
||||
try:
|
||||
result = self.llm.response_no_stream(
|
||||
short_term_memory_prompt,
|
||||
msgStr,
|
||||
@@ -169,12 +170,11 @@ class MemoryProvider(MemoryProviderBase):
|
||||
temperature=0.2,
|
||||
)
|
||||
json_str = extract_json_data(result)
|
||||
try:
|
||||
json.loads(json_str) # 检查json格式是否正确
|
||||
self.short_memory = json_str
|
||||
self.save_memory_to_file()
|
||||
except Exception as e:
|
||||
print("Error:", e)
|
||||
logger.bind(tag=TAG).error(f"Error in saving memory: {e}")
|
||||
else:
|
||||
# 当save_to_file为False时,调用Java端的聊天记录总结接口
|
||||
summary_id = session_id if session_id else self.role_id
|
||||
|
||||
@@ -573,3 +573,14 @@ def validate_mcp_endpoint(mcp_endpoint: str) -> bool:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def get_system_error_response(config: dict) -> str:
|
||||
"""获取系统错误时的回复
|
||||
|
||||
Args:
|
||||
config: 配置字典
|
||||
|
||||
Returns:
|
||||
str: 系统错误时的回复
|
||||
"""
|
||||
return config.get("system_error_response", "主人,小智现在有点忙,我们稍后再试吧。")
|
||||
Reference in New Issue
Block a user