better:让表情包和文字统一发送
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
import time
|
|
||||||
import traceback
|
import traceback
|
||||||
from typing import List, Optional, Dict, Any
|
from typing import List, Optional, Dict, Any, Tuple
|
||||||
from src.chat.message_receive.message import MessageRecv, MessageThinking, MessageSending
|
from src.chat.message_receive.message import MessageRecv, MessageThinking, MessageSending
|
||||||
from src.chat.message_receive.message import Seg # Local import needed after move
|
from src.chat.message_receive.message import Seg # Local import needed after move
|
||||||
from src.chat.message_receive.message import UserInfo
|
from src.chat.message_receive.message import UserInfo
|
||||||
@@ -18,6 +17,7 @@ from src.chat.utils.info_catcher import info_catcher_manager
|
|||||||
from src.manager.mood_manager import mood_manager
|
from src.manager.mood_manager import mood_manager
|
||||||
from src.chat.heart_flow.utils_chat import get_chat_type_and_target_info
|
from src.chat.heart_flow.utils_chat import get_chat_type_and_target_info
|
||||||
from src.chat.message_receive.chat_stream import ChatStream
|
from src.chat.message_receive.chat_stream import ChatStream
|
||||||
|
from src.chat.focus_chat.hfc_utils import parse_thinking_id_to_timestamp
|
||||||
|
|
||||||
logger = get_logger("expressor")
|
logger = get_logger("expressor")
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ class DefaultExpressor:
|
|||||||
async def initialize(self):
|
async def initialize(self):
|
||||||
self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.chat_id)
|
self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.chat_id)
|
||||||
|
|
||||||
async def _create_thinking_message(self, anchor_message: Optional[MessageRecv]) -> Optional[str]:
|
async def _create_thinking_message(self, anchor_message: Optional[MessageRecv], thinking_id: str):
|
||||||
"""创建思考消息 (尝试锚定到 anchor_message)"""
|
"""创建思考消息 (尝试锚定到 anchor_message)"""
|
||||||
if not anchor_message or not anchor_message.chat_stream:
|
if not anchor_message or not anchor_message.chat_stream:
|
||||||
logger.error(f"{self.log_prefix} 无法创建思考消息,缺少有效的锚点消息或聊天流。")
|
logger.error(f"{self.log_prefix} 无法创建思考消息,缺少有效的锚点消息或聊天流。")
|
||||||
@@ -49,6 +49,7 @@ class DefaultExpressor:
|
|||||||
|
|
||||||
chat = anchor_message.chat_stream
|
chat = anchor_message.chat_stream
|
||||||
messageinfo = anchor_message.message_info
|
messageinfo = anchor_message.message_info
|
||||||
|
thinking_time_point = parse_thinking_id_to_timestamp(thinking_id)
|
||||||
bot_user_info = UserInfo(
|
bot_user_info = UserInfo(
|
||||||
user_id=global_config.BOT_QQ,
|
user_id=global_config.BOT_QQ,
|
||||||
user_nickname=global_config.BOT_NICKNAME,
|
user_nickname=global_config.BOT_NICKNAME,
|
||||||
@@ -58,9 +59,6 @@ class DefaultExpressor:
|
|||||||
# logger.debug(f"创建思考消息chat:{chat}")
|
# logger.debug(f"创建思考消息chat:{chat}")
|
||||||
# logger.debug(f"创建思考消息bot_user_info:{bot_user_info}")
|
# logger.debug(f"创建思考消息bot_user_info:{bot_user_info}")
|
||||||
# logger.debug(f"创建思考消息messageinfo:{messageinfo}")
|
# logger.debug(f"创建思考消息messageinfo:{messageinfo}")
|
||||||
|
|
||||||
thinking_time_point = round(time.time(), 2)
|
|
||||||
thinking_id = "mt" + str(thinking_time_point)
|
|
||||||
thinking_message = MessageThinking(
|
thinking_message = MessageThinking(
|
||||||
message_id=thinking_id,
|
message_id=thinking_id,
|
||||||
chat_stream=chat,
|
chat_stream=chat,
|
||||||
@@ -69,9 +67,8 @@ class DefaultExpressor:
|
|||||||
thinking_start_time=thinking_time_point,
|
thinking_start_time=thinking_time_point,
|
||||||
)
|
)
|
||||||
logger.debug(f"创建思考消息thinking_message:{thinking_message}")
|
logger.debug(f"创建思考消息thinking_message:{thinking_message}")
|
||||||
# Access MessageManager directly (using heart_fc_sender)
|
|
||||||
await self.heart_fc_sender.register_thinking(thinking_message)
|
await self.heart_fc_sender.register_thinking(thinking_message)
|
||||||
return thinking_id
|
|
||||||
|
|
||||||
async def deal_reply(
|
async def deal_reply(
|
||||||
self,
|
self,
|
||||||
@@ -79,11 +76,10 @@ class DefaultExpressor:
|
|||||||
action_data: Dict[str, Any],
|
action_data: Dict[str, Any],
|
||||||
reasoning: str,
|
reasoning: str,
|
||||||
anchor_message: MessageRecv,
|
anchor_message: MessageRecv,
|
||||||
|
thinking_id: str,
|
||||||
) -> tuple[bool, Optional[List[str]]]:
|
) -> tuple[bool, Optional[List[str]]]:
|
||||||
# 创建思考消息
|
# 创建思考消息
|
||||||
thinking_id = await self._create_thinking_message(anchor_message)
|
await self._create_thinking_message(anchor_message, thinking_id)
|
||||||
if not thinking_id:
|
|
||||||
raise Exception("无法创建思考消息")
|
|
||||||
|
|
||||||
reply = None # 初始化 reply,防止未定义
|
reply = None # 初始化 reply,防止未定义
|
||||||
try:
|
try:
|
||||||
@@ -102,8 +98,14 @@ class DefaultExpressor:
|
|||||||
action_data=action_data,
|
action_data=action_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
with Timer("选择表情", cycle_timers):
|
||||||
|
emoji_keyword = action_data.get("emojis", [])
|
||||||
|
emoji_base64 = await self._choose_emoji(emoji_keyword)
|
||||||
|
if emoji_base64:
|
||||||
|
reply.append(("emoji", emoji_base64))
|
||||||
|
|
||||||
if reply:
|
if reply:
|
||||||
with Timer("发送文本消息", cycle_timers):
|
with Timer("发送消息", cycle_timers):
|
||||||
await self._send_response_messages(
|
await self._send_response_messages(
|
||||||
anchor_message=anchor_message,
|
anchor_message=anchor_message,
|
||||||
thinking_id=thinking_id,
|
thinking_id=thinking_id,
|
||||||
@@ -113,12 +115,6 @@ class DefaultExpressor:
|
|||||||
else:
|
else:
|
||||||
logger.warning(f"{self.log_prefix} 文本回复生成失败")
|
logger.warning(f"{self.log_prefix} 文本回复生成失败")
|
||||||
|
|
||||||
# 处理表情部分
|
|
||||||
emoji_keyword = action_data.get("emojis", [])
|
|
||||||
if emoji_keyword:
|
|
||||||
await self._handle_emoji(anchor_message, [], emoji_keyword)
|
|
||||||
has_sent_something = True
|
|
||||||
|
|
||||||
if not has_sent_something:
|
if not has_sent_something:
|
||||||
logger.warning(f"{self.log_prefix} 回复动作未包含任何有效内容")
|
logger.warning(f"{self.log_prefix} 回复动作未包含任何有效内容")
|
||||||
|
|
||||||
@@ -195,40 +191,46 @@ class DefaultExpressor:
|
|||||||
logger.info(f"想要表达:{in_mind_reply}")
|
logger.info(f"想要表达:{in_mind_reply}")
|
||||||
logger.info(f"理由:{reason}")
|
logger.info(f"理由:{reason}")
|
||||||
logger.info(f"生成回复: {content}\n")
|
logger.info(f"生成回复: {content}\n")
|
||||||
|
|
||||||
info_catcher.catch_after_llm_generated(
|
info_catcher.catch_after_llm_generated(
|
||||||
prompt=prompt, response=content, reasoning_content=reasoning_content, model_name=model_name
|
prompt=prompt, response=content, reasoning_content=reasoning_content, model_name=model_name
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as llm_e:
|
except Exception as llm_e:
|
||||||
# 精简报错信息
|
# 精简报错信息
|
||||||
logger.error(f"{self.log_prefix}[Replier-{thinking_id}] LLM 生成失败: {llm_e}")
|
logger.error(f"{self.log_prefix}LLM 生成失败: {llm_e}")
|
||||||
return None # LLM 调用失败则无法生成回复
|
return None # LLM 调用失败则无法生成回复
|
||||||
|
|
||||||
# 5. 处理 LLM 响应
|
|
||||||
if not content:
|
|
||||||
logger.warning(f"{self.log_prefix}[Replier-{thinking_id}] LLM 生成了空内容。")
|
|
||||||
return None
|
|
||||||
|
|
||||||
processed_response = process_llm_response(content)
|
processed_response = process_llm_response(content)
|
||||||
|
|
||||||
|
# 5. 处理 LLM 响应
|
||||||
|
if not content:
|
||||||
|
logger.warning(f"{self.log_prefix}LLM 生成了空内容。")
|
||||||
|
return None
|
||||||
if not processed_response:
|
if not processed_response:
|
||||||
logger.warning(f"{self.log_prefix}[Replier-{thinking_id}] 处理后的回复为空。")
|
logger.warning(f"{self.log_prefix}处理后的回复为空。")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return processed_response
|
reply_set = []
|
||||||
|
for str in processed_response:
|
||||||
|
reply_seg = ("text", str)
|
||||||
|
reply_set.append(reply_seg)
|
||||||
|
|
||||||
|
return reply_set
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{self.log_prefix}[Replier-{thinking_id}] 回复生成意外失败: {e}")
|
logger.error(f"{self.log_prefix}回复生成意外失败: {e}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# --- 发送器 (Sender) --- #
|
# --- 发送器 (Sender) --- #
|
||||||
|
|
||||||
async def _send_response_messages(
|
async def _send_response_messages(
|
||||||
self, anchor_message: Optional[MessageRecv], response_set: List[str], thinking_id: str
|
self, anchor_message: Optional[MessageRecv], response_set: List[Tuple[str, str]], thinking_id: str
|
||||||
) -> Optional[MessageSending]:
|
) -> Optional[MessageSending]:
|
||||||
"""发送回复消息 (尝试锚定到 anchor_message),使用 HeartFCSender"""
|
"""发送回复消息 (尝试锚定到 anchor_message),使用 HeartFCSender"""
|
||||||
chat = self.chat_stream
|
chat = self.chat_stream
|
||||||
|
chat_id = self.chat_id
|
||||||
if chat is None:
|
if chat is None:
|
||||||
logger.error(f"{self.log_prefix} 无法发送回复,chat_stream 为空。")
|
logger.error(f"{self.log_prefix} 无法发送回复,chat_stream 为空。")
|
||||||
return None
|
return None
|
||||||
@@ -236,98 +238,104 @@ class DefaultExpressor:
|
|||||||
logger.error(f"{self.log_prefix} 无法发送回复,anchor_message 为空。")
|
logger.error(f"{self.log_prefix} 无法发送回复,anchor_message 为空。")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
chat_id = self.chat_id
|
|
||||||
stream_name = chat_manager.get_stream_name(chat_id) or chat_id # 获取流名称用于日志
|
stream_name = chat_manager.get_stream_name(chat_id) or chat_id # 获取流名称用于日志
|
||||||
|
|
||||||
# 检查思考过程是否仍在进行,并获取开始时间
|
# 检查思考过程是否仍在进行,并获取开始时间
|
||||||
thinking_start_time = await self.heart_fc_sender.get_thinking_start_time(chat_id, thinking_id)
|
thinking_start_time = await self.heart_fc_sender.get_thinking_start_time(chat_id, thinking_id)
|
||||||
|
|
||||||
if thinking_start_time is None:
|
if thinking_start_time is None:
|
||||||
logger.warning(f"[{stream_name}] {thinking_id} 思考过程未找到或已结束,无法发送回复。")
|
logger.error(f"[{stream_name}]思考过程未找到或已结束,无法发送回复。")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
mark_head = False
|
mark_head = False
|
||||||
first_bot_msg: Optional[MessageSending] = None
|
first_bot_msg: Optional[MessageSending] = None
|
||||||
reply_message_ids = [] # 记录实际发送的消息ID
|
reply_message_ids = [] # 记录实际发送的消息ID
|
||||||
bot_user_info = UserInfo(
|
|
||||||
user_id=global_config.BOT_QQ,
|
|
||||||
user_nickname=global_config.BOT_NICKNAME,
|
|
||||||
platform=chat.platform,
|
|
||||||
)
|
|
||||||
|
|
||||||
for i, msg_text in enumerate(response_set):
|
for i, msg_text in enumerate(response_set):
|
||||||
# 为每个消息片段生成唯一ID
|
# 为每个消息片段生成唯一ID
|
||||||
|
type = msg_text[0]
|
||||||
|
data = msg_text[1]
|
||||||
|
|
||||||
part_message_id = f"{thinking_id}_{i}"
|
part_message_id = f"{thinking_id}_{i}"
|
||||||
message_segment = Seg(type="text", data=msg_text)
|
message_segment = Seg(type=type, data=data)
|
||||||
bot_message = MessageSending(
|
|
||||||
message_id=part_message_id, # 使用片段的唯一ID
|
if type == "emoji":
|
||||||
chat_stream=chat,
|
is_emoji = True
|
||||||
bot_user_info=bot_user_info,
|
else:
|
||||||
sender_info=anchor_message.message_info.user_info,
|
is_emoji = False
|
||||||
|
reply_to = not mark_head
|
||||||
|
|
||||||
|
bot_message = self._build_single_sending_message(
|
||||||
|
anchor_message=anchor_message,
|
||||||
|
message_id=part_message_id,
|
||||||
message_segment=message_segment,
|
message_segment=message_segment,
|
||||||
reply=anchor_message, # 回复原始锚点
|
reply_to=reply_to,
|
||||||
is_head=not mark_head,
|
is_emoji=is_emoji,
|
||||||
is_emoji=False,
|
thinking_id=thinking_id,
|
||||||
thinking_start_time=thinking_start_time, # 传递原始思考开始时间
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not mark_head:
|
if not mark_head:
|
||||||
mark_head = True
|
mark_head = True
|
||||||
first_bot_msg = bot_message # 保存第一个成功发送的消息对象
|
first_bot_msg = bot_message # 保存第一个成功发送的消息对象
|
||||||
await self.heart_fc_sender.type_and_send_message(bot_message, typing=False)
|
typing = False
|
||||||
else:
|
else:
|
||||||
await self.heart_fc_sender.type_and_send_message(bot_message, typing=True)
|
typing = True
|
||||||
|
|
||||||
|
await self.heart_fc_sender.send_message(bot_message, has_thinking=True, typing=typing)
|
||||||
|
|
||||||
reply_message_ids.append(part_message_id) # 记录我们生成的ID
|
reply_message_ids.append(part_message_id) # 记录我们生成的ID
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
logger.error(f"{self.log_prefix}发送回复片段 {i} ({part_message_id}) 时失败: {e}")
|
||||||
f"{self.log_prefix}[Sender-{thinking_id}] 发送回复片段 {i} ({part_message_id}) 时失败: {e}"
|
|
||||||
)
|
|
||||||
# 这里可以选择是继续发送下一个片段还是中止
|
# 这里可以选择是继续发送下一个片段还是中止
|
||||||
|
|
||||||
# 在尝试发送完所有片段后,完成原始的 thinking_id 状态
|
# 在尝试发送完所有片段后,完成原始的 thinking_id 状态
|
||||||
try:
|
try:
|
||||||
await self.heart_fc_sender.complete_thinking(chat_id, thinking_id)
|
await self.heart_fc_sender.complete_thinking(chat_id, thinking_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{self.log_prefix}[Sender-{thinking_id}] 完成思考状态 {thinking_id} 时出错: {e}")
|
logger.error(f"{self.log_prefix}完成思考状态 {thinking_id} 时出错: {e}")
|
||||||
|
|
||||||
return first_bot_msg # 返回第一个成功发送的消息对象
|
return first_bot_msg # 返回第一个成功发送的消息对象
|
||||||
|
|
||||||
async def _handle_emoji(self, anchor_message: Optional[MessageRecv], response_set: List[str], send_emoji: str = ""):
|
async def _choose_emoji(self, send_emoji: str):
|
||||||
"""处理表情包 (尝试锚定到 anchor_message),使用 HeartFCSender"""
|
"""
|
||||||
if not anchor_message or not anchor_message.chat_stream:
|
选择表情,根据send_emoji文本选择表情,返回表情base64
|
||||||
logger.error(f"{self.log_prefix} 无法处理表情包,缺少有效的锚点消息或聊天流。")
|
"""
|
||||||
return
|
|
||||||
|
|
||||||
chat = anchor_message.chat_stream
|
|
||||||
|
|
||||||
emoji_raw = await emoji_manager.get_emoji_for_text(send_emoji)
|
emoji_raw = await emoji_manager.get_emoji_for_text(send_emoji)
|
||||||
|
|
||||||
if emoji_raw:
|
if emoji_raw:
|
||||||
emoji_path, description = emoji_raw
|
emoji_path, _description = emoji_raw
|
||||||
|
emoji_base64 = image_path_to_base64(emoji_path)
|
||||||
|
return emoji_base64
|
||||||
|
|
||||||
emoji_cq = image_path_to_base64(emoji_path)
|
async def _build_single_sending_message(
|
||||||
thinking_time_point = round(time.time(), 2) # 用于唯一ID
|
self,
|
||||||
message_segment = Seg(type="emoji", data=emoji_cq)
|
anchor_message: MessageRecv,
|
||||||
|
message_id: str,
|
||||||
|
message_segment: Seg,
|
||||||
|
reply_to: bool,
|
||||||
|
is_emoji: bool,
|
||||||
|
thinking_id: str,
|
||||||
|
) -> MessageSending:
|
||||||
|
"""构建单个发送消息"""
|
||||||
|
|
||||||
|
thinking_start_time = await self.heart_fc_sender.get_thinking_start_time(self.chat_id, thinking_id)
|
||||||
bot_user_info = UserInfo(
|
bot_user_info = UserInfo(
|
||||||
user_id=global_config.BOT_QQ,
|
user_id=global_config.BOT_QQ,
|
||||||
user_nickname=global_config.BOT_NICKNAME,
|
user_nickname=global_config.BOT_NICKNAME,
|
||||||
platform=anchor_message.message_info.platform,
|
platform=self.chat_stream.platform,
|
||||||
)
|
)
|
||||||
|
|
||||||
bot_message = MessageSending(
|
bot_message = MessageSending(
|
||||||
message_id="me" + str(thinking_time_point), # 表情消息的唯一ID
|
message_id=message_id, # 使用片段的唯一ID
|
||||||
chat_stream=chat,
|
chat_stream=self.chat_stream,
|
||||||
bot_user_info=bot_user_info,
|
bot_user_info=bot_user_info,
|
||||||
sender_info=anchor_message.message_info.user_info,
|
sender_info=anchor_message.message_info.user_info,
|
||||||
message_segment=message_segment,
|
message_segment=message_segment,
|
||||||
reply=anchor_message, # 回复原始锚点
|
reply=anchor_message, # 回复原始锚点
|
||||||
is_head=False, # 表情通常不是头部消息
|
is_head=reply_to,
|
||||||
is_emoji=True,
|
is_emoji=is_emoji,
|
||||||
# 不需要 thinking_start_time
|
thinking_start_time=thinking_start_time, # 传递原始思考开始时间
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
return bot_message
|
||||||
await self.heart_fc_sender.send_and_store(bot_message)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"{self.log_prefix} 发送表情包 {bot_message.message_info.message_id} 时失败: {e}")
|
|
||||||
|
|||||||
@@ -58,9 +58,9 @@ def init_prompt() -> None:
|
|||||||
{chat_str}
|
{chat_str}
|
||||||
|
|
||||||
请从上面这段群聊中概括除了人名为"麦麦"之外的人的语法和句法特点,只考虑纯文字,不要考虑表情包和图片
|
请从上面这段群聊中概括除了人名为"麦麦"之外的人的语法和句法特点,只考虑纯文字,不要考虑表情包和图片
|
||||||
不要总结【图片】,【动画表情】,[图片],[动画表情],不总结 表情符号
|
不要总结【图片】,【动画表情】,[图片],[动画表情],不总结 表情符号 at @ 回复 和[回复]
|
||||||
不要涉及具体的人名,只考虑语法和句法特点,
|
不要涉及具体的人名,只考虑语法和句法特点,
|
||||||
语法和句法特点要包括,句子长短(具体字数),如何分局,有何种语病,如何拆分句子。
|
语法和句法特点要包括,句子长短(具体字数),有何种语病,如何拆分句子。
|
||||||
总结成如下格式的规律,总结的内容要简洁,不浮夸:
|
总结成如下格式的规律,总结的内容要简洁,不浮夸:
|
||||||
当"xxx"时,可以"xxx"
|
当"xxx"时,可以"xxx"
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservati
|
|||||||
from src.chat.heart_flow.observation.working_observation import WorkingObservation
|
from src.chat.heart_flow.observation.working_observation import WorkingObservation
|
||||||
from src.chat.focus_chat.info_processors.tool_processor import ToolProcessor
|
from src.chat.focus_chat.info_processors.tool_processor import ToolProcessor
|
||||||
from src.chat.focus_chat.expressors.default_expressor import DefaultExpressor
|
from src.chat.focus_chat.expressors.default_expressor import DefaultExpressor
|
||||||
from src.chat.focus_chat.hfc_utils import _create_empty_anchor_message
|
from src.chat.focus_chat.hfc_utils import create_empty_anchor_message, parse_thinking_id_to_timestamp
|
||||||
from src.chat.focus_chat.memory_activator import MemoryActivator
|
from src.chat.focus_chat.memory_activator import MemoryActivator
|
||||||
|
|
||||||
install(extra_lines=3)
|
install(extra_lines=3)
|
||||||
@@ -203,9 +203,9 @@ class HeartFChatting:
|
|||||||
self._cycle_counter = 0
|
self._cycle_counter = 0
|
||||||
self._cycle_history: Deque[CycleDetail] = deque(maxlen=10) # 保留最近10个循环的信息
|
self._cycle_history: Deque[CycleDetail] = deque(maxlen=10) # 保留最近10个循环的信息
|
||||||
self._current_cycle: Optional[CycleDetail] = None
|
self._current_cycle: Optional[CycleDetail] = None
|
||||||
self._lian_xu_bu_hui_fu_ci_shu: int = 0 # <--- 新增:连续不回复计数器
|
self.total_no_reply_count: int = 0 # <--- 新增:连续不回复计数器
|
||||||
self._shutting_down: bool = False # <--- 新增:关闭标志位
|
self._shutting_down: bool = False # <--- 新增:关闭标志位
|
||||||
self._lian_xu_deng_dai_shi_jian: float = 0.0 # <--- 新增:累计等待时间
|
self.total_waiting_time: float = 0.0 # <--- 新增:累计等待时间
|
||||||
|
|
||||||
async def _initialize(self) -> bool:
|
async def _initialize(self) -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -325,11 +325,12 @@ class HeartFChatting:
|
|||||||
await asyncio.sleep(0.1) # 短暂等待避免空转
|
await asyncio.sleep(0.1) # 短暂等待避免空转
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 记录规划开始时间点
|
# thinking_id 是思考过程的ID,用于标记每一轮思考
|
||||||
planner_start_db_time = time.time()
|
thinking_id = "tid" + str(round(time.time(), 2))
|
||||||
|
|
||||||
# 主循环:思考->决策->执行
|
# 主循环:思考->决策->执行
|
||||||
action_taken, thinking_id = await self._think_plan_execute_loop(cycle_timers, planner_start_db_time)
|
|
||||||
|
action_taken = await self._think_plan_execute_loop(cycle_timers, thinking_id)
|
||||||
|
|
||||||
# 更新循环信息
|
# 更新循环信息
|
||||||
self._current_cycle.set_thinking_id(thinking_id)
|
self._current_cycle.set_thinking_id(thinking_id)
|
||||||
@@ -391,9 +392,8 @@ class HeartFChatting:
|
|||||||
if acquired and self._processing_lock.locked():
|
if acquired and self._processing_lock.locked():
|
||||||
self._processing_lock.release()
|
self._processing_lock.release()
|
||||||
|
|
||||||
async def _think_plan_execute_loop(self, cycle_timers: dict, planner_start_db_time: float) -> tuple[bool, str]:
|
async def _think_plan_execute_loop(self, cycle_timers: dict, thinking_id: str) -> tuple[bool, str]:
|
||||||
try:
|
try:
|
||||||
await asyncio.sleep(1)
|
|
||||||
with Timer("观察", cycle_timers):
|
with Timer("观察", cycle_timers):
|
||||||
await self.observations[0].observe()
|
await self.observations[0].observe()
|
||||||
await self.memory_observation.observe()
|
await self.memory_observation.observe()
|
||||||
@@ -515,7 +515,7 @@ class HeartFChatting:
|
|||||||
|
|
||||||
self.hfcloop_observation.add_loop_info(self._current_cycle)
|
self.hfcloop_observation.add_loop_info(self._current_cycle)
|
||||||
|
|
||||||
return await self._handle_action(action, reasoning, action_data, cycle_timers, planner_start_db_time)
|
return await self._handle_action(action, reasoning, action_data, cycle_timers, thinking_id)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{self.log_prefix} 并行+串行处理失败: {e}")
|
logger.error(f"{self.log_prefix} 并行+串行处理失败: {e}")
|
||||||
@@ -523,7 +523,12 @@ class HeartFChatting:
|
|||||||
return False, ""
|
return False, ""
|
||||||
|
|
||||||
async def _handle_action(
|
async def _handle_action(
|
||||||
self, action: str, reasoning: str, action_data: dict, cycle_timers: dict, planner_start_db_time: float
|
self,
|
||||||
|
action: str,
|
||||||
|
reasoning: str,
|
||||||
|
action_data: dict,
|
||||||
|
cycle_timers: dict,
|
||||||
|
thinking_id: str,
|
||||||
) -> tuple[bool, str]:
|
) -> tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
处理规划动作
|
处理规划动作
|
||||||
@@ -550,17 +555,17 @@ class HeartFChatting:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if action == "reply":
|
if action == "reply":
|
||||||
return await handler(reasoning, action_data, cycle_timers)
|
return await handler(reasoning, action_data, cycle_timers, thinking_id)
|
||||||
else: # no_reply
|
else: # no_reply
|
||||||
return await handler(reasoning, planner_start_db_time, cycle_timers), ""
|
return await handler(reasoning, cycle_timers, thinking_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{self.log_prefix} 处理{action}时出错: {e}")
|
logger.error(f"{self.log_prefix} 处理{action}时出错: {e}")
|
||||||
# 出错时也重置计数器
|
# 出错时也重置计数器
|
||||||
self._lian_xu_bu_hui_fu_ci_shu = 0
|
self.total_no_reply_count = 0
|
||||||
self._lian_xu_deng_dai_shi_jian = 0.0
|
self.total_waiting_time = 0.0
|
||||||
return False, ""
|
return False, ""
|
||||||
|
|
||||||
async def _handle_no_reply(self, reasoning: str, planner_start_db_time: float, cycle_timers: dict) -> bool:
|
async def _handle_no_reply(self, reasoning: str, cycle_timers: dict, thinking_id: str) -> bool:
|
||||||
"""
|
"""
|
||||||
处理不回复的情况
|
处理不回复的情况
|
||||||
|
|
||||||
@@ -584,53 +589,50 @@ class HeartFChatting:
|
|||||||
try:
|
try:
|
||||||
with Timer("等待新消息", cycle_timers):
|
with Timer("等待新消息", cycle_timers):
|
||||||
# 等待新消息、超时或关闭信号,并获取结果
|
# 等待新消息、超时或关闭信号,并获取结果
|
||||||
await self._wait_for_new_message(observation, planner_start_db_time, self.log_prefix)
|
await self._wait_for_new_message(observation, thinking_id, self.log_prefix)
|
||||||
# 从计时器获取实际等待时间
|
# 从计时器获取实际等待时间
|
||||||
current_waiting = cycle_timers.get("等待新消息", 0.0)
|
current_waiting = cycle_timers.get("等待新消息", 0.0)
|
||||||
|
|
||||||
if not self._shutting_down:
|
if not self._shutting_down:
|
||||||
self._lian_xu_bu_hui_fu_ci_shu += 1
|
self.total_no_reply_count += 1
|
||||||
self._lian_xu_deng_dai_shi_jian += current_waiting # 累加等待时间
|
self.total_waiting_time += current_waiting # 累加等待时间
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"{self.log_prefix} 连续不回复计数增加: {self._lian_xu_bu_hui_fu_ci_shu}/{CONSECUTIVE_NO_REPLY_THRESHOLD}, "
|
f"{self.log_prefix} 连续不回复计数增加: {self.total_no_reply_count}/{CONSECUTIVE_NO_REPLY_THRESHOLD}, "
|
||||||
f"本次等待: {current_waiting:.2f}秒, 累计等待: {self._lian_xu_deng_dai_shi_jian:.2f}秒"
|
f"本次等待: {current_waiting:.2f}秒, 累计等待: {self.total_waiting_time:.2f}秒"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 检查是否同时达到次数和时间阈值
|
# 检查是否同时达到次数和时间阈值
|
||||||
time_threshold = 0.66 * WAITING_TIME_THRESHOLD * CONSECUTIVE_NO_REPLY_THRESHOLD
|
time_threshold = 0.66 * WAITING_TIME_THRESHOLD * CONSECUTIVE_NO_REPLY_THRESHOLD
|
||||||
if (
|
if (
|
||||||
self._lian_xu_bu_hui_fu_ci_shu >= CONSECUTIVE_NO_REPLY_THRESHOLD
|
self.total_no_reply_count >= CONSECUTIVE_NO_REPLY_THRESHOLD
|
||||||
and self._lian_xu_deng_dai_shi_jian >= time_threshold
|
and self.total_waiting_time >= time_threshold
|
||||||
):
|
):
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{self.log_prefix} 连续不回复达到阈值 ({self._lian_xu_bu_hui_fu_ci_shu}次) "
|
f"{self.log_prefix} 连续不回复达到阈值 ({self.total_no_reply_count}次) "
|
||||||
f"且累计等待时间达到 {self._lian_xu_deng_dai_shi_jian:.2f}秒 (阈值 {time_threshold}秒),"
|
f"且累计等待时间达到 {self.total_waiting_time:.2f}秒 (阈值 {time_threshold}秒),"
|
||||||
f"调用回调请求状态转换"
|
f"调用回调请求状态转换"
|
||||||
)
|
)
|
||||||
# 调用回调。注意:这里不重置计数器和时间,依赖回调函数成功改变状态来隐式重置上下文。
|
# 调用回调。注意:这里不重置计数器和时间,依赖回调函数成功改变状态来隐式重置上下文。
|
||||||
await self.on_consecutive_no_reply_callback()
|
await self.on_consecutive_no_reply_callback()
|
||||||
elif self._lian_xu_bu_hui_fu_ci_shu >= CONSECUTIVE_NO_REPLY_THRESHOLD:
|
elif self.total_no_reply_count >= CONSECUTIVE_NO_REPLY_THRESHOLD:
|
||||||
# 仅次数达到阈值,但时间未达到
|
# 仅次数达到阈值,但时间未达到
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"{self.log_prefix} 连续不回复次数达到阈值 ({self._lian_xu_bu_hui_fu_ci_shu}次) "
|
f"{self.log_prefix} 连续不回复次数达到阈值 ({self.total_no_reply_count}次) "
|
||||||
f"但累计等待时间 {self._lian_xu_deng_dai_shi_jian:.2f}秒 未达到时间阈值 ({time_threshold}秒),暂不调用回调"
|
f"但累计等待时间 {self.total_waiting_time:.2f}秒 未达到时间阈值 ({time_threshold}秒),暂不调用回调"
|
||||||
)
|
)
|
||||||
# else: 次数和时间都未达到阈值,不做处理
|
# else: 次数和时间都未达到阈值,不做处理
|
||||||
|
|
||||||
return True
|
return True, thinking_id
|
||||||
|
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
# 如果在等待过程中任务被取消(可能是因为 shutdown)
|
|
||||||
logger.info(f"{self.log_prefix} 处理 'no_reply' 时等待被中断 (CancelledError)")
|
logger.info(f"{self.log_prefix} 处理 'no_reply' 时等待被中断 (CancelledError)")
|
||||||
# 让异常向上传播,由 _hfc_loop 的异常处理逻辑接管
|
|
||||||
raise
|
raise
|
||||||
except Exception as e: # 捕获调用管理器或其他地方可能发生的错误
|
except Exception as e: # 捕获调用管理器或其他地方可能发生的错误
|
||||||
logger.error(f"{self.log_prefix} 处理 'no_reply' 时发生错误: {e}")
|
logger.error(f"{self.log_prefix} 处理 'no_reply' 时发生错误: {e}")
|
||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
# 发生意外错误时,可以选择是否重置计数器,这里选择不重置
|
return False, thinking_id
|
||||||
return False # 表示动作未成功
|
|
||||||
|
|
||||||
async def _wait_for_new_message(self, observation, planner_start_db_time: float, log_prefix: str) -> bool:
|
async def _wait_for_new_message(self, observation: ChattingObservation, thinking_id: str, log_prefix: str) -> bool:
|
||||||
"""
|
"""
|
||||||
等待新消息 或 检测到关闭信号
|
等待新消息 或 检测到关闭信号
|
||||||
|
|
||||||
@@ -650,8 +652,10 @@ class HeartFChatting:
|
|||||||
return False # 表示因为关闭而退出
|
return False # 表示因为关闭而退出
|
||||||
# -----------------------------------
|
# -----------------------------------
|
||||||
|
|
||||||
|
thinking_id_timestamp = parse_thinking_id_to_timestamp(thinking_id)
|
||||||
|
|
||||||
# 检查新消息
|
# 检查新消息
|
||||||
if await observation.has_new_messages_since(planner_start_db_time):
|
if await observation.has_new_messages_since(thinking_id_timestamp):
|
||||||
logger.info(f"{log_prefix} 检测到新消息")
|
logger.info(f"{log_prefix} 检测到新消息")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -925,38 +929,42 @@ class HeartFChatting:
|
|||||||
"llm_error": llm_error, # 返回错误状态
|
"llm_error": llm_error, # 返回错误状态
|
||||||
}
|
}
|
||||||
|
|
||||||
async def _handle_reply(self, reasoning: str, reply_data: dict, cycle_timers: dict) -> tuple[bool, str]:
|
async def _handle_reply(
|
||||||
|
self, reasoning: str, reply_data: dict, cycle_timers: dict, thinking_id: str
|
||||||
|
) -> tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
处理统一的回复动作 - 可包含文本和表情,顺序任意
|
处理统一的回复动作 - 可包含文本和表情,顺序任意
|
||||||
|
|
||||||
reply_data格式:
|
reply_data格式:
|
||||||
{
|
{
|
||||||
"text": ["你好啊", "今天天气真不错"], # 文本内容列表(可选)
|
"text": "你好啊" # 文本内容列表(可选)
|
||||||
"emojis": ["微笑", "阳光"] # 表情关键词列表(可选)
|
"target": "锚定消息", # 锚定消息的文本内容
|
||||||
|
"emojis": "微笑" # 表情关键词列表(可选)
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
# 重置连续不回复计数器
|
# 重置连续不回复计数器
|
||||||
self._lian_xu_bu_hui_fu_ci_shu = 0
|
self.total_no_reply_count = 0
|
||||||
self._lian_xu_deng_dai_shi_jian = 0.0
|
self.total_waiting_time = 0.0
|
||||||
|
|
||||||
# 获取锚定消息
|
# 从聊天观察获取锚定消息
|
||||||
observations: ChattingObservation = self.observations[0]
|
observations: ChattingObservation = self.observations[0]
|
||||||
anchor_message = observations.serch_message_by_text(reply_data["target"])
|
anchor_message = observations.serch_message_by_text(reply_data["target"])
|
||||||
|
|
||||||
# 如果没有找到锚点消息,创建一个占位符
|
# 如果没有找到锚点消息,创建一个占位符
|
||||||
if not anchor_message:
|
if not anchor_message:
|
||||||
logger.info(f"{self.log_prefix} 未找到锚点消息,创建占位符")
|
logger.info(f"{self.log_prefix} 未找到锚点消息,创建占位符")
|
||||||
anchor_message = await _create_empty_anchor_message(
|
anchor_message = await create_empty_anchor_message(
|
||||||
self.chat_stream.platform, self.chat_stream.group_info, self.chat_stream
|
self.chat_stream.platform, self.chat_stream.group_info, self.chat_stream
|
||||||
)
|
)
|
||||||
if not anchor_message:
|
|
||||||
logger.error(f"{self.log_prefix} 创建占位符失败,无法继续处理回复")
|
|
||||||
return False, ""
|
|
||||||
else:
|
else:
|
||||||
anchor_message.update_chat_stream(self.chat_stream)
|
anchor_message.update_chat_stream(self.chat_stream)
|
||||||
|
|
||||||
success, reply_set = await self.expressor.deal_reply(
|
success, reply_set = await self.expressor.deal_reply(
|
||||||
cycle_timers=cycle_timers, action_data=reply_data, anchor_message=anchor_message, reasoning=reasoning
|
cycle_timers=cycle_timers,
|
||||||
|
action_data=reply_data,
|
||||||
|
anchor_message=anchor_message,
|
||||||
|
reasoning=reasoning,
|
||||||
|
thinking_id=thinking_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
reply_text = ""
|
reply_text = ""
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from src.chat.utils.utils import truncate_message
|
|||||||
from src.common.logger_manager import get_logger
|
from src.common.logger_manager import get_logger
|
||||||
from src.chat.utils.utils import calculate_typing_time
|
from src.chat.utils.utils import calculate_typing_time
|
||||||
from rich.traceback import install
|
from rich.traceback import install
|
||||||
|
import traceback
|
||||||
|
|
||||||
install(extra_lines=3)
|
install(extra_lines=3)
|
||||||
|
|
||||||
@@ -16,17 +17,16 @@ logger = get_logger("sender")
|
|||||||
|
|
||||||
async def send_message(message: MessageSending) -> None:
|
async def send_message(message: MessageSending) -> None:
|
||||||
"""合并后的消息发送函数,包含WS发送和日志记录"""
|
"""合并后的消息发送函数,包含WS发送和日志记录"""
|
||||||
message_preview = truncate_message(message.processed_plain_text)
|
message_preview = truncate_message(message.processed_plain_text, max_length=40)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 直接调用API发送消息
|
# 直接调用API发送消息
|
||||||
await global_api.send_message(message)
|
await global_api.send_message(message)
|
||||||
logger.success(f"发送消息 '{message_preview}' 成功")
|
logger.success(f"已将消息 '{message_preview}' 发往平台'{message.message_info.platform}'")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"发送消息 '{message_preview}' 失败: {str(e)}")
|
logger.error(f"发送消息 '{message_preview}' 发往平台'{message.message_info.platform}' 失败: {str(e)}")
|
||||||
if not message.message_info.platform:
|
traceback.print_exc()
|
||||||
raise ValueError(f"未找到平台:{message.message_info.platform} 的url配置,请检查配置文件") from e
|
|
||||||
raise e # 重新抛出其他异常
|
raise e # 重新抛出其他异常
|
||||||
|
|
||||||
|
|
||||||
@@ -66,21 +66,24 @@ class HeartFCSender:
|
|||||||
del self.thinking_messages[chat_id]
|
del self.thinking_messages[chat_id]
|
||||||
logger.debug(f"[{chat_id}] Removed empty thinking message container.")
|
logger.debug(f"[{chat_id}] Removed empty thinking message container.")
|
||||||
|
|
||||||
def is_thinking(self, chat_id: str, message_id: str) -> bool:
|
|
||||||
"""检查指定的消息 ID 是否当前正处于思考状态。"""
|
|
||||||
return chat_id in self.thinking_messages and message_id in self.thinking_messages[chat_id]
|
|
||||||
|
|
||||||
async def get_thinking_start_time(self, chat_id: str, message_id: str) -> Optional[float]:
|
async def get_thinking_start_time(self, chat_id: str, message_id: str) -> Optional[float]:
|
||||||
"""获取已注册思考消息的开始时间。"""
|
"""获取已注册思考消息的开始时间。"""
|
||||||
async with self._thinking_lock:
|
async with self._thinking_lock:
|
||||||
thinking_message = self.thinking_messages.get(chat_id, {}).get(message_id)
|
thinking_message = self.thinking_messages.get(chat_id, {}).get(message_id)
|
||||||
return thinking_message.thinking_start_time if thinking_message else None
|
return thinking_message.thinking_start_time if thinking_message else None
|
||||||
|
|
||||||
async def type_and_send_message(self, message: MessageSending, typing=False):
|
async def send_message(self, message: MessageSending, has_thinking=False, typing=False):
|
||||||
"""
|
"""
|
||||||
立即处理、发送并存储单个 MessageSending 消息。
|
处理、发送并存储一条消息。
|
||||||
调用此方法前,应先调用 register_thinking 注册对应的思考消息。
|
|
||||||
此方法执行后会调用 complete_thinking 清理思考状态。
|
参数:
|
||||||
|
message: MessageSending 对象,待发送的消息。
|
||||||
|
has_thinking: 是否管理思考状态,表情包无思考状态(如需调用 register_thinking/complete_thinking)。
|
||||||
|
typing: 是否模拟打字等待(根据 has_thinking 控制等待时长)。
|
||||||
|
|
||||||
|
用法:
|
||||||
|
- has_thinking=True 时,自动处理思考消息的时间和清理。
|
||||||
|
- typing=True 时,发送前会有打字等待。
|
||||||
"""
|
"""
|
||||||
if not message.chat_stream:
|
if not message.chat_stream:
|
||||||
logger.error("消息缺少 chat_stream,无法发送")
|
logger.error("消息缺少 chat_stream,无法发送")
|
||||||
@@ -93,6 +96,7 @@ class HeartFCSender:
|
|||||||
message_id = message.message_info.message_id
|
message_id = message.message_info.message_id
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if has_thinking:
|
||||||
_ = message.update_thinking_time()
|
_ = message.update_thinking_time()
|
||||||
|
|
||||||
# --- 条件应用 set_reply 逻辑 ---
|
# --- 条件应用 set_reply 逻辑 ---
|
||||||
@@ -102,18 +106,19 @@ class HeartFCSender:
|
|||||||
and message.reply.processed_plain_text != "[System Trigger Context]"
|
and message.reply.processed_plain_text != "[System Trigger Context]"
|
||||||
):
|
):
|
||||||
logger.debug(f"[{chat_id}] 应用 set_reply 逻辑: {message.processed_plain_text[:20]}...")
|
logger.debug(f"[{chat_id}] 应用 set_reply 逻辑: {message.processed_plain_text[:20]}...")
|
||||||
message.set_reply(message.reply)
|
|
||||||
# --- 结束条件 set_reply ---
|
|
||||||
|
|
||||||
await message.process()
|
await message.process()
|
||||||
|
|
||||||
if typing:
|
if typing:
|
||||||
|
if has_thinking:
|
||||||
typing_time = calculate_typing_time(
|
typing_time = calculate_typing_time(
|
||||||
input_string=message.processed_plain_text,
|
input_string=message.processed_plain_text,
|
||||||
thinking_start_time=message.thinking_start_time,
|
thinking_start_time=message.thinking_start_time,
|
||||||
is_emoji=message.is_emoji,
|
is_emoji=message.is_emoji,
|
||||||
)
|
)
|
||||||
await asyncio.sleep(typing_time)
|
await asyncio.sleep(typing_time)
|
||||||
|
else:
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
|
||||||
await send_message(message)
|
await send_message(message)
|
||||||
await self.storage.store_message(message, message.chat_stream)
|
await self.storage.store_message(message, message.chat_stream)
|
||||||
@@ -123,30 +128,3 @@ class HeartFCSender:
|
|||||||
raise e
|
raise e
|
||||||
finally:
|
finally:
|
||||||
await self.complete_thinking(chat_id, message_id)
|
await self.complete_thinking(chat_id, message_id)
|
||||||
|
|
||||||
async def send_and_store(self, message: MessageSending):
|
|
||||||
"""处理、发送并存储单个消息,不涉及思考状态管理。"""
|
|
||||||
if not message.chat_stream:
|
|
||||||
logger.error(f"[{message.message_info.platform or 'UnknownPlatform'}] 消息缺少 chat_stream,无法发送")
|
|
||||||
return
|
|
||||||
if not message.message_info or not message.message_info.message_id:
|
|
||||||
logger.error(
|
|
||||||
f"[{message.chat_stream.stream_id if message.chat_stream else 'UnknownStream'}] 消息缺少 message_info 或 message_id,无法发送"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
chat_id = message.chat_stream.stream_id
|
|
||||||
message_id = message.message_info.message_id # 获取消息ID用于日志
|
|
||||||
|
|
||||||
try:
|
|
||||||
await message.process()
|
|
||||||
|
|
||||||
await asyncio.sleep(0.5)
|
|
||||||
|
|
||||||
await send_message(message) # 使用现有的发送方法
|
|
||||||
await self.storage.store_message(message, message.chat_stream) # 使用现有的存储方法
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"[{chat_id}] 处理或存储消息 {message_id} 时出错: {e}")
|
|
||||||
# 重新抛出异常,让调用者知道失败了
|
|
||||||
raise e
|
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ from maim_message import Seg
|
|||||||
from src.chat.heart_flow.heartflow import heartflow
|
from src.chat.heart_flow.heartflow import heartflow
|
||||||
from src.common.logger_manager import get_logger
|
from src.common.logger_manager import get_logger
|
||||||
from ..message_receive.chat_stream import chat_manager
|
from ..message_receive.chat_stream import chat_manager
|
||||||
from ..message_receive.message_buffer import message_buffer
|
|
||||||
|
# from ..message_receive.message_buffer import message_buffer
|
||||||
from ..utils.timer_calculator import Timer
|
from ..utils.timer_calculator import Timer
|
||||||
from src.chat.person_info.relationship_manager import relationship_manager
|
from src.chat.person_info.relationship_manager import relationship_manager
|
||||||
from typing import Optional, Tuple, Dict, Any
|
from typing import Optional, Tuple, Dict, Any
|
||||||
@@ -169,7 +170,7 @@ class HeartFCProcessor:
|
|||||||
messageinfo = message.message_info
|
messageinfo = message.message_info
|
||||||
|
|
||||||
# 2. 消息缓冲与流程序化
|
# 2. 消息缓冲与流程序化
|
||||||
await message_buffer.start_caching_messages(message)
|
# await message_buffer.start_caching_messages(message)
|
||||||
|
|
||||||
chat = await chat_manager.get_or_create_stream(
|
chat = await chat_manager.get_or_create_stream(
|
||||||
platform=messageinfo.platform,
|
platform=messageinfo.platform,
|
||||||
@@ -188,16 +189,16 @@ class HeartFCProcessor:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# 4. 缓冲检查
|
# 4. 缓冲检查
|
||||||
buffer_result = await message_buffer.query_buffer_result(message)
|
# buffer_result = await message_buffer.query_buffer_result(message)
|
||||||
if not buffer_result:
|
# if not buffer_result:
|
||||||
msg_type = _get_message_type(message)
|
# msg_type = _get_message_type(message)
|
||||||
type_messages = {
|
# type_messages = {
|
||||||
"text": f"触发缓冲,消息:{message.processed_plain_text}",
|
# "text": f"触发缓冲,消息:{message.processed_plain_text}",
|
||||||
"image": "触发缓冲,表情包/图片等待中",
|
# "image": "触发缓冲,表情包/图片等待中",
|
||||||
"seglist": "触发缓冲,消息列表等待中",
|
# "seglist": "触发缓冲,消息列表等待中",
|
||||||
}
|
# }
|
||||||
logger.debug(type_messages.get(msg_type, "触发未知类型缓冲"))
|
# logger.debug(type_messages.get(msg_type, "触发未知类型缓冲"))
|
||||||
return
|
# return
|
||||||
|
|
||||||
# 5. 消息存储
|
# 5. 消息存储
|
||||||
await self.storage.store_message(message, chat)
|
await self.storage.store_message(message, chat)
|
||||||
@@ -210,7 +211,7 @@ class HeartFCProcessor:
|
|||||||
|
|
||||||
# 7. 日志记录
|
# 7. 日志记录
|
||||||
mes_name = chat.group_info.group_name if chat.group_info else "私聊"
|
mes_name = chat.group_info.group_name if chat.group_info else "私聊"
|
||||||
current_time = time.strftime("%H点%M分%S秒", time.localtime(message.message_info.time))
|
current_time = time.strftime("%H:%M:%S", time.localtime(message.message_info.time))
|
||||||
logger.info(
|
logger.info(
|
||||||
f"[{current_time}][{mes_name}]"
|
f"[{current_time}][{mes_name}]"
|
||||||
f"{userinfo.user_nickname}:"
|
f"{userinfo.user_nickname}:"
|
||||||
|
|||||||
@@ -32,11 +32,11 @@ def init_prompt():
|
|||||||
以上是聊天内容,你需要了解聊天记录中的内容
|
以上是聊天内容,你需要了解聊天记录中的内容
|
||||||
|
|
||||||
{chat_target}
|
{chat_target}
|
||||||
你的名字是{bot_name},{prompt_personality},在这聊天中,"{target_message}"引起了你的注意,你想表达:{in_mind_reply},原因是:{reason}。你现在要思考怎么回复
|
你的名字是{bot_name},{prompt_personality},在这聊天中,"{target_message}"引起了你的注意,对这句话,你想表达:{in_mind_reply},原因是:{reason}。你现在要思考怎么回复
|
||||||
你需要使用合适的语法和句法,参考聊天内容,组织一条日常且口语化的回复。
|
你需要使用合适的语法和句法,参考聊天内容,组织一条日常且口语化的回复。
|
||||||
请你根据情景使用以下句法:
|
请你根据情景使用以下句法:
|
||||||
{grammar_habbits}
|
{grammar_habbits}
|
||||||
回复尽量简短一些。可以参考贴吧,知乎和微博的回复风格,你可以完全重组回复,保留最基本的表达含义就好,但注意回复要简短。
|
回复尽量简短一些。可以参考贴吧,知乎和微博的回复风格,你可以完全重组回复,保留最基本的表达含义就好,但注意回复要简短,但重组后保持语意通顺。
|
||||||
回复不要浮夸,不要用夸张修辞,平淡一些。不要输出多余内容(包括前后缀,冒号和引号,括号,表情包,at或 @等 ),只输出一条回复就好。
|
回复不要浮夸,不要用夸张修辞,平淡一些。不要输出多余内容(包括前后缀,冒号和引号,括号,表情包,at或 @等 ),只输出一条回复就好。
|
||||||
现在,你说:
|
现在,你说:
|
||||||
""",
|
""",
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import time
|
import time
|
||||||
import traceback
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from src.chat.message_receive.message import MessageRecv, BaseMessageInfo
|
from src.chat.message_receive.message import MessageRecv, BaseMessageInfo
|
||||||
from src.chat.message_receive.chat_stream import ChatStream
|
from src.chat.message_receive.chat_stream import ChatStream
|
||||||
@@ -10,7 +9,7 @@ import json
|
|||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def _create_empty_anchor_message(
|
async def create_empty_anchor_message(
|
||||||
platform: str, group_info: dict, chat_stream: ChatStream
|
platform: str, group_info: dict, chat_stream: ChatStream
|
||||||
) -> Optional[MessageRecv]:
|
) -> Optional[MessageRecv]:
|
||||||
"""
|
"""
|
||||||
@@ -18,7 +17,6 @@ async def _create_empty_anchor_message(
|
|||||||
如果重构失败或观察为空,则创建一个占位符。
|
如果重构失败或观察为空,则创建一个占位符。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
|
||||||
placeholder_id = f"mid_pf_{int(time.time() * 1000)}"
|
placeholder_id = f"mid_pf_{int(time.time() * 1000)}"
|
||||||
placeholder_user = UserInfo(user_id="system_trigger", user_nickname="System Trigger", platform=platform)
|
placeholder_user = UserInfo(user_id="system_trigger", user_nickname="System Trigger", platform=platform)
|
||||||
placeholder_msg_info = BaseMessageInfo(
|
placeholder_msg_info = BaseMessageInfo(
|
||||||
@@ -36,13 +34,19 @@ async def _create_empty_anchor_message(
|
|||||||
}
|
}
|
||||||
anchor_message = MessageRecv(placeholder_msg_dict)
|
anchor_message = MessageRecv(placeholder_msg_dict)
|
||||||
anchor_message.update_chat_stream(chat_stream)
|
anchor_message.update_chat_stream(chat_stream)
|
||||||
logger.debug(f"创建占位符锚点消息: ID={anchor_message.message_info.message_id}")
|
|
||||||
return anchor_message
|
return anchor_message
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error getting/creating anchor message: {e}")
|
def parse_thinking_id_to_timestamp(thinking_id: str) -> float:
|
||||||
logger.error(traceback.format_exc())
|
"""
|
||||||
return None
|
将形如 'tid<timestamp>' 的 thinking_id 解析回 float 时间戳
|
||||||
|
例如: 'tid1718251234.56' -> 1718251234.56
|
||||||
|
"""
|
||||||
|
if not thinking_id.startswith("tid"):
|
||||||
|
raise ValueError("thinking_id 格式不正确")
|
||||||
|
ts_str = thinking_id[3:]
|
||||||
|
return float(ts_str)
|
||||||
|
|
||||||
|
|
||||||
def get_keywords_from_json(json_str: str) -> list[str]:
|
def get_keywords_from_json(json_str: str) -> list[str]:
|
||||||
|
|||||||
@@ -94,13 +94,23 @@ class RelationshipManager:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def first_knowing_some_one(platform, user_id, user_nickname, user_cardname, user_avatar):
|
async def first_knowing_some_one(
|
||||||
|
platform: str, user_id: str, user_nickname: str, user_cardname: str, user_avatar: str
|
||||||
|
):
|
||||||
"""判断是否认识某人"""
|
"""判断是否认识某人"""
|
||||||
person_id = person_info_manager.get_person_id(platform, user_id)
|
person_id = person_info_manager.get_person_id(platform, user_id)
|
||||||
await person_info_manager.update_one_field(person_id, "nickname", user_nickname)
|
data = {
|
||||||
# await person_info_manager.update_one_field(person_id, "user_cardname", user_cardname)
|
"platform": platform,
|
||||||
# await person_info_manager.update_one_field(person_id, "user_avatar", user_avatar)
|
"user_id": user_id,
|
||||||
await person_info_manager.qv_person_name(person_id, user_nickname, user_cardname, user_avatar)
|
"nickname": user_nickname,
|
||||||
|
"konw_time": int(time.time()),
|
||||||
|
}
|
||||||
|
await person_info_manager.update_one_field(
|
||||||
|
person_id=person_id, field_name="nickname", value=user_nickname, data=data
|
||||||
|
)
|
||||||
|
await person_info_manager.qv_person_name(
|
||||||
|
person_id=person_id, user_nickname=user_nickname, user_cardname=user_cardname, user_avatar=user_avatar
|
||||||
|
)
|
||||||
|
|
||||||
async def calculate_update_relationship_value(self, user_info: UserInfo, platform: str, label: str, stance: str):
|
async def calculate_update_relationship_value(self, user_info: UserInfo, platform: str, label: str, stance: str):
|
||||||
"""计算并变更关系值
|
"""计算并变更关系值
|
||||||
|
|||||||
@@ -622,12 +622,6 @@ class BotConfig:
|
|||||||
# config.ban_user_id = set(groups_config.get("ban_user_id", []))
|
# config.ban_user_id = set(groups_config.get("ban_user_id", []))
|
||||||
config.ban_user_id = set(str(user) for user in groups_config.get("ban_user_id", []))
|
config.ban_user_id = set(str(user) for user in groups_config.get("ban_user_id", []))
|
||||||
|
|
||||||
def platforms(parent: dict):
|
|
||||||
platforms_config = parent["platforms"]
|
|
||||||
if platforms_config and isinstance(platforms_config, dict):
|
|
||||||
for k in platforms_config.keys():
|
|
||||||
config.api_urls[k] = platforms_config[k]
|
|
||||||
|
|
||||||
def experimental(parent: dict):
|
def experimental(parent: dict):
|
||||||
experimental_config = parent["experimental"]
|
experimental_config = parent["experimental"]
|
||||||
config.enable_friend_chat = experimental_config.get("enable_friend_chat", config.enable_friend_chat)
|
config.enable_friend_chat = experimental_config.get("enable_friend_chat", config.enable_friend_chat)
|
||||||
@@ -662,7 +656,6 @@ class BotConfig:
|
|||||||
"remote": {"func": remote, "support": ">=0.0.10", "necessary": False},
|
"remote": {"func": remote, "support": ">=0.0.10", "necessary": False},
|
||||||
"keywords_reaction": {"func": keywords_reaction, "support": ">=0.0.2", "necessary": False},
|
"keywords_reaction": {"func": keywords_reaction, "support": ">=0.0.2", "necessary": False},
|
||||||
"chinese_typo": {"func": chinese_typo, "support": ">=0.0.3", "necessary": False},
|
"chinese_typo": {"func": chinese_typo, "support": ">=0.0.3", "necessary": False},
|
||||||
"platforms": {"func": platforms, "support": ">=1.0.0"},
|
|
||||||
"response_splitter": {"func": response_splitter, "support": ">=0.0.11", "necessary": False},
|
"response_splitter": {"func": response_splitter, "support": ">=0.0.11", "necessary": False},
|
||||||
"experimental": {"func": experimental, "support": ">=0.0.11", "necessary": False},
|
"experimental": {"func": experimental, "support": ">=0.0.11", "necessary": False},
|
||||||
"chat": {"func": chat, "support": ">=1.6.0", "necessary": False},
|
"chat": {"func": chat, "support": ">=1.6.0", "necessary": False},
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ gender = "男" # 性别
|
|||||||
appearance = "用几句话描述外貌特征" # 外貌特征 该选项还在调试中,暂时未生效
|
appearance = "用几句话描述外貌特征" # 外貌特征 该选项还在调试中,暂时未生效
|
||||||
|
|
||||||
[platforms] # 必填项目,填写每个平台适配器提供的链接
|
[platforms] # 必填项目,填写每个平台适配器提供的链接
|
||||||
nonebot-qq="http://127.0.0.1:18002/api/message"
|
qq="http://127.0.0.1:18002/api/message"
|
||||||
|
|
||||||
[chat] #麦麦的聊天通用设置
|
[chat] #麦麦的聊天通用设置
|
||||||
allow_focus_mode = false # 是否允许专注聊天状态
|
allow_focus_mode = false # 是否允许专注聊天状态
|
||||||
|
|||||||
Reference in New Issue
Block a user