typing和防炸

This commit is contained in:
UnCLAS-Prommer
2025-08-11 11:35:14 +08:00
parent 43190b12d2
commit 4cb57278b1
3 changed files with 88 additions and 106 deletions

View File

@@ -11,7 +11,6 @@ from src.common.logger import get_logger
from src.chat.message_receive.chat_stream import ChatStream, get_chat_manager from src.chat.message_receive.chat_stream import ChatStream, get_chat_manager
from src.chat.utils.prompt_builder import global_prompt_manager from src.chat.utils.prompt_builder import global_prompt_manager
from src.chat.utils.timer_calculator import Timer from src.chat.utils.timer_calculator import Timer
from src.chat.utils.chat_message_builder import get_raw_msg_by_timestamp_with_chat
from src.chat.planner_actions.planner import ActionPlanner from src.chat.planner_actions.planner import ActionPlanner
from src.chat.planner_actions.action_modifier import ActionModifier from src.chat.planner_actions.action_modifier import ActionModifier
from src.chat.planner_actions.action_manager import ActionManager from src.chat.planner_actions.action_manager import ActionManager
@@ -25,6 +24,7 @@ from src.plugin_system.apis import generator_api, send_api, message_api, databas
from src.chat.willing.willing_manager import get_willing_manager from src.chat.willing.willing_manager import get_willing_manager
from src.mais4u.mai_think import mai_thinking_manager from src.mais4u.mai_think import mai_thinking_manager
from src.mais4u.constant_s4u import ENABLE_S4U from src.mais4u.constant_s4u import ENABLE_S4U
# no_reply逻辑已集成到heartFC_chat.py中不再需要导入 # no_reply逻辑已集成到heartFC_chat.py中不再需要导入
from src.chat.chat_loop.hfc_utils import send_typing, stop_typing from src.chat.chat_loop.hfc_utils import send_typing, stop_typing
@@ -91,7 +91,6 @@ class HeartFChatting:
self.relationship_builder = relationship_builder_manager.get_or_create_builder(self.stream_id) self.relationship_builder = relationship_builder_manager.get_or_create_builder(self.stream_id)
self.expression_learner = expression_learner_manager.get_expression_learner(self.stream_id) self.expression_learner = expression_learner_manager.get_expression_learner(self.stream_id)
self.action_manager = ActionManager() self.action_manager = ActionManager()
self.action_planner = ActionPlanner(chat_id=self.stream_id, action_manager=self.action_manager) self.action_planner = ActionPlanner(chat_id=self.stream_id, action_manager=self.action_manager)
self.action_modifier = ActionModifier(action_manager=self.action_manager, chat_id=self.stream_id) self.action_modifier = ActionModifier(action_manager=self.action_manager, chat_id=self.stream_id)
@@ -194,28 +193,27 @@ class HeartFChatting:
# 获取动作类型,兼容新旧格式 # 获取动作类型,兼容新旧格式
action_type = "未知动作" action_type = "未知动作"
if hasattr(self, '_current_cycle_detail') and self._current_cycle_detail: if hasattr(self, "_current_cycle_detail") and self._current_cycle_detail:
loop_plan_info = self._current_cycle_detail.loop_plan_info loop_plan_info = self._current_cycle_detail.loop_plan_info
if isinstance(loop_plan_info, dict): if isinstance(loop_plan_info, dict):
action_result = loop_plan_info.get('action_result', {}) action_result = loop_plan_info.get("action_result", {})
if isinstance(action_result, dict): if isinstance(action_result, dict):
# 旧格式action_result是字典 # 旧格式action_result是字典
action_type = action_result.get('action_type', '未知动作') action_type = action_result.get("action_type", "未知动作")
elif isinstance(action_result, list) and action_result: elif isinstance(action_result, list) and action_result:
# 新格式action_result是actions列表 # 新格式action_result是actions列表
action_type = action_result[0].get('action_type', '未知动作') action_type = action_result[0].get("action_type", "未知动作")
elif isinstance(loop_plan_info, list) and loop_plan_info: elif isinstance(loop_plan_info, list) and loop_plan_info:
# 直接是actions列表的情况 # 直接是actions列表的情况
action_type = loop_plan_info[0].get('action_type', '未知动作') action_type = loop_plan_info[0].get("action_type", "未知动作")
logger.info( logger.info(
f"{self.log_prefix}{self._current_cycle_detail.cycle_id}次思考," f"{self.log_prefix}{self._current_cycle_detail.cycle_id}次思考,"
f"耗时: {self._current_cycle_detail.end_time - self._current_cycle_detail.start_time:.1f}秒, " # type: ignore f"耗时: {self._current_cycle_detail.end_time - self._current_cycle_detail.start_time:.1f}秒, " # type: ignore
f"选择动作: {action_type}" f"选择动作: {action_type}" + (f"\n详情: {'; '.join(timer_strings)}" if timer_strings else "")
+ (f"\n详情: {'; '.join(timer_strings)}" if timer_strings else "")
) )
def _determine_form_type(self) -> str: def _determine_form_type(self):
"""判断使用哪种形式的no_reply""" """判断使用哪种形式的no_reply"""
# 如果连续no_reply次数少于3次使用waiting形式 # 如果连续no_reply次数少于3次使用waiting形式
if self.no_reply_consecutive <= 3: if self.no_reply_consecutive <= 3:
@@ -227,7 +225,9 @@ class HeartFChatting:
# 计算调整后的阈值 # 计算调整后的阈值
adjusted_threshold = 3 / global_config.chat.get_current_talk_frequency(self.stream_id) adjusted_threshold = 3 / global_config.chat.get_current_talk_frequency(self.stream_id)
logger.info(f"{self.log_prefix} 最近三次兴趣度总和: {total_recent_interest:.2f}, 调整后阈值: {adjusted_threshold:.2f}") logger.info(
f"{self.log_prefix} 最近三次兴趣度总和: {total_recent_interest:.2f}, 调整后阈值: {adjusted_threshold:.2f}"
)
# 如果兴趣度总和小于阈值进入breaking形式 # 如果兴趣度总和小于阈值进入breaking形式
if total_recent_interest < adjusted_threshold: if total_recent_interest < adjusted_threshold:
@@ -237,7 +237,7 @@ class HeartFChatting:
logger.info(f"{self.log_prefix} 兴趣度充足") logger.info(f"{self.log_prefix} 兴趣度充足")
self.focus_energy = 1 self.focus_energy = 1
async def _should_process_messages(self, new_message: List[Dict[str, Any]]) -> tuple[bool,float]: async def _should_process_messages(self, new_message: List[Dict[str, Any]]) -> tuple[bool, float]:
""" """
判断是否应该处理消息 判断是否应该处理消息
@@ -250,7 +250,6 @@ class HeartFChatting:
""" """
new_message_count = len(new_message) new_message_count = len(new_message)
# talk_frequency = global_config.chat.get_current_talk_frequency(self.stream_id) # talk_frequency = global_config.chat.get_current_talk_frequency(self.stream_id)
modified_exit_count_threshold = self.focus_energy / global_config.chat.focus_value modified_exit_count_threshold = self.focus_energy / global_config.chat.focus_value
@@ -263,21 +262,22 @@ class HeartFChatting:
if new_message_count >= modified_exit_count_threshold: if new_message_count >= modified_exit_count_threshold:
# 记录兴趣度到列表 # 记录兴趣度到列表
self.recent_interest_records.append(total_interest) self.recent_interest_records.append(total_interest)
logger.info( logger.info(
f"{self.log_prefix} 累计消息数量达到{new_message_count}条(>{modified_exit_count_threshold:.1f}),结束等待" f"{self.log_prefix} 累计消息数量达到{new_message_count}条(>{modified_exit_count_threshold:.1f}),结束等待"
) )
logger.info(self.last_read_time) logger.info(str(self.last_read_time))
logger.info(new_message) logger.info(str(new_message))
return True,total_interest/new_message_count return True, total_interest / new_message_count
# 检查累计兴趣值 # 检查累计兴趣值
if new_message_count > 0: if new_message_count > 0:
# 只在兴趣值变化时输出log # 只在兴趣值变化时输出log
if not hasattr(self, "_last_accumulated_interest") or total_interest != self._last_accumulated_interest: if not hasattr(self, "_last_accumulated_interest") or total_interest != self._last_accumulated_interest:
logger.info(f"{self.log_prefix} breaking形式当前累计兴趣值: {total_interest:.2f}, 专注度: {global_config.chat.focus_value:.1f}") logger.info(
f"{self.log_prefix} breaking形式当前累计兴趣值: {total_interest:.2f}, 专注度: {global_config.chat.focus_value:.1f}"
)
self._last_accumulated_interest = total_interest self._last_accumulated_interest = total_interest
if total_interest >= 3 / global_config.chat.focus_value: if total_interest >= 3 / global_config.chat.focus_value:
@@ -287,7 +287,7 @@ class HeartFChatting:
logger.info( logger.info(
f"{self.log_prefix} 累计兴趣值达到{total_interest:.2f}(>{3 / global_config.chat.focus_value}),结束等待" f"{self.log_prefix} 累计兴趣值达到{total_interest:.2f}(>{3 / global_config.chat.focus_value}),结束等待"
) )
return True,total_interest/new_message_count return True, total_interest / new_message_count
# 每10秒输出一次等待状态 # 每10秒输出一次等待状态
if int(time.time() - self.last_read_time) > 0 and int(time.time() - self.last_read_time) % 10 == 0: if int(time.time() - self.last_read_time) > 0 and int(time.time() - self.last_read_time) % 10 == 0:
@@ -296,28 +296,27 @@ class HeartFChatting:
) )
await asyncio.sleep(0.5) await asyncio.sleep(0.5)
return False,0.0 return False, 0.0
async def _loopbody(self): async def _loopbody(self):
recent_messages_dict = message_api.get_messages_by_time_in_chat( recent_messages_dict = message_api.get_messages_by_time_in_chat(
chat_id=self.stream_id, chat_id=self.stream_id,
start_time=self.last_read_time, start_time=self.last_read_time,
end_time=time.time(), end_time=time.time(),
limit = 10, limit=10,
limit_mode="latest", limit_mode="latest",
filter_mai=True, filter_mai=True,
filter_command=True, filter_command=True,
) )
# 统一的消息处理逻辑 # 统一的消息处理逻辑
should_process,interest_value = await self._should_process_messages(recent_messages_dict) should_process, interest_value = await self._should_process_messages(recent_messages_dict)
if should_process: if should_process:
# earliest_message_data = recent_messages_dict[0] # earliest_message_data = recent_messages_dict[0]
# self.last_read_time = earliest_message_data.get("time") # self.last_read_time = earliest_message_data.get("time")
self.last_read_time = time.time() self.last_read_time = time.time()
await self._observe(interest_value = interest_value) await self._observe(interest_value=interest_value)
else: else:
# Normal模式消息数量不足等待 # Normal模式消息数量不足等待
@@ -394,11 +393,9 @@ class HeartFChatting:
return loop_info, reply_text, cycle_timers return loop_info, reply_text, cycle_timers
async def _observe(self,interest_value:float = 0.0) -> bool: async def _observe(self, interest_value: float = 0.0) -> bool:
action_type = "no_action" action_type = "no_action"
reply_text = "" # 初始化reply_text变量避免UnboundLocalError reply_text = "" # 初始化reply_text变量避免UnboundLocalError
reply_to_str = "" # 初始化reply_to_str变量
# 根据interest_value计算概率决定使用哪种planner模式 # 根据interest_value计算概率决定使用哪种planner模式
# interest_value越高越倾向于使用Normal模式 # interest_value越高越倾向于使用Normal模式
@@ -418,15 +415,21 @@ class HeartFChatting:
x0 = 1.0 # 控制曲线中心点 x0 = 1.0 # 控制曲线中心点
return 1.0 / (1.0 + math.exp(-k * (interest_val - x0))) return 1.0 / (1.0 + math.exp(-k * (interest_val - x0)))
normal_mode_probability = calculate_normal_mode_probability(interest_value) / global_config.chat.get_current_talk_frequency(self.stream_id) normal_mode_probability = calculate_normal_mode_probability(
interest_value
) / global_config.chat.get_current_talk_frequency(self.stream_id)
# 根据概率决定使用哪种模式 # 根据概率决定使用哪种模式
if random.random() < normal_mode_probability: if random.random() < normal_mode_probability:
mode = ChatMode.NORMAL mode = ChatMode.NORMAL
logger.info(f"{self.log_prefix} 基于兴趣值 {interest_value:.2f},概率 {normal_mode_probability:.2f}选择Normal planner模式") logger.info(
f"{self.log_prefix} 基于兴趣值 {interest_value:.2f},概率 {normal_mode_probability:.2f}选择Normal planner模式"
)
else: else:
mode = ChatMode.FOCUS mode = ChatMode.FOCUS
logger.info(f"{self.log_prefix} 基于兴趣值 {interest_value:.2f},概率 {normal_mode_probability:.2f}选择Focus planner模式") logger.info(
f"{self.log_prefix} 基于兴趣值 {interest_value:.2f},概率 {normal_mode_probability:.2f}选择Focus planner模式"
)
# 创建新的循环信息 # 创建新的循环信息
cycle_timers, thinking_id = self.start_cycle() cycle_timers, thinking_id = self.start_cycle()
@@ -463,7 +466,7 @@ class HeartFChatting:
): ):
return False return False
with Timer("规划器", cycle_timers): with Timer("规划器", cycle_timers):
actions, _= await self.action_planner.plan( actions, _ = await self.action_planner.plan(
mode=mode, mode=mode,
loop_start_time=loop_start_time, loop_start_time=loop_start_time,
available_actions=available_actions, available_actions=available_actions,
@@ -477,7 +480,6 @@ class HeartFChatting:
# action_result.get("is_parallel", True), # action_result.get("is_parallel", True),
# ) # )
# 3. 并行执行所有动作 # 3. 并行执行所有动作
async def execute_action(action_info): async def execute_action(action_info):
"""执行单个动作的通用函数""" """执行单个动作的通用函数"""
@@ -498,12 +500,7 @@ class HeartFChatting:
action_name="no_reply", action_name="no_reply",
) )
return { return {"action_type": "no_reply", "success": True, "reply_text": "", "command": ""}
"action_type": "no_reply",
"success": True,
"reply_text": "",
"command": ""
}
elif action_info["action_type"] != "reply": elif action_info["action_type"] != "reply":
# 执行普通动作 # 执行普通动作
with Timer("动作执行", cycle_timers): with Timer("动作执行", cycle_timers):
@@ -513,19 +510,18 @@ class HeartFChatting:
action_info["action_data"], action_info["action_data"],
cycle_timers, cycle_timers,
thinking_id, thinking_id,
action_info["action_message"] action_info["action_message"],
) )
return { return {
"action_type": action_info["action_type"], "action_type": action_info["action_type"],
"success": success, "success": success,
"reply_text": reply_text, "reply_text": reply_text,
"command": command "command": command,
} }
else: else:
# 执行回复动作 # 执行回复动作
reply_to_str = await self.build_reply_to_str(action_info["action_message"]) reply_to_str = await self.build_reply_to_str(action_info["action_message"])
# 生成回复 # 生成回复
gather_timeout = global_config.chat.thinking_timeout gather_timeout = global_config.chat.thinking_timeout
try: try:
@@ -536,35 +532,20 @@ class HeartFChatting:
reply_to=reply_to_str, reply_to=reply_to_str,
request_type="chat.replyer", request_type="chat.replyer",
), ),
timeout=gather_timeout timeout=gather_timeout,
) )
except asyncio.TimeoutError: except asyncio.TimeoutError:
logger.warning( logger.warning(
f"{self.log_prefix} 并行执行:回复生成超时>{global_config.chat.thinking_timeout}s已跳过" f"{self.log_prefix} 并行执行:回复生成超时>{global_config.chat.thinking_timeout}s已跳过"
) )
return { return {"action_type": "reply", "success": False, "reply_text": "", "loop_info": None}
"action_type": "reply",
"success": False,
"reply_text": "",
"loop_info": None
}
except asyncio.CancelledError: except asyncio.CancelledError:
logger.debug(f"{self.log_prefix} 并行执行:回复生成任务已被取消") logger.debug(f"{self.log_prefix} 并行执行:回复生成任务已被取消")
return { return {"action_type": "reply", "success": False, "reply_text": "", "loop_info": None}
"action_type": "reply",
"success": False,
"reply_text": "",
"loop_info": None
}
if not response_set: if not response_set:
logger.warning(f"{self.log_prefix} 模型超时或生成回复内容为空") logger.warning(f"{self.log_prefix} 模型超时或生成回复内容为空")
return { return {"action_type": "reply", "success": False, "reply_text": "", "loop_info": None}
"action_type": "reply",
"success": False,
"reply_text": "",
"loop_info": None
}
loop_info, reply_text, cycle_timers_reply = await self._send_and_store_reply( loop_info, reply_text, cycle_timers_reply = await self._send_and_store_reply(
response_set, response_set,
@@ -579,7 +560,7 @@ class HeartFChatting:
"action_type": "reply", "action_type": "reply",
"success": True, "success": True,
"reply_text": reply_text, "reply_text": reply_text,
"loop_info": loop_info "loop_info": loop_info,
} }
except Exception as e: except Exception as e:
logger.error(f"{self.log_prefix} 执行动作时出错: {e}") logger.error(f"{self.log_prefix} 执行动作时出错: {e}")
@@ -589,7 +570,7 @@ class HeartFChatting:
"success": False, "success": False,
"reply_text": "", "reply_text": "",
"loop_info": None, "loop_info": None,
"error": str(e) "error": str(e),
} }
# 创建所有动作的后台任务 # 创建所有动作的后台任务
@@ -607,12 +588,11 @@ class HeartFChatting:
action_reply_text = "" action_reply_text = ""
action_command = "" action_command = ""
for i, result in enumerate(results): for result in results:
if isinstance(result, BaseException): if isinstance(result, BaseException):
logger.error(f"{self.log_prefix} 动作执行异常: {result}") logger.error(f"{self.log_prefix} 动作执行异常: {result}")
continue continue
action_info = actions[i]
if result["action_type"] != "reply": if result["action_type"] != "reply":
action_success = result["success"] action_success = result["success"]
action_reply_text = result["reply_text"] action_reply_text = result["reply_text"]
@@ -652,7 +632,6 @@ class HeartFChatting:
} }
reply_text = action_reply_text reply_text = action_reply_text
if ENABLE_S4U: if ENABLE_S4U:
await stop_typing() await stop_typing()
await mai_thinking_manager.get_mai_think(self.stream_id).do_think_after_response(reply_text) await mai_thinking_manager.get_mai_think(self.stream_id).do_think_after_response(reply_text)

