feat:统一normal和focus的动作调整,emoji统一可选随机激活或llm激活
This commit is contained in:
@@ -10,6 +10,8 @@
|
|||||||
- 优化计时信息和Log
|
- 优化计时信息和Log
|
||||||
- 添加回复超时检查
|
- 添加回复超时检查
|
||||||
- normal的插件允许llm激活
|
- normal的插件允许llm激活
|
||||||
|
- 合并action激活器
|
||||||
|
- emoji统一可选随机激活或llm激活
|
||||||
|
|
||||||
## [0.8.1] - 2025-7-5
|
## [0.8.1] - 2025-7-5
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ from src.chat.heart_flow.observation.actions_observation import ActionObservatio
|
|||||||
|
|
||||||
from src.chat.focus_chat.memory_activator import MemoryActivator
|
from src.chat.focus_chat.memory_activator import MemoryActivator
|
||||||
from src.chat.focus_chat.info_processors.base_processor import BaseProcessor
|
from src.chat.focus_chat.info_processors.base_processor import BaseProcessor
|
||||||
from src.chat.focus_chat.planners.planner_simple import ActionPlanner
|
from src.chat.planner_actions.planner_focus import ActionPlanner
|
||||||
from src.chat.focus_chat.planners.modify_actions import ActionModifier
|
from src.chat.planner_actions.action_modifier import ActionModifier
|
||||||
from src.chat.focus_chat.planners.action_manager import ActionManager
|
from src.chat.planner_actions.action_manager import ActionManager
|
||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
from src.chat.focus_chat.hfc_performance_logger import HFCPerformanceLogger
|
from src.chat.focus_chat.hfc_performance_logger import HFCPerformanceLogger
|
||||||
from src.chat.focus_chat.hfc_version_manager import get_hfc_version
|
from src.chat.focus_chat.hfc_version_manager import get_hfc_version
|
||||||
@@ -50,24 +50,6 @@ PROCESSOR_CLASSES = {
|
|||||||
logger = get_logger("hfc") # Logger Name Changed
|
logger = get_logger("hfc") # Logger Name Changed
|
||||||
|
|
||||||
|
|
||||||
async def _handle_cycle_delay(action_taken_this_cycle: bool, cycle_start_time: float, log_prefix: str):
|
|
||||||
"""处理循环延迟"""
|
|
||||||
cycle_duration = time.monotonic() - cycle_start_time
|
|
||||||
|
|
||||||
try:
|
|
||||||
sleep_duration = 0.0
|
|
||||||
if not action_taken_this_cycle and cycle_duration < 1:
|
|
||||||
sleep_duration = 1 - cycle_duration
|
|
||||||
elif cycle_duration < 0.2:
|
|
||||||
sleep_duration = 0.2
|
|
||||||
|
|
||||||
if sleep_duration > 0:
|
|
||||||
await asyncio.sleep(sleep_duration)
|
|
||||||
|
|
||||||
except asyncio.CancelledError:
|
|
||||||
logger.info(f"{log_prefix} Sleep interrupted, loop likely cancelling.")
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
class HeartFChatting:
|
class HeartFChatting:
|
||||||
"""
|
"""
|
||||||
@@ -80,7 +62,6 @@ class HeartFChatting:
|
|||||||
self,
|
self,
|
||||||
chat_id: str,
|
chat_id: str,
|
||||||
on_stop_focus_chat: Optional[Callable[[], Awaitable[None]]] = None,
|
on_stop_focus_chat: Optional[Callable[[], Awaitable[None]]] = None,
|
||||||
performance_version: str = None,
|
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
HeartFChatting 初始化函数
|
HeartFChatting 初始化函数
|
||||||
@@ -122,7 +103,7 @@ class HeartFChatting:
|
|||||||
self.action_planner = ActionPlanner(
|
self.action_planner = ActionPlanner(
|
||||||
log_prefix=self.log_prefix, action_manager=self.action_manager
|
log_prefix=self.log_prefix, action_manager=self.action_manager
|
||||||
)
|
)
|
||||||
self.action_modifier = ActionModifier(action_manager=self.action_manager)
|
self.action_modifier = ActionModifier(action_manager=self.action_manager, chat_id=self.stream_id)
|
||||||
self.action_observation = ActionObservation(observe_id=self.stream_id)
|
self.action_observation = ActionObservation(observe_id=self.stream_id)
|
||||||
self.action_observation.set_action_manager(self.action_manager)
|
self.action_observation.set_action_manager(self.action_manager)
|
||||||
|
|
||||||
@@ -146,7 +127,7 @@ class HeartFChatting:
|
|||||||
|
|
||||||
# 初始化性能记录器
|
# 初始化性能记录器
|
||||||
# 如果没有指定版本号,则使用全局版本管理器的版本号
|
# 如果没有指定版本号,则使用全局版本管理器的版本号
|
||||||
actual_version = performance_version or get_hfc_version()
|
actual_version = get_hfc_version()
|
||||||
self.performance_logger = HFCPerformanceLogger(chat_id, actual_version)
|
self.performance_logger = HFCPerformanceLogger(chat_id, actual_version)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -287,7 +268,6 @@ class HeartFChatting:
|
|||||||
|
|
||||||
# 初始化周期状态
|
# 初始化周期状态
|
||||||
cycle_timers = {}
|
cycle_timers = {}
|
||||||
loop_cycle_start_time = time.monotonic()
|
|
||||||
|
|
||||||
# 执行规划和处理阶段
|
# 执行规划和处理阶段
|
||||||
try:
|
try:
|
||||||
@@ -370,11 +350,6 @@ class HeartFChatting:
|
|||||||
|
|
||||||
self._current_cycle_detail.timers = cycle_timers
|
self._current_cycle_detail.timers = cycle_timers
|
||||||
|
|
||||||
# 防止循环过快消耗资源
|
|
||||||
await _handle_cycle_delay(
|
|
||||||
loop_info["loop_action_info"]["action_taken"], loop_cycle_start_time, self.log_prefix
|
|
||||||
)
|
|
||||||
|
|
||||||
# 完成当前循环并保存历史
|
# 完成当前循环并保存历史
|
||||||
self._current_cycle_detail.complete_cycle()
|
self._current_cycle_detail.complete_cycle()
|
||||||
self._cycle_history.append(self._current_cycle_detail)
|
self._cycle_history.append(self._current_cycle_detail)
|
||||||
@@ -543,6 +518,7 @@ class HeartFChatting:
|
|||||||
# 调用完整的动作修改流程
|
# 调用完整的动作修改流程
|
||||||
await self.action_modifier.modify_actions(
|
await self.action_modifier.modify_actions(
|
||||||
observations=self.observations,
|
observations=self.observations,
|
||||||
|
mode="focus",
|
||||||
)
|
)
|
||||||
|
|
||||||
await self.action_observation.observe()
|
await self.action_observation.observe()
|
||||||
@@ -567,7 +543,7 @@ class HeartFChatting:
|
|||||||
logger.debug(f"{self.log_prefix} 并行阶段完成,准备进入规划器,plan_info数量: {len(all_plan_info)}")
|
logger.debug(f"{self.log_prefix} 并行阶段完成,准备进入规划器,plan_info数量: {len(all_plan_info)}")
|
||||||
|
|
||||||
with Timer("规划器", cycle_timers):
|
with Timer("规划器", cycle_timers):
|
||||||
plan_result = await self.action_planner.plan(all_plan_info, self.observations, loop_start_time)
|
plan_result = await self.action_planner.plan(all_plan_info, loop_start_time)
|
||||||
|
|
||||||
loop_plan_info = {
|
loop_plan_info = {
|
||||||
"action_result": plan_result.get("action_result", {}),
|
"action_result": plan_result.get("action_result", {}),
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
from abc import ABC, abstractmethod
|
|
||||||
from typing import List, Dict, Any
|
|
||||||
from src.chat.focus_chat.planners.action_manager import ActionManager
|
|
||||||
from src.chat.focus_chat.info.info_base import InfoBase
|
|
||||||
|
|
||||||
|
|
||||||
class BasePlanner(ABC):
|
|
||||||
"""规划器基类"""
|
|
||||||
|
|
||||||
def __init__(self, log_prefix: str, action_manager: ActionManager):
|
|
||||||
self.log_prefix = log_prefix
|
|
||||||
self.action_manager = action_manager
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
async def plan(
|
|
||||||
self, all_plan_info: List[InfoBase], running_memorys: List[Dict[str, Any]], loop_start_time: float
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
规划下一步行动
|
|
||||||
|
|
||||||
Args:
|
|
||||||
all_plan_info: 所有计划信息
|
|
||||||
running_memorys: 回忆信息
|
|
||||||
loop_start_time: 循环开始时间
|
|
||||||
Returns:
|
|
||||||
Dict[str, Any]: 规划结果
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
# 外部世界可以是某个聊天 不同平台的聊天 也可以是任意媒体
|
# 外部世界可以是某个聊天 不同平台的聊天 也可以是任意媒体
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
from src.chat.focus_chat.planners.action_manager import ActionManager
|
from src.chat.planner_actions.action_manager import ActionManager
|
||||||
|
|
||||||
logger = get_logger("observation")
|
logger = get_logger("observation")
|
||||||
|
|
||||||
|
|||||||
@@ -9,18 +9,17 @@ from src.plugin_system.apis import generator_api
|
|||||||
from maim_message import UserInfo, Seg
|
from maim_message import UserInfo, Seg
|
||||||
from src.chat.message_receive.chat_stream import ChatStream, get_chat_manager
|
from src.chat.message_receive.chat_stream import ChatStream, get_chat_manager
|
||||||
from src.chat.utils.timer_calculator import Timer
|
from src.chat.utils.timer_calculator import Timer
|
||||||
|
from src.common.message_repository import count_messages
|
||||||
from src.chat.utils.prompt_builder import global_prompt_manager
|
from src.chat.utils.prompt_builder import global_prompt_manager
|
||||||
from ..message_receive.message import MessageSending, MessageRecv, MessageThinking, MessageSet
|
from ..message_receive.message import MessageSending, MessageRecv, MessageThinking, MessageSet
|
||||||
from src.chat.message_receive.message_sender import message_manager
|
from src.chat.message_receive.message_sender import message_manager
|
||||||
from src.chat.normal_chat.willing.willing_manager import get_willing_manager
|
from src.chat.normal_chat.willing.willing_manager import get_willing_manager
|
||||||
from src.chat.normal_chat.normal_chat_utils import get_recent_message_stats
|
from src.chat.planner_actions.action_manager import ActionManager
|
||||||
from src.chat.focus_chat.planners.action_manager import ActionManager
|
|
||||||
from src.person_info.relationship_builder_manager import relationship_builder_manager
|
from src.person_info.relationship_builder_manager import relationship_builder_manager
|
||||||
from .priority_manager import PriorityManager
|
from .priority_manager import PriorityManager
|
||||||
import traceback
|
import traceback
|
||||||
from src.chat.normal_chat.normal_chat_planner import NormalChatPlanner
|
from src.chat.planner_actions.planner_normal import NormalChatPlanner
|
||||||
from src.chat.normal_chat.normal_chat_action_modifier import NormalChatActionModifier
|
from src.chat.planner_actions.action_modifier import ActionModifier
|
||||||
|
|
||||||
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.manager.mood_manager import mood_manager
|
from src.manager.mood_manager import mood_manager
|
||||||
@@ -71,7 +70,7 @@ class NormalChat:
|
|||||||
# Planner相关初始化
|
# Planner相关初始化
|
||||||
self.action_manager = ActionManager()
|
self.action_manager = ActionManager()
|
||||||
self.planner = NormalChatPlanner(self.stream_name, self.action_manager)
|
self.planner = NormalChatPlanner(self.stream_name, self.action_manager)
|
||||||
self.action_modifier = NormalChatActionModifier(self.action_manager, self.stream_id, self.stream_name)
|
self.action_modifier = ActionModifier(self.action_manager, self.stream_id)
|
||||||
self.enable_planner = global_config.normal_chat.enable_planner # 从配置中读取是否启用planner
|
self.enable_planner = global_config.normal_chat.enable_planner # 从配置中读取是否启用planner
|
||||||
|
|
||||||
# 记录最近的回复内容,每项包含: {time, user_message, response, is_mentioned, is_reference_reply}
|
# 记录最近的回复内容,每项包含: {time, user_message, response, is_mentioned, is_reference_reply}
|
||||||
@@ -569,8 +568,8 @@ class NormalChat:
|
|||||||
available_actions = None
|
available_actions = None
|
||||||
if self.enable_planner:
|
if self.enable_planner:
|
||||||
try:
|
try:
|
||||||
await self.action_modifier.modify_actions_for_normal_chat(
|
await self.action_modifier.modify_actions(
|
||||||
self.chat_stream, self.recent_replies, message.processed_plain_text
|
mode="normal", message_content=message.processed_plain_text
|
||||||
)
|
)
|
||||||
available_actions = self.action_manager.get_using_actions_for_mode("normal")
|
available_actions = self.action_manager.get_using_actions_for_mode("normal")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -1003,3 +1002,29 @@ class NormalChat:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[{self.stream_name}] 清理思考消息 {thinking_id} 时出错: {e}")
|
logger.error(f"[{self.stream_name}] 清理思考消息 {thinking_id} 时出错: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def get_recent_message_stats(minutes: int = 30, chat_id: str = None) -> dict:
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
minutes (int): 检索的分钟数,默认30分钟
|
||||||
|
chat_id (str, optional): 指定的chat_id,仅统计该chat下的消息。为None时统计全部。
|
||||||
|
Returns:
|
||||||
|
dict: {"bot_reply_count": int, "total_message_count": int}
|
||||||
|
"""
|
||||||
|
|
||||||
|
now = time.time()
|
||||||
|
start_time = now - minutes * 60
|
||||||
|
bot_id = global_config.bot.qq_account
|
||||||
|
|
||||||
|
filter_base = {"time": {"$gte": start_time}}
|
||||||
|
if chat_id is not None:
|
||||||
|
filter_base["chat_id"] = chat_id
|
||||||
|
|
||||||
|
# 总消息数
|
||||||
|
total_message_count = count_messages(filter_base)
|
||||||
|
# bot自身回复数
|
||||||
|
bot_filter = filter_base.copy()
|
||||||
|
bot_filter["user_id"] = bot_id
|
||||||
|
bot_reply_count = count_messages(bot_filter)
|
||||||
|
|
||||||
|
return {"bot_reply_count": bot_reply_count, "total_message_count": total_message_count}
|
||||||
@@ -1,403 +0,0 @@
|
|||||||
from typing import List, Any, Dict
|
|
||||||
from src.common.logger import get_logger
|
|
||||||
from src.chat.focus_chat.planners.action_manager import ActionManager
|
|
||||||
from src.chat.utils.chat_message_builder import build_readable_messages, get_raw_msg_before_timestamp_with_chat
|
|
||||||
from src.config.config import global_config
|
|
||||||
import random
|
|
||||||
import time
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
logger = get_logger("normal_chat_action_modifier")
|
|
||||||
|
|
||||||
|
|
||||||
class NormalChatActionModifier:
|
|
||||||
"""Normal Chat动作修改器
|
|
||||||
|
|
||||||
负责根据Normal Chat的上下文和状态动态调整可用的动作集合
|
|
||||||
实现与Focus Chat类似的动作激活策略,但将LLM_JUDGE转换为概率激活以提升性能
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, action_manager: ActionManager, stream_id: str, stream_name: str):
|
|
||||||
"""初始化动作修改器"""
|
|
||||||
self.action_manager = action_manager
|
|
||||||
self.stream_id = stream_id
|
|
||||||
self.stream_name = stream_name
|
|
||||||
self.log_prefix = f"[{stream_name}]动作修改器"
|
|
||||||
|
|
||||||
# 缓存所有注册的动作
|
|
||||||
self.all_actions = self.action_manager.get_registered_actions()
|
|
||||||
|
|
||||||
async def modify_actions_for_normal_chat(
|
|
||||||
self,
|
|
||||||
chat_stream,
|
|
||||||
recent_replies: List[dict],
|
|
||||||
message_content: str,
|
|
||||||
**kwargs: Any,
|
|
||||||
):
|
|
||||||
"""为Normal Chat修改可用动作集合
|
|
||||||
|
|
||||||
实现动作激活策略:
|
|
||||||
1. 基于关联类型的动态过滤
|
|
||||||
2. 基于激活类型的智能判定(LLM_JUDGE转为概率激活)
|
|
||||||
|
|
||||||
Args:
|
|
||||||
chat_stream: 聊天流对象
|
|
||||||
recent_replies: 最近的回复记录
|
|
||||||
message_content: 当前消息内容
|
|
||||||
**kwargs: 其他参数
|
|
||||||
"""
|
|
||||||
|
|
||||||
reasons = []
|
|
||||||
merged_action_changes = {"add": [], "remove": []}
|
|
||||||
type_mismatched_actions = [] # 在外层定义避免作用域问题
|
|
||||||
|
|
||||||
self.action_manager.restore_default_actions()
|
|
||||||
|
|
||||||
# 第一阶段:基于关联类型的动态过滤
|
|
||||||
if chat_stream:
|
|
||||||
chat_context = chat_stream.context if hasattr(chat_stream, "context") else None
|
|
||||||
if chat_context:
|
|
||||||
# 获取Normal模式下的可用动作(已经过滤了mode_enable)
|
|
||||||
current_using_actions = self.action_manager.get_using_actions_for_mode("normal")
|
|
||||||
# print(f"current_using_actions: {current_using_actions}")
|
|
||||||
for action_name in current_using_actions.keys():
|
|
||||||
if action_name in self.all_actions:
|
|
||||||
data = self.all_actions[action_name]
|
|
||||||
if data.get("associated_types"):
|
|
||||||
if not chat_context.check_types(data["associated_types"]):
|
|
||||||
type_mismatched_actions.append(action_name)
|
|
||||||
logger.debug(f"{self.log_prefix} 动作 {action_name} 关联类型不匹配,移除该动作")
|
|
||||||
|
|
||||||
if type_mismatched_actions:
|
|
||||||
merged_action_changes["remove"].extend(type_mismatched_actions)
|
|
||||||
reasons.append(f"移除{type_mismatched_actions}(关联类型不匹配)")
|
|
||||||
|
|
||||||
# 第二阶段:应用激活类型判定
|
|
||||||
# 构建聊天内容 - 使用与planner一致的方式
|
|
||||||
chat_content = ""
|
|
||||||
if chat_stream and hasattr(chat_stream, "stream_id"):
|
|
||||||
try:
|
|
||||||
# 获取消息历史,使用与normal_chat_planner相同的方法
|
|
||||||
message_list_before_now = get_raw_msg_before_timestamp_with_chat(
|
|
||||||
chat_id=chat_stream.stream_id,
|
|
||||||
timestamp=time.time(),
|
|
||||||
limit=global_config.chat.max_context_size, # 使用相同的配置
|
|
||||||
)
|
|
||||||
|
|
||||||
# 构建可读的聊天上下文
|
|
||||||
chat_content = build_readable_messages(
|
|
||||||
message_list_before_now,
|
|
||||||
replace_bot_name=True,
|
|
||||||
merge_messages=False,
|
|
||||||
timestamp_mode="relative",
|
|
||||||
read_mark=0.0,
|
|
||||||
show_actions=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.debug(f"{self.log_prefix} 成功构建聊天内容,长度: {len(chat_content)}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"{self.log_prefix} 构建聊天内容失败: {e}")
|
|
||||||
chat_content = ""
|
|
||||||
|
|
||||||
# 获取当前Normal模式下的动作集进行激活判定
|
|
||||||
current_actions = self.action_manager.get_using_actions_for_mode("normal")
|
|
||||||
|
|
||||||
# print(f"current_actions: {current_actions}")
|
|
||||||
# print(f"chat_content: {chat_content}")
|
|
||||||
final_activated_actions = await self._apply_normal_activation_filtering(
|
|
||||||
current_actions, chat_content, message_content, recent_replies
|
|
||||||
)
|
|
||||||
# print(f"final_activated_actions: {final_activated_actions}")
|
|
||||||
|
|
||||||
# 统一处理所有需要移除的动作,避免重复移除
|
|
||||||
all_actions_to_remove = set() # 使用set避免重复
|
|
||||||
|
|
||||||
# 添加关联类型不匹配的动作
|
|
||||||
if type_mismatched_actions:
|
|
||||||
all_actions_to_remove.update(type_mismatched_actions)
|
|
||||||
|
|
||||||
# 添加激活类型判定未通过的动作
|
|
||||||
for action_name in current_actions.keys():
|
|
||||||
if action_name not in final_activated_actions:
|
|
||||||
all_actions_to_remove.add(action_name)
|
|
||||||
|
|
||||||
# 统计移除原因(避免重复)
|
|
||||||
activation_failed_actions = [
|
|
||||||
name
|
|
||||||
for name in current_actions.keys()
|
|
||||||
if name not in final_activated_actions and name not in type_mismatched_actions
|
|
||||||
]
|
|
||||||
if activation_failed_actions:
|
|
||||||
reasons.append(f"移除{activation_failed_actions}(激活类型判定未通过)")
|
|
||||||
|
|
||||||
# 统一执行移除操作
|
|
||||||
for action_name in all_actions_to_remove:
|
|
||||||
success = self.action_manager.remove_action_from_using(action_name)
|
|
||||||
if success:
|
|
||||||
logger.debug(f"{self.log_prefix} 移除动作: {action_name}")
|
|
||||||
else:
|
|
||||||
logger.debug(f"{self.log_prefix} 动作 {action_name} 已经不在使用集中,跳过移除")
|
|
||||||
|
|
||||||
# 应用动作添加(如果有的话)
|
|
||||||
for action_name in merged_action_changes["add"]:
|
|
||||||
if action_name in self.all_actions:
|
|
||||||
success = self.action_manager.add_action_to_using(action_name)
|
|
||||||
if success:
|
|
||||||
logger.debug(f"{self.log_prefix} 添加动作: {action_name}")
|
|
||||||
|
|
||||||
# 记录变更原因
|
|
||||||
if reasons:
|
|
||||||
logger.info(f"{self.log_prefix} 动作调整完成: {' | '.join(reasons)}")
|
|
||||||
|
|
||||||
# 获取最终的Normal模式可用动作并记录
|
|
||||||
final_actions = self.action_manager.get_using_actions_for_mode("normal")
|
|
||||||
logger.debug(f"{self.log_prefix} 当前Normal模式可用动作: {list(final_actions.keys())}")
|
|
||||||
|
|
||||||
async def _apply_normal_activation_filtering(
|
|
||||||
self,
|
|
||||||
actions_with_info: Dict[str, Any],
|
|
||||||
chat_content: str = "",
|
|
||||||
message_content: str = "",
|
|
||||||
recent_replies: List[dict] = None,
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
应用Normal模式的激活类型过滤逻辑
|
|
||||||
|
|
||||||
与Focus模式的区别:
|
|
||||||
1. LLM_JUDGE类型转换为概率激活(避免LLM调用)
|
|
||||||
2. RANDOM类型保持概率激活
|
|
||||||
3. KEYWORD类型保持关键词匹配
|
|
||||||
4. ALWAYS类型直接激活
|
|
||||||
|
|
||||||
Args:
|
|
||||||
actions_with_info: 带完整信息的动作字典
|
|
||||||
chat_content: 聊天内容
|
|
||||||
message_content: 当前消息内容
|
|
||||||
recent_replies: 最近的回复记录列表
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict[str, Any]: 过滤后激活的actions字典
|
|
||||||
"""
|
|
||||||
activated_actions = {}
|
|
||||||
|
|
||||||
# 分类处理不同激活类型的actions
|
|
||||||
always_actions = {}
|
|
||||||
random_actions = {}
|
|
||||||
keyword_actions = {}
|
|
||||||
llm_judge_actions = {}
|
|
||||||
|
|
||||||
for action_name, action_info in actions_with_info.items():
|
|
||||||
# 使用normal_activation_type
|
|
||||||
activation_type = action_info.get("normal_activation_type", "always")
|
|
||||||
|
|
||||||
# 现在统一是字符串格式的激活类型值
|
|
||||||
if activation_type == "always":
|
|
||||||
always_actions[action_name] = action_info
|
|
||||||
elif activation_type == "random":
|
|
||||||
random_actions[action_name] = action_info
|
|
||||||
elif activation_type == "llm_judge":
|
|
||||||
llm_judge_actions[action_name] = action_info
|
|
||||||
elif activation_type == "keyword":
|
|
||||||
keyword_actions[action_name] = action_info
|
|
||||||
else:
|
|
||||||
logger.warning(f"{self.log_prefix}未知的激活类型: {activation_type},跳过处理")
|
|
||||||
|
|
||||||
# 1. 处理ALWAYS类型(直接激活)
|
|
||||||
for action_name, action_info in always_actions.items():
|
|
||||||
activated_actions[action_name] = action_info
|
|
||||||
logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: ALWAYS类型直接激活")
|
|
||||||
|
|
||||||
# 2. 处理RANDOM类型(概率激活)
|
|
||||||
for action_name, action_info in random_actions.items():
|
|
||||||
probability = action_info.get("random_activation_probability", ActionManager.DEFAULT_RANDOM_PROBABILITY)
|
|
||||||
should_activate = random.random() < probability
|
|
||||||
if should_activate:
|
|
||||||
activated_actions[action_name] = action_info
|
|
||||||
logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: RANDOM类型触发(概率{probability})")
|
|
||||||
else:
|
|
||||||
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: RANDOM类型未触发(概率{probability})")
|
|
||||||
|
|
||||||
# 3. 处理KEYWORD类型(关键词匹配)
|
|
||||||
for action_name, action_info in keyword_actions.items():
|
|
||||||
should_activate = self._check_keyword_activation(action_name, action_info, chat_content, message_content)
|
|
||||||
if should_activate:
|
|
||||||
activated_actions[action_name] = action_info
|
|
||||||
keywords = action_info.get("activation_keywords", [])
|
|
||||||
logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: KEYWORD类型匹配关键词({keywords})")
|
|
||||||
else:
|
|
||||||
keywords = action_info.get("activation_keywords", [])
|
|
||||||
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: KEYWORD类型未匹配关键词({keywords})")
|
|
||||||
|
|
||||||
# 4. 处理LLM_JUDGE类型(并行判定)
|
|
||||||
if llm_judge_actions:
|
|
||||||
# 直接并行处理所有LLM判定actions
|
|
||||||
llm_results = await self._process_llm_judge_actions_parallel(
|
|
||||||
llm_judge_actions,
|
|
||||||
chat_content,
|
|
||||||
)
|
|
||||||
|
|
||||||
# 添加激活的LLM判定actions
|
|
||||||
for action_name, should_activate in llm_results.items():
|
|
||||||
if should_activate:
|
|
||||||
activated_actions[action_name] = llm_judge_actions[action_name]
|
|
||||||
logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: LLM_JUDGE类型判定通过")
|
|
||||||
else:
|
|
||||||
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: LLM_JUDGE类型判定未通过")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
logger.debug(f"{self.log_prefix}Normal模式激活类型过滤完成: {list(activated_actions.keys())}")
|
|
||||||
return activated_actions
|
|
||||||
|
|
||||||
def _check_keyword_activation(
|
|
||||||
self,
|
|
||||||
action_name: str,
|
|
||||||
action_info: Dict[str, Any],
|
|
||||||
chat_content: str = "",
|
|
||||||
message_content: str = "",
|
|
||||||
) -> bool:
|
|
||||||
"""
|
|
||||||
检查是否匹配关键词触发条件
|
|
||||||
|
|
||||||
Args:
|
|
||||||
action_name: 动作名称
|
|
||||||
action_info: 动作信息
|
|
||||||
chat_content: 聊天内容(已经是格式化后的可读消息)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 是否应该激活此action
|
|
||||||
"""
|
|
||||||
|
|
||||||
activation_keywords = action_info.get("activation_keywords", [])
|
|
||||||
case_sensitive = action_info.get("keyword_case_sensitive", False)
|
|
||||||
|
|
||||||
if not activation_keywords:
|
|
||||||
logger.warning(f"{self.log_prefix}动作 {action_name} 设置为关键词触发但未配置关键词")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 使用构建好的聊天内容作为检索文本
|
|
||||||
search_text = chat_content + message_content
|
|
||||||
|
|
||||||
# 如果不区分大小写,转换为小写
|
|
||||||
if not case_sensitive:
|
|
||||||
search_text = search_text.lower()
|
|
||||||
|
|
||||||
# 检查每个关键词
|
|
||||||
matched_keywords = []
|
|
||||||
for keyword in activation_keywords:
|
|
||||||
check_keyword = keyword if case_sensitive else keyword.lower()
|
|
||||||
if check_keyword in search_text:
|
|
||||||
matched_keywords.append(keyword)
|
|
||||||
|
|
||||||
# print(f"search_text: {search_text}")
|
|
||||||
# print(f"activation_keywords: {activation_keywords}")
|
|
||||||
|
|
||||||
if matched_keywords:
|
|
||||||
logger.debug(f"{self.log_prefix}动作 {action_name} 匹配到关键词: {matched_keywords}")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
logger.debug(f"{self.log_prefix}动作 {action_name} 未匹配到任何关键词: {activation_keywords}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
async def _process_llm_judge_actions_parallel(
|
|
||||||
self,
|
|
||||||
llm_judge_actions: Dict[str, Any],
|
|
||||||
chat_content: str = "",
|
|
||||||
) -> Dict[str, bool]:
|
|
||||||
"""
|
|
||||||
并行处理LLM判定actions,支持智能缓存
|
|
||||||
|
|
||||||
Args:
|
|
||||||
llm_judge_actions: 需要LLM判定的actions
|
|
||||||
chat_content: 聊天内容
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict[str, bool]: action名称到激活结果的映射
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 生成当前上下文的哈希值
|
|
||||||
current_context_hash = self._generate_context_hash(chat_content)
|
|
||||||
current_time = time.time()
|
|
||||||
|
|
||||||
results = {}
|
|
||||||
tasks_to_run = {}
|
|
||||||
|
|
||||||
# 检查缓存
|
|
||||||
for action_name, action_info in llm_judge_actions.items():
|
|
||||||
cache_key = f"{action_name}_{current_context_hash}"
|
|
||||||
|
|
||||||
# 检查是否有有效的缓存
|
|
||||||
if (
|
|
||||||
cache_key in self._llm_judge_cache
|
|
||||||
and current_time - self._llm_judge_cache[cache_key]["timestamp"] < self._cache_expiry_time
|
|
||||||
):
|
|
||||||
results[action_name] = self._llm_judge_cache[cache_key]["result"]
|
|
||||||
logger.debug(
|
|
||||||
f"{self.log_prefix}使用缓存结果 {action_name}: {'激活' if results[action_name] else '未激活'}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# 需要进行LLM判定
|
|
||||||
tasks_to_run[action_name] = action_info
|
|
||||||
|
|
||||||
# 如果有需要运行的任务,并行执行
|
|
||||||
if tasks_to_run:
|
|
||||||
logger.debug(f"{self.log_prefix}并行执行LLM判定,任务数: {len(tasks_to_run)}")
|
|
||||||
|
|
||||||
# 创建并行任务
|
|
||||||
tasks = []
|
|
||||||
task_names = []
|
|
||||||
|
|
||||||
for action_name, action_info in tasks_to_run.items():
|
|
||||||
task = self._llm_judge_action(
|
|
||||||
action_name,
|
|
||||||
action_info,
|
|
||||||
chat_content,
|
|
||||||
)
|
|
||||||
tasks.append(task)
|
|
||||||
task_names.append(action_name)
|
|
||||||
|
|
||||||
# 并行执行所有任务
|
|
||||||
try:
|
|
||||||
task_results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
||||||
|
|
||||||
# 处理结果并更新缓存
|
|
||||||
for _, (action_name, result) in enumerate(zip(task_names, task_results)):
|
|
||||||
if isinstance(result, Exception):
|
|
||||||
logger.error(f"{self.log_prefix}LLM判定action {action_name} 时出错: {result}")
|
|
||||||
results[action_name] = False
|
|
||||||
else:
|
|
||||||
results[action_name] = result
|
|
||||||
|
|
||||||
# 更新缓存
|
|
||||||
cache_key = f"{action_name}_{current_context_hash}"
|
|
||||||
self._llm_judge_cache[cache_key] = {"result": result, "timestamp": current_time}
|
|
||||||
|
|
||||||
logger.debug(f"{self.log_prefix}并行LLM判定完成,耗时: {time.time() - current_time:.2f}s")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"{self.log_prefix}并行LLM判定失败: {e}")
|
|
||||||
# 如果并行执行失败,为所有任务返回False
|
|
||||||
for action_name in tasks_to_run.keys():
|
|
||||||
results[action_name] = False
|
|
||||||
|
|
||||||
# 清理过期缓存
|
|
||||||
self._cleanup_expired_cache(current_time)
|
|
||||||
|
|
||||||
return results
|
|
||||||
|
|
||||||
def get_available_actions_count(self) -> int:
|
|
||||||
"""获取当前可用动作数量(排除默认的no_action)"""
|
|
||||||
current_actions = self.action_manager.get_using_actions_for_mode("normal")
|
|
||||||
# 排除no_action(如果存在)
|
|
||||||
filtered_actions = {k: v for k, v in current_actions.items() if k != "no_action"}
|
|
||||||
return len(filtered_actions)
|
|
||||||
|
|
||||||
def should_skip_planning(self) -> bool:
|
|
||||||
"""判断是否应该跳过规划过程"""
|
|
||||||
available_count = self.get_available_actions_count()
|
|
||||||
if available_count == 0:
|
|
||||||
logger.debug(f"{self.log_prefix} 没有可用动作,跳过规划")
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import time
|
|
||||||
from src.config.config import global_config
|
|
||||||
from src.common.message_repository import count_messages
|
|
||||||
|
|
||||||
|
|
||||||
def get_recent_message_stats(minutes: int = 30, chat_id: str = None) -> dict:
|
|
||||||
"""
|
|
||||||
Args:
|
|
||||||
minutes (int): 检索的分钟数,默认30分钟
|
|
||||||
chat_id (str, optional): 指定的chat_id,仅统计该chat下的消息。为None时统计全部。
|
|
||||||
Returns:
|
|
||||||
dict: {"bot_reply_count": int, "total_message_count": int}
|
|
||||||
"""
|
|
||||||
|
|
||||||
now = time.time()
|
|
||||||
start_time = now - minutes * 60
|
|
||||||
bot_id = global_config.bot.qq_account
|
|
||||||
|
|
||||||
filter_base = {"time": {"$gte": start_time}}
|
|
||||||
if chat_id is not None:
|
|
||||||
filter_base["chat_id"] = chat_id
|
|
||||||
|
|
||||||
# 总消息数
|
|
||||||
total_message_count = count_messages(filter_base)
|
|
||||||
# bot自身回复数
|
|
||||||
bot_filter = filter_base.copy()
|
|
||||||
bot_filter["user_id"] = bot_id
|
|
||||||
bot_reply_count = count_messages(bot_filter)
|
|
||||||
|
|
||||||
return {"bot_reply_count": bot_reply_count, "total_message_count": total_message_count}
|
|
||||||
@@ -292,10 +292,6 @@ class ActionManager:
|
|||||||
)
|
)
|
||||||
self._using_actions = self._default_actions.copy()
|
self._using_actions = self._default_actions.copy()
|
||||||
|
|
||||||
def restore_default_actions(self) -> None:
|
|
||||||
"""恢复默认动作集到使用集"""
|
|
||||||
self._using_actions = self._default_actions.copy()
|
|
||||||
|
|
||||||
def add_system_action_if_needed(self, action_name: str) -> bool:
|
def add_system_action_if_needed(self, action_name: str) -> bool:
|
||||||
"""
|
"""
|
||||||
根据需要添加系统动作到使用集
|
根据需要添加系统动作到使用集
|
||||||
@@ -10,7 +10,8 @@ import random
|
|||||||
import asyncio
|
import asyncio
|
||||||
import hashlib
|
import hashlib
|
||||||
import time
|
import time
|
||||||
from src.chat.focus_chat.planners.action_manager import ActionManager
|
from src.chat.planner_actions.action_manager import ActionManager
|
||||||
|
from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat, build_readable_messages
|
||||||
|
|
||||||
logger = get_logger("action_manager")
|
logger = get_logger("action_manager")
|
||||||
|
|
||||||
@@ -23,12 +24,13 @@ class ActionModifier:
|
|||||||
支持并行判定和智能缓存优化。
|
支持并行判定和智能缓存优化。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
log_prefix = "动作处理"
|
def __init__(self, action_manager: ActionManager, chat_id: str):
|
||||||
|
|
||||||
def __init__(self, action_manager: ActionManager):
|
|
||||||
"""初始化动作处理器"""
|
"""初始化动作处理器"""
|
||||||
|
self.chat_id = chat_id
|
||||||
|
self.chat_stream = get_chat_manager().get_stream(self.chat_id)
|
||||||
|
self.log_prefix = f"[{get_chat_manager().get_stream_name(self.chat_id) or self.chat_id}]"
|
||||||
|
|
||||||
self.action_manager = action_manager
|
self.action_manager = action_manager
|
||||||
self.all_actions = self.action_manager.get_using_actions_for_mode("focus")
|
|
||||||
|
|
||||||
# 用于LLM判定的小模型
|
# 用于LLM判定的小模型
|
||||||
self.llm_judge = LLMRequest(
|
self.llm_judge = LLMRequest(
|
||||||
@@ -43,11 +45,12 @@ class ActionModifier:
|
|||||||
|
|
||||||
async def modify_actions(
|
async def modify_actions(
|
||||||
self,
|
self,
|
||||||
|
mode: str = "focus",
|
||||||
observations: Optional[List[Observation]] = None,
|
observations: Optional[List[Observation]] = None,
|
||||||
**kwargs: Any,
|
message_content: str = "",
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
完整的动作修改流程,整合传统观察处理和新的激活类型判定
|
动作修改流程,整合传统观察处理和新的激活类型判定
|
||||||
|
|
||||||
这个方法处理完整的动作管理流程:
|
这个方法处理完整的动作管理流程:
|
||||||
1. 基于观察的传统动作修改(循环历史分析、类型匹配等)
|
1. 基于观察的传统动作修改(循环历史分析、类型匹配等)
|
||||||
@@ -57,230 +60,156 @@ class ActionModifier:
|
|||||||
"""
|
"""
|
||||||
logger.debug(f"{self.log_prefix}开始完整动作修改流程")
|
logger.debug(f"{self.log_prefix}开始完整动作修改流程")
|
||||||
|
|
||||||
|
removals_s1 = []
|
||||||
|
removals_s2 = []
|
||||||
|
|
||||||
|
self.action_manager.restore_actions()
|
||||||
|
all_actions = self.action_manager.get_using_actions_for_mode(mode)
|
||||||
|
|
||||||
|
message_list_before_now_half = get_raw_msg_before_timestamp_with_chat(
|
||||||
|
chat_id=self.chat_stream.stream_id,
|
||||||
|
timestamp=time.time(),
|
||||||
|
limit=int(global_config.chat.max_context_size * 0.5),
|
||||||
|
)
|
||||||
|
chat_content = build_readable_messages(
|
||||||
|
message_list_before_now_half,
|
||||||
|
replace_bot_name=True,
|
||||||
|
merge_messages=False,
|
||||||
|
timestamp_mode="relative",
|
||||||
|
read_mark=0.0,
|
||||||
|
show_actions=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if message_content:
|
||||||
|
chat_content = chat_content + "\n" + f"现在,最新的消息是:{message_content}"
|
||||||
|
|
||||||
# === 第一阶段:传统观察处理 ===
|
# === 第一阶段:传统观察处理 ===
|
||||||
chat_content = None
|
|
||||||
|
|
||||||
if observations:
|
if observations:
|
||||||
hfc_obs = None
|
|
||||||
chat_obs = None
|
|
||||||
|
|
||||||
# 收集所有观察对象
|
|
||||||
for obs in observations:
|
for obs in observations:
|
||||||
if isinstance(obs, HFCloopObservation):
|
if isinstance(obs, HFCloopObservation):
|
||||||
hfc_obs = obs
|
|
||||||
if isinstance(obs, ChattingObservation):
|
|
||||||
chat_obs = obs
|
|
||||||
chat_content = obs.talking_message_str_truncate_short
|
|
||||||
|
|
||||||
# 合并所有动作变更
|
|
||||||
merged_action_changes = {"add": [], "remove": []}
|
|
||||||
reasons = []
|
|
||||||
|
|
||||||
# 处理HFCloopObservation - 传统的循环历史分析
|
|
||||||
if hfc_obs:
|
|
||||||
obs = hfc_obs
|
|
||||||
# 获取适用于FOCUS模式的动作
|
# 获取适用于FOCUS模式的动作
|
||||||
all_actions = self.all_actions
|
removals_from_loop = await self.analyze_loop_actions(obs)
|
||||||
action_changes = await self.analyze_loop_actions(obs)
|
if removals_from_loop:
|
||||||
if action_changes["add"] or action_changes["remove"]:
|
removals_s1.extend(removals_from_loop)
|
||||||
# 合并动作变更
|
|
||||||
merged_action_changes["add"].extend(action_changes["add"])
|
|
||||||
merged_action_changes["remove"].extend(action_changes["remove"])
|
|
||||||
reasons.append("基于循环历史分析")
|
|
||||||
|
|
||||||
# 详细记录循环历史分析的变更原因
|
|
||||||
for action_name in action_changes["add"]:
|
|
||||||
logger.info(f"{self.log_prefix}添加动作: {action_name},原因: 循环历史分析建议添加")
|
|
||||||
for action_name in action_changes["remove"]:
|
|
||||||
logger.info(f"{self.log_prefix}移除动作: {action_name},原因: 循环历史分析建议移除")
|
|
||||||
|
|
||||||
# 处理ChattingObservation - 传统的类型匹配检查
|
|
||||||
if chat_obs:
|
|
||||||
# 检查动作的关联类型
|
# 检查动作的关联类型
|
||||||
chat_context = get_chat_manager().get_stream(chat_obs.chat_id).context
|
chat_context = self.chat_stream.context
|
||||||
type_mismatched_actions = []
|
type_mismatched_actions = self._check_action_associated_types(all_actions, chat_context)
|
||||||
|
|
||||||
for action_name in all_actions.keys():
|
|
||||||
data = all_actions[action_name]
|
|
||||||
if data.get("associated_types"):
|
|
||||||
if not chat_context.check_types(data["associated_types"]):
|
|
||||||
type_mismatched_actions.append(action_name)
|
|
||||||
associated_types_str = ", ".join(data["associated_types"])
|
|
||||||
logger.info(
|
|
||||||
f"{self.log_prefix}移除动作: {action_name},原因: 关联类型不匹配(需要: {associated_types_str})"
|
|
||||||
)
|
|
||||||
|
|
||||||
if type_mismatched_actions:
|
if type_mismatched_actions:
|
||||||
# 合并到移除列表中
|
removals_s1.extend(type_mismatched_actions)
|
||||||
merged_action_changes["remove"].extend(type_mismatched_actions)
|
|
||||||
reasons.append("基于关联类型检查")
|
|
||||||
|
|
||||||
# 应用传统的动作变更到ActionManager
|
# 应用第一阶段的移除
|
||||||
for action_name in merged_action_changes["add"]:
|
for action_name, reason in removals_s1:
|
||||||
if action_name in self.action_manager.get_registered_actions():
|
|
||||||
self.action_manager.add_action_to_using(action_name)
|
|
||||||
logger.debug(f"{self.log_prefix}应用添加动作: {action_name},原因集合: {reasons}")
|
|
||||||
|
|
||||||
for action_name in merged_action_changes["remove"]:
|
|
||||||
self.action_manager.remove_action_from_using(action_name)
|
self.action_manager.remove_action_from_using(action_name)
|
||||||
logger.debug(f"{self.log_prefix}应用移除动作: {action_name},原因集合: {reasons}")
|
logger.debug(f"{self.log_prefix}阶段一移除动作: {action_name},原因: {reason}")
|
||||||
|
|
||||||
logger.info(
|
|
||||||
f"{self.log_prefix}传统动作修改完成,当前使用动作: {list(self.action_manager.get_using_actions().keys())}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 注释:已移除exit_focus_chat动作,现在由no_reply动作处理频率检测退出专注模式
|
|
||||||
|
|
||||||
# === 第二阶段:激活类型判定 ===
|
# === 第二阶段:激活类型判定 ===
|
||||||
# 如果提供了聊天上下文,则进行激活类型判定
|
|
||||||
if chat_content is not None:
|
if chat_content is not None:
|
||||||
logger.debug(f"{self.log_prefix}开始激活类型判定阶段")
|
logger.debug(f"{self.log_prefix}开始激活类型判定阶段")
|
||||||
|
|
||||||
# 获取当前使用的动作集(经过第一阶段处理,且适用于FOCUS模式)
|
# 获取当前使用的动作集(经过第一阶段处理)
|
||||||
current_using_actions = self.action_manager.get_using_actions()
|
current_using_actions = self.action_manager.get_using_actions_for_mode(mode)
|
||||||
all_registered_actions = self.action_manager.get_registered_actions()
|
|
||||||
|
|
||||||
# 构建完整的动作信息
|
# 获取因激活类型判定而需要移除的动作
|
||||||
current_actions_with_info = {}
|
removals_s2 = await self._get_deactivated_actions_by_type(
|
||||||
for action_name in current_using_actions.keys():
|
current_using_actions,
|
||||||
if action_name in all_registered_actions:
|
mode,
|
||||||
current_actions_with_info[action_name] = all_registered_actions[action_name]
|
|
||||||
else:
|
|
||||||
logger.warning(f"{self.log_prefix}使用中的动作 {action_name} 未在已注册动作中找到")
|
|
||||||
|
|
||||||
# 应用激活类型判定
|
|
||||||
final_activated_actions = await self._apply_activation_type_filtering(
|
|
||||||
current_actions_with_info,
|
|
||||||
chat_content,
|
chat_content,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 更新ActionManager,移除未激活的动作
|
# 应用第二阶段的移除
|
||||||
actions_to_remove = []
|
for action_name, reason in removals_s2:
|
||||||
removal_reasons = {}
|
|
||||||
|
|
||||||
for action_name in current_using_actions.keys():
|
|
||||||
if action_name not in final_activated_actions:
|
|
||||||
actions_to_remove.append(action_name)
|
|
||||||
# 确定移除原因
|
|
||||||
if action_name in all_registered_actions:
|
|
||||||
action_info = all_registered_actions[action_name]
|
|
||||||
activation_type = action_info.get("focus_activation_type", "always")
|
|
||||||
|
|
||||||
# 处理字符串格式的激活类型值
|
|
||||||
if activation_type == "random":
|
|
||||||
probability = action_info.get("random_probability", 0.3)
|
|
||||||
removal_reasons[action_name] = f"RANDOM类型未触发(概率{probability})"
|
|
||||||
elif activation_type == "llm_judge":
|
|
||||||
removal_reasons[action_name] = "LLM判定未激活"
|
|
||||||
elif activation_type == "keyword":
|
|
||||||
keywords = action_info.get("activation_keywords", [])
|
|
||||||
removal_reasons[action_name] = f"关键词未匹配(关键词: {keywords})"
|
|
||||||
else:
|
|
||||||
removal_reasons[action_name] = "激活判定未通过"
|
|
||||||
else:
|
|
||||||
removal_reasons[action_name] = "动作信息不完整"
|
|
||||||
|
|
||||||
for action_name in actions_to_remove:
|
|
||||||
self.action_manager.remove_action_from_using(action_name)
|
self.action_manager.remove_action_from_using(action_name)
|
||||||
reason = removal_reasons.get(action_name, "未知原因")
|
logger.debug(f"{self.log_prefix}阶段二移除动作: {action_name},原因: {reason}")
|
||||||
logger.info(f"{self.log_prefix}移除动作: {action_name},原因: {reason}")
|
|
||||||
|
|
||||||
# 注释:已完全移除exit_focus_chat动作
|
# === 统一日志记录 ===
|
||||||
|
all_removals = removals_s1 + removals_s2
|
||||||
logger.info(f"{self.log_prefix}激活类型判定完成,最终可用动作: {list(final_activated_actions.keys())}")
|
if all_removals:
|
||||||
|
removals_summary = " | ".join([f"{name}({reason})" for name, reason in all_removals])
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{self.log_prefix}完整动作修改流程结束,最终动作集: {list(self.action_manager.get_using_actions().keys())}"
|
f"{self.log_prefix}{mode}模式动作修改流程结束,最终可用动作: {list(self.action_manager.get_using_actions_for_mode(mode).keys())}||移除记录: {removals_summary}"
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _apply_activation_type_filtering(
|
def _check_action_associated_types(self, all_actions, chat_context):
|
||||||
|
type_mismatched_actions = []
|
||||||
|
for action_name, data in all_actions.items():
|
||||||
|
if data.get("associated_types"):
|
||||||
|
if not chat_context.check_types(data["associated_types"]):
|
||||||
|
associated_types_str = ", ".join(data["associated_types"])
|
||||||
|
reason = f"适配器不支持(需要: {associated_types_str})"
|
||||||
|
type_mismatched_actions.append((action_name, reason))
|
||||||
|
logger.debug(
|
||||||
|
f"{self.log_prefix}决定移除动作: {action_name},原因: {reason}"
|
||||||
|
)
|
||||||
|
return type_mismatched_actions
|
||||||
|
|
||||||
|
async def _get_deactivated_actions_by_type(
|
||||||
self,
|
self,
|
||||||
actions_with_info: Dict[str, Any],
|
actions_with_info: Dict[str, Any],
|
||||||
|
mode: str = "focus",
|
||||||
chat_content: str = "",
|
chat_content: str = "",
|
||||||
) -> Dict[str, Any]:
|
) -> List[tuple[str, str]]:
|
||||||
"""
|
"""
|
||||||
应用激活类型过滤逻辑,支持四种激活类型的并行处理
|
根据激活类型过滤,返回需要停用的动作列表及原因
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
actions_with_info: 带完整信息的动作字典
|
actions_with_info: 带完整信息的动作字典
|
||||||
chat_content: 聊天内容
|
chat_content: 聊天内容
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dict[str, Any]: 过滤后激活的actions字典
|
List[Tuple[str, str]]: 需要停用的 (action_name, reason) 元组列表
|
||||||
"""
|
"""
|
||||||
activated_actions = {}
|
deactivated_actions = []
|
||||||
|
|
||||||
# 分类处理不同激活类型的actions
|
# 分类处理不同激活类型的actions
|
||||||
always_actions = {}
|
|
||||||
random_actions = {}
|
|
||||||
llm_judge_actions = {}
|
llm_judge_actions = {}
|
||||||
keyword_actions = {}
|
|
||||||
|
|
||||||
for action_name, action_info in actions_with_info.items():
|
actions_to_check = list(actions_with_info.items())
|
||||||
activation_type = action_info.get("focus_activation_type", "always")
|
random.shuffle(actions_to_check)
|
||||||
|
|
||||||
# print(f"action_name: {action_name}, activation_type: {activation_type}")
|
for action_name, action_info in actions_to_check:
|
||||||
|
activation_type = f"{mode}_activation_type"
|
||||||
|
activation_type = action_info.get(activation_type, "always")
|
||||||
|
|
||||||
# 现在统一是字符串格式的激活类型值
|
|
||||||
if activation_type == "always":
|
if activation_type == "always":
|
||||||
always_actions[action_name] = action_info
|
continue # 总是激活,无需处理
|
||||||
|
|
||||||
elif activation_type == "random":
|
elif activation_type == "random":
|
||||||
random_actions[action_name] = action_info
|
probability = action_info.get("random_activation_probability", ActionManager.DEFAULT_RANDOM_PROBABILITY)
|
||||||
|
if not (random.random() < probability):
|
||||||
|
reason = f"RANDOM类型未触发(概率{probability})"
|
||||||
|
deactivated_actions.append((action_name, reason))
|
||||||
|
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: {reason}")
|
||||||
|
|
||||||
|
elif activation_type == "keyword":
|
||||||
|
if not self._check_keyword_activation(action_name, action_info, chat_content):
|
||||||
|
keywords = action_info.get("activation_keywords", [])
|
||||||
|
reason = f"关键词未匹配(关键词: {keywords})"
|
||||||
|
deactivated_actions.append((action_name, reason))
|
||||||
|
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: {reason}")
|
||||||
|
|
||||||
elif activation_type == "llm_judge":
|
elif activation_type == "llm_judge":
|
||||||
llm_judge_actions[action_name] = action_info
|
llm_judge_actions[action_name] = action_info
|
||||||
elif activation_type == "keyword":
|
|
||||||
keyword_actions[action_name] = action_info
|
|
||||||
else:
|
else:
|
||||||
logger.warning(f"{self.log_prefix}未知的激活类型: {activation_type},跳过处理")
|
logger.warning(f"{self.log_prefix}未知的激活类型: {activation_type},跳过处理")
|
||||||
|
|
||||||
# 1. 处理ALWAYS类型(直接激活)
|
# 并行处理LLM_JUDGE类型
|
||||||
for action_name, action_info in always_actions.items():
|
|
||||||
activated_actions[action_name] = action_info
|
|
||||||
logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: ALWAYS类型直接激活")
|
|
||||||
|
|
||||||
# 2. 处理RANDOM类型
|
|
||||||
for action_name, action_info in random_actions.items():
|
|
||||||
probability = action_info.get("random_activation_probability", ActionManager.DEFAULT_RANDOM_PROBABILITY)
|
|
||||||
should_activate = random.random() < probability
|
|
||||||
if should_activate:
|
|
||||||
activated_actions[action_name] = action_info
|
|
||||||
logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: RANDOM类型触发(概率{probability})")
|
|
||||||
else:
|
|
||||||
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: RANDOM类型未触发(概率{probability})")
|
|
||||||
|
|
||||||
# 3. 处理KEYWORD类型(快速判定)
|
|
||||||
for action_name, action_info in keyword_actions.items():
|
|
||||||
should_activate = self._check_keyword_activation(
|
|
||||||
action_name,
|
|
||||||
action_info,
|
|
||||||
chat_content,
|
|
||||||
)
|
|
||||||
if should_activate:
|
|
||||||
activated_actions[action_name] = action_info
|
|
||||||
keywords = action_info.get("activation_keywords", [])
|
|
||||||
logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: KEYWORD类型匹配关键词({keywords})")
|
|
||||||
else:
|
|
||||||
keywords = action_info.get("activation_keywords", [])
|
|
||||||
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: KEYWORD类型未匹配关键词({keywords})")
|
|
||||||
|
|
||||||
# 4. 处理LLM_JUDGE类型(并行判定)
|
|
||||||
if llm_judge_actions:
|
if llm_judge_actions:
|
||||||
# 直接并行处理所有LLM判定actions
|
|
||||||
llm_results = await self._process_llm_judge_actions_parallel(
|
llm_results = await self._process_llm_judge_actions_parallel(
|
||||||
llm_judge_actions,
|
llm_judge_actions,
|
||||||
chat_content,
|
chat_content,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 添加激活的LLM判定actions
|
|
||||||
for action_name, should_activate in llm_results.items():
|
for action_name, should_activate in llm_results.items():
|
||||||
if should_activate:
|
if not should_activate:
|
||||||
activated_actions[action_name] = llm_judge_actions[action_name]
|
reason = "LLM判定未激活"
|
||||||
logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: LLM_JUDGE类型判定通过")
|
deactivated_actions.append((action_name, reason))
|
||||||
else:
|
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: {reason}")
|
||||||
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: LLM_JUDGE类型判定未通过")
|
|
||||||
|
|
||||||
logger.debug(f"{self.log_prefix}激活类型过滤完成: {list(activated_actions.keys())}")
|
return deactivated_actions
|
||||||
return activated_actions
|
|
||||||
|
|
||||||
async def process_actions_for_planner(
|
async def process_actions_for_planner(
|
||||||
self, observed_messages_str: str = "", chat_context: Optional[str] = None, extra_context: Optional[str] = None
|
self, observed_messages_str: str = "", chat_context: Optional[str] = None, extra_context: Optional[str] = None
|
||||||
@@ -538,22 +467,19 @@ class ActionModifier:
|
|||||||
logger.debug(f"{self.log_prefix}动作 {action_name} 未匹配到任何关键词: {activation_keywords}")
|
logger.debug(f"{self.log_prefix}动作 {action_name} 未匹配到任何关键词: {activation_keywords}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def analyze_loop_actions(self, obs: HFCloopObservation) -> Dict[str, List[str]]:
|
async def analyze_loop_actions(self, obs: HFCloopObservation) -> List[tuple[str, str]]:
|
||||||
"""分析最近的循环内容并决定动作的增减
|
"""分析最近的循环内容并决定动作的移除
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dict[str, List[str]]: 包含要增加和删除的动作
|
List[Tuple[str, str]]: 包含要删除的动作及原因的元组列表
|
||||||
{
|
[("action3", "some reason")]
|
||||||
"add": ["action1", "action2"],
|
|
||||||
"remove": ["action3"]
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
result = {"add": [], "remove": []}
|
removals = []
|
||||||
|
|
||||||
# 获取最近10次循环
|
# 获取最近10次循环
|
||||||
recent_cycles = obs.history_loop[-10:] if len(obs.history_loop) > 10 else obs.history_loop
|
recent_cycles = obs.history_loop[-10:] if len(obs.history_loop) > 10 else obs.history_loop
|
||||||
if not recent_cycles:
|
if not recent_cycles:
|
||||||
return result
|
return removals
|
||||||
|
|
||||||
reply_sequence = [] # 记录最近的动作序列
|
reply_sequence = [] # 记录最近的动作序列
|
||||||
|
|
||||||
@@ -584,36 +510,39 @@ class ActionModifier:
|
|||||||
# 根据最近的reply情况决定是否移除reply动作
|
# 根据最近的reply情况决定是否移除reply动作
|
||||||
if len(last_max_reply_num) >= max_reply_num and all(last_max_reply_num):
|
if len(last_max_reply_num) >= max_reply_num and all(last_max_reply_num):
|
||||||
# 如果最近max_reply_num次都是reply,直接移除
|
# 如果最近max_reply_num次都是reply,直接移除
|
||||||
result["remove"].append("reply")
|
reason = f"连续回复过多(最近{len(last_max_reply_num)}次全是reply,超过阈值{max_reply_num})"
|
||||||
|
removals.append(("reply", reason))
|
||||||
# reply_count = len(last_max_reply_num) - no_reply_count
|
# reply_count = len(last_max_reply_num) - no_reply_count
|
||||||
logger.info(
|
|
||||||
f"{self.log_prefix}移除reply动作,原因: 连续回复过多(最近{len(last_max_reply_num)}次全是reply,超过阈值{max_reply_num})"
|
|
||||||
)
|
|
||||||
elif len(last_max_reply_num) >= sec_thres_reply_num and all(last_max_reply_num[-sec_thres_reply_num:]):
|
elif len(last_max_reply_num) >= sec_thres_reply_num and all(last_max_reply_num[-sec_thres_reply_num:]):
|
||||||
# 如果最近sec_thres_reply_num次都是reply,40%概率移除
|
# 如果最近sec_thres_reply_num次都是reply,40%概率移除
|
||||||
removal_probability = 0.4 / global_config.focus_chat.consecutive_replies
|
removal_probability = 0.4 / global_config.focus_chat.consecutive_replies
|
||||||
if random.random() < removal_probability:
|
if random.random() < removal_probability:
|
||||||
result["remove"].append("reply")
|
reason = f"连续回复较多(最近{sec_thres_reply_num}次全是reply,{removal_probability:.2f}概率移除,触发移除)"
|
||||||
logger.info(
|
removals.append(("reply", reason))
|
||||||
f"{self.log_prefix}移除reply动作,原因: 连续回复较多(最近{sec_thres_reply_num}次全是reply,{removal_probability:.2f}概率移除,触发移除)"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logger.debug(
|
|
||||||
f"{self.log_prefix}连续回复检测:最近{sec_thres_reply_num}次全是reply,{removal_probability:.2f}概率移除,未触发"
|
|
||||||
)
|
|
||||||
elif len(last_max_reply_num) >= one_thres_reply_num and all(last_max_reply_num[-one_thres_reply_num:]):
|
elif len(last_max_reply_num) >= one_thres_reply_num and all(last_max_reply_num[-one_thres_reply_num:]):
|
||||||
# 如果最近one_thres_reply_num次都是reply,20%概率移除
|
# 如果最近one_thres_reply_num次都是reply,20%概率移除
|
||||||
removal_probability = 0.2 / global_config.focus_chat.consecutive_replies
|
removal_probability = 0.2 / global_config.focus_chat.consecutive_replies
|
||||||
if random.random() < removal_probability:
|
if random.random() < removal_probability:
|
||||||
result["remove"].append("reply")
|
reason = f"连续回复检测(最近{one_thres_reply_num}次全是reply,{removal_probability:.2f}概率移除,触发移除)"
|
||||||
logger.info(
|
removals.append(("reply", reason))
|
||||||
f"{self.log_prefix}移除reply动作,原因: 连续回复检测(最近{one_thres_reply_num}次全是reply,{removal_probability:.2f}概率移除,触发移除)"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logger.debug(
|
|
||||||
f"{self.log_prefix}连续回复检测:最近{one_thres_reply_num}次全是reply,{removal_probability:.2f}概率移除,未触发"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
logger.debug(f"{self.log_prefix}连续回复检测:无需移除reply动作,最近回复模式正常")
|
logger.debug(f"{self.log_prefix}连续回复检测:无需移除reply动作,最近回复模式正常")
|
||||||
|
|
||||||
return result
|
return removals
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_available_actions_count(self) -> int:
|
||||||
|
"""获取当前可用动作数量(排除默认的no_action)"""
|
||||||
|
current_actions = self.action_manager.get_using_actions_for_mode("normal")
|
||||||
|
# 排除no_action(如果存在)
|
||||||
|
filtered_actions = {k: v for k, v in current_actions.items() if k != "no_action"}
|
||||||
|
return len(filtered_actions)
|
||||||
|
|
||||||
|
def should_skip_planning(self) -> bool:
|
||||||
|
"""判断是否应该跳过规划过程"""
|
||||||
|
available_count = self.get_available_actions_count()
|
||||||
|
if available_count == 0:
|
||||||
|
logger.debug(f"{self.log_prefix} 没有可用动作,跳过规划")
|
||||||
|
return True
|
||||||
|
return False
|
||||||
@@ -9,9 +9,8 @@ from src.chat.focus_chat.info.obs_info import ObsInfo
|
|||||||
from src.chat.focus_chat.info.action_info import ActionInfo
|
from src.chat.focus_chat.info.action_info import ActionInfo
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
|
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
|
||||||
from src.chat.focus_chat.planners.action_manager import ActionManager
|
from src.chat.planner_actions.action_manager import ActionManager
|
||||||
from json_repair import repair_json
|
from json_repair import repair_json
|
||||||
from src.chat.focus_chat.planners.base_planner import BasePlanner
|
|
||||||
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 datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
@@ -69,9 +68,10 @@ def init_prompt():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ActionPlanner(BasePlanner):
|
class ActionPlanner:
|
||||||
def __init__(self, log_prefix: str, action_manager: ActionManager):
|
def __init__(self, log_prefix: str, action_manager: ActionManager):
|
||||||
super().__init__(log_prefix, action_manager)
|
self.log_prefix = log_prefix
|
||||||
|
self.action_manager = action_manager
|
||||||
# LLM规划器配置
|
# LLM规划器配置
|
||||||
self.planner_llm = LLMRequest(
|
self.planner_llm = LLMRequest(
|
||||||
model=global_config.model.planner,
|
model=global_config.model.planner,
|
||||||
@@ -84,7 +84,7 @@ class ActionPlanner(BasePlanner):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def plan(
|
async def plan(
|
||||||
self, all_plan_info: List[InfoBase], running_memorys: List[Dict[str, Any]], loop_start_time: float
|
self, all_plan_info: List[InfoBase],loop_start_time: float
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
规划器 (Planner): 使用LLM根据上下文决定做出什么动作。
|
规划器 (Planner): 使用LLM根据上下文决定做出什么动作。
|
||||||
@@ -6,7 +6,7 @@ from src.config.config import global_config
|
|||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
|
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
|
||||||
from src.individuality.individuality import get_individuality
|
from src.individuality.individuality import get_individuality
|
||||||
from src.chat.focus_chat.planners.action_manager import ActionManager
|
from src.chat.planner_actions.action_manager import ActionManager
|
||||||
from src.chat.message_receive.message import MessageThinking
|
from src.chat.message_receive.message import MessageThinking
|
||||||
from json_repair import repair_json
|
from json_repair import repair_json
|
||||||
from src.chat.utils.chat_message_builder import build_readable_messages, get_raw_msg_before_timestamp_with_chat
|
from src.chat.utils.chat_message_builder import build_readable_messages, get_raw_msg_before_timestamp_with_chat
|
||||||
@@ -340,7 +340,7 @@ MODULE_COLORS = {
|
|||||||
"memory": "\033[34m",
|
"memory": "\033[34m",
|
||||||
"hfc": "\033[96m",
|
"hfc": "\033[96m",
|
||||||
"base_action": "\033[96m",
|
"base_action": "\033[96m",
|
||||||
"action_manager": "\033[34m",
|
"action_manager": "\033[32m",
|
||||||
# 关系系统
|
# 关系系统
|
||||||
"relation": "\033[38;5;201m", # 深粉色
|
"relation": "\033[38;5;201m", # 深粉色
|
||||||
# 聊天相关模块
|
# 聊天相关模块
|
||||||
@@ -414,7 +414,7 @@ MODULE_COLORS = {
|
|||||||
"confirm": "\033[1;93m", # 黄色+粗体
|
"confirm": "\033[1;93m", # 黄色+粗体
|
||||||
# 模型相关
|
# 模型相关
|
||||||
"model_utils": "\033[38;5;164m", # 紫红色
|
"model_utils": "\033[38;5;164m", # 紫红色
|
||||||
|
"relationship_fetcher": "\033[38;5;170m", # 浅紫色
|
||||||
"relationship_builder": "\033[38;5;117m", # 浅蓝色
|
"relationship_builder": "\033[38;5;117m", # 浅蓝色
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -273,12 +273,6 @@ class MessageReceiveConfig(ConfigBase):
|
|||||||
class NormalChatConfig(ConfigBase):
|
class NormalChatConfig(ConfigBase):
|
||||||
"""普通聊天配置类"""
|
"""普通聊天配置类"""
|
||||||
|
|
||||||
message_buffer: bool = False
|
|
||||||
"""消息缓冲器"""
|
|
||||||
|
|
||||||
emoji_chance: float = 0.2
|
|
||||||
"""发送表情包的基础概率"""
|
|
||||||
|
|
||||||
|
|
||||||
willing_mode: str = "classical"
|
willing_mode: str = "classical"
|
||||||
"""意愿模式"""
|
"""意愿模式"""
|
||||||
@@ -295,14 +289,6 @@ class NormalChatConfig(ConfigBase):
|
|||||||
enable_planner: bool = False
|
enable_planner: bool = False
|
||||||
"""是否启用动作规划器"""
|
"""是否启用动作规划器"""
|
||||||
|
|
||||||
gather_timeout: int = 110 # planner和generator的并行执行超时时间
|
|
||||||
"""planner和generator的并行执行超时时间"""
|
|
||||||
|
|
||||||
auto_focus_threshold: float = 1.0 # 自动切换到专注模式的阈值,值越大越难触发
|
|
||||||
"""自动切换到专注模式的阈值,值越大越难触发"""
|
|
||||||
|
|
||||||
fatigue_talk_frequency: float = 0.2 # 疲劳模式下的基础对话频率 (条/分钟)
|
|
||||||
"""疲劳模式下的基础对话频率 (条/分钟)"""
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -363,6 +349,12 @@ class ToolConfig(ConfigBase):
|
|||||||
class EmojiConfig(ConfigBase):
|
class EmojiConfig(ConfigBase):
|
||||||
"""表情包配置类"""
|
"""表情包配置类"""
|
||||||
|
|
||||||
|
emoji_chance: float = 0.6
|
||||||
|
"""发送表情包的基础概率"""
|
||||||
|
|
||||||
|
emoji_activate_type: str = "random"
|
||||||
|
"""表情包激活类型,可选:random,llm,random下,表情包动作随机启用,llm下,表情包动作根据llm判断是否启用"""
|
||||||
|
|
||||||
max_reg_num: int = 200
|
max_reg_num: int = 200
|
||||||
"""表情包最大注册数量"""
|
"""表情包最大注册数量"""
|
||||||
|
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ class RelationshipFetcher:
|
|||||||
|
|
||||||
# 检查是否返回了不需要查询的标志
|
# 检查是否返回了不需要查询的标志
|
||||||
if "none" in content_json:
|
if "none" in content_json:
|
||||||
logger.info(f"{self.log_prefix} LLM判断当前不需要查询任何信息:{content_json.get('none', '')}")
|
logger.debug(f"{self.log_prefix} LLM判断当前不需要查询任何信息:{content_json.get('none', '')}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
info_type = content_json.get("info_type")
|
info_type = content_json.get("info_type")
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ async def get_by_description(description: str) -> Optional[Tuple[str, str, str]]
|
|||||||
Optional[Tuple[str, str, str]]: (base64编码, 表情包描述, 匹配的情感标签) 或 None
|
Optional[Tuple[str, str, str]]: (base64编码, 表情包描述, 匹配的情感标签) 或 None
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
logger.info(f"[EmojiAPI] 根据描述获取表情包: {description}")
|
logger.debug(f"[EmojiAPI] 根据描述获取表情包: {description}")
|
||||||
|
|
||||||
emoji_manager = get_emoji_manager()
|
emoji_manager = get_emoji_manager()
|
||||||
emoji_result = await emoji_manager.get_emoji_for_text(description)
|
emoji_result = await emoji_manager.get_emoji_for_text(description)
|
||||||
@@ -47,7 +47,7 @@ async def get_by_description(description: str) -> Optional[Tuple[str, str, str]]
|
|||||||
logger.error(f"[EmojiAPI] 无法将表情包文件转换为base64: {emoji_path}")
|
logger.error(f"[EmojiAPI] 无法将表情包文件转换为base64: {emoji_path}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
logger.info(f"[EmojiAPI] 成功获取表情包: {emoji_description}, 匹配情感: {matched_emotion}")
|
logger.debug(f"[EmojiAPI] 成功获取表情包: {emoji_description}, 匹配情感: {matched_emotion}")
|
||||||
return emoji_base64, emoji_description, matched_emotion
|
return emoji_base64, emoji_description, matched_emotion
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ async def _send_to_target(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if sent_msg:
|
if sent_msg:
|
||||||
logger.info(f"[SendAPI] 成功发送消息到 {stream_id}")
|
logger.debug(f"[SendAPI] 成功发送消息到 {stream_id}")
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
logger.error("[SendAPI] 发送消息失败")
|
logger.error("[SendAPI] 发送消息失败")
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class EmojiAction(BaseAction):
|
|||||||
"""表情动作 - 发送表情包"""
|
"""表情动作 - 发送表情包"""
|
||||||
|
|
||||||
# 激活设置
|
# 激活设置
|
||||||
focus_activation_type = ActionActivationType.LLM_JUDGE
|
focus_activation_type = ActionActivationType.RANDOM
|
||||||
normal_activation_type = ActionActivationType.RANDOM
|
normal_activation_type = ActionActivationType.RANDOM
|
||||||
mode_enable = ChatMode.ALL
|
mode_enable = ChatMode.ALL
|
||||||
parallel_action = True
|
parallel_action = True
|
||||||
|
|||||||
@@ -180,8 +180,15 @@ class CoreActionsPlugin(BasePlugin):
|
|||||||
"""返回插件包含的组件列表"""
|
"""返回插件包含的组件列表"""
|
||||||
|
|
||||||
# --- 从配置动态设置Action/Command ---
|
# --- 从配置动态设置Action/Command ---
|
||||||
emoji_chance = global_config.normal_chat.emoji_chance
|
emoji_chance = global_config.emoji.emoji_chance
|
||||||
|
if global_config.emoji.emoji_activate_type == "random":
|
||||||
EmojiAction.random_activation_probability = emoji_chance
|
EmojiAction.random_activation_probability = emoji_chance
|
||||||
|
EmojiAction.focus_activation_type = ActionActivationType.RANDOM
|
||||||
|
EmojiAction.normal_activation_type = ActionActivationType.RANDOM
|
||||||
|
elif global_config.emoji.emoji_activate_type == "llm":
|
||||||
|
EmojiAction.random_activation_probability = 0.0
|
||||||
|
EmojiAction.focus_activation_type = ActionActivationType.LLM_JUDGE
|
||||||
|
EmojiAction.normal_activation_type = ActionActivationType.LLM_JUDGE
|
||||||
|
|
||||||
no_reply_probability = self.get_config("no_reply.random_probability", 0.8)
|
no_reply_probability = self.get_config("no_reply.random_probability", 0.8)
|
||||||
NoReplyAction.random_activation_probability = no_reply_probability
|
NoReplyAction.random_activation_probability = no_reply_probability
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ class ToolExecutor:
|
|||||||
if tool_results:
|
if tool_results:
|
||||||
self._set_cache(cache_key, tool_results)
|
self._set_cache(cache_key, tool_results)
|
||||||
|
|
||||||
|
if used_tools:
|
||||||
logger.info(f"{self.log_prefix}工具执行完成,共执行{len(used_tools)}个工具: {used_tools}")
|
logger.info(f"{self.log_prefix}工具执行完成,共执行{len(used_tools)}个工具: {used_tools}")
|
||||||
|
|
||||||
if return_details:
|
if return_details:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[inner]
|
[inner]
|
||||||
version = "3.3.0"
|
version = "3.4.0"
|
||||||
|
|
||||||
#----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读----
|
#----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读----
|
||||||
#如果你想要修改配置文件,请在修改后将version的值进行变更
|
#如果你想要修改配置文件,请在修改后将version的值进行变更
|
||||||
@@ -118,7 +118,6 @@ ban_msgs_regex = [
|
|||||||
|
|
||||||
[normal_chat] #普通聊天
|
[normal_chat] #普通聊天
|
||||||
#一般回复参数
|
#一般回复参数
|
||||||
emoji_chance = 0.2 # 麦麦一般回复时使用表情包的概率
|
|
||||||
willing_mode = "classical" # 回复意愿模式 —— 经典模式:classical,mxp模式:mxp,自定义模式:custom(需要你自己实现)
|
willing_mode = "classical" # 回复意愿模式 —— 经典模式:classical,mxp模式:mxp,自定义模式:custom(需要你自己实现)
|
||||||
response_interested_rate_amplifier = 1 # 麦麦回复兴趣度放大系数
|
response_interested_rate_amplifier = 1 # 麦麦回复兴趣度放大系数
|
||||||
mentioned_bot_inevitable_reply = true # 提及 bot 必然回复
|
mentioned_bot_inevitable_reply = true # 提及 bot 必然回复
|
||||||
@@ -137,6 +136,9 @@ enable_in_normal_chat = false # 是否在普通聊天中启用工具
|
|||||||
enable_in_focus_chat = true # 是否在专注聊天中启用工具
|
enable_in_focus_chat = true # 是否在专注聊天中启用工具
|
||||||
|
|
||||||
[emoji]
|
[emoji]
|
||||||
|
emoji_chance = 0.6 # 麦麦激活表情包动作的概率
|
||||||
|
emoji_activate_type = "random" # 表情包激活类型,可选:random,llm ; random下,表情包动作随机启用,llm下,表情包动作根据llm判断是否启用
|
||||||
|
|
||||||
max_reg_num = 60 # 表情包最大注册数量
|
max_reg_num = 60 # 表情包最大注册数量
|
||||||
do_replace = true # 开启则在达到最大数量时删除(替换)表情包,关闭则达到最大数量时不会继续收集表情包
|
do_replace = true # 开启则在达到最大数量时删除(替换)表情包,关闭则达到最大数量时不会继续收集表情包
|
||||||
check_interval = 10 # 检查表情包(注册,破损,删除)的时间间隔(分钟)
|
check_interval = 10 # 检查表情包(注册,破损,删除)的时间间隔(分钟)
|
||||||
|
|||||||
Reference in New Issue
Block a user