diff --git a/src/chat/heart_flow/heartflow_message_processor.py b/src/chat/heart_flow/heartflow_message_processor.py index 95b059892..406d0e6d0 100644 --- a/src/chat/heart_flow/heartflow_message_processor.py +++ b/src/chat/heart_flow/heartflow_message_processor.py @@ -12,7 +12,7 @@ from src.chat.message_receive.storage import MessageStorage from src.chat.heart_flow.heartflow import heartflow from src.chat.utils.utils import is_mentioned_bot_in_message from src.chat.utils.timer_calculator import Timer -from src.chat.utils.chat_message_builder import replace_user_references_in_content +from src.chat.utils.chat_message_builder import replace_user_references_sync from src.common.logger import get_logger from src.person_info.relationship_manager import get_relationship_manager from src.mood.mood_manager import mood_manager @@ -151,10 +151,9 @@ class HeartFCMessageReceiver: processed_plain_text = re.sub(picid_pattern, "[图片]", message.processed_plain_text) # 应用用户引用格式替换,将回复和@格式转换为可读格式 - processed_plain_text = replace_user_references_in_content( - processed_plain_text, - message.message_info.platform, - is_async=False, + processed_plain_text = replace_user_references_sync( + processed_plain_text, + message.message_info.platform, # type: ignore replace_bot_name=True ) diff --git a/src/chat/replyer/default_generator.py b/src/chat/replyer/default_generator.py index efefa0934..2e207c609 100644 --- a/src/chat/replyer/default_generator.py +++ b/src/chat/replyer/default_generator.py @@ -17,7 +17,11 @@ from src.chat.message_receive.uni_message_sender import HeartFCSender from src.chat.utils.timer_calculator import Timer # <--- Import Timer from src.chat.utils.utils import get_chat_type_and_target_info from src.chat.utils.prompt_builder import Prompt, global_prompt_manager -from src.chat.utils.chat_message_builder import build_readable_messages, get_raw_msg_before_timestamp_with_chat, replace_user_references_in_content +from src.chat.utils.chat_message_builder import ( + build_readable_messages, + get_raw_msg_before_timestamp_with_chat, + replace_user_references_sync, +) from src.chat.express.expression_selector import expression_selector from src.chat.knowledge.knowledge_lib import qa_manager from src.chat.memory_system.memory_activator import MemoryActivator @@ -30,6 +34,7 @@ from src.plugin_system.base.component_types import ActionInfo logger = get_logger("replyer") + def init_prompt(): Prompt("你正在qq群里聊天,下面是群里在聊的内容:", "chat_target_group1") Prompt("你正在和{sender_name}聊天,这是你们之前聊的内容:", "chat_target_private1") @@ -356,17 +361,20 @@ class DefaultReplyer: expression_habits_block = "" expression_habits_title = "" if style_habits_str.strip(): - expression_habits_title = "你可以参考以下的语言习惯,当情景合适就使用,但不要生硬使用,以合理的方式结合到你的回复中:" + expression_habits_title = ( + "你可以参考以下的语言习惯,当情景合适就使用,但不要生硬使用,以合理的方式结合到你的回复中:" + ) expression_habits_block += f"{style_habits_str}\n" if grammar_habits_str.strip(): - expression_habits_title = "你可以选择下面的句法进行回复,如果情景合适就使用,不要盲目使用,不要生硬使用,以合理的方式使用:" + expression_habits_title = ( + "你可以选择下面的句法进行回复,如果情景合适就使用,不要盲目使用,不要生硬使用,以合理的方式使用:" + ) expression_habits_block += f"{grammar_habits_str}\n" - + if style_habits_str.strip() and grammar_habits_str.strip(): expression_habits_title = "你可以参考以下的语言习惯和句法,如果情景合适就使用,不要盲目使用,不要生硬使用,以合理的方式结合到你的回复中:" - + expression_habits_block = f"{expression_habits_title}\n{expression_habits_block}" - return expression_habits_block @@ -375,27 +383,27 @@ class DefaultReplyer: return "" instant_memory = None - + running_memories = await self.memory_activator.activate_memory_with_chat_history( target_message=target, chat_history_prompt=chat_history ) - + if global_config.memory.enable_instant_memory: asyncio.create_task(self.instant_memory.create_and_store_memory(chat_history)) instant_memory = await self.instant_memory.get_memory(target) logger.info(f"即时记忆:{instant_memory}") - + if not running_memories: return "" memory_str = "以下是当前在聊天中,你回忆起的记忆:\n" for running_memory in running_memories: memory_str += f"- {running_memory['content']}\n" - + if instant_memory: memory_str += f"- {instant_memory}\n" - + return memory_str async def build_tool_info(self, chat_history, reply_data: Optional[Dict], enable_tool: bool = True): @@ -438,7 +446,7 @@ class DefaultReplyer: tool_info_str += "以上是你获取到的实时信息,请在回复时参考这些信息。" logger.info(f"获取到 {len(tool_results)} 个工具结果") - + return tool_info_str else: logger.debug("未获取到任何工具结果") @@ -469,7 +477,7 @@ class DefaultReplyer: # 添加None检查,防止NoneType错误 if target is None: return keywords_reaction_prompt - + # 处理关键词规则 for rule in global_config.keyword_reaction.keyword_rules: if any(keyword in target for keyword in rule.keywords): @@ -621,7 +629,7 @@ class DefaultReplyer: is_group_chat = bool(chat_stream.group_info) reply_to = reply_data.get("reply_to", "none") extra_info_block = reply_data.get("extra_info", "") or reply_data.get("extra_info_block", "") - + if global_config.mood.enable_mood: chat_mood = mood_manager.get_mood_by_chat_id(chat_id) mood_prompt = chat_mood.mood_state @@ -629,14 +637,8 @@ class DefaultReplyer: mood_prompt = "" sender, target = self._parse_reply_target(reply_to) - - target = replace_user_references_in_content( - target, - chat_stream.platform, - is_async=False, - replace_bot_name=True - ) - + + target = replace_user_references_sync(target, chat_stream.platform, replace_bot_name=True) # 构建action描述 (如果启用planner) action_descriptions = "" @@ -687,25 +689,21 @@ class DefaultReplyer: self._time_and_run_task( self.build_expression_habits(chat_talking_prompt_short, target), "expression_habits" ), - self._time_and_run_task( - self.build_relation_info(reply_data), "relation_info" - ), + self._time_and_run_task(self.build_relation_info(reply_data), "relation_info"), self._time_and_run_task(self.build_memory_block(chat_talking_prompt_short, target), "memory_block"), self._time_and_run_task( self.build_tool_info(chat_talking_prompt_short, reply_data, enable_tool=enable_tool), "tool_info" ), - self._time_and_run_task( - get_prompt_info(target, threshold=0.38), "prompt_info" - ), + self._time_and_run_task(get_prompt_info(target, threshold=0.38), "prompt_info"), ) # 任务名称中英文映射 task_name_mapping = { "expression_habits": "选取表达方式", - "relation_info": "感受关系", + "relation_info": "感受关系", "memory_block": "回忆", "tool_info": "使用工具", - "prompt_info": "获取知识" + "prompt_info": "获取知识", } # 处理结果 @@ -798,7 +796,7 @@ class DefaultReplyer: core_dialogue_prompt, background_dialogue_prompt = self.build_s4u_chat_history_prompts( message_list_before_now_long, target_user_id ) - + self.build_mai_think_context( chat_id=chat_id, memory_block=memory_block, @@ -815,9 +813,8 @@ class DefaultReplyer: -------------------------------- {time_block} 这是你和{sender}的对话,你们正在交流中: -{core_dialogue_prompt}""" +{core_dialogue_prompt}""", ) - # 使用 s4u 风格的模板 template_name = "s4u_style_prompt" @@ -855,9 +852,9 @@ class DefaultReplyer: identity_block=identity_block, sender=sender, target=target, - chat_info=chat_talking_prompt + chat_info=chat_talking_prompt, ) - + # 使用原有的模式 return await global_prompt_manager.format_prompt( template_name, @@ -1079,9 +1076,11 @@ async def get_prompt_info(message: str, threshold: float): related_info += found_knowledge_from_lpmm logger.debug(f"获取知识库内容耗时: {(end_time - start_time):.3f}秒") logger.debug(f"获取知识库内容,相关信息:{related_info[:100]}...,信息长度: {len(related_info)}") - + # 格式化知识信息 - formatted_prompt_info = await global_prompt_manager.format_prompt("knowledge_prompt", prompt_info=related_info) + formatted_prompt_info = await global_prompt_manager.format_prompt( + "knowledge_prompt", prompt_info=related_info + ) return formatted_prompt_info else: logger.debug("从LPMM知识库获取知识失败,可能是从未导入过知识,返回空知识...") diff --git a/src/chat/utils/chat_message_builder.py b/src/chat/utils/chat_message_builder.py index 22f56d1df..a4edf33d3 100644 --- a/src/chat/utils/chat_message_builder.py +++ b/src/chat/utils/chat_message_builder.py @@ -2,7 +2,7 @@ import time # 导入 time 模块以获取当前时间 import random import re -from typing import List, Dict, Any, Tuple, Optional, Union, Callable +from typing import List, Dict, Any, Tuple, Optional, Callable from rich.traceback import install from src.config.config import global_config @@ -10,61 +10,48 @@ from src.common.message_repository import find_messages, count_messages from src.common.database.database_model import ActionRecords from src.common.database.database_model import Images from src.person_info.person_info import PersonInfoManager, get_person_info_manager -from src.chat.utils.utils import translate_timestamp_to_human_readable,assign_message_ids +from src.chat.utils.utils import translate_timestamp_to_human_readable, assign_message_ids install(extra_lines=3) -def replace_user_references_in_content( +def replace_user_references_sync( content: str, platform: str, - name_resolver: Union[Callable[[str, str], str], Callable[[str, str], Any]] = None, - is_async: bool = False, - replace_bot_name: bool = True -) -> Union[str, Any]: + name_resolver: Optional[Callable[[str, str], str]] = None, + replace_bot_name: bool = True, +) -> str: """ 替换内容中的用户引用格式,包括回复和@格式 - + Args: content: 要处理的内容字符串 platform: 平台标识 name_resolver: 名称解析函数,接收(platform, user_id)参数,返回用户名称 - 如果为None,则使用默认的person_info_manager - is_async: 是否为异步模式 + 如果为None,则使用默认的person_info_manager replace_bot_name: 是否将机器人的user_id替换为"机器人昵称(你)" - + Returns: - 处理后的内容字符串(同步模式)或awaitable对象(异步模式) + str: 处理后的内容字符串 """ - if is_async: - return _replace_user_references_async(content, platform, name_resolver, replace_bot_name) - else: - return _replace_user_references_sync(content, platform, name_resolver, replace_bot_name) - - -def _replace_user_references_sync( - content: str, - platform: str, - name_resolver: Optional[Callable[[str, str], str]] = None, - replace_bot_name: bool = True -) -> str: - """同步版本的用户引用替换""" if name_resolver is None: person_info_manager = get_person_info_manager() + def default_resolver(platform: str, user_id: str) -> str: # 检查是否是机器人自己 if replace_bot_name and user_id == global_config.bot.qq_account: return f"{global_config.bot.nickname}(你)" person_id = PersonInfoManager.get_person_id(platform, user_id) - return person_info_manager.get_value_sync(person_id, "person_name") or user_id + return person_info_manager.get_value_sync(person_id, "person_name") or user_id # type: ignore + name_resolver = default_resolver - + # 处理回复格式 reply_pattern = r"回复<([^:<>]+):([^:<>]+)>" match = re.search(reply_pattern, content) if match: - aaa = match.group(1) - bbb = match.group(2) + aaa = match[1] + bbb = match[2] try: # 检查是否是机器人自己 if replace_bot_name and bbb == global_config.bot.qq_account: @@ -75,7 +62,7 @@ def _replace_user_references_sync( except Exception: # 如果解析失败,使用原始昵称 content = re.sub(reply_pattern, f"回复 {aaa}", content, count=1) - + # 处理@格式 at_pattern = r"@<([^:<>]+):([^:<>]+)>" at_matches = list(re.finditer(at_pattern, content)) @@ -83,7 +70,7 @@ def _replace_user_references_sync( new_content = "" last_end = 0 for m in at_matches: - new_content += content[last_end:m.start()] + new_content += content[last_end : m.start()] aaa = m.group(1) bbb = m.group(2) try: @@ -99,27 +86,41 @@ def _replace_user_references_sync( last_end = m.end() new_content += content[last_end:] content = new_content - + return content -async def _replace_user_references_async( +async def replace_user_references_async( content: str, platform: str, name_resolver: Optional[Callable[[str, str], Any]] = None, - replace_bot_name: bool = True + replace_bot_name: bool = True, ) -> str: - """异步版本的用户引用替换""" + """ + 替换内容中的用户引用格式,包括回复和@格式 + + Args: + content: 要处理的内容字符串 + platform: 平台标识 + name_resolver: 名称解析函数,接收(platform, user_id)参数,返回用户名称 + 如果为None,则使用默认的person_info_manager + replace_bot_name: 是否将机器人的user_id替换为"机器人昵称(你)" + + Returns: + str: 处理后的内容字符串 + """ if name_resolver is None: person_info_manager = get_person_info_manager() + async def default_resolver(platform: str, user_id: str) -> str: # 检查是否是机器人自己 if replace_bot_name and user_id == global_config.bot.qq_account: return f"{global_config.bot.nickname}(你)" person_id = PersonInfoManager.get_person_id(platform, user_id) - return await person_info_manager.get_value(person_id, "person_name") or user_id + return await person_info_manager.get_value(person_id, "person_name") or user_id # type: ignore + name_resolver = default_resolver - + # 处理回复格式 reply_pattern = r"回复<([^:<>]+):([^:<>]+)>" match = re.search(reply_pattern, content) @@ -136,7 +137,7 @@ async def _replace_user_references_async( except Exception: # 如果解析失败,使用原始昵称 content = re.sub(reply_pattern, f"回复 {aaa}", content, count=1) - + # 处理@格式 at_pattern = r"@<([^:<>]+):([^:<>]+)>" at_matches = list(re.finditer(at_pattern, content)) @@ -144,7 +145,7 @@ async def _replace_user_references_async( new_content = "" last_end = 0 for m in at_matches: - new_content += content[last_end:m.start()] + new_content += content[last_end : m.start()] aaa = m.group(1) bbb = m.group(2) try: @@ -160,7 +161,7 @@ async def _replace_user_references_async( last_end = m.end() new_content += content[last_end:] content = new_content - + return content @@ -524,7 +525,7 @@ def _build_readable_messages_internal( person_name = "某人" # 使用独立函数处理用户引用格式 - content = replace_user_references_in_content(content, platform, is_async=False, replace_bot_name=replace_bot_name) + content = replace_user_references_sync(content, platform, replace_bot_name=replace_bot_name) target_str = "这是QQ的一个功能,用于提及某人,但没那么明显" if target_str in content and random.random() < 0.6: @@ -778,6 +779,7 @@ async def build_readable_messages_with_list( return formatted_string, details_list + def build_readable_messages_with_id( messages: List[Dict[str, Any]], replace_bot_name: bool = True, @@ -793,9 +795,9 @@ def build_readable_messages_with_id( 允许通过参数控制格式化行为。 """ message_id_list = assign_message_ids(messages) - + formatted_string = build_readable_messages( - messages = messages, + messages=messages, replace_bot_name=replace_bot_name, merge_messages=merge_messages, timestamp_mode=timestamp_mode, @@ -806,10 +808,7 @@ def build_readable_messages_with_id( message_id_list=message_id_list, ) - - - - return formatted_string , message_id_list + return formatted_string, message_id_list def build_readable_messages( @@ -894,7 +893,13 @@ def build_readable_messages( if read_mark <= 0: # 没有有效的 read_mark,直接格式化所有消息 formatted_string, _, pic_id_mapping, _ = _build_readable_messages_internal( - copy_messages, replace_bot_name, merge_messages, timestamp_mode, truncate, show_pic=show_pic, message_id_list=message_id_list + copy_messages, + replace_bot_name, + merge_messages, + timestamp_mode, + truncate, + show_pic=show_pic, + message_id_list=message_id_list, ) # 生成图片映射信息并添加到最前面 @@ -1017,7 +1022,7 @@ async def build_anonymous_messages(messages: List[Dict[str, Any]]) -> str: for msg in messages: try: - platform = msg.get("chat_info_platform") + platform: str = msg.get("chat_info_platform") # type: ignore user_id = msg.get("user_id") _timestamp = msg.get("time") content: str = "" @@ -1046,8 +1051,8 @@ async def build_anonymous_messages(messages: List[Dict[str, Any]]) -> str: return get_anon_name(platform, user_id) except Exception: return "?" - - content = replace_user_references_in_content(content, platform, anon_name_resolver, is_async=False, replace_bot_name=False) + + content = replace_user_references_sync(content, platform, anon_name_resolver, replace_bot_name=False) header = f"{anon_name}说 " output_lines.append(header) diff --git a/src/plugin_system/apis/send_api.py b/src/plugin_system/apis/send_api.py index f7b3092ef..f7af02591 100644 --- a/src/plugin_system/apis/send_api.py +++ b/src/plugin_system/apis/send_api.py @@ -19,11 +19,9 @@ await send_api.custom_message("video", video_data, "123456", True) """ -import asyncio import traceback import time import difflib -import re from typing import Optional, Union from src.common.logger import get_logger @@ -31,7 +29,7 @@ from src.common.logger import get_logger from src.chat.message_receive.chat_stream import get_chat_manager from src.chat.message_receive.uni_message_sender import HeartFCSender from src.chat.message_receive.message import MessageSending, MessageRecv -from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat, replace_user_references_in_content +from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat, replace_user_references_async from src.person_info.person_info import get_person_info_manager from maim_message import Seg, UserInfo from src.config.config import global_config @@ -185,7 +183,7 @@ async def _find_reply_message(target_stream, reply_to: str) -> Optional[MessageR translate_text = message["processed_plain_text"] # 使用独立函数处理用户引用格式 - translate_text = await replace_user_references_in_content(translate_text, platform, is_async=True) + translate_text = await replace_user_references_async(translate_text, platform) similarity = difflib.SequenceMatcher(None, text, translate_text).ratio() if similarity >= 0.9: