mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 15:13:55 +08:00
* fix: 退出指令无法识别 (#302) 修复指令text包含标点的情况下,无法正确识别退出指令的问题 * 优化提示词 (#303) * 添加 使用 coze 会话。 (#282) 历史消息在coze 上可配置历史消息的轮次。 同个会话id,coze 上自动带历史上下文。 会话和当前session关联。一次连接一个会话,断开后,重连是新会话无历史消息。 * update:去除无用代码 --------- Co-authored-by: Jad <journey.adc@gmail.com> Co-authored-by: Jiao Haoyang <108573524+XuSenfeng@users.noreply.github.com> Co-authored-by: xu <494462498@qq.com> Co-authored-by: hrz <1710360675@qq.com>
168 lines
5.6 KiB
Python
168 lines
5.6 KiB
Python
from config.logger import setup_logging
|
|
import json
|
|
from core.handle.sendAudioHandle import send_stt_message
|
|
from core.utils.dialogue import Message
|
|
from core.utils.util import remove_punctuation_and_length
|
|
from config.functionCallConfig import FunctionCallConfig
|
|
import asyncio
|
|
from enum import Enum
|
|
|
|
TAG = __name__
|
|
logger = setup_logging()
|
|
|
|
|
|
class Action(Enum):
|
|
NOTFOUND = (0, "没有找到函数")
|
|
NONE = (1, "啥也不干")
|
|
RESPONSE = (2, "直接回复")
|
|
REQLLM = (3, "调用函数后再请求llm生成回复")
|
|
|
|
def __init__(self, code, message):
|
|
self.code = code
|
|
self.message = message
|
|
|
|
|
|
class ActionResponse:
|
|
def __init__(self, action: Action, result, response):
|
|
self.action = action # 动作类型
|
|
self.result = result # 动作产生的结果
|
|
self.response = response # 直接回复的内容
|
|
|
|
|
|
def get_functions():
|
|
"""获取功能调用配置"""
|
|
return FunctionCallConfig
|
|
|
|
|
|
def handle_llm_function_call(conn, function_call_data):
|
|
try:
|
|
function_name = function_call_data["name"]
|
|
|
|
if function_name == "handle_exit_intent":
|
|
# 处理退出意图
|
|
try:
|
|
say_goodbye = json.loads(function_call_data["arguments"]).get("say_goodbye", "再见")
|
|
conn.close_after_chat = True
|
|
logger.bind(tag=TAG).info(f"退出意图已处理:{say_goodbye}")
|
|
return ActionResponse(action=Action.RESPONSE, result="退出意图已处理", response=say_goodbye)
|
|
except Exception as e:
|
|
logger.bind(tag=TAG).error(f"处理退出意图错误: {e}")
|
|
|
|
elif function_name == "play_music":
|
|
# 处理音乐播放意图
|
|
try:
|
|
song_name = "random"
|
|
arguments = function_call_data["arguments"]
|
|
if arguments is not None and len(arguments) > 0:
|
|
args = json.loads(arguments)
|
|
song_name = args.get("song_name", "random")
|
|
music_intent = f"播放音乐 {song_name}" if song_name != "random" else "随机播放音乐"
|
|
|
|
# 执行音乐播放命令
|
|
future = asyncio.run_coroutine_threadsafe(
|
|
conn.music_handler.handle_music_command(conn, music_intent),
|
|
conn.loop
|
|
)
|
|
future.result()
|
|
return ActionResponse(action=Action.RESPONSE, result="退出意图已处理", response="还想听什么歌?")
|
|
except Exception as e:
|
|
logger.bind(tag=TAG).error(f"处理音乐意图错误: {e}")
|
|
else:
|
|
return ActionResponse(action=Action.NOTFOUND, result="没有找到对应的函数", response="没有找到对应的函数处理相对于的功能呢,你可以需要添加预设的对应函数处理呢")
|
|
except Exception as e:
|
|
logger.bind(tag=TAG).error(f"处理function call错误: {e}")
|
|
|
|
return None
|
|
|
|
|
|
async def handle_user_intent(conn, text):
|
|
"""
|
|
Handle user intent before starting chat
|
|
|
|
Args:
|
|
conn: Connection object
|
|
text: User's text input
|
|
|
|
Returns:
|
|
bool: True if intent was handled, False if should proceed to chat
|
|
"""
|
|
# 检查是否有明确的退出命令
|
|
if await check_direct_exit(conn, text):
|
|
return True
|
|
|
|
if conn.use_function_call_mode:
|
|
# 使用支持function calling的聊天方法,不再进行意图分析
|
|
return False
|
|
|
|
# 使用LLM进行意图分析
|
|
intent = await analyze_intent_with_llm(conn, text)
|
|
|
|
if not intent:
|
|
return False
|
|
|
|
# 处理各种意图
|
|
return await process_intent_result(conn, intent, text)
|
|
|
|
|
|
async def check_direct_exit(conn, text):
|
|
"""检查是否有明确的退出命令"""
|
|
_, text = remove_punctuation_and_length(text)
|
|
cmd_exit = conn.cmd_exit
|
|
for cmd in cmd_exit:
|
|
if text == cmd:
|
|
logger.bind(tag=TAG).info(f"识别到明确的退出命令: {text}")
|
|
await conn.close()
|
|
return True
|
|
return False
|
|
|
|
|
|
async def analyze_intent_with_llm(conn, text):
|
|
"""使用LLM分析用户意图"""
|
|
if not hasattr(conn, 'intent') or not conn.intent:
|
|
logger.bind(tag=TAG).warning("意图识别服务未初始化")
|
|
return None
|
|
|
|
# 对话历史记录
|
|
dialogue = conn.dialogue
|
|
try:
|
|
intent_result = await conn.intent.detect_intent(conn, dialogue.dialogue, text)
|
|
|
|
# 尝试解析JSON结果
|
|
try:
|
|
intent_data = json.loads(intent_result)
|
|
if "intent" in intent_data:
|
|
return intent_data["intent"]
|
|
except json.JSONDecodeError:
|
|
# 如果不是JSON格式,尝试直接获取意图文本
|
|
return intent_result.strip()
|
|
|
|
except Exception as e:
|
|
logger.bind(tag=TAG).error(f"意图识别失败: {str(e)}")
|
|
|
|
return None
|
|
|
|
|
|
async def process_intent_result(conn, intent, original_text):
|
|
"""处理意图识别结果"""
|
|
# 处理退出意图
|
|
if "结束聊天" in intent:
|
|
logger.bind(tag=TAG).info(f"识别到退出意图: {intent}")
|
|
|
|
# 如果正在播放音乐,可以关了 TODO
|
|
|
|
# 如果是明确的离别意图,发送告别语并关闭连接
|
|
await send_stt_message(conn, original_text)
|
|
conn.executor.submit(conn.chat_and_close, original_text)
|
|
return True
|
|
|
|
# 处理播放音乐意图
|
|
if "播放音乐" in intent:
|
|
logger.bind(tag=TAG).info(f"识别到音乐播放意图: {intent}")
|
|
await conn.music_handler.handle_music_command(conn, intent)
|
|
return True
|
|
|
|
# 其他意图处理可以在这里扩展
|
|
|
|
# 默认返回False,表示继续常规聊天流程
|
|
return False
|