迁移:a247be0(ref:彻底合并normal和focus,完全基于planner决定target message)
This commit is contained in:
@@ -49,15 +49,21 @@ class CycleProcessor:
|
|||||||
action_message,
|
action_message,
|
||||||
cycle_timers: Dict[str, float],
|
cycle_timers: Dict[str, float],
|
||||||
thinking_id,
|
thinking_id,
|
||||||
plan_result,
|
actions,
|
||||||
) -> Tuple[Dict[str, Any], str, Dict[str, float]]:
|
) -> Tuple[Dict[str, Any], str, Dict[str, float]]:
|
||||||
with Timer("回复发送", cycle_timers):
|
with Timer("回复发送", cycle_timers):
|
||||||
reply_text = await self.response_handler.send_response(response_set, reply_to_str, loop_start_time, action_message)
|
reply_text = await self.response_handler.send_response(response_set, reply_to_str, loop_start_time, action_message)
|
||||||
|
|
||||||
# 存储reply action信息
|
# 存储reply action信息
|
||||||
person_info_manager = get_person_info_manager()
|
person_info_manager = get_person_info_manager()
|
||||||
|
|
||||||
|
# 获取 platform,如果不存在则从 chat_stream 获取,如果还是 None 则使用默认值
|
||||||
|
platform = action_message.get("chat_info_platform")
|
||||||
|
if platform is None:
|
||||||
|
platform = getattr(self.chat_stream, "platform", "unknown")
|
||||||
|
|
||||||
person_id = person_info_manager.get_person_id(
|
person_id = person_info_manager.get_person_id(
|
||||||
action_message.get("chat_info_platform", ""),
|
platform,
|
||||||
action_message.get("user_id", ""),
|
action_message.get("user_id", ""),
|
||||||
)
|
)
|
||||||
person_name = await person_info_manager.get_value(person_id, "person_name")
|
person_name = await person_info_manager.get_value(person_id, "person_name")
|
||||||
@@ -76,7 +82,7 @@ class CycleProcessor:
|
|||||||
# 构建循环信息
|
# 构建循环信息
|
||||||
loop_info: Dict[str, Any] = {
|
loop_info: Dict[str, Any] = {
|
||||||
"loop_plan_info": {
|
"loop_plan_info": {
|
||||||
"action_result": plan_result.get("action_result", {}),
|
"action_result": actions,
|
||||||
},
|
},
|
||||||
"loop_action_info": {
|
"loop_action_info": {
|
||||||
"action_taken": True,
|
"action_taken": True,
|
||||||
@@ -88,12 +94,12 @@ class CycleProcessor:
|
|||||||
|
|
||||||
return loop_info, reply_text, cycle_timers
|
return loop_info, reply_text, cycle_timers
|
||||||
|
|
||||||
async def observe(self, message_data: Optional[Dict[str, Any]] = None) -> bool:
|
async def observe(self,interest_value:float = 0.0) -> bool:
|
||||||
"""
|
"""
|
||||||
观察和处理单次思考循环的核心方法
|
观察和处理单次思考循环的核心方法
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
message_data: 可选的消息数据字典,包含用户消息、平台信息等
|
interest_value: 兴趣值
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: 处理是否成功
|
bool: 处理是否成功
|
||||||
@@ -105,13 +111,40 @@ class CycleProcessor:
|
|||||||
- 执行动作规划或直接回复
|
- 执行动作规划或直接回复
|
||||||
- 根据动作类型分发到相应的处理方法
|
- 根据动作类型分发到相应的处理方法
|
||||||
"""
|
"""
|
||||||
if not message_data:
|
action_type = "no_action"
|
||||||
message_data = {}
|
reply_text = "" # 初始化reply_text变量,避免UnboundLocalError
|
||||||
|
reply_to_str = "" # 初始化reply_to_str变量
|
||||||
|
|
||||||
|
# 根据interest_value计算概率,决定使用哪种planner模式
|
||||||
|
# interest_value越高,越倾向于使用Normal模式
|
||||||
|
import random
|
||||||
|
import math
|
||||||
|
|
||||||
|
# 使用sigmoid函数将interest_value转换为概率
|
||||||
|
# 当interest_value为0时,概率接近0(使用Focus模式)
|
||||||
|
# 当interest_value很高时,概率接近1(使用Normal模式)
|
||||||
|
def calculate_normal_mode_probability(interest_val: float) -> float:
|
||||||
|
# 使用sigmoid函数,调整参数使概率分布更合理
|
||||||
|
# 当interest_value = 0时,概率约为0.1
|
||||||
|
# 当interest_value = 1时,概率约为0.5
|
||||||
|
# 当interest_value = 2时,概率约为0.8
|
||||||
|
# 当interest_value = 3时,概率约为0.95
|
||||||
|
k = 2.0 # 控制曲线陡峭程度
|
||||||
|
x0 = 1.0 # 控制曲线中心点
|
||||||
|
return 1.0 / (1.0 + math.exp(-k * (interest_val - x0)))
|
||||||
|
|
||||||
|
normal_mode_probability = calculate_normal_mode_probability(interest_value)
|
||||||
|
|
||||||
|
# 根据概率决定使用哪种模式
|
||||||
|
if random.random() < normal_mode_probability:
|
||||||
|
mode = ChatMode.NORMAL
|
||||||
|
logger.info(f"{self.log_prefix} 基于兴趣值 {interest_value:.2f},概率 {normal_mode_probability:.2f},选择Normal planner模式")
|
||||||
|
else:
|
||||||
|
mode = ChatMode.FOCUS
|
||||||
|
logger.info(f"{self.log_prefix} 基于兴趣值 {interest_value:.2f},概率 {normal_mode_probability:.2f},选择Focus planner模式")
|
||||||
|
|
||||||
cycle_timers, thinking_id = self.cycle_tracker.start_cycle()
|
cycle_timers, thinking_id = self.cycle_tracker.start_cycle()
|
||||||
logger.info(
|
logger.info(f"{self.log_prefix} 开始第{self.context.cycle_counter}次思考")
|
||||||
f"{self.context.log_prefix} 开始第{self.context.cycle_counter}次思考[模式:{self.context.loop_mode}]"
|
|
||||||
)
|
|
||||||
|
|
||||||
if ENABLE_S4U:
|
if ENABLE_S4U:
|
||||||
await send_typing()
|
await send_typing()
|
||||||
@@ -127,75 +160,26 @@ class CycleProcessor:
|
|||||||
logger.error(f"{self.context.log_prefix} 动作修改失败: {e}")
|
logger.error(f"{self.context.log_prefix} 动作修改失败: {e}")
|
||||||
available_actions = {}
|
available_actions = {}
|
||||||
|
|
||||||
is_mentioned_bot = message_data.get("is_mentioned", False)
|
# 执行planner
|
||||||
at_bot_mentioned = (global_config.chat.mentioned_bot_inevitable_reply and is_mentioned_bot) or (
|
planner_info = self.action_planner.get_necessary_info()
|
||||||
global_config.chat.at_bot_inevitable_reply and is_mentioned_bot
|
prompt_info = await self.action_planner.build_planner_prompt(
|
||||||
|
is_group_chat=planner_info[0],
|
||||||
|
chat_target_info=planner_info[1],
|
||||||
|
current_available_actions=planner_info[2],
|
||||||
)
|
)
|
||||||
|
|
||||||
# 专注模式下提及bot必定回复
|
|
||||||
if self.context.loop_mode == ChatMode.FOCUS and at_bot_mentioned and "no_reply" in available_actions:
|
|
||||||
available_actions = {k: v for k, v in available_actions.items() if k != "no_reply"}
|
|
||||||
|
|
||||||
# 检查是否在normal模式下没有可用动作(除了reply相关动作)
|
|
||||||
skip_planner = False
|
|
||||||
|
|
||||||
if self.context.loop_mode == ChatMode.NORMAL:
|
|
||||||
non_reply_actions = {
|
|
||||||
k: v for k, v in available_actions.items() if k not in ["reply", "no_reply", "no_action"]
|
|
||||||
}
|
|
||||||
if not non_reply_actions:
|
|
||||||
skip_planner = True
|
|
||||||
logger.info(f"Normal模式下没有可用动作,直接回复")
|
|
||||||
plan_result = self._get_direct_reply_plan(loop_start_time)
|
|
||||||
target_message = message_data
|
|
||||||
|
|
||||||
|
|
||||||
# Focus模式
|
|
||||||
if not skip_planner:
|
|
||||||
from src.plugin_system.core.event_manager import event_manager
|
from src.plugin_system.core.event_manager import event_manager
|
||||||
from src.plugin_system.base.component_types import EventType
|
from src.plugin_system import EventType
|
||||||
|
|
||||||
# 触发 ON_PLAN 事件
|
result = await event_manager.trigger_event(EventType.ON_PLAN,plugin_name="SYSTEM", stream_id=self.context.chat_stream)
|
||||||
result = await event_manager.trigger_event(
|
if not result.all_continue_process():
|
||||||
EventType.ON_PLAN, plugin_name="SYSTEM", stream_id=self.context.stream_id
|
raise UserWarning(f"插件{result.get_summary().get('stopped_handlers', '')}于规划前中断了内容生成")
|
||||||
)
|
|
||||||
if result and not result.all_continue_process():
|
|
||||||
return
|
|
||||||
|
|
||||||
with Timer("规划器", cycle_timers):
|
with Timer("规划器", cycle_timers):
|
||||||
plan_result, target_message = await self.action_planner.plan(mode=self.context.loop_mode)
|
actions, _= await self.action_planner.plan(
|
||||||
|
mode=mode,
|
||||||
action_result = plan_result.get("action_result", {})
|
loop_start_time=loop_start_time,
|
||||||
|
available_actions=available_actions,
|
||||||
action_type = action_result.get("action_type", "error")
|
)
|
||||||
action_data = action_result.get("action_data", {})
|
|
||||||
reasoning = action_result.get("reasoning", "未提供理由")
|
|
||||||
is_parallel = action_result.get("is_parallel", True)
|
|
||||||
|
|
||||||
action_data["loop_start_time"] = loop_start_time
|
|
||||||
action_message = message_data or target_message
|
|
||||||
|
|
||||||
# is_private_chat = self.context.chat_stream.group_info is None if self.context.chat_stream else False
|
|
||||||
|
|
||||||
# 重构后的动作处理逻辑:先汇总所有动作,然后并行执行
|
|
||||||
actions = []
|
|
||||||
|
|
||||||
# 1. 添加Planner取得的动作
|
|
||||||
actions.append({
|
|
||||||
"action_type": action_type,
|
|
||||||
"reasoning": reasoning,
|
|
||||||
"action_data": action_data,
|
|
||||||
"action_message": action_message,
|
|
||||||
"available_actions": available_actions # 添加这个字段
|
|
||||||
})
|
|
||||||
|
|
||||||
# 2. 如果不是reply动作且需要并行执行,额外添加reply动作
|
|
||||||
if action_type != "reply" and is_parallel:
|
|
||||||
actions.append({
|
|
||||||
"action_type": "reply",
|
|
||||||
"action_message": action_message,
|
|
||||||
"available_actions": available_actions
|
|
||||||
})
|
|
||||||
|
|
||||||
async def execute_action(action_info):
|
async def execute_action(action_info):
|
||||||
"""执行单个动作的通用函数"""
|
"""执行单个动作的通用函数"""
|
||||||
@@ -242,7 +226,6 @@ class CycleProcessor:
|
|||||||
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"])
|
||||||
request_type = "chat.replyer"
|
|
||||||
|
|
||||||
# 生成回复
|
# 生成回复
|
||||||
gather_timeout = global_config.chat.thinking_timeout
|
gather_timeout = global_config.chat.thinking_timeout
|
||||||
@@ -252,7 +235,7 @@ class CycleProcessor:
|
|||||||
message_data=action_info["action_message"],
|
message_data=action_info["action_message"],
|
||||||
available_actions=action_info["available_actions"],
|
available_actions=action_info["available_actions"],
|
||||||
reply_to=reply_to_str,
|
reply_to=reply_to_str,
|
||||||
request_type=request_type,
|
request_type="chat.replyer",
|
||||||
),
|
),
|
||||||
timeout=gather_timeout
|
timeout=gather_timeout
|
||||||
)
|
)
|
||||||
@@ -291,7 +274,7 @@ class CycleProcessor:
|
|||||||
action_info["action_message"],
|
action_info["action_message"],
|
||||||
cycle_timers,
|
cycle_timers,
|
||||||
thinking_id,
|
thinking_id,
|
||||||
plan_result,
|
actions,
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
"action_type": "reply",
|
"action_type": "reply",
|
||||||
@@ -301,6 +284,7 @@ class CycleProcessor:
|
|||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{self.log_prefix} 执行动作时出错: {e}")
|
logger.error(f"{self.log_prefix} 执行动作时出错: {e}")
|
||||||
|
logger.error(f"{self.log_prefix} 错误信息: {traceback.format_exc()}")
|
||||||
return {
|
return {
|
||||||
"action_type": action_info["action_type"],
|
"action_type": action_info["action_type"],
|
||||||
"success": False,
|
"success": False,
|
||||||
@@ -310,6 +294,7 @@ class CycleProcessor:
|
|||||||
}
|
}
|
||||||
|
|
||||||
# 创建所有动作的后台任务
|
# 创建所有动作的后台任务
|
||||||
|
|
||||||
action_tasks = [asyncio.create_task(execute_action(action)) for action in actions]
|
action_tasks = [asyncio.create_task(execute_action(action)) for action in actions]
|
||||||
|
|
||||||
# 并行执行所有任务
|
# 并行执行所有任务
|
||||||
@@ -356,7 +341,7 @@ class CycleProcessor:
|
|||||||
# 没有回复信息,构建纯动作的loop_info
|
# 没有回复信息,构建纯动作的loop_info
|
||||||
loop_info = {
|
loop_info = {
|
||||||
"loop_plan_info": {
|
"loop_plan_info": {
|
||||||
"action_result": plan_result.get("action_result", {}),
|
"action_result": actions,
|
||||||
},
|
},
|
||||||
"loop_action_info": {
|
"loop_action_info": {
|
||||||
"action_taken": action_success,
|
"action_taken": action_success,
|
||||||
@@ -367,29 +352,21 @@ class CycleProcessor:
|
|||||||
}
|
}
|
||||||
reply_text = action_reply_text
|
reply_text = action_reply_text
|
||||||
|
|
||||||
self.last_action = action_type
|
|
||||||
|
|
||||||
if ENABLE_S4U:
|
if ENABLE_S4U:
|
||||||
await stop_typing()
|
await stop_typing()
|
||||||
|
|
||||||
self.context.chat_instance.cycle_tracker.end_cycle(loop_info, cycle_timers)
|
self.context.chat_instance.cycle_tracker.end_cycle(loop_info, cycle_timers)
|
||||||
self.context.chat_instance.cycle_tracker.print_cycle_info(cycle_timers)
|
self.context.chat_instance.cycle_tracker.print_cycle_info(cycle_timers)
|
||||||
|
|
||||||
if self.context.loop_mode == ChatMode.NORMAL:
|
# await self.willing_manager.after_generate_reply_handle(message_data.get("message_id", ""))
|
||||||
await self.context.chat_instance.willing_manager.after_generate_reply_handle(message_data.get("message_id", ""))
|
action_type = actions[0]["action_type"] if actions else "no_action"
|
||||||
|
|
||||||
# 管理no_reply计数器:当执行了非no_reply动作时,重置计数器
|
# 管理no_reply计数器:当执行了非no_reply动作时,重置计数器
|
||||||
if action_type != "no_reply" and action_type != "no_action":
|
if action_type != "no_reply":
|
||||||
# no_reply逻辑已集成到heartFC_chat.py中,直接重置计数器
|
# no_reply逻辑已集成到heartFC_chat.py中,直接重置计数器
|
||||||
self.context.chat_instance.recent_interest_records.clear()
|
self.context.chat_instance.recent_interest_records.clear()
|
||||||
self.context.no_reply_consecutive = 0
|
self.context.no_reply_consecutive = 0
|
||||||
logger.info(f"{self.log_prefix} 执行了{action_type}动作,重置no_reply计数器")
|
logger.debug(f"{self.log_prefix} 执行了{action_type}动作,重置no_reply计数器")
|
||||||
return True
|
return True
|
||||||
elif action_type == "no_action":
|
|
||||||
# 当执行回复动作时,也重置no_reply计数
|
|
||||||
self.context.chat_instance.recent_interest_records.clear()
|
|
||||||
self.context.no_reply_consecutive = 0
|
|
||||||
logger.info(f"{self.log_prefix} 执行了回复动作,重置no_reply计数器")
|
|
||||||
|
|
||||||
if action_type == "no_reply":
|
if action_type == "no_reply":
|
||||||
self.context.no_reply_consecutive += 1
|
self.context.no_reply_consecutive += 1
|
||||||
|
|||||||
@@ -89,11 +89,27 @@ class CycleTracker:
|
|||||||
formatted_time = f"{elapsed * 1000:.2f}毫秒" if elapsed < 1 else f"{elapsed:.2f}秒"
|
formatted_time = f"{elapsed * 1000:.2f}毫秒" if elapsed < 1 else f"{elapsed:.2f}秒"
|
||||||
timer_strings.append(f"{name}: {formatted_time}")
|
timer_strings.append(f"{name}: {formatted_time}")
|
||||||
|
|
||||||
|
# 获取动作类型,兼容新旧格式
|
||||||
|
action_type = "未知动作"
|
||||||
|
if hasattr(self, '_current_cycle_detail') and self._current_cycle_detail:
|
||||||
|
loop_plan_info = self._current_cycle_detail.loop_plan_info
|
||||||
|
if isinstance(loop_plan_info, dict):
|
||||||
|
action_result = loop_plan_info.get('action_result', {})
|
||||||
|
if isinstance(action_result, dict):
|
||||||
|
# 旧格式:action_result是字典
|
||||||
|
action_type = action_result.get('action_type', '未知动作')
|
||||||
|
elif isinstance(action_result, list) and action_result:
|
||||||
|
# 新格式:action_result是actions列表
|
||||||
|
action_type = action_result[0].get('action_type', '未知动作')
|
||||||
|
elif isinstance(loop_plan_info, list) and loop_plan_info:
|
||||||
|
# 直接是actions列表的情况
|
||||||
|
action_type = loop_plan_info[0].get('action_type', '未知动作')
|
||||||
|
|
||||||
if self.context.current_cycle_detail.end_time and self.context.current_cycle_detail.start_time:
|
if self.context.current_cycle_detail.end_time and self.context.current_cycle_detail.start_time:
|
||||||
duration = self.context.current_cycle_detail.end_time - self.context.current_cycle_detail.start_time
|
duration = self.context.current_cycle_detail.end_time - self.context.current_cycle_detail.start_time
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{self.context.log_prefix} 第{self.context.current_cycle_detail.cycle_id}次思考,"
|
f"{self.context.log_prefix} 第{self.context.current_cycle_detail.cycle_id}次思考,"
|
||||||
f"耗时: {duration:.1f}秒, "
|
f"耗时: {duration:.1f}秒, "
|
||||||
f"选择动作: {self.context.current_cycle_detail.loop_plan_info.get('action_result', {}).get('action_type', '未知动作')}"
|
f"选择动作: {action_type}"
|
||||||
+ (f"\n详情: {'; '.join(timer_strings)}" if timer_strings else "")
|
+ (f"\n详情: {'; '.join(timer_strings)}" if timer_strings else "")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -107,20 +107,11 @@ class EnergyManager:
|
|||||||
else:
|
else:
|
||||||
# 清醒时:处理能量衰减
|
# 清醒时:处理能量衰减
|
||||||
is_group_chat = self.context.chat_stream.group_info is not None
|
is_group_chat = self.context.chat_stream.group_info is not None
|
||||||
if is_group_chat and global_config.chat.group_chat_mode != "auto":
|
if is_group_chat:
|
||||||
if global_config.chat.group_chat_mode == "focus":
|
self.context.energy_value = 25
|
||||||
self.context.loop_mode = ChatMode.FOCUS
|
|
||||||
self.context.energy_value = 35
|
|
||||||
elif global_config.chat.group_chat_mode == "normal":
|
|
||||||
self.context.loop_mode = ChatMode.NORMAL
|
|
||||||
self.context.energy_value = 15
|
|
||||||
continue
|
|
||||||
|
|
||||||
if self.context.loop_mode == ChatMode.NORMAL:
|
await asyncio.sleep(12)
|
||||||
self.context.energy_value -= 0.3
|
self.context.energy_value -= 0.5
|
||||||
self.context.energy_value = max(self.context.energy_value, 0.3)
|
|
||||||
if self.context.loop_mode == ChatMode.FOCUS:
|
|
||||||
self.context.energy_value -= 0.6
|
|
||||||
self.context.energy_value = max(self.context.energy_value, 0.3)
|
self.context.energy_value = max(self.context.energy_value, 0.3)
|
||||||
|
|
||||||
self._log_energy_change("能量值衰减")
|
self._log_energy_change("能量值衰减")
|
||||||
|
|||||||
@@ -77,12 +77,7 @@ class HeartFChatting:
|
|||||||
"""
|
"""
|
||||||
is_group_chat = self.context.chat_stream.group_info is not None if self.context.chat_stream else False
|
is_group_chat = self.context.chat_stream.group_info is not None if self.context.chat_stream else False
|
||||||
if is_group_chat and global_config.chat.group_chat_mode != "auto":
|
if is_group_chat and global_config.chat.group_chat_mode != "auto":
|
||||||
if global_config.chat.group_chat_mode == "focus":
|
self.context.energy_value = 25
|
||||||
self.context.loop_mode = ChatMode.FOCUS
|
|
||||||
self.context.energy_value = 35
|
|
||||||
elif global_config.chat.group_chat_mode == "normal":
|
|
||||||
self.context.loop_mode = ChatMode.NORMAL
|
|
||||||
self.context.energy_value = 15
|
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
"""
|
"""
|
||||||
@@ -241,20 +236,29 @@ class HeartFChatting:
|
|||||||
|
|
||||||
if current_sleep_state == SleepState.SLEEPING:
|
if current_sleep_state == SleepState.SLEEPING:
|
||||||
# 只有在纯粹的 SLEEPING 状态下才跳过消息处理
|
# 只有在纯粹的 SLEEPING 状态下才跳过消息处理
|
||||||
return has_new_messages
|
return True
|
||||||
|
|
||||||
if current_sleep_state == SleepState.WOKEN_UP:
|
if current_sleep_state == SleepState.WOKEN_UP:
|
||||||
logger.info(f"{self.context.log_prefix} 从睡眠中被唤醒,将处理积压的消息。")
|
logger.info(f"{self.context.log_prefix} 从睡眠中被唤醒,将处理积压的消息。")
|
||||||
|
|
||||||
# 根据聊天模式处理新消息
|
# 根据聊天模式处理新消息
|
||||||
# 统一使用 _should_process_messages 判断是否应该处理
|
# 统一使用 _should_process_messages 判断是否应该处理
|
||||||
if not self._should_process_messages(recent_messages if has_new_messages else None):
|
should_process,interest_value = await self._should_process_messages(recent_messages if has_new_messages else None)
|
||||||
|
if should_process:
|
||||||
|
earliest_message_data = recent_messages[0]
|
||||||
|
self.last_read_time = earliest_message_data.get("time")
|
||||||
|
await self.cycle_processor.observe(interest_value = interest_value)
|
||||||
|
else:
|
||||||
|
# Normal模式:消息数量不足,等待
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
return True
|
||||||
|
|
||||||
|
if not await self._should_process_messages(recent_messages if has_new_messages else None):
|
||||||
return has_new_messages
|
return has_new_messages
|
||||||
|
|
||||||
if self.context.loop_mode == ChatMode.FOCUS:
|
|
||||||
# 处理新消息
|
# 处理新消息
|
||||||
for message in recent_messages:
|
for message in recent_messages:
|
||||||
await self.cycle_processor.observe(message)
|
await self.cycle_processor.observe(interest_value = interest_value)
|
||||||
|
|
||||||
# 如果成功观察,增加能量值
|
# 如果成功观察,增加能量值
|
||||||
if has_new_messages:
|
if has_new_messages:
|
||||||
@@ -262,20 +266,11 @@ class HeartFChatting:
|
|||||||
logger.info(f"{self.context.log_prefix} 能量值增加,当前能量值:{self.context.energy_value:.1f}")
|
logger.info(f"{self.context.log_prefix} 能量值增加,当前能量值:{self.context.energy_value:.1f}")
|
||||||
|
|
||||||
self._check_focus_exit()
|
self._check_focus_exit()
|
||||||
elif self.context.loop_mode == ChatMode.NORMAL:
|
|
||||||
self._check_focus_entry(len(recent_messages))
|
|
||||||
# 检查是否有足够的新消息触发处理
|
|
||||||
if new_message_count >= self.context.focus_energy:
|
|
||||||
earliest_messages_data = recent_messages[0]
|
|
||||||
self.context.last_read_time = earliest_messages_data.get("time")
|
|
||||||
for message in recent_messages:
|
|
||||||
await self.normal_mode_handler.handle_message(message)
|
|
||||||
else:
|
else:
|
||||||
# 无新消息时,只进行模式检查,不进行思考循环
|
# 无新消息时,只进行模式检查,不进行思考循环
|
||||||
if self.context.loop_mode == ChatMode.FOCUS:
|
|
||||||
self._check_focus_exit()
|
self._check_focus_exit()
|
||||||
elif self.context.loop_mode == ChatMode.NORMAL:
|
|
||||||
self._check_focus_entry(0) # 传入0表示无新消息
|
|
||||||
|
|
||||||
# 更新上一帧的睡眠状态
|
# 更新上一帧的睡眠状态
|
||||||
self.context.was_sleeping = is_sleeping
|
self.context.was_sleeping = is_sleeping
|
||||||
@@ -319,7 +314,6 @@ class HeartFChatting:
|
|||||||
|
|
||||||
if self.context.energy_value <= 1: # 如果能量值小于等于1(非强制情况)
|
if self.context.energy_value <= 1: # 如果能量值小于等于1(非强制情况)
|
||||||
self.context.energy_value = 1 # 将能量值设置为1
|
self.context.energy_value = 1 # 将能量值设置为1
|
||||||
self.context.loop_mode = ChatMode.NORMAL # 切换到普通模式
|
|
||||||
|
|
||||||
def _check_focus_entry(self, new_message_count: int):
|
def _check_focus_entry(self, new_message_count: int):
|
||||||
"""
|
"""
|
||||||
@@ -339,7 +333,6 @@ class HeartFChatting:
|
|||||||
is_group_chat = not is_private_chat
|
is_group_chat = not is_private_chat
|
||||||
|
|
||||||
if global_config.chat.force_focus_private and is_private_chat:
|
if global_config.chat.force_focus_private and is_private_chat:
|
||||||
self.context.loop_mode = ChatMode.FOCUS
|
|
||||||
self.context.energy_value = 10
|
self.context.energy_value = 10
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -350,15 +343,11 @@ class HeartFChatting:
|
|||||||
if new_message_count > 3 / pow(
|
if new_message_count > 3 / pow(
|
||||||
global_config.chat.focus_value, 0.5
|
global_config.chat.focus_value, 0.5
|
||||||
): # 如果新消息数超过阈值(基于专注值计算)
|
): # 如果新消息数超过阈值(基于专注值计算)
|
||||||
self.context.loop_mode = ChatMode.FOCUS # 进入专注模式
|
|
||||||
self.context.energy_value = (
|
self.context.energy_value = (
|
||||||
10 + (new_message_count / (3 / pow(global_config.chat.focus_value, 0.5))) * 10
|
10 + (new_message_count / (3 / pow(global_config.chat.focus_value, 0.5))) * 10
|
||||||
) # 根据消息数量计算能量值
|
) # 根据消息数量计算能量值
|
||||||
return # 返回,不再检查其他条件
|
return # 返回,不再检查其他条件
|
||||||
|
|
||||||
if self.context.energy_value >= 30: # 如果能量值达到或超过30
|
|
||||||
self.context.loop_mode = ChatMode.FOCUS # 进入专注模式
|
|
||||||
|
|
||||||
def _handle_wakeup_messages(self, messages):
|
def _handle_wakeup_messages(self, messages):
|
||||||
"""
|
"""
|
||||||
处理休眠状态下的消息,累积唤醒度
|
处理休眠状态下的消息,累积唤醒度
|
||||||
@@ -419,19 +408,57 @@ class HeartFChatting:
|
|||||||
logger.info(f"{self.context.log_prefix} 兴趣度充足")
|
logger.info(f"{self.context.log_prefix} 兴趣度充足")
|
||||||
self.context.focus_energy = 1
|
self.context.focus_energy = 1
|
||||||
|
|
||||||
def _should_process_messages(self, messages: List[Dict[str, Any]] = None) -> bool:
|
async def _should_process_messages(self, new_message: List[Dict[str, Any]]) -> tuple[bool,float]:
|
||||||
"""
|
"""
|
||||||
统一判断是否应该处理消息的函数
|
统一判断是否应该处理消息的函数
|
||||||
根据当前循环模式和消息内容决定是否继续处理
|
根据当前循环模式和消息内容决定是否继续处理
|
||||||
"""
|
"""
|
||||||
if self.context.loop_mode == ChatMode.FOCUS:
|
new_message_count = len(new_message)
|
||||||
if self.context.last_action == "no_reply":
|
talk_frequency = global_config.chat.get_current_talk_frequency(self.context.chat_stream.stream_id)
|
||||||
if messages:
|
modified_exit_count_threshold = self.context.focus_energy / talk_frequency
|
||||||
return self._execute_no_reply(messages)
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
return True
|
if new_message_count >= modified_exit_count_threshold:
|
||||||
|
# 记录兴趣度到列表
|
||||||
|
total_interest = 0.0
|
||||||
|
for msg_dict in new_message:
|
||||||
|
interest_value = msg_dict.get("interest_value", 0.0)
|
||||||
|
if msg_dict.get("processed_plain_text", ""):
|
||||||
|
total_interest += interest_value
|
||||||
|
|
||||||
|
self.recent_interest_records.append(total_interest)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"{self.context.log_prefix} 累计消息数量达到{new_message_count}条(>{modified_exit_count_threshold}),结束等待"
|
||||||
|
)
|
||||||
|
return True,total_interest/new_message_count
|
||||||
|
|
||||||
|
# 检查累计兴趣值
|
||||||
|
if new_message_count > 0:
|
||||||
|
accumulated_interest = 0.0
|
||||||
|
for msg_dict in new_message:
|
||||||
|
text = msg_dict.get("processed_plain_text", "")
|
||||||
|
interest_value = msg_dict.get("interest_value", 0.0)
|
||||||
|
if text:
|
||||||
|
accumulated_interest += interest_value
|
||||||
|
# 只在兴趣值变化时输出log
|
||||||
|
if not hasattr(self, "_last_accumulated_interest") or accumulated_interest != self._last_accumulated_interest:
|
||||||
|
logger.info(f"{self.context.log_prefix} breaking形式当前累计兴趣值: {accumulated_interest:.2f}, 当前聊天频率: {talk_frequency:.2f}")
|
||||||
|
self._last_accumulated_interest = accumulated_interest
|
||||||
|
if accumulated_interest >= 3 / talk_frequency:
|
||||||
|
# 记录兴趣度到列表
|
||||||
|
self.recent_interest_records.append(accumulated_interest)
|
||||||
|
logger.info(
|
||||||
|
f"{self.context.log_prefix} 累计兴趣值达到{accumulated_interest:.2f}(>{5 / talk_frequency}),结束等待"
|
||||||
|
)
|
||||||
|
return True,accumulated_interest/new_message_count
|
||||||
|
# 每10秒输出一次等待状态
|
||||||
|
if int(time.time() - self.last_read_time) > 0 and int(time.time() - self.last_read_time) % 10 == 0:
|
||||||
|
logger.info(
|
||||||
|
f"{self.context.log_prefix} 已等待{time.time() - self.last_read_time:.0f}秒,累计{new_message_count}条消息,继续等待..."
|
||||||
|
)
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
|
||||||
|
return False,0.0
|
||||||
|
|
||||||
async def _execute_no_reply(self, new_message: List[Dict[str, Any]]) -> bool:
|
async def _execute_no_reply(self, new_message: List[Dict[str, Any]]) -> bool:
|
||||||
"""执行breaking形式的no_reply(原有逻辑)"""
|
"""执行breaking形式的no_reply(原有逻辑)"""
|
||||||
|
|||||||
@@ -41,10 +41,6 @@ class HfcContext:
|
|||||||
self.relationship_builder: Optional[RelationshipBuilder] = None
|
self.relationship_builder: Optional[RelationshipBuilder] = None
|
||||||
self.expression_learner: Optional[ExpressionLearner] = None
|
self.expression_learner: Optional[ExpressionLearner] = None
|
||||||
|
|
||||||
self.loop_mode = ChatMode.NORMAL
|
|
||||||
|
|
||||||
self.last_action = "no_action"
|
|
||||||
|
|
||||||
self.energy_value = self.chat_stream.energy_value
|
self.energy_value = self.chat_stream.energy_value
|
||||||
self.sleep_pressure = self.chat_stream.sleep_pressure
|
self.sleep_pressure = self.chat_stream.sleep_pressure
|
||||||
self.was_sleeping = False # 用于检测睡眠状态的切换
|
self.was_sleeping = False # 用于检测睡眠状态的切换
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import orjson
|
import orjson
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Dict, Any, Optional, Tuple
|
from typing import Dict, Any, Optional, Tuple, List
|
||||||
from rich.traceback import install
|
from rich.traceback import install
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from json_repair import repair_json
|
from json_repair import repair_json
|
||||||
@@ -148,7 +148,10 @@ class ActionPlanner:
|
|||||||
return "回忆时出现了一些问题。"
|
return "回忆时出现了一些问题。"
|
||||||
|
|
||||||
async def _build_action_options(
|
async def _build_action_options(
|
||||||
self, current_available_actions: Dict[str, ActionInfo], mode: ChatMode, target_prompt: str = ""
|
self,
|
||||||
|
current_available_actions: Dict[str, ActionInfo],
|
||||||
|
mode: ChatMode,
|
||||||
|
target_prompt: str = "",
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
构建动作选项
|
构建动作选项
|
||||||
@@ -171,12 +174,15 @@ class ActionPlanner:
|
|||||||
param_text = ""
|
param_text = ""
|
||||||
if action_info.action_parameters:
|
if action_info.action_parameters:
|
||||||
param_text = "\n" + "\n".join(
|
param_text = "\n" + "\n".join(
|
||||||
f' "{p_name}":"{p_desc}"' for p_name, p_desc in action_info.action_parameters.items()
|
f' "{p_name}":"{p_desc}"'
|
||||||
|
for p_name, p_desc in action_info.action_parameters.items()
|
||||||
)
|
)
|
||||||
|
|
||||||
require_text = "\n".join(f"- {req}" for req in action_info.action_require)
|
require_text = "\n".join(f"- {req}" for req in action_info.action_require)
|
||||||
|
|
||||||
using_action_prompt = await global_prompt_manager.get_prompt_async("action_prompt")
|
using_action_prompt = await global_prompt_manager.get_prompt_async(
|
||||||
|
"action_prompt"
|
||||||
|
)
|
||||||
action_options_block += using_action_prompt.format(
|
action_options_block += using_action_prompt.format(
|
||||||
action_name=action_name,
|
action_name=action_name,
|
||||||
action_description=action_info.description,
|
action_description=action_info.description,
|
||||||
@@ -186,7 +192,9 @@ class ActionPlanner:
|
|||||||
)
|
)
|
||||||
return action_options_block
|
return action_options_block
|
||||||
|
|
||||||
def find_message_by_id(self, message_id: str, message_id_list: list) -> Optional[Dict[str, Any]]:
|
def find_message_by_id(
|
||||||
|
self, message_id: str, message_id_list: list
|
||||||
|
) -> Optional[Dict[str, Any]]:
|
||||||
# sourcery skip: use-next
|
# sourcery skip: use-next
|
||||||
"""
|
"""
|
||||||
根据message_id从message_id_list中查找对应的原始消息
|
根据message_id从message_id_list中查找对应的原始消息
|
||||||
@@ -218,7 +226,12 @@ class ActionPlanner:
|
|||||||
# 假设消息列表是按时间顺序排列的,最后一个是最新的
|
# 假设消息列表是按时间顺序排列的,最后一个是最新的
|
||||||
return message_id_list[-1].get("message")
|
return message_id_list[-1].get("message")
|
||||||
|
|
||||||
async def plan(self, mode: ChatMode = ChatMode.FOCUS) -> Tuple[Dict[str, Any], Optional[Dict[str, Any]]]:
|
async def plan(
|
||||||
|
self,
|
||||||
|
mode: ChatMode = ChatMode.FOCUS,
|
||||||
|
loop_start_time:float = 0.0,
|
||||||
|
available_actions: Optional[Dict[str, ActionInfo]] = None,
|
||||||
|
) -> Tuple[List[Dict[str, Any]], Optional[Dict[str, Any]]]:
|
||||||
"""
|
"""
|
||||||
规划器 (Planner): 使用LLM根据上下文决定做出什么动作。
|
规划器 (Planner): 使用LLM根据上下文决定做出什么动作。
|
||||||
"""
|
"""
|
||||||
@@ -279,36 +292,27 @@ class ActionPlanner:
|
|||||||
parsed_json = {}
|
parsed_json = {}
|
||||||
|
|
||||||
action = parsed_json.get("action", "no_reply")
|
action = parsed_json.get("action", "no_reply")
|
||||||
reasoning = parsed_json.get("reasoning", "未提供原因")
|
reasoning = parsed_json.get("reason", "未提供原因")
|
||||||
|
|
||||||
# 将所有其他属性添加到action_data
|
# 将所有其他属性添加到action_data
|
||||||
for key, value in parsed_json.items():
|
for key, value in parsed_json.items():
|
||||||
if key not in ["action", "reasoning"]:
|
if key not in ["action", "reason"]:
|
||||||
action_data[key] = value
|
action_data[key] = value
|
||||||
|
|
||||||
# 在FOCUS模式下,非no_reply动作需要target_message_id
|
# 在FOCUS模式下,非no_reply动作需要target_message_id
|
||||||
if mode == ChatMode.FOCUS and action != "no_reply":
|
if action != "no_reply":
|
||||||
if target_message_id := parsed_json.get("target_message_id"):
|
if target_message_id := parsed_json.get("target_message_id"):
|
||||||
if isinstance(target_message_id, int):
|
|
||||||
target_message_id = str(target_message_id)
|
|
||||||
|
|
||||||
if isinstance(target_message_id, str) and not target_message_id.startswith("m"):
|
|
||||||
target_message_id = f"m{target_message_id}"
|
|
||||||
# 根据target_message_id查找原始消息
|
# 根据target_message_id查找原始消息
|
||||||
target_message = self.find_message_by_id(target_message_id, message_id_list)
|
target_message = self.find_message_by_id(target_message_id, message_id_list)
|
||||||
# target_message = None
|
# target_message = None
|
||||||
# 如果获取的target_message为None,输出warning并重新plan
|
# 如果获取的target_message为None,输出warning并重新plan
|
||||||
if target_message is None:
|
if target_message is None:
|
||||||
self.plan_retry_count += 1
|
self.plan_retry_count += 1
|
||||||
logger.warning(
|
logger.warning(f"{self.log_prefix}无法找到target_message_id '{target_message_id}' 对应的消息,重试次数: {self.plan_retry_count}/{self.max_plan_retries}")
|
||||||
f"{self.log_prefix}无法找到target_message_id '{target_message_id}' 对应的消息,重试次数: {self.plan_retry_count}/{self.max_plan_retries}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 如果连续三次plan均为None,输出error并选取最新消息
|
# 如果连续三次plan均为None,输出error并选取最新消息
|
||||||
if self.plan_retry_count >= self.max_plan_retries:
|
if self.plan_retry_count >= self.max_plan_retries:
|
||||||
logger.error(
|
logger.error(f"{self.log_prefix}连续{self.max_plan_retries}次plan获取target_message失败,选择最新消息作为target_message")
|
||||||
f"{self.log_prefix}连续{self.max_plan_retries}次plan获取target_message失败,选择最新消息作为target_message"
|
|
||||||
)
|
|
||||||
target_message = self.get_latest_message(message_id_list)
|
target_message = self.get_latest_message(message_id_list)
|
||||||
self.plan_retry_count = 0 # 重置计数器
|
self.plan_retry_count = 0 # 重置计数器
|
||||||
else:
|
else:
|
||||||
@@ -318,12 +322,11 @@ class ActionPlanner:
|
|||||||
# 成功获取到target_message,重置计数器
|
# 成功获取到target_message,重置计数器
|
||||||
self.plan_retry_count = 0
|
self.plan_retry_count = 0
|
||||||
else:
|
else:
|
||||||
logger.warning(f"{self.log_prefix}FOCUS模式下动作'{action}'缺少target_message_id")
|
logger.warning(f"{self.log_prefix}动作'{action}'缺少target_message_id")
|
||||||
|
|
||||||
|
|
||||||
if action == "no_action":
|
if action == "no_action":
|
||||||
reasoning = "normal决定不使用额外动作"
|
reasoning = "normal决定不使用额外动作"
|
||||||
elif mode == ChatMode.PROACTIVE and action == "do_nothing":
|
|
||||||
pass # 在PROACTIVE模式下,do_nothing是有效动作
|
|
||||||
elif action != "no_reply" and action != "reply" and action not in current_available_actions:
|
elif action != "no_reply" and action != "reply" and action not in current_available_actions:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"{self.log_prefix}LLM 返回了当前不可用或无效的动作: '{action}' (可用: {list(current_available_actions.keys())}),将强制使用 'no_reply'"
|
f"{self.log_prefix}LLM 返回了当前不可用或无效的动作: '{action}' (可用: {list(current_available_actions.keys())}),将强制使用 'no_reply'"
|
||||||
@@ -331,94 +334,51 @@ class ActionPlanner:
|
|||||||
reasoning = f"LLM 返回了当前不可用的动作 '{action}' (可用: {list(current_available_actions.keys())})。原始理由: {reasoning}"
|
reasoning = f"LLM 返回了当前不可用的动作 '{action}' (可用: {list(current_available_actions.keys())})。原始理由: {reasoning}"
|
||||||
action = "no_reply"
|
action = "no_reply"
|
||||||
|
|
||||||
# 检查no_reply是否可用,如果不可用则使用reply作为终极回退
|
|
||||||
if "no_reply" not in current_available_actions:
|
|
||||||
if "reply" in current_available_actions:
|
|
||||||
action = "reply"
|
|
||||||
reasoning += " (no_reply不可用,使用reply作为回退)"
|
|
||||||
logger.warning(f"{self.log_prefix}no_reply不可用,使用reply作为回退")
|
|
||||||
else:
|
|
||||||
# 如果连reply都不可用,使用第一个可用的动作
|
|
||||||
if current_available_actions:
|
|
||||||
action = list(current_available_actions.keys())[0]
|
|
||||||
reasoning += f" (no_reply和reply都不可用,使用{action}作为回退)"
|
|
||||||
logger.warning(f"{self.log_prefix}no_reply和reply都不可用,使用{action}作为回退")
|
|
||||||
else:
|
|
||||||
# 如果没有任何可用动作,这是一个严重错误
|
|
||||||
logger.error(f"{self.log_prefix}没有任何可用动作,系统状态异常")
|
|
||||||
action = "no_reply" # 仍然尝试no_reply,让上层处理
|
|
||||||
|
|
||||||
# 对no_reply动作本身也进行可用性检查
|
|
||||||
elif action == "no_reply" and "no_reply" not in current_available_actions:
|
|
||||||
if "reply" in current_available_actions:
|
|
||||||
action = "reply"
|
|
||||||
reasoning = f"no_reply不可用,自动回退到reply。原因: {reasoning}"
|
|
||||||
logger.warning(f"{self.log_prefix}no_reply不可用,自动回退到reply")
|
|
||||||
elif current_available_actions:
|
|
||||||
action = list(current_available_actions.keys())[0]
|
|
||||||
reasoning = f"no_reply不可用,自动回退到{action}。原因: {reasoning}"
|
|
||||||
logger.warning(f"{self.log_prefix}no_reply不可用,自动回退到{action}")
|
|
||||||
else:
|
|
||||||
logger.error(f"{self.log_prefix}没有任何可用动作,保持no_reply让上层处理")
|
|
||||||
|
|
||||||
except Exception as json_e:
|
except Exception as json_e:
|
||||||
logger.warning(f"{self.log_prefix}解析LLM响应JSON失败 {json_e}. LLM原始输出: '{llm_content}'")
|
logger.warning(f"{self.log_prefix}解析LLM响应JSON失败 {json_e}. LLM原始输出: '{llm_content}'")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
reasoning = f"解析LLM响应JSON失败: {json_e}. 将使用默认动作 'no_reply'."
|
reasoning = f"解析LLM响应JSON失败: {json_e}. 将使用默认动作 'no_reply'."
|
||||||
action = "no_reply"
|
action = "no_reply"
|
||||||
|
|
||||||
# 检查no_reply是否可用
|
|
||||||
if "no_reply" not in current_available_actions:
|
|
||||||
if "reply" in current_available_actions:
|
|
||||||
action = "reply"
|
|
||||||
reasoning += " (no_reply不可用,使用reply)"
|
|
||||||
elif current_available_actions:
|
|
||||||
action = list(current_available_actions.keys())[0]
|
|
||||||
reasoning += f" (no_reply不可用,使用{action})"
|
|
||||||
|
|
||||||
except Exception as outer_e:
|
except Exception as outer_e:
|
||||||
logger.error(f"{self.log_prefix}Planner 处理过程中发生意外错误,规划失败,将执行 no_reply: {outer_e}")
|
logger.error(f"{self.log_prefix}Planner 处理过程中发生意外错误,规划失败,将执行 no_reply: {outer_e}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
action = "no_reply"
|
action = "no_reply"
|
||||||
reasoning = f"Planner 内部处理错误: {outer_e}"
|
reasoning = f"Planner 内部处理错误: {outer_e}"
|
||||||
|
|
||||||
# 检查no_reply是否可用
|
|
||||||
current_available_actions = self.action_manager.get_using_actions()
|
|
||||||
if "no_reply" not in current_available_actions:
|
|
||||||
if "reply" in current_available_actions:
|
|
||||||
action = "reply"
|
|
||||||
reasoning += " (no_reply不可用,使用reply)"
|
|
||||||
elif current_available_actions:
|
|
||||||
action = list(current_available_actions.keys())[0]
|
|
||||||
reasoning += f" (no_reply不可用,使用{action})"
|
|
||||||
else:
|
|
||||||
logger.error(f"{self.log_prefix}严重错误:没有任何可用动作")
|
|
||||||
|
|
||||||
is_parallel = False
|
is_parallel = False
|
||||||
if mode == ChatMode.NORMAL and action in current_available_actions:
|
if mode == ChatMode.NORMAL and action in current_available_actions:
|
||||||
is_parallel = current_available_actions[action].parallel_action
|
is_parallel = current_available_actions[action].parallel_action
|
||||||
|
|
||||||
action_result = {
|
|
||||||
"action_type": action,
|
|
||||||
"action_data": action_data,
|
|
||||||
"reasoning": reasoning,
|
|
||||||
"timestamp": time.time(),
|
|
||||||
"is_parallel": is_parallel,
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
action_data["loop_start_time"] = loop_start_time
|
||||||
{
|
|
||||||
"action_result": action_result,
|
actions = []
|
||||||
"action_prompt": prompt,
|
|
||||||
},
|
# 1. 添加Planner取得的动作
|
||||||
target_message,
|
actions.append({
|
||||||
)
|
"action_type": action,
|
||||||
|
"reasoning": reasoning,
|
||||||
|
"action_data": action_data,
|
||||||
|
"action_message": target_message,
|
||||||
|
"available_actions": available_actions # 添加这个字段
|
||||||
|
})
|
||||||
|
|
||||||
|
if action != "reply" and is_parallel:
|
||||||
|
actions.append({
|
||||||
|
"action_type": "reply",
|
||||||
|
"action_message": target_message,
|
||||||
|
"available_actions": available_actions
|
||||||
|
})
|
||||||
|
|
||||||
|
return actions,target_message
|
||||||
|
|
||||||
async def build_planner_prompt(
|
async def build_planner_prompt(
|
||||||
self,
|
self,
|
||||||
is_group_chat: bool, # Now passed as argument
|
is_group_chat: bool, # Now passed as argument
|
||||||
chat_target_info: Optional[dict], # Now passed as argument
|
chat_target_info: Optional[dict], # Now passed as argument
|
||||||
current_available_actions: Dict[str, ActionInfo],
|
current_available_actions: Dict[str, ActionInfo],
|
||||||
|
refresh_time :bool = False,
|
||||||
mode: ChatMode = ChatMode.FOCUS,
|
mode: ChatMode = ChatMode.FOCUS,
|
||||||
) -> tuple[str, list]: # sourcery skip: use-join
|
) -> tuple[str, list]: # sourcery skip: use-join
|
||||||
"""构建 Planner LLM 的提示词 (获取模板并填充数据)"""
|
"""构建 Planner LLM 的提示词 (获取模板并填充数据)"""
|
||||||
@@ -427,15 +387,21 @@ class ActionPlanner:
|
|||||||
time_block = f"当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
time_block = f"当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
||||||
bot_name = global_config.bot.nickname
|
bot_name = global_config.bot.nickname
|
||||||
bot_nickname = (
|
bot_nickname = (
|
||||||
f",也有人叫你{','.join(global_config.bot.alias_names)}" if global_config.bot.alias_names else ""
|
f",也有人叫你{','.join(global_config.bot.alias_names)}"
|
||||||
|
if global_config.bot.alias_names
|
||||||
|
else ""
|
||||||
)
|
)
|
||||||
bot_core_personality = global_config.personality.personality_core
|
bot_core_personality = global_config.personality.personality_core
|
||||||
identity_block = f"你的名字是{bot_name}{bot_nickname},你{bot_core_personality}:"
|
identity_block = (
|
||||||
|
f"你的名字是{bot_name}{bot_nickname},你{bot_core_personality}:"
|
||||||
|
)
|
||||||
|
|
||||||
schedule_block = ""
|
schedule_block = ""
|
||||||
if global_config.schedule.enable:
|
if global_config.schedule.enable:
|
||||||
if current_activity := schedule_manager.get_current_activity():
|
if current_activity := schedule_manager.get_current_activity():
|
||||||
schedule_block = f"你当前正在:{current_activity},但注意它与群聊的聊天无关。"
|
schedule_block = (
|
||||||
|
f"你当前正在:{current_activity},但注意它与群聊的聊天无关。"
|
||||||
|
)
|
||||||
|
|
||||||
mood_block = ""
|
mood_block = ""
|
||||||
if global_config.mood.enable_mood:
|
if global_config.mood.enable_mood:
|
||||||
@@ -445,9 +411,13 @@ class ActionPlanner:
|
|||||||
# --- 根据模式构建不同的Prompt ---
|
# --- 根据模式构建不同的Prompt ---
|
||||||
if mode == ChatMode.PROACTIVE:
|
if mode == ChatMode.PROACTIVE:
|
||||||
long_term_memory_block = await self._get_long_term_memory_context()
|
long_term_memory_block = await self._get_long_term_memory_context()
|
||||||
action_options_text = await self._build_action_options(current_available_actions, mode)
|
action_options_text = await self._build_action_options(
|
||||||
|
current_available_actions, mode
|
||||||
|
)
|
||||||
|
|
||||||
prompt_template = await global_prompt_manager.get_prompt_async("proactive_planner_prompt")
|
prompt_template = await global_prompt_manager.get_prompt_async(
|
||||||
|
"proactive_planner_prompt"
|
||||||
|
)
|
||||||
prompt = prompt_template.format(
|
prompt = prompt_template.format(
|
||||||
time_block=time_block,
|
time_block=time_block,
|
||||||
identity_block=identity_block,
|
identity_block=identity_block,
|
||||||
@@ -480,22 +450,27 @@ class ActionPlanner:
|
|||||||
limit=5,
|
limit=5,
|
||||||
)
|
)
|
||||||
|
|
||||||
actions_before_now_block = build_readable_actions(actions=actions_before_now)
|
actions_before_now_block = build_readable_actions(
|
||||||
actions_before_now_block = f"你刚刚选择并执行过的action是:\n{actions_before_now_block}"
|
actions=actions_before_now
|
||||||
|
)
|
||||||
|
actions_before_now_block = (
|
||||||
|
f"你刚刚选择并执行过的action是:\n{actions_before_now_block}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if refresh_time:
|
||||||
self.last_obs_time_mark = time.time()
|
self.last_obs_time_mark = time.time()
|
||||||
|
|
||||||
if mode == ChatMode.FOCUS:
|
|
||||||
mentioned_bonus = ""
|
mentioned_bonus = ""
|
||||||
if global_config.chat.mentioned_bot_inevitable_reply:
|
if global_config.chat.mentioned_bot_inevitable_reply:
|
||||||
mentioned_bonus = "\n- 有人提到你"
|
mentioned_bonus = "\n- 有人提到你"
|
||||||
if global_config.chat.at_bot_inevitable_reply:
|
if global_config.chat.at_bot_inevitable_reply:
|
||||||
mentioned_bonus = "\n- 有人提到你,或者at你"
|
mentioned_bonus = "\n- 有人提到你,或者at你"
|
||||||
|
|
||||||
|
if mode == ChatMode.FOCUS:
|
||||||
by_what = "聊天内容"
|
by_what = "聊天内容"
|
||||||
target_prompt = '\n "target_message_id":"触发action的消息id"'
|
target_prompt = '\n "target_message_id":"触发action的消息id"'
|
||||||
no_action_block = f"""重要说明:
|
no_action_block = f"""重要说明:
|
||||||
- 'no_reply' 表示只进行不进行回复,等待合适的回复时机(由系统直接处理)
|
- 'no_reply' 表示只进行不进行回复,等待合适的回复时机
|
||||||
- 当你刚刚发送了消息,没有人回复时,选择no_reply
|
- 当你刚刚发送了消息,没有人回复时,选择no_reply
|
||||||
- 当你一次发送了太多消息,为了避免打扰聊天节奏,选择no_reply
|
- 当你一次发送了太多消息,为了避免打扰聊天节奏,选择no_reply
|
||||||
|
|
||||||
@@ -503,6 +478,7 @@ class ActionPlanner:
|
|||||||
动作描述:参与聊天回复,发送文本进行表达
|
动作描述:参与聊天回复,发送文本进行表达
|
||||||
- 你想要闲聊或者随便附和{mentioned_bonus}
|
- 你想要闲聊或者随便附和{mentioned_bonus}
|
||||||
- 如果你刚刚进行了回复,不要对同一个话题重复回应
|
- 如果你刚刚进行了回复,不要对同一个话题重复回应
|
||||||
|
- 不要回复自己发送的消息
|
||||||
{{
|
{{
|
||||||
"action": "reply",
|
"action": "reply",
|
||||||
"target_message_id":"触发action的消息id",
|
"target_message_id":"触发action的消息id",
|
||||||
@@ -513,26 +489,46 @@ class ActionPlanner:
|
|||||||
else: # NORMAL Mode
|
else: # NORMAL Mode
|
||||||
by_what = "聊天内容和用户的最新消息"
|
by_what = "聊天内容和用户的最新消息"
|
||||||
target_prompt = ""
|
target_prompt = ""
|
||||||
no_action_block = """重要说明:
|
no_action_block = f"""重要说明:
|
||||||
- 'reply' 表示只进行普通聊天回复,不执行任何额外动作
|
- 'reply' 表示只进行普通聊天回复,不执行任何额外动作
|
||||||
- 其他action表示在普通回复的基础上,执行相应的额外动作"""
|
- 其他action表示在普通回复的基础上,执行相应的额外动作
|
||||||
|
|
||||||
|
动作:reply
|
||||||
|
动作描述:参与聊天回复,发送文本进行表达
|
||||||
|
- 你想要闲聊或者随便附
|
||||||
|
- {mentioned_bonus}
|
||||||
|
- 如果你刚刚进行了回复,不要对同一个话题重复回应
|
||||||
|
- 不要回复自己发送的消息
|
||||||
|
{{
|
||||||
|
"action": "reply",
|
||||||
|
"target_message_id":"触发action的消息id",
|
||||||
|
"reason":"回复的原因"
|
||||||
|
}}"""
|
||||||
|
|
||||||
chat_context_description = "你现在正在一个群聊中"
|
chat_context_description = "你现在正在一个群聊中"
|
||||||
if not is_group_chat and chat_target_info:
|
if not is_group_chat and chat_target_info:
|
||||||
chat_target_name = (
|
chat_target_name = (
|
||||||
chat_target_info.get("person_name") or chat_target_info.get("user_nickname") or "对方"
|
chat_target_info.get("person_name")
|
||||||
|
or chat_target_info.get("user_nickname")
|
||||||
|
or "对方"
|
||||||
)
|
)
|
||||||
chat_context_description = f"你正在和 {chat_target_name} 私聊"
|
chat_context_description = f"你正在和 {chat_target_name} 私聊"
|
||||||
|
|
||||||
action_options_block = await self._build_action_options(current_available_actions, mode, target_prompt)
|
action_options_block = await self._build_action_options(
|
||||||
|
current_available_actions, mode, target_prompt
|
||||||
|
)
|
||||||
|
|
||||||
moderation_prompt_block = "请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。"
|
moderation_prompt_block = "请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。"
|
||||||
|
|
||||||
custom_prompt_block = ""
|
custom_prompt_block = ""
|
||||||
if global_config.custom_prompt.planner_custom_prompt_content:
|
if global_config.custom_prompt.planner_custom_prompt_content:
|
||||||
custom_prompt_block = global_config.custom_prompt.planner_custom_prompt_content
|
custom_prompt_block = (
|
||||||
|
global_config.custom_prompt.planner_custom_prompt_content
|
||||||
|
)
|
||||||
|
|
||||||
planner_prompt_template = await global_prompt_manager.get_prompt_async("planner_prompt")
|
planner_prompt_template = await global_prompt_manager.get_prompt_async(
|
||||||
|
"planner_prompt"
|
||||||
|
)
|
||||||
prompt = planner_prompt_template.format(
|
prompt = planner_prompt_template.format(
|
||||||
schedule_block=schedule_block,
|
schedule_block=schedule_block,
|
||||||
mood_block=mood_block,
|
mood_block=mood_block,
|
||||||
@@ -560,7 +556,9 @@ class ActionPlanner:
|
|||||||
"""
|
"""
|
||||||
is_group_chat = True
|
is_group_chat = True
|
||||||
is_group_chat, chat_target_info = get_chat_type_and_target_info(self.chat_id)
|
is_group_chat, chat_target_info = get_chat_type_and_target_info(self.chat_id)
|
||||||
logger.debug(f"{self.log_prefix}获取到聊天信息 - 群聊: {is_group_chat}, 目标信息: {chat_target_info}")
|
logger.debug(
|
||||||
|
f"{self.log_prefix}获取到聊天信息 - 群聊: {is_group_chat}, 目标信息: {chat_target_info}"
|
||||||
|
)
|
||||||
|
|
||||||
current_available_actions_dict = self.action_manager.get_using_actions()
|
current_available_actions_dict = self.action_manager.get_using_actions()
|
||||||
|
|
||||||
@@ -571,9 +569,13 @@ class ActionPlanner:
|
|||||||
current_available_actions = {}
|
current_available_actions = {}
|
||||||
for action_name in current_available_actions_dict:
|
for action_name in current_available_actions_dict:
|
||||||
if action_name in all_registered_actions:
|
if action_name in all_registered_actions:
|
||||||
current_available_actions[action_name] = all_registered_actions[action_name]
|
current_available_actions[action_name] = all_registered_actions[
|
||||||
|
action_name
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
logger.warning(f"{self.log_prefix}使用中的动作 {action_name} 未在已注册动作中找到")
|
logger.warning(
|
||||||
|
f"{self.log_prefix}使用中的动作 {action_name} 未在已注册动作中找到"
|
||||||
|
)
|
||||||
|
|
||||||
# 将no_reply作为系统级特殊动作添加到可用动作中
|
# 将no_reply作为系统级特殊动作添加到可用动作中
|
||||||
# no_reply虽然是系统级决策,但需要让规划器认为它是可用的
|
# no_reply虽然是系统级决策,但需要让规划器认为它是可用的
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ from src.plugin_system.base.config_types import ConfigField
|
|||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
|
|
||||||
# 导入API模块 - 标准Python包方式
|
# 导入API模块 - 标准Python包方式
|
||||||
from src.plugins.built_in.core_actions.reply import ReplyAction
|
|
||||||
from src.plugins.built_in.core_actions.emoji import EmojiAction
|
from src.plugins.built_in.core_actions.emoji import EmojiAction
|
||||||
from src.plugins.built_in.core_actions.anti_injector_manager import AntiInjectorStatusCommand
|
from src.plugins.built_in.core_actions.anti_injector_manager import AntiInjectorStatusCommand
|
||||||
|
|
||||||
@@ -68,8 +67,6 @@ class CoreActionsPlugin(BasePlugin):
|
|||||||
|
|
||||||
# --- 根据配置注册组件 ---
|
# --- 根据配置注册组件 ---
|
||||||
components = []
|
components = []
|
||||||
if self.get_config("components.enable_reply", True):
|
|
||||||
components.append((ReplyAction.get_action_info(), ReplyAction))
|
|
||||||
if self.get_config("components.enable_emoji", True):
|
if self.get_config("components.enable_emoji", True):
|
||||||
components.append((EmojiAction.get_action_info(), EmojiAction))
|
components.append((EmojiAction.get_action_info(), EmojiAction))
|
||||||
if self.get_config("components.enable_anti_injector_manager", True):
|
if self.get_config("components.enable_anti_injector_manager", True):
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
from typing import Tuple
|
|
||||||
|
|
||||||
# 导入新插件系统
|
|
||||||
from src.plugin_system import BaseAction, ActionActivationType, ChatMode
|
|
||||||
|
|
||||||
# 导入依赖的系统组件
|
|
||||||
from src.common.logger import get_logger
|
|
||||||
from src.plugin_system.apis import generator_api
|
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger("reply_action")
|
|
||||||
|
|
||||||
|
|
||||||
class ReplyAction(BaseAction):
|
|
||||||
"""基本回复动作:确保系统始终有一个可用的回退动作!!!"""
|
|
||||||
|
|
||||||
focus_activation_type = ActionActivationType.ALWAYS
|
|
||||||
normal_activation_type = ActionActivationType.ALWAYS
|
|
||||||
mode_enable = ChatMode.ALL
|
|
||||||
parallel_action = False
|
|
||||||
|
|
||||||
# 动作基本信息
|
|
||||||
action_name = "reply"
|
|
||||||
action_description = "进行基本回复"
|
|
||||||
|
|
||||||
# 动作参数定义
|
|
||||||
action_parameters = {}
|
|
||||||
|
|
||||||
# 动作使用场景
|
|
||||||
action_require = [""]
|
|
||||||
|
|
||||||
# 关联类型
|
|
||||||
associated_types = []
|
|
||||||
|
|
||||||
async def execute(self) -> Tuple[bool, str]:
|
|
||||||
"""执行回复动作"""
|
|
||||||
try:
|
|
||||||
reason = self.action_data.get("reason", "")
|
|
||||||
|
|
||||||
logger.info(f"{self.log_prefix} 执行基本回复动作,原因: {reason}")
|
|
||||||
|
|
||||||
# 获取当前消息和上下文
|
|
||||||
if not self.chat_stream or not self.chat_stream.get_latest_message():
|
|
||||||
logger.warning(f"{self.log_prefix} 没有可回复的消息")
|
|
||||||
return False, ""
|
|
||||||
|
|
||||||
latest_message = self.chat_stream.get_latest_message()
|
|
||||||
|
|
||||||
# 使用生成器API生成回复
|
|
||||||
success, reply_set, _ = await generator_api.generate_reply(
|
|
||||||
target_message=latest_message.processed_plain_text,
|
|
||||||
chat_stream=self.chat_stream,
|
|
||||||
reasoning=reason,
|
|
||||||
action_message={},
|
|
||||||
)
|
|
||||||
|
|
||||||
if success and reply_set:
|
|
||||||
# 提取回复文本
|
|
||||||
reply_text = ""
|
|
||||||
for message_type, content in reply_set:
|
|
||||||
if message_type == "text":
|
|
||||||
reply_text += content
|
|
||||||
break
|
|
||||||
|
|
||||||
if reply_text:
|
|
||||||
logger.info(f"{self.log_prefix} 回复生成成功: {reply_text[:50]}...")
|
|
||||||
return True, reply_text
|
|
||||||
else:
|
|
||||||
logger.warning(f"{self.log_prefix} 生成的回复为空")
|
|
||||||
return False, ""
|
|
||||||
else:
|
|
||||||
logger.warning(f"{self.log_prefix} 回复生成失败")
|
|
||||||
return False, ""
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"{self.log_prefix} 执行回复动作时发生异常: {e}")
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
traceback.print_exc()
|
|
||||||
return False, ""
|
|
||||||
Reference in New Issue
Block a user