From e76a8d547bcff668318d2fcfdfd41c945c7664e6 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Fri, 9 May 2025 13:45:01 +0800 Subject: [PATCH] =?UTF-8?q?update=EF=BC=9A=E4=BD=BF=E7=94=A8intent=5Fllm?= =?UTF-8?q?=E4=B8=AD=E7=9A=84LLM=E8=BF=9B=E8=A1=8C=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E5=9B=9E=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/config.yaml | 2 +- .../core/handle/intentHandler.py | 76 +++++-------------- .../providers/intent/intent_llm/intent_llm.py | 19 +++-- 3 files changed, 31 insertions(+), 66 deletions(-) diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index 98c3d058..3b82dba0 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -157,7 +157,7 @@ selected_module: Memory: nomem # 意图识别模块开启后,可以播放音乐、控制音量、识别退出指令。 # 不想开通意图识别,就设置成:nointent - # 意图识别可使用intent_llm。优点:通用性强,缺点:增加串行前置意图识别模块,会增加处理时间,这个意图识别暂时不支持控制音量大小等iot操作 + # 意图识别可使用intent_llm。优点:通用性强,缺点:增加串行前置意图识别模块,会增加处理时间,支持控制音量大小等iot操作 # 意图识别可使用function_call,缺点:需要所选择的LLM支持function_call,优点:按需调用工具、速度快,理论上能全部操作所有iot指令 # 默认免费的ChatGLMLLM就已经支持function_call,但是如果像追求稳定建议把LLM设置成:DoubaoLLM,使用的具体model_name是:doubao-1-5-pro-32k-250115 Intent: function_call diff --git a/main/xiaozhi-server/core/handle/intentHandler.py b/main/xiaozhi-server/core/handle/intentHandler.py index 383e4e65..c9cfeeb0 100644 --- a/main/xiaozhi-server/core/handle/intentHandler.py +++ b/main/xiaozhi-server/core/handle/intentHandler.py @@ -108,43 +108,21 @@ async def process_intent_result(conn, intent_result, original_text): if result.action == Action.RESPONSE: # 直接回复前端 text = result.response if text is not None: - text_index = ( - conn.tts_last_text_index + 1 - if hasattr(conn, "tts_last_text_index") - else 0 - ) - conn.recode_first_last_text(text, text_index) - future = conn.executor.submit( - conn.speak_and_play, text, text_index - ) - conn.llm_finish_task = True - conn.tts_queue.put((future, text_index)) - conn.dialogue.put(Message(role="assistant", content=text)) + speak_and_play(conn, text) elif result.action == Action.REQLLM: # 调用函数后再请求llm生成回复 text = result.result - if text is not None and len(text) > 0: - conn.dialogue.put(Message(role="tool", content=text)) - conn.executor.submit( - conn.chat_with_function_calling, text, True - ) + conn.dialogue.put(Message(role="tool", content=text)) + llm_result = conn.intent.replyResult(text) + if llm_result is None: + llm_result = text + speak_and_play(conn, llm_result) elif ( result.action == Action.NOTFOUND or result.action == Action.ERROR ): text = result.result if text is not None: - text_index = ( - conn.tts_last_text_index + 1 - if hasattr(conn, "tts_last_text_index") - else 0 - ) - conn.recode_first_last_text(text, text_index) - future = conn.executor.submit( - conn.speak_and_play, text, text_index - ) - conn.llm_finish_task = True - conn.tts_queue.put((future, text_index)) - conn.dialogue.put(Message(role="assistant", content=text)) + speak_and_play(conn, text) elif function_name != "play_music": # For backward compatibility with original code # 获取当前最新的文本索引 @@ -152,18 +130,7 @@ async def process_intent_result(conn, intent_result, original_text): if text is None: text = result.result if text is not None: - text_index = ( - conn.tts_last_text_index + 1 - if hasattr(conn, "tts_last_text_index") - else 0 - ) - conn.recode_first_last_text(text, text_index) - future = conn.executor.submit( - conn.speak_and_play, text, text_index - ) - conn.llm_finish_task = True - conn.tts_queue.put((future, text_index)) - conn.dialogue.put(Message(role="assistant", content=text)) + speak_and_play(conn, text) # 将函数执行放在线程池中 conn.executor.submit(process_function_call) @@ -174,21 +141,12 @@ async def process_intent_result(conn, intent_result, original_text): return False -def extract_text_in_brackets(s): - """ - 从字符串中提取中括号内的文字 - - :param s: 输入字符串 - :return: 中括号内的文字,如果不存在则返回空字符串 - """ - left_bracket_index = s.find("[") - right_bracket_index = s.find("]") - - if ( - left_bracket_index != -1 - and right_bracket_index != -1 - and left_bracket_index < right_bracket_index - ): - return s[left_bracket_index + 1 : right_bracket_index] - else: - return "" +def speak_and_play(conn, text): + text_index = ( + conn.tts_last_text_index + 1 if hasattr(conn, "tts_last_text_index") else 0 + ) + conn.recode_first_last_text(text, text_index) + future = conn.executor.submit(conn.speak_and_play, text, text_index) + conn.llm_finish_task = True + conn.tts_queue.put((future, text_index)) + conn.dialogue.put(Message(role="assistant", content=text)) diff --git a/main/xiaozhi-server/core/providers/intent/intent_llm/intent_llm.py b/main/xiaozhi-server/core/providers/intent/intent_llm/intent_llm.py index d8b18916..2398ca9a 100644 --- a/main/xiaozhi-server/core/providers/intent/intent_llm/intent_llm.py +++ b/main/xiaozhi-server/core/providers/intent/intent_llm/intent_llm.py @@ -20,6 +20,7 @@ class IntentProvider(IntentProviderBase): self.intent_cache = {} # 缓存意图识别结果 self.cache_expiry = 600 # 缓存有效期10分钟 self.cache_max_size = 100 # 最多缓存100个意图 + self.history_count = 4 # 默认使用最近4条对话记录 def get_intent_system_prompt(self, functions_list: str) -> str: """ @@ -104,6 +105,13 @@ class IntentProvider(IntentProviderBase): for key, _ in sorted_items[: len(sorted_items) - self.cache_max_size]: del self.intent_cache[key] + def replyResult(self, text: str): + llm_result = self.llm.response_no_stream( + system_prompt=text, + user_prompt="请总结以上内容,像人类一样说话的口吻回复用户,要求简洁,请直接返回结果。", + ) + return llm_result + async def detect_intent(self, conn, dialogue_history: List[Dict], text: str) -> str: if not self.llm: raise ValueError("LLM provider not set") @@ -150,14 +158,13 @@ class IntentProvider(IntentProviderBase): logger.bind(tag=TAG).debug(f"User prompt: {prompt_music}") - # 构建用户最后一句话的提示 + # 构建用户对话历史的提示 msgStr = "" - # 只使用最后两句即可 - if len(dialogue_history) >= 2: - # 保证最少有两句话的时候处理 - msgStr += f"{dialogue_history[-2].role}: {dialogue_history[-2].content}\n" - msgStr += f"{dialogue_history[-1].role}: {dialogue_history[-1].content}\n" + # 获取最近的对话历史 + start_idx = max(0, len(dialogue_history) - self.history_count) + for i in range(start_idx, len(dialogue_history)): + msgStr += f"{dialogue_history[i].role}: {dialogue_history[i].content}\n" msgStr += f"User: {text}\n" user_prompt = f"current dialogue:\n{msgStr}"