From 0738c52d142541a7e393735cfdc4219d0725ef01 Mon Sep 17 00:00:00 2001 From: 3030332422 <3030332422@qq.com> Date: Wed, 10 Sep 2025 18:00:39 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8DLLM=E6=84=8F=E5=9B=BE?= =?UTF-8?q?=E8=AF=86=E5=88=AB=E7=9A=84=E8=8B=A5=E5=B9=B2=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/handle/intentHandler.py | 24 +++++++ .../providers/intent/intent_llm/intent_llm.py | 67 ++++++++++--------- .../core/utils/prompt_manager.py | 7 +- 3 files changed, 65 insertions(+), 33 deletions(-) diff --git a/main/xiaozhi-server/core/handle/intentHandler.py b/main/xiaozhi-server/core/handle/intentHandler.py index 72424968..b1486ab9 100644 --- a/main/xiaozhi-server/core/handle/intentHandler.py +++ b/main/xiaozhi-server/core/handle/intentHandler.py @@ -91,6 +91,30 @@ async def process_intent_result(conn, intent_result, original_text): if function_name == "continue_chat": return False + if function_name == "result_for_context": + await send_stt_message(conn, original_text) + conn.client_abort = False + + def process_context_result(): + conn.dialogue.put(Message(role="user", content=original_text)) + + # 使用现有的 prompt_manager 获取完整上下文 + from core.utils.prompt_manager import PromptManager + prompt_manager = PromptManager(conn.config, conn.logger) + + # 构建带上下文的基础提示 + context_prompt = prompt_manager.build_enhanced_prompt( + "当前时间:{{current_time}}\n今天日期:{{today_date}} ({{today_weekday}})\n今天农历:{{lunar_date}}\n用户所在城市:{{local_address}}", + conn.device_id, + getattr(conn, 'client_ip', None) + ) + + response = conn.intent.replyResult(context_prompt, original_text) + speak_txt(conn, response) + + conn.executor.submit(process_context_result) + return True + function_args = {} if "arguments" in intent_data["function_call"]: function_args = intent_data["function_call"]["arguments"] 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 26fbaf70..8d426d08 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 @@ -53,24 +53,33 @@ class IntentProvider(IntentProviderBase): functions_desc += "---\n" prompt = ( + "【严格格式要求】你必须只能返回JSON格式,绝对不能返回任何自然语言!\n\n" "你是一个意图识别助手。请分析用户的最后一句话,判断用户意图并调用相应的函数。\n\n" + + "【重要规则】以下类型的查询请直接返回result_for_context,无需调用函数:\n" + "- 询问当前时间(如:现在几点、当前时间、查询时间等)\n" + "- 询问今天日期(如:今天几号、今天星期几、今天是什么日期等)\n" + "- 询问今天农历(如:今天农历几号、今天什么节气等)\n" + "- 询问所在城市(如:我现在在哪里、你知道我在哪个城市吗等)" + "系统会根据上下文信息直接构建回答。\n\n" "- 如果用户使用疑问词(如'怎么'、'为什么'、'如何')询问退出相关的问题(例如'怎么退出了?'),注意这不是让你退出,请返回 {'function_call': {'name': 'continue_chat'}\n" "- 仅当用户明确使用'退出系统'、'结束对话'、'我不想和你说话了'等指令时,才触发 handle_exit_intent\n\n" f"{functions_desc}\n" "处理步骤:\n" "1. 分析用户输入,确定用户意图\n" - "2. 从可用函数列表中选择最匹配的函数\n" - "3. 如果找到匹配的函数,生成对应的function_call 格式\n" - '4. 如果没有找到匹配的函数,返回{"function_call": {"name": "continue_chat"}}\n\n' + "2. 检查是否为上述基础信息查询(时间、日期等),如是则返回result_for_context\n" + "3. 从可用函数列表中选择最匹配的函数\n" + "4. 如果找到匹配的函数,生成对应的function_call 格式\n" + '5. 如果没有找到匹配的函数,返回{"function_call": {"name": "continue_chat"}}\n\n' "返回格式要求:\n" - "1. 必须返回纯JSON格式\n" + "1. 必须返回纯JSON格式,不要包含任何其他文字\n" "2. 必须包含function_call字段\n" "3. function_call必须包含name字段\n" "4. 如果函数需要参数,必须包含arguments字段\n\n" "示例:\n" "```\n" "用户: 现在几点了?\n" - '返回: {"function_call": {"name": "get_time"}}\n' + '返回: {"function_call": {"name": "result_for_context"}}\n' "```\n" "```\n" "用户: 当前电池电量是多少?\n" @@ -94,12 +103,15 @@ class IntentProvider(IntentProviderBase): "```\n\n" "注意:\n" "1. 只返回JSON格式,不要包含任何其他文字\n" - '2. 如果没有找到匹配的函数,返回{"function_call": {"name": "continue_chat"}}\n' - "3. 确保返回的JSON格式正确,包含所有必要的字段\n" + '2. 优先检查用户查询是否为基础信息(时间、日期等),如是则返回{"function_call": {"name": "result_for_context"}},不需要arguments参数\n' + '3. 如果没有找到匹配的函数,返回{"function_call": {"name": "continue_chat"}}\n' + "4. 确保返回的JSON格式正确,包含所有必要的字段\n" + "5. result_for_context不需要任何参数,系统会自动从上下文获取信息\n" "特殊说明:\n" "- 当用户单次输入包含多个指令时(如'打开灯并且调高音量')\n" "- 请返回多个function_call组成的JSON数组\n" - "- 示例:{'function_calls': [{name:'light_on'}, {name:'volume_up'}]}" + "- 示例:{'function_calls': [{name:'light_on'}, {name:'volume_up'}]}\n\n" + "【最终警告】绝对禁止输出任何自然语言、表情符号或解释文字!只能输出有效JSON格式!违反此规则将导致系统错误!" ) return prompt @@ -223,8 +235,13 @@ class IntentProvider(IntentProviderBase): f"llm 识别到意图: {function_name}, 参数: {function_args}" ) - # 如果是继续聊天,清理工具调用相关的历史消息 - if function_name == "continue_chat": + # 处理不同类型的意图 + if function_name == "result_for_context": + # 处理基础信息查询,直接从context构建结果 + logger.bind(tag=TAG).info("检测到result_for_context意图,将使用上下文信息直接回答") + + elif function_name == "continue_chat": + # 处理普通对话 # 保留非工具相关的消息 clean_history = [ msg @@ -232,26 +249,16 @@ class IntentProvider(IntentProviderBase): if msg.role not in ["tool", "function"] ] conn.dialogue.dialogue = clean_history + + else: + # 处理函数调用 + logger.bind(tag=TAG).info(f"检测到函数调用意图: {function_name}") - # 添加到缓存 - self.cache_manager.set(self.CacheType.INTENT, cache_key, intent) - - # 后处理时间 - postprocess_time = time.time() - postprocess_start_time - logger.bind(tag=TAG).debug(f"意图后处理耗时: {postprocess_time:.4f}秒") - - # 确保返回完全序列化的JSON字符串 - return intent - else: - # 添加到缓存 - self.cache_manager.set(self.CacheType.INTENT, cache_key, intent) - - # 后处理时间 - postprocess_time = time.time() - postprocess_start_time - logger.bind(tag=TAG).debug(f"意图后处理耗时: {postprocess_time:.4f}秒") - - # 返回普通意图 - return intent + # 统一缓存处理和返回 + self.cache_manager.set(self.CacheType.INTENT, cache_key, intent) + postprocess_time = time.time() - postprocess_start_time + logger.bind(tag=TAG).debug(f"意图后处理耗时: {postprocess_time:.4f}秒") + return intent except json.JSONDecodeError: # 后处理时间 postprocess_time = time.time() - postprocess_start_time @@ -259,4 +266,4 @@ class IntentProvider(IntentProviderBase): f"无法解析意图JSON: {intent}, 后处理耗时: {postprocess_time:.4f}秒" ) # 如果解析失败,默认返回继续聊天意图 - return '{"intent": "继续聊天"}' + return '{"function_call": {"name": "continue_chat"}}' diff --git a/main/xiaozhi-server/core/utils/prompt_manager.py b/main/xiaozhi-server/core/utils/prompt_manager.py index 3f6c429a..fca3a977 100644 --- a/main/xiaozhi-server/core/utils/prompt_manager.py +++ b/main/xiaozhi-server/core/utils/prompt_manager.py @@ -120,6 +120,7 @@ class PromptManager: from datetime import datetime now = datetime.now() + current_time = now.strftime("%Y年%m月%d日 %H:%M:%S") today_date = now.strftime("%Y-%m-%d") today_weekday = WEEKDAY_MAP[now.strftime("%A")] today_lunar = cnlunar.Lunar(now, godType="8char") @@ -129,7 +130,7 @@ class PromptManager: today_lunar.lunarDayCn, ) - return today_date, today_weekday, lunar_date + return current_time, today_date, today_weekday, lunar_date def _get_location_info(self, client_ip: str) -> str: """获取位置信息""" @@ -198,7 +199,7 @@ class PromptManager: try: # 获取最新的时间信息(不缓存) - today_date, today_weekday, lunar_date = ( + current_time, today_date, today_weekday, lunar_date = ( self._get_current_time_info() ) @@ -223,7 +224,7 @@ class PromptManager: template = Template(self.base_prompt_template) enhanced_prompt = template.render( base_prompt=user_prompt, - current_time="{{current_time}}", + current_time=current_time, today_date=today_date, today_weekday=today_weekday, lunar_date=lunar_date, From f0a7ee1c0c113e129a997f09dc5ed65cab481a72 Mon Sep 17 00:00:00 2001 From: 3030332422 <3030332422@qq.com> Date: Thu, 11 Sep 2025 14:38:27 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8DLLM=E6=84=8F=E5=9B=BE?= =?UTF-8?q?=E8=AF=86=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/handle/intentHandler.py | 16 ++--- .../xiaozhi-server/core/utils/current_time.py | 68 +++++++++++++++++++ .../core/utils/prompt_manager.py | 23 +++---- 3 files changed, 84 insertions(+), 23 deletions(-) create mode 100644 main/xiaozhi-server/core/utils/current_time.py diff --git a/main/xiaozhi-server/core/handle/intentHandler.py b/main/xiaozhi-server/core/handle/intentHandler.py index b1486ab9..23b2d0aa 100644 --- a/main/xiaozhi-server/core/handle/intentHandler.py +++ b/main/xiaozhi-server/core/handle/intentHandler.py @@ -98,16 +98,16 @@ async def process_intent_result(conn, intent_result, original_text): def process_context_result(): conn.dialogue.put(Message(role="user", content=original_text)) - # 使用现有的 prompt_manager 获取完整上下文 - from core.utils.prompt_manager import PromptManager - prompt_manager = PromptManager(conn.config, conn.logger) + from core.utils.current_time import get_current_time_info + + current_time, today_date, today_weekday, lunar_date = get_current_time_info() # 构建带上下文的基础提示 - context_prompt = prompt_manager.build_enhanced_prompt( - "当前时间:{{current_time}}\n今天日期:{{today_date}} ({{today_weekday}})\n今天农历:{{lunar_date}}\n用户所在城市:{{local_address}}", - conn.device_id, - getattr(conn, 'client_ip', None) - ) + context_prompt = f"""当前时间:{current_time} + 今天日期:{today_date} ({today_weekday}) + 今天农历:{lunar_date} + + 请根据以上信息回答用户的问题:{original_text}""" response = conn.intent.replyResult(context_prompt, original_text) speak_txt(conn, response) diff --git a/main/xiaozhi-server/core/utils/current_time.py b/main/xiaozhi-server/core/utils/current_time.py new file mode 100644 index 00000000..714dd50f --- /dev/null +++ b/main/xiaozhi-server/core/utils/current_time.py @@ -0,0 +1,68 @@ +""" +时间工具模块 +提供统一的时间获取功能 +""" + +import cnlunar +from datetime import datetime + +WEEKDAY_MAP = { + "Monday": "星期一", + "Tuesday": "星期二", + "Wednesday": "星期三", + "Thursday": "星期四", + "Friday": "星期五", + "Saturday": "星期六", + "Sunday": "星期日", +} + + +def get_current_time() -> str: + """ + 获取当前时间字符串 (格式: HH:MM) + """ + return datetime.now().strftime("%H:%M") + + +def get_current_date() -> str: + """ + 获取今天日期字符串 (格式: YYYY-MM-DD) + """ + return datetime.now().strftime("%Y-%m-%d") + + +def get_current_weekday() -> str: + """ + 获取今天星期几 + """ + now = datetime.now() + return WEEKDAY_MAP[now.strftime("%A")] + + +def get_current_lunar_date() -> str: + """ + 获取农历日期字符串 + """ + try: + now = datetime.now() + today_lunar = cnlunar.Lunar(now, godType="8char") + return "%s年%s%s" % ( + today_lunar.lunarYearCn, + today_lunar.lunarMonthCn[:-1], + today_lunar.lunarDayCn, + ) + except Exception: + return "农历获取失败" + + +def get_current_time_info() -> tuple: + """ + 获取当前时间信息 + 返回: (当前时间字符串, 今天日期, 今天星期, 农历日期) + """ + current_time = get_current_time() + today_date = get_current_date() + today_weekday = get_current_weekday() + lunar_date = get_current_lunar_date() + + return current_time, today_date, today_weekday, lunar_date diff --git a/main/xiaozhi-server/core/utils/prompt_manager.py b/main/xiaozhi-server/core/utils/prompt_manager.py index fca3a977..b6c4bef0 100644 --- a/main/xiaozhi-server/core/utils/prompt_manager.py +++ b/main/xiaozhi-server/core/utils/prompt_manager.py @@ -117,20 +117,13 @@ class PromptManager: def _get_current_time_info(self) -> tuple: """获取当前时间信息""" - from datetime import datetime + from .current_time import get_current_date, get_current_weekday, get_current_lunar_date + + today_date = get_current_date() + today_weekday = get_current_weekday() + lunar_date = get_current_lunar_date() + "\n" - now = datetime.now() - current_time = now.strftime("%Y年%m月%d日 %H:%M:%S") - today_date = now.strftime("%Y-%m-%d") - today_weekday = WEEKDAY_MAP[now.strftime("%A")] - today_lunar = cnlunar.Lunar(now, godType="8char") - lunar_date = "%s年%s%s\n" % ( - today_lunar.lunarYearCn, - today_lunar.lunarMonthCn[:-1], - today_lunar.lunarDayCn, - ) - - return current_time, today_date, today_weekday, lunar_date + return today_date, today_weekday, lunar_date def _get_location_info(self, client_ip: str) -> str: """获取位置信息""" @@ -199,7 +192,7 @@ class PromptManager: try: # 获取最新的时间信息(不缓存) - current_time, today_date, today_weekday, lunar_date = ( + today_date, today_weekday, lunar_date = ( self._get_current_time_info() ) @@ -224,7 +217,7 @@ class PromptManager: template = Template(self.base_prompt_template) enhanced_prompt = template.render( base_prompt=user_prompt, - current_time=current_time, + current_time="{{current_time}}", today_date=today_date, today_weekday=today_weekday, lunar_date=lunar_date,