fix:修改版本和小问题

This commit is contained in:
SengokuCola
2025-05-09 21:00:52 +08:00
parent af2596d431
commit 35b8d8ad0c
6 changed files with 130 additions and 269 deletions

View File

@@ -1,145 +0,0 @@
{
"测试时间": "2025-04-28 14:12:36",
"测试迭代次数": 10,
"不使用工具调用": {
"平均耗时": 4.596814393997192,
"最短耗时": 2.957131862640381,
"最长耗时": 10.121938705444336,
"标准差": 2.1705468730949593,
"所有耗时": [
3.18,
4.65,
10.12,
3.5,
4.46,
4.24,
3.23,
6.2,
2.96,
3.42
]
},
"不使用工具调用_详细响应": [
{
"内容摘要": "```json\n{\n \"action\": \"text_reply\",\n \"content\": \"怎么啦?\",\n \"emoji_query\": \"友好地询问\"\n}\n```",
"推理内容摘要": ""
},
{
"内容摘要": "decide_reply_action(\n action=\"text_reply\",\n reasoning=\"千石连续两次叫了我的名字,显然是想引起我的注意或有事要说,作为礼貌应当回应\",\n emoji_query=\"友善的回应\"\n)",
"推理内容摘要": ""
},
{
"内容摘要": "```json\n{\n \"action\": \"text_reply\",\n \"content\": \"怎么啦?我在呢~\",\n \"emoji_query\": \"友好的询问\",\n \"reasoning\": \"由于对方连续两次提到我的名字,显然是想与我交流,应当及时给予友好回应避免冷场\"\n}\n```",
"推理内容摘要": ""
},
{
"内容摘要": "```json\n{\n \"action\": \"text_reply\",\n \"content\": \"在呢在呢~怎么啦?\",\n \"emoji_query\": \"好奇的回应\"\n}\n```",
"推理内容摘要": ""
},
{
"内容摘要": "decide_reply_action(\n action=\"text_reply\",\n reasoning=\"千石连续两次提到我的名字,显然需要我回应。文字回复更正式且能明确表示我在关注他的信息。\",\n emoji_query=\"友好的回应\"\n)",
"推理内容摘要": ""
},
{
"内容摘要": "```json\n{\n \"action\": \"text_reply\",\n \"reasoning\": \"千石连续两次提到我的名字,显然是需要我的回应。作为日常交流,应该给予友善简短的答复。\",\n \"emoji_query\": \"疑惑的歪头\"\n}\n```",
"推理内容摘要": ""
},
{
"内容摘要": "```json\n{\n \"action\": \"text_reply\",\n \"content\": \"咋啦千石~\",\n \"emoji_query\": \"好奇的询问\"\n}\n```",
"推理内容摘要": ""
},
{
"内容摘要": "decide_reply_action\n```json\n{\n \"action\": \"text_reply\",\n \"content\": \"我在呢~怎么啦?\",\n \"emoji_query\": \"友好的关心\",\n \"reasoning\": \"千石连续两次呼唤我的名字显然是有事情要找我或想引起我的注意。根据回复原则2有人提到你但未回应应该用友善的文字进行回应并附上表达关心的表情符号来延...",
"推理内容摘要": ""
},
{
"内容摘要": "```json\n{\n \"action\": \"text_reply\",\n \"content\": \"千石怎么啦~\",\n \"emoji_query\": \"好奇的探询\"\n}\n```",
"推理内容摘要": ""
},
{
"内容摘要": "```json\n{\n \"action\": \"text_reply\",\n \"content\": \"怎么啦?突然叫我两次\",\n \"emoji_query\": \"好奇的疑问\"\n}\n```",
"推理内容摘要": ""
}
],
"使用工具调用": {
"平均耗时": 8.139546775817871,
"最短耗时": 4.9980738162994385,
"最长耗时": 18.803313732147217,
"标准差": 4.008772720760647,
"所有耗时": [
5.81,
18.8,
6.06,
8.06,
10.07,
6.34,
7.9,
6.66,
5.0,
6.69
]
},
"使用工具调用_详细响应": [
{
"内容摘要": "",
"推理内容摘要": "",
"工具调用数量": 0,
"工具调用详情": []
},
{
"内容摘要": "",
"推理内容摘要": "",
"工具调用数量": 0,
"工具调用详情": []
},
{
"内容摘要": "",
"推理内容摘要": "",
"工具调用数量": 0,
"工具调用详情": []
},
{
"内容摘要": "",
"推理内容摘要": "",
"工具调用数量": 0,
"工具调用详情": []
},
{
"内容摘要": "",
"推理内容摘要": "",
"工具调用数量": 0,
"工具调用详情": []
},
{
"内容摘要": "",
"推理内容摘要": "",
"工具调用数量": 0,
"工具调用详情": []
},
{
"内容摘要": "",
"推理内容摘要": "",
"工具调用数量": 0,
"工具调用详情": []
},
{
"内容摘要": "",
"推理内容摘要": "",
"工具调用数量": 0,
"工具调用详情": []
},
{
"内容摘要": "",
"推理内容摘要": "",
"工具调用数量": 0,
"工具调用详情": []
},
{
"内容摘要": "",
"推理内容摘要": "",
"工具调用数量": 0,
"工具调用详情": []
}
],
"差异百分比": 77.07
}

