feat:修复normal_chat BUG,添加调试选项
This commit is contained in:
@@ -409,6 +409,10 @@ class DefaultExpressor:
|
||||
type = msg_text[0]
|
||||
data = msg_text[1]
|
||||
|
||||
if global_config.experimental.debug_show_chat_mode and type == "text":
|
||||
data += "ᶠ"
|
||||
|
||||
|
||||
part_message_id = f"{thinking_id}_{i}"
|
||||
message_segment = Seg(type=type, data=data)
|
||||
|
||||
|
||||
@@ -13,10 +13,11 @@ from src.chat.heart_flow.mai_state_manager import MaiStateInfo
|
||||
from src.chat.heart_flow.chat_state_info import ChatState, ChatStateInfo
|
||||
from .utils_chat import get_chat_type_and_target_info
|
||||
from src.config.config import global_config
|
||||
|
||||
from rich.traceback import install
|
||||
|
||||
logger = get_logger("sub_heartflow")
|
||||
|
||||
install(extra_lines=3)
|
||||
|
||||
class SubHeartflow:
|
||||
def __init__(
|
||||
@@ -49,7 +50,7 @@ class SubHeartflow:
|
||||
self.chat_target_info: Optional[dict] = None
|
||||
# --- End Initialization ---
|
||||
|
||||
# 兴趣检测器
|
||||
# 兴趣消息集合
|
||||
self.interest_dict: Dict[str, tuple[MessageRecv, float, bool]] = {}
|
||||
|
||||
# 活动状态管理
|
||||
|
||||
@@ -91,9 +91,6 @@ class SubHeartflowManager:
|
||||
if subflow.should_stop:
|
||||
logger.warning(f"尝试获取已停止的子心流 {subheartflow_id},正在重新激活")
|
||||
subflow.should_stop = False # 重置停止标志
|
||||
|
||||
subflow.last_active_time = time.time() # 更新活跃时间
|
||||
# logger.debug(f"获取到已存在的子心流: {subheartflow_id}")
|
||||
return subflow
|
||||
|
||||
try:
|
||||
|
||||
@@ -30,10 +30,8 @@ class NormalChat:
|
||||
def __init__(self, chat_stream: ChatStream, interest_dict: dict = None, on_switch_to_focus_callback=None):
|
||||
"""初始化 NormalChat 实例。只进行同步操作。"""
|
||||
|
||||
# Basic info from chat_stream (sync)
|
||||
self.chat_stream = chat_stream
|
||||
self.stream_id = chat_stream.stream_id
|
||||
# Get initial stream name, might be updated in initialize
|
||||
self.stream_name = chat_manager.get_stream_name(self.stream_id) or self.stream_id
|
||||
|
||||
# Interest dict
|
||||
@@ -46,7 +44,6 @@ class NormalChat:
|
||||
self.gpt = NormalChatGenerator()
|
||||
self.mood_manager = mood_manager
|
||||
self.start_time = time.time()
|
||||
self.last_speak_time = 0
|
||||
self._chat_task: Optional[asyncio.Task] = None
|
||||
self._initialized = False # Track initialization status
|
||||
|
||||
@@ -57,20 +54,14 @@ class NormalChat:
|
||||
# 添加回调函数,用于在满足条件时通知切换到focus_chat模式
|
||||
self.on_switch_to_focus_callback = on_switch_to_focus_callback
|
||||
|
||||
# 最近回复检查相关
|
||||
self._last_check_time = time.time()
|
||||
self._check_interval = 10 # 每10秒检查一次是否需要切换到focus模式
|
||||
|
||||
async def initialize(self):
|
||||
"""异步初始化,获取聊天类型和目标信息。"""
|
||||
if self._initialized:
|
||||
return
|
||||
|
||||
# --- Use utility function to determine chat type and fetch info ---
|
||||
self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.stream_id)
|
||||
# Update stream_name again after potential async call in util func
|
||||
self.stream_name = chat_manager.get_stream_name(self.stream_id) or self.stream_id
|
||||
# --- End using utility function ---
|
||||
self._initialized = True
|
||||
logger.info(f"[{self.stream_name}] NormalChat 实例 initialize 完成 (异步部分)。")
|
||||
|
||||
@@ -123,6 +114,8 @@ class NormalChat:
|
||||
mark_head = False
|
||||
first_bot_msg = None
|
||||
for msg in response_set:
|
||||
if global_config.experimental.debug_show_chat_mode:
|
||||
msg += "ⁿ"
|
||||
message_segment = Seg(type="text", data=msg)
|
||||
bot_message = MessageSending(
|
||||
message_id=thinking_id,
|
||||
@@ -147,8 +140,6 @@ class NormalChat:
|
||||
|
||||
await message_manager.add_message(message_set)
|
||||
|
||||
self.last_speak_time = time.time()
|
||||
|
||||
return first_bot_msg
|
||||
|
||||
# 改为实例方法
|
||||
@@ -208,12 +199,6 @@ class NormalChat:
|
||||
logger.info(f"[{self.stream_name}] 兴趣监控任务被取消或置空,退出")
|
||||
break
|
||||
|
||||
# 定期检查是否需要切换到focus模式
|
||||
# current_time = time.time()
|
||||
# if current_time - self._last_check_time > self._check_interval:
|
||||
# await self._check_switch_to_focus()
|
||||
# self._last_check_time = current_time
|
||||
|
||||
items_to_process = list(self.interest_dict.items())
|
||||
if not items_to_process:
|
||||
continue
|
||||
@@ -378,118 +363,10 @@ class NormalChat:
|
||||
elif not do_reply:
|
||||
# 不回复处理
|
||||
await willing_manager.not_reply_handle(message.message_info.message_id)
|
||||
# else: # do_reply is True but response_set is None (handled above)
|
||||
# logger.info(f"[{self.stream_name}] 决定回复但模型未生成内容。触发: {message.processed_plain_text[:20]}...")
|
||||
|
||||
# 意愿管理器:注销当前message信息 (无论是否回复,只要处理过就删除)
|
||||
willing_manager.delete(message.message_info.message_id)
|
||||
|
||||
# --- 新增:处理初始高兴趣消息的私有方法 ---
|
||||
async def _process_initial_interest_messages(self):
|
||||
"""处理启动时存在于 interest_dict 中的高兴趣消息。"""
|
||||
if not self.interest_dict:
|
||||
return # 如果 interest_dict 为 None 或空,直接返回
|
||||
|
||||
items_to_process = list(self.interest_dict.items())
|
||||
if not items_to_process:
|
||||
return # 没有初始消息,直接返回
|
||||
|
||||
logger.info(f"[{self.stream_name}] 发现 {len(items_to_process)} 条初始兴趣消息,开始处理高兴趣部分...")
|
||||
interest_values = [item[1][1] for item in items_to_process] # 提取兴趣值列表
|
||||
|
||||
messages_to_reply = [] # 需要立即回复的消息
|
||||
|
||||
if len(interest_values) == 1:
|
||||
# 如果只有一个消息,直接处理
|
||||
messages_to_reply.append(items_to_process[0])
|
||||
logger.info(f"[{self.stream_name}] 只有一条初始消息,直接处理。")
|
||||
elif len(interest_values) > 1:
|
||||
# 计算均值和标准差
|
||||
try:
|
||||
mean_interest = statistics.mean(interest_values)
|
||||
stdev_interest = statistics.stdev(interest_values)
|
||||
threshold = mean_interest + stdev_interest
|
||||
logger.info(
|
||||
f"[{self.stream_name}] 初始兴趣值 均值: {mean_interest:.2f}, 标准差: {stdev_interest:.2f}, 阈值: {threshold:.2f}"
|
||||
)
|
||||
|
||||
# 找出高于阈值的消息
|
||||
for item in items_to_process:
|
||||
msg_id, (message, interest_value, is_mentioned) = item
|
||||
if interest_value > threshold:
|
||||
messages_to_reply.append(item)
|
||||
logger.info(f"[{self.stream_name}] 找到 {len(messages_to_reply)} 条高于阈值的初始消息进行处理。")
|
||||
except statistics.StatisticsError as e:
|
||||
logger.error(f"[{self.stream_name}] 计算初始兴趣统计值时出错: {e},跳过初始处理。")
|
||||
|
||||
# 处理需要回复的消息
|
||||
processed_count = 0
|
||||
# --- 修改:迭代前创建要处理的ID列表副本,防止迭代时修改 ---
|
||||
messages_to_process_initially = list(messages_to_reply) # 创建副本
|
||||
# --- 新增:限制最多处理两条消息 ---
|
||||
messages_to_process_initially = messages_to_process_initially[:2]
|
||||
# --- 新增结束 ---
|
||||
for item in messages_to_process_initially: # 使用副本迭代
|
||||
msg_id, (message, interest_value, is_mentioned) = item
|
||||
# --- 修改:在处理前尝试 pop,防止竞争 ---
|
||||
popped_item = self.interest_dict.pop(msg_id, None)
|
||||
if popped_item is None:
|
||||
logger.warning(f"[{self.stream_name}] 初始兴趣消息 {msg_id} 在处理前已被移除,跳过。")
|
||||
continue # 如果消息已被其他任务处理(pop),则跳过
|
||||
# --- 修改结束 ---
|
||||
|
||||
try:
|
||||
logger.info(f"[{self.stream_name}] 处理初始高兴趣消息 {msg_id} (兴趣值: {interest_value:.2f})")
|
||||
await self.normal_response(
|
||||
message=message, is_mentioned=is_mentioned, interested_rate=interest_value, rewind_response=True
|
||||
)
|
||||
processed_count += 1
|
||||
except Exception as e:
|
||||
logger.error(f"[{self.stream_name}] 处理初始兴趣消息 {msg_id} 时出错: {e}\\n{traceback.format_exc()}")
|
||||
|
||||
# --- 新增:处理完后清空整个字典 ---
|
||||
logger.info(
|
||||
f"[{self.stream_name}] 处理了 {processed_count} 条初始高兴趣消息。现在清空所有剩余的初始兴趣消息..."
|
||||
)
|
||||
self.interest_dict.clear()
|
||||
# --- 新增结束 ---
|
||||
|
||||
logger.info(
|
||||
f"[{self.stream_name}] 初始高兴趣消息处理完毕,共处理 {processed_count} 条。剩余 {len(self.interest_dict)} 条待轮询。"
|
||||
)
|
||||
|
||||
# --- 新增结束 ---
|
||||
|
||||
# 保持 staticmethod, 因为不依赖实例状态, 但需要 chat 对象来获取日志上下文
|
||||
@staticmethod
|
||||
def _check_ban_words(text: str, chat: ChatStream, userinfo: UserInfo) -> bool:
|
||||
"""检查消息中是否包含过滤词"""
|
||||
stream_name = chat_manager.get_stream_name(chat.stream_id) or chat.stream_id
|
||||
for word in global_config.chat.ban_words:
|
||||
if word in text:
|
||||
logger.info(
|
||||
f"[{stream_name}][{chat.group_info.group_name if chat.group_info else '私聊'}]"
|
||||
f"{userinfo.user_nickname}:{text}"
|
||||
)
|
||||
logger.info(f"[{stream_name}][过滤词识别] 消息中含有 '{word}',filtered")
|
||||
return True
|
||||
return False
|
||||
|
||||
# 保持 staticmethod, 因为不依赖实例状态, 但需要 chat 对象来获取日志上下文
|
||||
@staticmethod
|
||||
def _check_ban_regex(text: str, chat: ChatStream, userinfo: UserInfo) -> bool:
|
||||
"""检查消息是否匹配过滤正则表达式"""
|
||||
stream_name = chat_manager.get_stream_name(chat.stream_id) or chat.stream_id
|
||||
for pattern in global_config.chat.ban_msgs_regex:
|
||||
if pattern.search(text):
|
||||
logger.info(
|
||||
f"[{stream_name}][{chat.group_info.group_name if chat.group_info else '私聊'}]"
|
||||
f"{userinfo.user_nickname}:{text}"
|
||||
)
|
||||
logger.info(f"[{stream_name}][正则表达式过滤] 消息匹配到 '{pattern.pattern}',filtered")
|
||||
return True
|
||||
return False
|
||||
|
||||
# 改为实例方法, 移除 chat 参数
|
||||
|
||||
async def start_chat(self):
|
||||
@@ -498,10 +375,6 @@ class NormalChat:
|
||||
await self.initialize() # Ensure initialized before starting tasks
|
||||
|
||||
if self._chat_task is None or self._chat_task.done():
|
||||
logger.info(f"[{self.stream_name}] 开始回顾消息...")
|
||||
# Process initial messages first
|
||||
await self._process_initial_interest_messages()
|
||||
# Then start polling task
|
||||
logger.info(f"[{self.stream_name}] 开始处理兴趣消息...")
|
||||
polling_task = asyncio.create_task(self._reply_interested_message())
|
||||
polling_task.add_done_callback(lambda t: self._handle_task_completion(t))
|
||||
|
||||
@@ -8,6 +8,7 @@ from src.chat.utils.utils import process_llm_response
|
||||
from src.chat.utils.timer_calculator import Timer
|
||||
from src.common.logger_manager import get_logger
|
||||
from src.chat.utils.info_catcher import info_catcher_manager
|
||||
from src.person_info.person_info import person_info_manager
|
||||
|
||||
|
||||
logger = get_logger("llm")
|
||||
@@ -63,22 +64,25 @@ class NormalChatGenerator:
|
||||
async def _generate_response_with_model(self, message: MessageThinking, model: LLMRequest, thinking_id: str):
|
||||
info_catcher = info_catcher_manager.get_info_catcher(thinking_id)
|
||||
|
||||
|
||||
person_id = person_info_manager.get_person_id(message.chat_stream.user_info.platform, message.chat_stream.user_info.user_id)
|
||||
|
||||
person_name = await person_info_manager.get_value(person_id, "person_name")
|
||||
|
||||
if message.chat_stream.user_info.user_cardname and message.chat_stream.user_info.user_nickname:
|
||||
sender_name = (
|
||||
f"[({message.chat_stream.user_info.user_id}){message.chat_stream.user_info.user_nickname}]"
|
||||
f"{message.chat_stream.user_info.user_cardname}"
|
||||
f"[{message.chat_stream.user_info.user_nickname}]"
|
||||
f"[群昵称:{message.chat_stream.user_info.user_cardname}](你叫ta{person_name})"
|
||||
)
|
||||
elif message.chat_stream.user_info.user_nickname:
|
||||
sender_name = f"({message.chat_stream.user_info.user_id}){message.chat_stream.user_info.user_nickname}"
|
||||
sender_name = f"[{message.chat_stream.user_info.user_nickname}](你叫ta{person_name})"
|
||||
else:
|
||||
sender_name = f"用户({message.chat_stream.user_info.user_id})"
|
||||
|
||||
|
||||
# 构建prompt
|
||||
with Timer() as t_build_prompt:
|
||||
prompt = await prompt_builder.build_prompt(
|
||||
build_mode="normal",
|
||||
reason="",
|
||||
current_mind_info="",
|
||||
structured_info="",
|
||||
message_txt=message.processed_plain_text,
|
||||
sender_name=sender_name,
|
||||
chat_stream=message.chat_stream,
|
||||
|
||||
@@ -17,14 +17,6 @@ logger = get_logger("prompt")
|
||||
|
||||
|
||||
def init_prompt():
|
||||
# Prompt(
|
||||
# """
|
||||
# 你有以下信息可供参考:
|
||||
# {structured_info}
|
||||
# 以上的消息是你获取到的消息,或许可以帮助你更好地回复。
|
||||
# """,
|
||||
# "info_from_tools",
|
||||
# )
|
||||
|
||||
Prompt("你正在qq群里聊天,下面是群里在聊的内容:", "chat_target_group1")
|
||||
Prompt("你正在和{sender_name}聊天,这是你们之前聊的内容:", "chat_target_private1")
|
||||
@@ -84,15 +76,9 @@ class PromptBuilder:
|
||||
|
||||
async def build_prompt(
|
||||
self,
|
||||
build_mode,
|
||||
chat_stream,
|
||||
reason=None,
|
||||
current_mind_info=None,
|
||||
structured_info=None,
|
||||
message_txt=None,
|
||||
sender_name="某人",
|
||||
in_mind_reply=None,
|
||||
target_message=None,
|
||||
) -> Optional[str]:
|
||||
return await self._build_prompt_normal(chat_stream, message_txt or "", sender_name)
|
||||
|
||||
@@ -188,8 +174,6 @@ class PromptBuilder:
|
||||
prompt_ger += "你喜欢用反问句"
|
||||
if random.random() < 0.02:
|
||||
prompt_ger += "你喜欢用文言文"
|
||||
if random.random() < 0.04:
|
||||
prompt_ger += "你喜欢用流行梗"
|
||||
|
||||
moderation_prompt_block = "请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。"
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ import re
|
||||
from src.common.message_repository import find_messages, count_messages
|
||||
from src.person_info.person_info import person_info_manager
|
||||
from src.chat.utils.utils import translate_timestamp_to_human_readable
|
||||
from rich.traceback import install
|
||||
|
||||
install(extra_lines=3)
|
||||
|
||||
def get_raw_msg_by_timestamp(
|
||||
timestamp_start: float, timestamp_end: float, limit: int = 0, limit_mode: str = "latest"
|
||||
@@ -225,7 +227,7 @@ async def _build_readable_messages_internal(
|
||||
if not reply_person_name:
|
||||
reply_person_name = aaa
|
||||
# 在内容前加上回复信息
|
||||
content = re.sub(reply_pattern, f"回复 {reply_person_name}", content, count=1)
|
||||
content = re.sub(reply_pattern, lambda m, name=reply_person_name: f"回复 {name}", content, count=1)
|
||||
|
||||
# 检查是否有 @<aaa:bbb> 字段 @<{member_info.get('nickname')}:{member_info.get('user_id')}>
|
||||
at_pattern = r"@<([^:<>]+):([^:<>]+)>"
|
||||
|
||||
@@ -341,6 +341,9 @@ class TelemetryConfig(ConfigBase):
|
||||
class ExperimentalConfig(ConfigBase):
|
||||
"""实验功能配置类"""
|
||||
|
||||
debug_show_chat_mode: bool = True
|
||||
"""是否在回复后显示当前聊天模式"""
|
||||
|
||||
enable_friend_chat: bool = False
|
||||
"""是否启用好友聊天"""
|
||||
|
||||
|
||||
@@ -2,6 +2,5 @@
|
||||
|
||||
# 导入所有动作模块以确保装饰器被执行
|
||||
from . import test_action # noqa
|
||||
|
||||
# from . import online_action # noqa
|
||||
from . import mute_action # noqa
|
||||
|
||||
@@ -13,7 +13,7 @@ class MuteAction(PluginAction):
|
||||
action_description = "如果某人违反了公序良俗,或者别人戳你太多,或者某人刷屏,一定要禁言某人,如果你很生气,可以禁言某人,可以自选禁言时长,视严重程度而定。"
|
||||
action_parameters = {
|
||||
"target": "禁言对象,输入你要禁言的对象的名字,必填",
|
||||
"duration": "禁言时长,输入你要禁言的时长,单位为秒,必填,必须为数字",
|
||||
"duration": "禁言时长,输入你要禁言的时长(秒),单位为秒,必填,必须为数字",
|
||||
"reason": "禁言理由,可选",
|
||||
}
|
||||
action_require = [
|
||||
@@ -21,7 +21,6 @@ class MuteAction(PluginAction):
|
||||
"当有人刷屏时使用",
|
||||
"当有人要求禁言自己时使用",
|
||||
"当有人戳你两次以上时,防止刷屏,禁言他,必须牢记",
|
||||
"当千石可乐或可乐酱要求你禁言时使用",
|
||||
"当你想回避某个话题时使用",
|
||||
]
|
||||
default = True # 默认动作,是否手动添加到使用集
|
||||
@@ -47,22 +46,27 @@ class MuteAction(PluginAction):
|
||||
|
||||
if not user_id:
|
||||
error_msg = f"未找到用户 {target} 的ID"
|
||||
await self.send_message_by_expressor(f"压根没 {target} 这个人")
|
||||
logger.error(f"{self.log_prefix} {error_msg}")
|
||||
return False, error_msg
|
||||
|
||||
# 发送表达情绪的消息
|
||||
await self.send_message_by_expressor(f"我要禁言{target},时长{duration}秒,理由:{reason}")
|
||||
await self.send_message_by_expressor(f"禁言{target} {duration}秒,因为{reason}")
|
||||
|
||||
try:
|
||||
# 确保duration是字符串类型
|
||||
duration_str = str(duration)
|
||||
if int(duration) < 60:
|
||||
duration = 60
|
||||
if int(duration) > 3600*24*30:
|
||||
duration = 3600*24*30
|
||||
duration_str = str(int(duration))
|
||||
|
||||
# 发送群聊禁言命令,按照新格式
|
||||
await self.send_message(
|
||||
type="command", data={"name": "GROUP_BAN", "args": {"qq_id": str(user_id), "duration": duration_str}}
|
||||
)
|
||||
|
||||
logger.info(f"{self.log_prefix} 成功禁言用户 {target}({user_id}),时长 {duration} 秒")
|
||||
logger.info(f"{self.log_prefix} 成功发送禁言命令,用户 {target}({user_id}),时长 {duration} 秒")
|
||||
return True, f"成功禁言 {target},时长 {duration} 秒"
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -180,6 +180,7 @@ key_file = "" # SSL密钥文件路径,仅在use_wss=true时有效
|
||||
enable = true
|
||||
|
||||
[experimental] #实验性功能
|
||||
debug_show_chat_mode = true # 是否在回复后显示当前聊天模式
|
||||
enable_friend_chat = false # 是否启用好友聊天
|
||||
pfc_chatting = false # 是否启用PFC聊天,该功能仅作用于私聊,与回复模式独立,在0.7.0暂时无效
|
||||
|
||||
|
||||
Reference in New Issue
Block a user