feat:修复no_reply起始时间,移除normal消息管理器,不再并行生成回复,为focus提供退出方法
This commit is contained in:
@@ -1,8 +1,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from collections import deque
|
from typing import Optional, List
|
||||||
from typing import Optional, Deque, List
|
|
||||||
|
|
||||||
from src.chat.message_receive.chat_stream import get_chat_manager
|
from src.chat.message_receive.chat_stream import get_chat_manager
|
||||||
from rich.traceback import install
|
from rich.traceback import install
|
||||||
@@ -16,9 +15,9 @@ from src.config.config import global_config
|
|||||||
from src.person_info.relationship_builder_manager import relationship_builder_manager
|
from src.person_info.relationship_builder_manager import relationship_builder_manager
|
||||||
from src.chat.focus_chat.hfc_utils import CycleDetail
|
from src.chat.focus_chat.hfc_utils import CycleDetail
|
||||||
from random import random
|
from random import random
|
||||||
from src.chat.focus_chat.hfc_utils import create_thinking_message_from_dict, add_messages_to_manager,get_recent_message_stats,cleanup_thinking_message_by_id
|
from src.chat.focus_chat.hfc_utils import get_recent_message_stats
|
||||||
from src.person_info.person_info import get_person_info_manager
|
from src.person_info.person_info import get_person_info_manager
|
||||||
from src.plugin_system.apis import generator_api
|
from src.plugin_system.apis import generator_api,send_api,message_api
|
||||||
from src.chat.willing.willing_manager import get_willing_manager
|
from src.chat.willing.willing_manager import get_willing_manager
|
||||||
from .priority_manager import PriorityManager
|
from .priority_manager import PriorityManager
|
||||||
from src.chat.utils.chat_message_builder import get_raw_msg_by_timestamp_with_chat
|
from src.chat.utils.chat_message_builder import get_raw_msg_by_timestamp_with_chat
|
||||||
@@ -201,14 +200,22 @@ class HeartFChatting:
|
|||||||
|
|
||||||
async def _loopbody(self):
|
async def _loopbody(self):
|
||||||
if self.loop_mode == "focus":
|
if self.loop_mode == "focus":
|
||||||
|
|
||||||
|
self.energy_value -= 5 * (1/global_config.chat.exit_focus_threshold)
|
||||||
|
if self.energy_value <= 0:
|
||||||
|
self.loop_mode = "normal"
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
return await self._observe()
|
return await self._observe()
|
||||||
elif self.loop_mode == "normal":
|
elif self.loop_mode == "normal":
|
||||||
new_messages_data = get_raw_msg_by_timestamp_with_chat(
|
new_messages_data = get_raw_msg_by_timestamp_with_chat(
|
||||||
chat_id=self.stream_id, timestamp_start=self.last_read_time, timestamp_end=time.time(),limit=10,limit_mode="earliest",fliter_bot=True
|
chat_id=self.stream_id, timestamp_start=self.last_read_time, timestamp_end=time.time(),limit=10,limit_mode="earliest",fliter_bot=True
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(new_messages_data) > 5:
|
if len(new_messages_data) > 4 * global_config.chat.auto_focus_threshold:
|
||||||
self.loop_mode = "focus"
|
self.loop_mode = "focus"
|
||||||
|
self.energy_value = 100
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if new_messages_data:
|
if new_messages_data:
|
||||||
@@ -228,10 +235,8 @@ class HeartFChatting:
|
|||||||
# 创建新的循环信息
|
# 创建新的循环信息
|
||||||
cycle_timers, thinking_id = self.start_cycle()
|
cycle_timers, thinking_id = self.start_cycle()
|
||||||
|
|
||||||
logger.info(f"{self.log_prefix} 开始第{self._cycle_counter}次思考")
|
logger.info(f"{self.log_prefix} 开始第{self._cycle_counter}次思考[模式:{self.loop_mode}]")
|
||||||
|
|
||||||
if message_data:
|
|
||||||
await create_thinking_message_from_dict(message_data,self.chat_stream,thinking_id)
|
|
||||||
|
|
||||||
async with global_prompt_manager.async_message_scope(
|
async with global_prompt_manager.async_message_scope(
|
||||||
self.chat_stream.context.get_template_name()
|
self.chat_stream.context.get_template_name()
|
||||||
@@ -257,7 +262,14 @@ class HeartFChatting:
|
|||||||
|
|
||||||
#如果normal,开始一个回复生成进程,先准备好回复(其实是和planer同时进行的)
|
#如果normal,开始一个回复生成进程,先准备好回复(其实是和planer同时进行的)
|
||||||
if self.loop_mode == "normal":
|
if self.loop_mode == "normal":
|
||||||
gen_task = asyncio.create_task(self._generate_normal_response(message_data, available_actions))
|
person_info_manager = get_person_info_manager()
|
||||||
|
person_id = person_info_manager.get_person_id(
|
||||||
|
message_data.get("chat_info_platform"), message_data.get("user_id")
|
||||||
|
)
|
||||||
|
person_name = await person_info_manager.get_value(person_id, "person_name")
|
||||||
|
reply_to_str = f"{person_name}:{message_data.get('processed_plain_text')}"
|
||||||
|
|
||||||
|
gen_task = asyncio.create_task(self._generate_response(message_data, available_actions,reply_to_str))
|
||||||
|
|
||||||
|
|
||||||
with Timer("规划器", cycle_timers):
|
with Timer("规划器", cycle_timers):
|
||||||
@@ -299,6 +311,7 @@ class HeartFChatting:
|
|||||||
|
|
||||||
|
|
||||||
if action_type == "no_action":
|
if action_type == "no_action":
|
||||||
|
# 等待回复生成完毕
|
||||||
gather_timeout = global_config.chat.thinking_timeout
|
gather_timeout = global_config.chat.thinking_timeout
|
||||||
try:
|
try:
|
||||||
response_set = await asyncio.wait_for(gen_task, timeout=gather_timeout)
|
response_set = await asyncio.wait_for(gen_task, timeout=gather_timeout)
|
||||||
@@ -308,7 +321,7 @@ class HeartFChatting:
|
|||||||
if response_set:
|
if response_set:
|
||||||
content = " ".join([item[1] for item in response_set if item[0] == "text"])
|
content = " ".join([item[1] for item in response_set if item[0] == "text"])
|
||||||
|
|
||||||
|
# 模型炸了,没有回复内容生成
|
||||||
if not response_set or (
|
if not response_set or (
|
||||||
action_type not in ["no_action"] and not is_parallel
|
action_type not in ["no_action"] and not is_parallel
|
||||||
):
|
):
|
||||||
@@ -318,25 +331,15 @@ class HeartFChatting:
|
|||||||
logger.info(
|
logger.info(
|
||||||
f"[{self.log_prefix}] {global_config.bot.nickname} 原本想要回复:{content},但选择执行{action_type},不发表回复"
|
f"[{self.log_prefix}] {global_config.bot.nickname} 原本想要回复:{content},但选择执行{action_type},不发表回复"
|
||||||
)
|
)
|
||||||
# 如果模型未生成回复,移除思考消息
|
|
||||||
await cleanup_thinking_message_by_id(self.chat_stream.stream_id,thinking_id,self.log_prefix)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logger.info(f"[{self.log_prefix}] {global_config.bot.nickname} 决定的回复内容: {content}")
|
logger.info(f"[{self.log_prefix}] {global_config.bot.nickname} 决定的回复内容: {content}")
|
||||||
|
|
||||||
# 提取回复文本
|
|
||||||
reply_texts = [item[1] for item in response_set if item[0] == "text"]
|
|
||||||
if not reply_texts:
|
|
||||||
logger.info(f"[{self.log_prefix}] 回复内容中没有文本,不发送消息")
|
|
||||||
await cleanup_thinking_message_by_id(self.chat_stream.stream_id,thinking_id,self.log_prefix)
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 发送回复 (不再需要传入 chat)
|
# 发送回复 (不再需要传入 chat)
|
||||||
await add_messages_to_manager(message_data, reply_texts, thinking_id,self.chat_stream.stream_id)
|
await self._send_response(response_set, reply_to_str, loop_start_time)
|
||||||
|
|
||||||
return response_set if response_set else False
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -465,7 +468,7 @@ class HeartFChatting:
|
|||||||
# 新增:消息计数和疲惫检查
|
# 新增:消息计数和疲惫检查
|
||||||
if action == "reply" and success:
|
if action == "reply" and success:
|
||||||
self._message_count += 1
|
self._message_count += 1
|
||||||
current_threshold = self._get_current_fatigue_threshold()
|
current_threshold = max(10, int(30 / global_config.chat.exit_focus_threshold))
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{self.log_prefix} 已发送第 {self._message_count} 条消息(动态阈值: {current_threshold}, exit_focus_threshold: {global_config.chat.exit_focus_threshold})"
|
f"{self.log_prefix} 已发送第 {self._message_count} 条消息(动态阈值: {current_threshold}, exit_focus_threshold: {global_config.chat.exit_focus_threshold})"
|
||||||
)
|
)
|
||||||
@@ -486,14 +489,6 @@ class HeartFChatting:
|
|||||||
return command
|
return command
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def _get_current_fatigue_threshold(self) -> int:
|
|
||||||
"""动态获取当前的疲惫阈值,基于exit_focus_threshold配置
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: 当前的疲惫阈值
|
|
||||||
"""
|
|
||||||
return max(10, int(30 / global_config.chat.exit_focus_threshold))
|
|
||||||
|
|
||||||
|
|
||||||
async def shutdown(self):
|
async def shutdown(self):
|
||||||
"""优雅关闭HeartFChatting实例,取消活动循环任务"""
|
"""优雅关闭HeartFChatting实例,取消活动循环任务"""
|
||||||
@@ -653,21 +648,14 @@ class HeartFChatting:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def _generate_normal_response(
|
async def _generate_response(
|
||||||
self, message_data: dict, available_actions: Optional[list]
|
self, message_data: dict, available_actions: Optional[list],reply_to:str
|
||||||
) -> Optional[list]:
|
) -> Optional[list]:
|
||||||
"""生成普通回复"""
|
"""生成普通回复"""
|
||||||
try:
|
try:
|
||||||
person_info_manager = get_person_info_manager()
|
|
||||||
person_id = person_info_manager.get_person_id(
|
|
||||||
message_data.get("chat_info_platform"), message_data.get("user_id")
|
|
||||||
)
|
|
||||||
person_name = await person_info_manager.get_value(person_id, "person_name")
|
|
||||||
reply_to_str = f"{person_name}:{message_data.get('processed_plain_text')}"
|
|
||||||
|
|
||||||
success, reply_set = await generator_api.generate_reply(
|
success, reply_set = await generator_api.generate_reply(
|
||||||
chat_stream=self.chat_stream,
|
chat_stream=self.chat_stream,
|
||||||
reply_to=reply_to_str,
|
reply_to=reply_to,
|
||||||
available_actions=available_actions,
|
available_actions=available_actions,
|
||||||
enable_tool=global_config.tool.enable_in_normal_chat,
|
enable_tool=global_config.tool.enable_in_normal_chat,
|
||||||
request_type="normal.replyer",
|
request_type="normal.replyer",
|
||||||
@@ -682,3 +670,37 @@ class HeartFChatting:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[{self.log_prefix}] 回复生成出现错误:{str(e)} {traceback.format_exc()}")
|
logger.error(f"[{self.log_prefix}] 回复生成出现错误:{str(e)} {traceback.format_exc()}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
async def _send_response(
|
||||||
|
self, reply_set, reply_to, thinking_start_time
|
||||||
|
):
|
||||||
|
current_time = time.time()
|
||||||
|
new_message_count = message_api.count_new_messages(
|
||||||
|
chat_id=self.chat_stream.stream_id, start_time=thinking_start_time, end_time=current_time
|
||||||
|
)
|
||||||
|
|
||||||
|
need_reply = new_message_count >= random.randint(2, 4)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,{'使用' if need_reply else '不使用'}引用回复"
|
||||||
|
)
|
||||||
|
|
||||||
|
reply_text = ""
|
||||||
|
first_replyed = False
|
||||||
|
for reply_seg in reply_set:
|
||||||
|
data = reply_seg[1]
|
||||||
|
if not first_replyed:
|
||||||
|
if need_reply:
|
||||||
|
await send_api.text_to_stream(text=data, stream_id=self.chat_stream.stream_id, reply_to=reply_to, typing=False)
|
||||||
|
first_replyed = True
|
||||||
|
else:
|
||||||
|
await send_api.text_to_stream(text=data, stream_id=self.chat_stream.stream_id, typing=False)
|
||||||
|
first_replyed = True
|
||||||
|
else:
|
||||||
|
await send_api.text_to_stream(text=data, stream_id=self.chat_stream.stream_id, typing=True)
|
||||||
|
reply_text += data
|
||||||
|
|
||||||
|
return reply_text
|
||||||
|
|
||||||
|
|
||||||
@@ -7,11 +7,7 @@ from typing import Dict, Any
|
|||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
from src.chat.message_receive.message import MessageThinking
|
from src.chat.message_receive.message import MessageThinking
|
||||||
from src.chat.message_receive.normal_message_sender import message_manager
|
from src.chat.message_receive.normal_message_sender import message_manager
|
||||||
from typing import List
|
|
||||||
from maim_message import Seg
|
|
||||||
from src.common.message_repository import count_messages
|
from src.common.message_repository import count_messages
|
||||||
from ..message_receive.message import MessageSending, MessageSet, message_from_db_dict
|
|
||||||
from src.chat.message_receive.chat_stream import get_chat_manager
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -123,71 +119,6 @@ async def cleanup_thinking_message_by_id(chat_id: str, thinking_id: str, log_pre
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{log_prefix} 清理思考消息 {thinking_id} 时出错: {e}")
|
logger.error(f"{log_prefix} 清理思考消息 {thinking_id} 时出错: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def add_messages_to_manager(
|
|
||||||
message_data: dict, response_set: List[str], thinking_id, chat_id
|
|
||||||
) -> Optional[MessageSending]:
|
|
||||||
"""发送回复消息"""
|
|
||||||
|
|
||||||
chat_stream = get_chat_manager().get_stream(chat_id)
|
|
||||||
|
|
||||||
container = await message_manager.get_container(chat_id) # 使用 self.stream_id
|
|
||||||
thinking_message = None
|
|
||||||
|
|
||||||
for msg in container.messages[:]:
|
|
||||||
# print(msg)
|
|
||||||
if isinstance(msg, MessageThinking) and msg.message_info.message_id == thinking_id:
|
|
||||||
thinking_message = msg
|
|
||||||
container.messages.remove(msg)
|
|
||||||
break
|
|
||||||
|
|
||||||
if not thinking_message:
|
|
||||||
logger.warning(f"[{chat_id}] 未找到对应的思考消息 {thinking_id},可能已超时被移除")
|
|
||||||
return None
|
|
||||||
|
|
||||||
thinking_start_time = thinking_message.thinking_start_time
|
|
||||||
message_set = MessageSet(chat_stream, thinking_id) # 使用 self.chat_stream
|
|
||||||
|
|
||||||
sender_info = UserInfo(
|
|
||||||
user_id=message_data.get("user_id"),
|
|
||||||
user_nickname=message_data.get("user_nickname"),
|
|
||||||
platform=message_data.get("chat_info_platform"),
|
|
||||||
)
|
|
||||||
|
|
||||||
reply = message_from_db_dict(message_data)
|
|
||||||
|
|
||||||
|
|
||||||
mark_head = False
|
|
||||||
first_bot_msg = None
|
|
||||||
for msg in response_set:
|
|
||||||
if global_config.debug.debug_show_chat_mode:
|
|
||||||
msg += "ⁿ"
|
|
||||||
message_segment = Seg(type="text", data=msg)
|
|
||||||
bot_message = MessageSending(
|
|
||||||
message_id=thinking_id,
|
|
||||||
chat_stream=chat_stream, # 使用 self.chat_stream
|
|
||||||
bot_user_info=UserInfo(
|
|
||||||
user_id=global_config.bot.qq_account,
|
|
||||||
user_nickname=global_config.bot.nickname,
|
|
||||||
platform=message_data.get("chat_info_platform"),
|
|
||||||
),
|
|
||||||
sender_info=sender_info,
|
|
||||||
message_segment=message_segment,
|
|
||||||
reply=reply,
|
|
||||||
is_head=not mark_head,
|
|
||||||
is_emoji=False,
|
|
||||||
thinking_start_time=thinking_start_time,
|
|
||||||
apply_set_reply_logic=True,
|
|
||||||
)
|
|
||||||
if not mark_head:
|
|
||||||
mark_head = True
|
|
||||||
first_bot_msg = bot_message
|
|
||||||
message_set.add_message(bot_message)
|
|
||||||
|
|
||||||
await message_manager.add_message(message_set)
|
|
||||||
|
|
||||||
return first_bot_msg
|
|
||||||
|
|
||||||
|
|
||||||
def get_recent_message_stats(minutes: int = 30, chat_id: str = None) -> dict:
|
def get_recent_message_stats(minutes: int = 30, chat_id: str = None) -> dict:
|
||||||
|
|||||||
@@ -280,17 +280,12 @@ class NormalChatConfig(ConfigBase):
|
|||||||
at_bot_inevitable_reply: bool = False
|
at_bot_inevitable_reply: bool = False
|
||||||
"""@bot 必然回复"""
|
"""@bot 必然回复"""
|
||||||
|
|
||||||
enable_planner: bool = False
|
|
||||||
"""是否启用动作规划器"""
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class FocusChatConfig(ConfigBase):
|
class FocusChatConfig(ConfigBase):
|
||||||
"""专注聊天配置类"""
|
"""专注聊天配置类"""
|
||||||
|
|
||||||
think_interval: float = 1
|
|
||||||
"""思考间隔(秒)"""
|
|
||||||
|
|
||||||
consecutive_replies: float = 1
|
consecutive_replies: float = 1
|
||||||
"""连续回复能力,值越高,麦麦连续回复的概率越高"""
|
"""连续回复能力,值越高,麦麦连续回复的概率越高"""
|
||||||
|
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ class NoReplyAction(BaseAction):
|
|||||||
count = NoReplyAction._consecutive_count
|
count = NoReplyAction._consecutive_count
|
||||||
|
|
||||||
reason = self.action_data.get("reason", "")
|
reason = self.action_data.get("reason", "")
|
||||||
start_time = time.time()
|
start_time = self.action_data.get("loop_start_time", time.time())
|
||||||
check_interval = 1.0 # 每秒检查一次
|
check_interval = 0.6 # 每秒检查一次
|
||||||
|
|
||||||
# 随机生成本次等待需要的新消息数量阈值
|
# 随机生成本次等待需要的新消息数量阈值
|
||||||
exit_message_count_threshold = random.randint(self._min_exit_message_count, self._max_exit_message_count)
|
exit_message_count_threshold = random.randint(self._min_exit_message_count, self._max_exit_message_count)
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ class ReplyAction(BaseAction):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# 根据新消息数量决定是否使用reply_to
|
# 根据新消息数量决定是否使用reply_to
|
||||||
need_reply = new_message_count >= random.randint(2, 5)
|
need_reply = new_message_count >= random.randint(2, 4)
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,{'使用' if need_reply else '不使用'}引用回复"
|
f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,{'使用' if need_reply else '不使用'}引用回复"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -119,10 +119,8 @@ willing_mode = "classical" # 回复意愿模式 —— 经典模式:classical
|
|||||||
response_interested_rate_amplifier = 1 # 麦麦回复兴趣度放大系数
|
response_interested_rate_amplifier = 1 # 麦麦回复兴趣度放大系数
|
||||||
mentioned_bot_inevitable_reply = true # 提及 bot 必然回复
|
mentioned_bot_inevitable_reply = true # 提及 bot 必然回复
|
||||||
at_bot_inevitable_reply = true # @bot 必然回复(包含提及)
|
at_bot_inevitable_reply = true # @bot 必然回复(包含提及)
|
||||||
enable_planner = true # 是否启用动作规划器(与focus_chat共享actions)
|
|
||||||
|
|
||||||
[focus_chat] #专注聊天
|
[focus_chat] #专注聊天
|
||||||
think_interval = 3 # 思考间隔 单位秒,可以有效减少消耗
|
|
||||||
consecutive_replies = 1 # 连续回复能力,值越高,麦麦连续回复的概率越高
|
consecutive_replies = 1 # 连续回复能力,值越高,麦麦连续回复的概率越高
|
||||||
|
|
||||||
[tool]
|
[tool]
|
||||||
|
|||||||
Reference in New Issue
Block a user