refactor(chat): 优化自消息处理逻辑和回复目标选择机制

- 添加自消息阻断机制,避免机器人回复自己的消息
- 重构回复目标选择逻辑,优先选择非机器人用户的消息作为回复目标
This commit is contained in:
雅诺狐
2025-09-20 18:08:07 +08:00
parent 43c93a0e04
commit 4ca2dfe65a
2 changed files with 48 additions and 54 deletions

View File

@@ -1,25 +0,0 @@
[inner]
version = "0.2.0" # 版本号
# 请勿修改版本号,除非你知道自己在做什么
[nickname] # 现在没用
nickname = ""
[napcat_server] # Napcat连接的ws服务设置
mode = "reverse" # 连接模式reverse=反向连接(作为服务器), forward=正向连接(作为客户端)
host = "localhost" # 主机地址
port = 8095 # 端口号
url = "" # 正向连接时的完整WebSocket URL如 ws://localhost:8080/ws (仅在forward模式下使用)
access_token = "" # WebSocket 连接的访问令牌,用于身份验证(可选)
heartbeat_interval = 30 # 心跳间隔时间(按秒计)
[maibot_server] # 连接麦麦的ws服务设置
host = "localhost" # 麦麦在.env文件中设置的主机地址即HOST字段
port = 8000 # 麦麦在.env文件中设置的端口即PORT字段
[voice] # 发送语音设置
use_tts = false # 是否使用tts语音请确保你配置了tts并有对应的adapter
[debug]
level = "INFO" # 日志等级DEBUG, INFO, WARNING, ERROR, CRITICAL

View File

@@ -233,6 +233,19 @@ class DefaultReplyer:
self.tool_executor = ToolExecutor(chat_id=self.chat_stream.stream_id) self.tool_executor = ToolExecutor(chat_id=self.chat_stream.stream_id)
def _should_block_self_message(self, reply_message: Optional[Dict[str, Any]]) -> bool:
"""判定是否应阻断当前待处理消息(自消息且无外部触发)"""
try:
bot_id = str(global_config.bot.qq_account)
uid = str(reply_message.get("user_id"))
if uid != bot_id:
return False
return True
except Exception as e:
logger.warning(f"[SelfGuard] 判定异常,回退为不阻断: {e}")
return False
async def generate_reply_with_context( async def generate_reply_with_context(
self, self,
reply_to: str = "", reply_to: str = "",
@@ -260,6 +273,10 @@ class DefaultReplyer:
prompt = None prompt = None
if available_actions is None: if available_actions is None:
available_actions = {} available_actions = {}
# 自消息阻断
if self._should_block_self_message(reply_message):
logger.debug("[SelfGuard] 阻断:自消息且无外部触发。")
return False, None, None
llm_response = None llm_response = None
try: try:
# 构建 Prompt # 构建 Prompt
@@ -822,36 +839,35 @@ class DefaultReplyer:
# 兼容旧的reply_to # 兼容旧的reply_to
sender, target = self._parse_reply_target(reply_to) sender, target = self._parse_reply_target(reply_to)
else: else:
# 获取 platform如果不存在则从 chat_stream 获取,如果还是 None 则使用默认值 # 需求:遍历最近消息,找到第一条 user_id != bot_id 的消息作为目标;找不到则静默退出
if reply_message is None:
logger.warning("reply_message 为 None无法构建prompt")
return ""
platform = reply_message.get("chat_info_platform")
person_id = person_info_manager.get_person_id(
platform, # type: ignore
reply_message.get("user_id"), # type: ignore
)
person_info = await person_info_manager.get_values(person_id, ["person_name", "user_id"])
person_name = person_info.get("person_name")
# 如果person_name为None使用fallback值
if person_name is None:
# 尝试从reply_message获取用户名
fallback_name = reply_message.get("user_nickname") or reply_message.get("user_id", "未知用户")
logger.warning(f"无法获取person_name使用fallback: {fallback_name}")
person_name = str(fallback_name)
# 检查是否是bot自己的名字如果是则替换为"(你)"
bot_user_id = str(global_config.bot.qq_account) bot_user_id = str(global_config.bot.qq_account)
current_user_id = person_info.get("user_id") # 优先使用传入的 reply_message 如果它不是 bot
current_platform = reply_message.get("chat_info_platform") candidate_msg = None
if reply_message and str(reply_message.get("user_id")) != bot_user_id:
if current_user_id == bot_user_id and current_platform == global_config.bot.platform: candidate_msg = reply_message
sender = f"{person_name}(你)"
else: else:
# 如果不是bot自己直接使用person_name try:
recent_msgs = await get_raw_msg_before_timestamp_with_chat(
chat_id=chat_id,
timestamp=time.time(),
limit= max(10, int(global_config.chat.max_context_size * 0.5)),
)
# 从最近到更早遍历找第一条不是bot的
for m in reversed(recent_msgs):
if str(m.get("user_id")) != bot_user_id:
candidate_msg = m
break
except Exception as e:
logger.error(f"获取最近消息失败: {e}")
if not candidate_msg:
logger.debug("未找到可作为目标的非bot消息静默不回复。")
return ""
platform = candidate_msg.get("chat_info_platform") or self.chat_stream.platform
person_id = person_info_manager.get_person_id(platform, candidate_msg.get("user_id"))
person_info = await person_info_manager.get_values(person_id, ["person_name", "user_id"]) if person_id else {}
person_name = person_info.get("person_name") or candidate_msg.get("user_nickname") or candidate_msg.get("user_id") or "未知用户"
sender = person_name sender = person_name
target = reply_message.get("processed_plain_text") target = candidate_msg.get("processed_plain_text") or candidate_msg.get("raw_message") or ""
# 最终的空值检查确保sender和target不为None # 最终的空值检查确保sender和target不为None
if sender is None: if sender is None:
@@ -867,6 +883,8 @@ class DefaultReplyer:
target = replace_user_references_sync(target, chat_stream.platform, replace_bot_name=True) target = replace_user_references_sync(target, chat_stream.platform, replace_bot_name=True)
# (简化)不再对自消息做额外任务段落清理,只通过前置选择逻辑避免自目标
# 构建action描述 (如果启用planner) # 构建action描述 (如果启用planner)
action_descriptions = "" action_descriptions = ""
if available_actions: if available_actions:
@@ -895,7 +913,6 @@ class DefaultReplyer:
read_mark=0.0, read_mark=0.0,
show_actions=True, show_actions=True,
) )
# 获取目标用户信息用于s4u模式 # 获取目标用户信息用于s4u模式
target_user_info = None target_user_info = None
if sender: if sender:
@@ -1068,6 +1085,8 @@ class DefaultReplyer:
prompt = Prompt(template=template_prompt.template, parameters=prompt_parameters) prompt = Prompt(template=template_prompt.template, parameters=prompt_parameters)
prompt_text = await prompt.build() prompt_text = await prompt.build()
# 自目标情况已在上游通过筛选避免,这里不再额外修改 prompt
# --- 动态添加分割指令 --- # --- 动态添加分割指令 ---
if global_config.response_splitter.enable and global_config.response_splitter.split_mode == "llm": if global_config.response_splitter.enable and global_config.response_splitter.split_mode == "llm":
split_instruction = """ split_instruction = """