View File

@@ -25,7 +25,7 @@ logger = get_logger("config")
# 考虑到实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码
is_test = False
mai_version_main = "0.6.3"
mai_version_fix = "fix-2"
mai_version_fix = "fix-3"
if mai_version_fix:
if is_test:

View File

@@ -110,7 +110,7 @@ class SubHeartflow:
logger.error(f"{self.log_prefix} 停止 NormalChat 监控任务时出错: {e}")
logger.error(traceback.format_exc())
async def _start_normal_chat(self) -> bool:
async def _start_normal_chat(self, rewind = False) -> bool:
"""
启动 NormalChat 实例,并进行异步初始化。
进入 CHAT 状态时使用。
@@ -125,8 +125,11 @@ class SubHeartflow:
if not chat_stream:
logger.error(f"{log_prefix} 无法获取 chat_stream无法启动 NormalChat。")
return False
if rewind:
self.normal_chat_instance = NormalChat(chat_stream=chat_stream, interest_dict=self.get_interest_dict())
else:
self.normal_chat_instance = NormalChat(chat_stream=chat_stream, interest_dict=self.get_interest_dict())
self.normal_chat_instance = NormalChat(chat_stream=chat_stream)
# 进行异步初始化
await self.normal_chat_instance.initialize()
@@ -218,13 +221,22 @@ class SubHeartflow:
if new_state == ChatState.CHAT:
# 移除限额检查逻辑
logger.debug(f"{log_prefix} 准备进入或保持 聊天 状态")
if await self._start_normal_chat():
# logger.info(f"{log_prefix} 成功进入或保持 NormalChat 状态。")
state_changed = True
if current_state == ChatState.FOCUSED:
if await self._start_normal_chat(rewind=False):
# logger.info(f"{log_prefix} 成功进入或保持 NormalChat 状态。")
state_changed = True
else:
logger.error(f"{log_prefix} 从FOCUSED状态启动 NormalChat 失败,无法进入 CHAT 状态。")
# 考虑是否需要回滚状态或采取其他措施
return # 启动失败,不改变状态
else:
logger.error(f"{log_prefix} 启动 NormalChat 失败,无法进入 CHAT 状态。")
# 考虑是否需要回滚状态或采取其他措施
return # 启动失败,不改变状态
if await self._start_normal_chat(rewind=True):
# logger.info(f"{log_prefix} 成功进入或保持 NormalChat 状态。")
state_changed = True
else:
logger.error(f"{log_prefix} 从ABSENT状态启动 NormalChat 失败,无法进入 CHAT 状态。")
# 考虑是否需要回滚状态或采取其他措施
return # 启动失败,不改变状态
elif new_state == ChatState.FOCUSED:
# 移除限额检查逻辑
@@ -239,6 +251,8 @@ class SubHeartflow:
elif new_state == ChatState.ABSENT:
logger.info(f"{log_prefix} 进入 ABSENT 状态,停止所有聊天活动...")
await self.clear_interest_dict()
await self._stop_normal_chat()
await self._stop_heart_fc_chat()
state_changed = True # 总是可以成功转换到 ABSENT

View File

@@ -14,6 +14,8 @@ from src.plugins.chat.chat_stream import chat_manager
from src.plugins.heartFC_chat.heartFC_Cycleinfo import CycleInfo
import difflib
from src.plugins.person_info.relationship_manager import relationship_manager
from src.plugins.memory_system.Hippocampus import HippocampusManager
import jieba
logger = get_logger("sub_heartflow")
@@ -223,7 +225,56 @@ class SubMind:
chat_observe_info = observation.get_observe_info()
person_list = observation.person_list
# ---------- 2. 准备工具和个性化数据 ----------
# ---------- 2. 获取记忆 ----------
try:
# 从聊天内容中提取关键词
chat_words = set(jieba.cut(chat_observe_info))
# 过滤掉停用词和单字词
keywords = [word for word in chat_words if len(word) > 1]
# 去重并限制数量
keywords = list(set(keywords))[:5]
logger.debug(f"{self.log_prefix} 提取的关键词: {keywords}")
# 检查已有记忆,过滤掉已存在的主题
existing_topics = set()
for item in self.structured_info:
if item["type"] == "memory":
existing_topics.add(item["id"])
# 过滤掉已存在的主题
filtered_keywords = [k for k in keywords if k not in existing_topics]
if not filtered_keywords:
logger.debug(f"{self.log_prefix} 所有关键词对应的记忆都已存在,跳过记忆提取")
else:
# 调用记忆系统获取相关记忆
related_memory = await HippocampusManager.get_instance().get_memory_from_topic(
valid_keywords=filtered_keywords,
max_memory_num=3,
max_memory_length=2,
max_depth=3
)
logger.debug(f"{self.log_prefix} 获取到的记忆: {related_memory}")
if related_memory:
for topic, memory in related_memory:
new_item = {
"type": "memory",
"id": topic,
"content": memory,
"ttl": 3
}
self.structured_info.append(new_item)
logger.debug(f"{self.log_prefix} 添加新记忆: {topic} - {memory}")
else:
logger.debug(f"{self.log_prefix} 没有找到相关记忆")
except Exception as e:
logger.error(f"{self.log_prefix} 获取记忆时出错: {e}")
logger.error(traceback.format_exc())
# ---------- 3. 准备工具和个性化数据 ----------
# 初始化工具
tool_instance = ToolUser()
tools = tool_instance._define_tools()
@@ -244,7 +295,7 @@ class SubMind:
# 获取当前时间
time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
# ---------- 3. 构建思考指导部分 ----------
# ---------- 4. 构建思考指导部分 ----------
# 创建本地随机数生成器,基于分钟数作为种子
local_random = random.Random()
current_minute = int(time.strftime("%M"))
@@ -328,7 +379,7 @@ class SubMind:
[option[0] for option in hf_options], weights=[option[1] for option in hf_options], k=1
)[0]
# ---------- 4. 构建最终提示词 ----------
# ---------- 5. 构建最终提示词 ----------
# --- Choose template based on chat type ---
logger.debug(f"is_group_chat: {is_group_chat}")
if is_group_chat:
@@ -363,7 +414,7 @@ class SubMind:
)
# --- End choosing template ---
# ---------- 5. 执行LLM请求并处理响应 ----------
# ---------- 6. 执行LLM请求并处理响应 ----------
content = "" # 初始化内容变量
_reasoning_content = "" # 初始化推理内容变量
@@ -412,7 +463,7 @@ class SubMind:
content = "(不知道该想些什么...)"
logger.warning(f"{self.log_prefix} LLM返回空结果思考失败。")
# ---------- 6. 应用概率性去重和修饰 ----------
# ---------- 7. 应用概率性去重和修饰 ----------
new_content = content # 保存 LLM 直接输出的结果
try:
similarity = calculate_similarity(previous_mind, new_content)
@@ -485,7 +536,7 @@ class SubMind:
# 出错时保留原始 content
content = new_content
# ---------- 7. 更新思考状态并返回结果 ----------
# ---------- 8. 更新思考状态并返回结果 ----------
logger.info(f"{self.log_prefix} 最终心流思考结果: {content}")
# 更新当前思考内容
self.update_current_mind(content)

View File

@@ -274,19 +274,17 @@ class SubHeartflowManager:
async def sbhf_absent_into_focus(self):
"""评估子心流兴趣度满足条件且未达上限则提升到FOCUSED状态基于start_hfc_probability"""
try:
log_prefix = "[兴趣评估]"
# 使用 self.mai_state_info 获取当前状态和限制
current_state = self.mai_state_info.get_current_state()
focused_limit = current_state.get_focused_chat_max_num()
# --- 新增:检查是否允许进入 FOCUS 模式 --- #
if not global_config.allow_focus_mode:
if int(time.time()) % 60 == 0: # 每60秒输出一次日志避免刷屏
logger.debug(f"{log_prefix} 配置不允许进入 FOCUSED 状态 (allow_focus_mode=False)")
logger.trace(f"未开启 FOCUSED 状态 (allow_focus_mode=False)")
return # 如果不允许,直接返回
# --- 结束新增 ---
logger.debug(f"{log_prefix} 当前状态 ({current_state.value}) 可以在{focused_limit}个群激情聊天")
logger.info(f"当前状态 ({current_state.value}) 可以在{focused_limit}个群 专注聊天")
if focused_limit <= 0:
# logger.debug(f"{log_prefix} 当前状态 ({current_state.value}) 不允许 FOCUSED 子心流")
@@ -294,35 +292,36 @@ class SubHeartflowManager:
current_focused_count = self.count_subflows_by_state(ChatState.FOCUSED)
if current_focused_count >= focused_limit:
logger.debug(f"{log_prefix} 已达专注上限 ({current_focused_count}/{focused_limit})")
logger.debug(f"已达专注上限 ({current_focused_count}/{focused_limit})")
return
for sub_hf in list(self.subheartflows.values()):
flow_id = sub_hf.subheartflow_id
stream_name = chat_manager.get_stream_name(flow_id) or flow_id
logger.debug(f"{log_prefix} 检查子心流: {stream_name},现在状态: {sub_hf.chat_state.chat_status.value}")
# 跳过非CHAT状态或已经是FOCUSED状态的子心流
if sub_hf.chat_state.chat_status == ChatState.FOCUSED:
continue
from .mai_state_manager import enable_unlimited_hfc_chat
if sub_hf.interest_chatting.start_hfc_probability == 0:
continue
else:
logger.debug(f"{stream_name},现在状态: {sub_hf.chat_state.chat_status.value},进入专注概率: {sub_hf.interest_chatting.start_hfc_probability}")
# 调试用
from .mai_state_manager import enable_unlimited_hfc_chat
if not enable_unlimited_hfc_chat:
if sub_hf.chat_state.chat_status != ChatState.CHAT:
continue
# 检查是否满足提升概率
logger.debug(
f"{log_prefix} 检查子心流: {stream_name},现在概率: {sub_hf.interest_chatting.start_hfc_probability}"
)
if random.random() >= sub_hf.interest_chatting.start_hfc_probability:
continue
# 再次检查是否达到上限
if current_focused_count >= focused_limit:
logger.debug(f"{log_prefix} [{stream_name}] 已达专注上限")
logger.debug(f"{stream_name} 已达专注上限")
break
# 获取最新状态并执行提升
@@ -331,7 +330,7 @@ class SubHeartflowManager:
continue
logger.info(
f"{log_prefix} [{stream_name}] 触发 认真水群 (概率={current_subflow.interest_chatting.start_hfc_probability:.2f})"
f"{stream_name} 触发 认真水群 (概率={current_subflow.interest_chatting.start_hfc_probability:.2f})"
)
# 执行状态提升
@@ -372,12 +371,6 @@ class SubHeartflowManager:
stream_name = chat_manager.get_stream_name(flow_id) or flow_id
log_prefix = f"[{stream_name}]"
# --- Private chat check (redundant due to filter above, but safe) ---
# if not sub_hf_to_evaluate.is_group_chat:
# logger.debug(f"{log_prefix} 是私聊,跳过 CHAT 状态评估。")
# return
# --- End check ---
# 3. 检查 CHAT 上限
current_chat_count = self.count_subflows_by_state_nolock(ChatState.CHAT)
if current_chat_count >= chat_limit:
@@ -403,9 +396,9 @@ class SubHeartflowManager:
first_observation = sub_hf_to_evaluate.observations[0] # 喵~第一个观察者肯定存在的说
await first_observation.observe()
current_chat_log = first_observation.talking_message_str or "当前没啥聊天内容。"
_observation_summary = f"最近聊了这些:\n{current_chat_log}"
_observation_summary = f"在[{stream_name}]这个群中,你最近看群友聊了这些:\n{current_chat_log}"
mai_state_description = f"你当前状态: {current_mai_state.value}"
_mai_state_description = f"你当前状态: {current_mai_state.value}"
individuality = Individuality.get_instance()
personality_prompt = individuality.get_prompt(x_person=2, level=2)
prompt_personality = f"你正在扮演名为{individuality.name}的人类,{personality_prompt}"
@@ -414,27 +407,26 @@ class SubHeartflowManager:
chat_status_lines = []
if chatting_group_names:
chat_status_lines.append(
f"正在闲聊 ({current_chat_count}/{chat_limit}): {', '.join(chatting_group_names)}"
f"正在这些群闲聊 ({current_chat_count}/{chat_limit}): {', '.join(chatting_group_names)}"
)
if focused_group_names:
chat_status_lines.append(
f"正在专注 ({current_focused_count}/{focused_limit}): {', '.join(focused_group_names)}"
f"正在这些群专注的聊天 ({current_focused_count}/{focused_limit}): {', '.join(focused_group_names)}"
)
chat_status_prompt = "当前没有在任何群聊中。" # 默认消息喵~
if chat_status_lines:
chat_status_prompt = "当前聊天情况:\n" + "\n".join(chat_status_lines) # 拼接状态信息
chat_status_prompt = "当前聊天情况,你已经参与了下面这几个群的聊天\n" + "\n".join(chat_status_lines) # 拼接状态信息
prompt = (
f"{prompt_personality}\\n"
f"你当前没在 [{stream_name}] 群聊天。\\n"
f"{mai_state_description}\\n"
f"{chat_status_prompt}\\n" # <-- 喵!用了新的状态信息~
f"{_observation_summary}\\n---\\n"
f"基于以上信息,你想不想开始在这个群闲聊?\\n"
f"请说明理由,并以 JSON 格式回答,包含 'decision' (布尔值) 和 'reason' (字符串)。\\n"
f'例如:{{"decision": true, "reason": "看起来挺热闹的,插个话"}}\\n'
f'例如:{{"decision": false, "reason": "已经聊了好多,休息一下"}}\\n'
f"{prompt_personality}\n"
f"{chat_status_prompt}\n" # <-- 喵!用了新的状态信息~
f"你当前尚未加入 [{stream_name}] 群聊天。\n"
f"{_observation_summary}\n---\n"
f"基于以上信息,你想不想开始在这个群闲聊?\n"
f"请说明理由,并以 JSON 格式回答,包含 'decision' (布尔值) 和 'reason' (字符串)。\n"
f'例如:{{"decision": true, "reason": "看起来挺热闹的,插个话"}}\n'
f'例如:{{"decision": false, "reason": "已经聊了好多,休息一下"}}\n'
f"请只输出有效的 JSON 对象。"
)
# --- 结束修改 ---
@@ -493,7 +485,6 @@ class SubHeartflowManager:
checked_count = len(subflows_snapshot)
if not subflows_snapshot:
# logger.debug(f"{log_prefix_task} 没有子心流需要检查超时。")
return
for sub_hf in subflows_snapshot:
@@ -509,25 +500,33 @@ class SubHeartflowManager:
reason = ""
try:
# 使用变量名 last_bot_dong_zuo_time 替代 last_bot_activity_time
last_bot_dong_zuo_time = sub_hf.get_normal_chat_last_speak_time()
if last_bot_dong_zuo_time > 0:
current_time = time.time()
# 使用变量名 time_since_last_bb 替代 time_since_last_reply
time_since_last_bb = current_time - last_bot_dong_zuo_time
minutes_since_last_bb = time_since_last_bb / 60
if time_since_last_bb > NORMAL_CHAT_TIMEOUT_SECONDS:
# 60分钟强制退出
if minutes_since_last_bb >= 60:
should_deactivate = True
reason = f"超过 {NORMAL_CHAT_TIMEOUT_SECONDS / 60:.0f} 分钟没 BB"
logger.info(
f"{log_prefix} 太久没有发言 ({reason}),不看了。上次活动时间: {last_bot_dong_zuo_time:.0f}"
)
# else:
# logger.debug(f"{log_prefix} Bot活动时间未超时 ({time_since_last_bb:.0f}s < {NORMAL_CHAT_TIMEOUT_SECONDS}s),保持 CHAT 状态。")
# else:
# 如果没有记录到Bot的活动时间暂时不因为超时而转换状态
# logger.debug(f"{log_prefix} 未找到有效的 Bot 最后活动时间记录,不执行超时检查。")
reason = "超过60分钟未发言强制退出"
else:
# 根据时间区间确定退出概率
exit_probability = 0
if minutes_since_last_bb < 5:
exit_probability = 0.01 # 1%
elif minutes_since_last_bb < 15:
exit_probability = 0.02 # 2%
elif minutes_since_last_bb < 30:
exit_probability = 0.04 # 4%
else:
exit_probability = 0.08 # 8%
# 随机判断是否退出
if random.random() < exit_probability:
should_deactivate = True
reason = f"{minutes_since_last_bb:.1f}分钟未发言,触发{exit_probability*100:.0f}%退出概率"
except AttributeError:
logger.error(
@@ -536,7 +535,7 @@ class SubHeartflowManager:
except Exception as e:
logger.error(f"{log_prefix} 检查 Bot 超时状态时出错: {e}", exc_info=True)
# --- 执行状态转换(如果超时) ---
# 执行状态转换(如果超时)
if should_deactivate:
logger.debug(f"{log_prefix} 因超时 ({reason}),尝试转换为 ABSENT 状态。")
await sub_hf.change_chat_state(ChatState.ABSENT)
@@ -816,11 +815,6 @@ class SubHeartflowManager:
if has_new:
is_active = True
logger.debug(f"{log_prefix} 检测到新消息,标记为活跃。")
# 可选检查兴趣度是否大于0 (如果需要)
# interest_level = await sub_hf.interest_chatting.get_interest()
# if interest_level > 0:
# is_active = True
# logger.debug(f"{log_prefix} 检测到兴趣度 > 0 ({interest_level:.2f}),标记为活跃。")
else:
logger.warning(f"{log_prefix} 无法获取主要观察者来检查活动状态。")
@@ -850,56 +844,3 @@ class SubHeartflowManager:
logger.debug(
f"{log_prefix_task} 完成,共检查 {checked_count} 个私聊,{transitioned_count} 个转换为 FOCUSED。"
)
# --- 结束新增 --- #
# --- 结束新增:处理来自 HeartFChatting 的状态转换请求 --- #
# 临时函数用于GUI切换有api后删除
# async def detect_command_from_gui(self):
# """检测来自GUI的命令"""
# command_file = Path("temp_command/gui_command.json")
# if not command_file.exists():
# return
# try:
# # 读取并解析命令文件
# command_data = json.loads(command_file.read_text())
# subflow_id = command_data.get("subflow_id")
# target_state = command_data.get("target_state")
# if not subflow_id or not target_state:
# logger.warning("GUI命令文件格式不正确缺少必要字段")
# return
# # 尝试转换为ChatState枚举
# try:
# target_state_enum = ChatState[target_state.upper()]
# except KeyError:
# logger.warning(f"无效的目标状态: {target_state}")
# command_file.unlink()
# return
# # 执行状态转换
# await self.force_change_by_gui(subflow_id, target_state_enum)
# # 转换成功后删除文件
# command_file.unlink()
# logger.debug(f"已处理GUI命令并删除命令文件: {command_file}")
# except json.JSONDecodeError:
# logger.warning("GUI命令文件不是有效的JSON格式")
# except Exception as e:
# logger.error(f"处理GUI命令时发生错误: {e}", exc_info=True)
# async def force_change_by_gui(self, subflow_id: Any, target_state: ChatState):
# """强制改变指定子心流的状态"""
# async with self._lock:
# subflow = self.subheartflows.get(subflow_id)
# if not subflow:
# logger.warning(f"[强制状态转换] 尝试转换不存在的子心流 {subflow_id} 到 {target_state.value}")
# return
# await subflow.change_chat_state(target_state)
# logger.info(f"[强制状态转换] 成功将 {subflow_id} 的状态转换为 {target_state.value}")
# --- 结束新增 --- #

View File

@@ -26,7 +26,7 @@ logger = get_logger("chat")
class NormalChat:
def __init__(self, chat_stream: ChatStream, interest_dict: dict):
def __init__(self, chat_stream: ChatStream, interest_dict: dict = None):
"""初始化 NormalChat 实例。只进行同步操作。"""
# Basic info from chat_stream (sync)