View File

@@ -24,7 +24,9 @@ class QAManager:
self.kg_manager = kg_manager self.kg_manager = kg_manager
self.qa_model = LLMRequest(model_set=model_config.model_task_config.lpmm_qa, request_type="lpmm.qa") self.qa_model = LLMRequest(model_set=model_config.model_task_config.lpmm_qa, request_type="lpmm.qa")
async def process_query(self, question: str) -> Optional[Tuple[List[Tuple[str, float, float]], Optional[Dict[str, float]]]]: async def process_query(
self, question: str
) -> Optional[Tuple[List[Tuple[str, float, float]], Optional[Dict[str, float]]]]:
"""处理查询""" """处理查询"""
# 生成问题的Embedding # 生成问题的Embedding
@@ -56,7 +58,8 @@ class QAManager:
logger.debug(f"关系检索用时:{part_end_time - part_start_time:.5f}s") logger.debug(f"关系检索用时:{part_end_time - part_start_time:.5f}s")
for res in relation_search_res: for res in relation_search_res:
rel_str = self.embed_manager.relation_embedding_store.store.get(res[0]).str if store_item := self.embed_manager.relation_embedding_store.store.get(res[0]):
rel_str = store_item.str
print(f"找到相关关系,相似度:{(res[1] * 100):.2f}% - {rel_str}") print(f"找到相关关系,相似度:{(res[1] * 100):.2f}% - {rel_str}")
# TODO: 使用LLM过滤三元组结果 # TODO: 使用LLM过滤三元组结果

View File

@@ -70,8 +70,8 @@ def get_key_comment(toml_table, key):
return item.trivia.comment return item.trivia.comment
if hasattr(toml_table, "keys"): if hasattr(toml_table, "keys"):
for k in toml_table.keys(): for k in toml_table.keys():
if isinstance(k, KeyType) and k.key == key: if isinstance(k, KeyType) and k.key == key: # type: ignore
return k.trivia.comment return k.trivia.comment # type: ignore
return None return None