回退“feat: 实现KEYWORD_OR_LLM_JUDGE激活类型”

This commit is contained in:
tt-P607
2025-09-17 22:37:45 +08:00
committed by Windpicker-owo
parent 78615fc8ce
commit 65087b8c98
15 changed files with 57 additions and 3683 deletions

View File

@@ -3,7 +3,7 @@ import time
import traceback
import math
import random
from typing import Dict, Any, Tuple, Optional
from typing import Dict, Any, Tuple
from src.chat.utils.timer_calculator import Timer
from src.common.logger import get_logger
@@ -135,16 +135,6 @@ class CycleProcessor:
action_type = "no_action"
reply_text = "" # 初始化reply_text变量避免UnboundLocalError
# 开始新的思考循环
cycle_timers, thinking_id = self.cycle_tracker.start_cycle()
logger.info(f"{self.log_prefix} 开始第{self.context.cycle_counter}次思考")
if ENABLE_S4U and self.context.chat_stream and self.context.chat_stream.user_info:
await send_typing(self.context.chat_stream.user_info.user_id)
loop_start_time = time.time()
# 使用sigmoid函数将interest_value转换为概率
# 当interest_value为0时概率接近0使用Focus模式
# 当interest_value很高时概率接近1使用Normal模式
@@ -198,7 +188,7 @@ class CycleProcessor:
# 第一步:动作修改
with Timer("动作修改", cycle_timers):
try:
await self.action_modifier.modify_actions(mode=mode)
await self.action_modifier.modify_actions()
available_actions = self.context.action_manager.get_using_actions()
except Exception as e:
logger.error(f"{self.context.log_prefix} 动作修改失败: {e}")
@@ -351,16 +341,7 @@ class CycleProcessor:
# 2. 然后并行执行所有其他动作
if other_actions:
logger.info(f"{self.log_prefix} 正在执行附加动作: {[a.get('action_type') for a in other_actions]}")
# 从回复文本中提取歌名
song_name_from_reply = self._extract_song_name_from_reply(reply_text_from_reply)
other_action_tasks = []
for action in other_actions:
if action.get("action_type") == "music_search" and song_name_from_reply:
action["action_data"]["song_name"] = song_name_from_reply
other_action_tasks.append(asyncio.create_task(execute_action(action)))
other_action_tasks = [asyncio.create_task(execute_action(action)) for action in other_actions]
results = await asyncio.gather(*other_action_tasks, return_exceptions=True)
for i, result in enumerate(results):
if isinstance(result, BaseException):
@@ -474,7 +455,7 @@ class CycleProcessor:
if not action_handler:
logger.error(f"{self.context.log_prefix} 回退方案也失败,无法创建任何动作处理器")
return False, "", ""
# 执行动作
success, reply_text = await action_handler.handle_action()
return success, reply_text, ""
@@ -482,12 +463,3 @@ class CycleProcessor:
logger.error(f"{self.context.log_prefix} 处理{action}时出错: {e}")
traceback.print_exc()
return False, "", ""
def _extract_song_name_from_reply(self, reply_text: str) -> Optional[str]:
"""从回复文本中提取歌名"""
import re
# 匹配书名号中的内容
match = re.search(r"《(.*?)》", reply_text)
if match:
return match.group(1)
return None

View File

@@ -10,7 +10,7 @@ from src.llm_models.utils_model import LLMRequest
from src.chat.message_receive.chat_stream import get_chat_manager, ChatMessageContext
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
from src.plugin_system.base.component_types import ActionInfo, ActionActivationType, ChatMode
from src.plugin_system.base.component_types import ActionInfo, ActionActivationType
from src.plugin_system.core.global_announcement_manager import global_announcement_manager
if TYPE_CHECKING:
@@ -43,7 +43,10 @@ class ActionModifier:
self._cache_expiry_time = 30 # 缓存过期时间(秒)
self._last_context_hash = None # 上次上下文的哈希值
async def modify_actions(self, mode: ChatMode, message_content: str = ""):
async def modify_actions(
self,
message_content: str = "",
): # sourcery skip: use-named-expression
"""
动作修改流程,整合传统观察处理和新的激活类型判定
@@ -143,7 +146,6 @@ class ActionModifier:
removals_s3 = await self._get_deactivated_actions_by_type(
current_using_actions,
chat_content,
mode,
)
# 应用第三阶段的移除
@@ -176,7 +178,6 @@ class ActionModifier:
self,
actions_with_info: Dict[str, ActionInfo],
chat_content: str = "",
mode: ChatMode = ChatMode.NORMAL,
) -> List[tuple[str, str]]:
"""
根据激活类型过滤,返回需要停用的动作列表及原因
@@ -197,26 +198,34 @@ class ActionModifier:
random.shuffle(actions_to_check)
for action_name, action_info in actions_to_check:
if mode == ChatMode.FOCUS:
activation_type = action_info.focus_activation_type
else:
activation_type = action_info.normal_activation_type
activation_type = action_info.activation_type or action_info.focus_activation_type
if activation_type == ActionActivationType.ALWAYS:
continue
continue # 总是激活,无需处理
elif activation_type == ActionActivationType.RANDOM:
if random.random() >= action_info.random_activation_probability:
deactivated_actions.append((action_name, f"RANDOM类型未触发概率{action_info.random_activation_probability}"))
probability = action_info.random_activation_probability
probability = action_info.random_activation_probability
if 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 == ActionActivationType.KEYWORD:
if not self._check_keyword_activation(action_name, action_info, chat_content):
deactivated_actions.append((action_name, f"关键词未匹配(关键词: {action_info.activation_keywords}"))
keywords = action_info.activation_keywords
reason = f"关键词未匹配(关键词: {keywords}"
deactivated_actions.append((action_name, reason))
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: {reason}")
elif activation_type == ActionActivationType.LLM_JUDGE:
llm_judge_actions[action_name] = action_info
elif activation_type == ActionActivationType.KEYWORD_OR_LLM_JUDGE:
if not self._check_keyword_activation(action_name, action_info, chat_content):
llm_judge_actions[action_name] = action_info
elif activation_type == ActionActivationType.NEVER:
deactivated_actions.append((action_name, "激活类型为never"))
reason = "激活类型为never"
deactivated_actions.append((action_name, reason))
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: 激活类型为never")
else:
logger.warning(f"{self.log_prefix}未知的激活类型: {activation_type},跳过处理")

View File

@@ -2,14 +2,14 @@
PlanGenerator: 负责搜集和汇总所有决策所需的信息,生成一个未经筛选的“原始计划” (Plan)。
"""
import time
from typing import Dict, Optional, Tuple, List
from typing import Dict, Optional, Tuple
from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat
from src.chat.utils.utils import get_chat_type_and_target_info
from src.common.data_models.database_data_model import DatabaseMessages
from src.common.data_models.info_data_model import Plan, TargetPersonInfo
from src.config.config import global_config
from src.plugin_system.base.component_types import ActionActivationType, ActionInfo, ChatMode, ComponentType
from src.plugin_system.base.component_types import ActionInfo, ChatMode, ComponentType
from src.plugin_system.core.component_registry import component_registry
@@ -57,13 +57,13 @@ class PlanGenerator:
if chat_target_info_dict:
target_info = TargetPersonInfo(**chat_target_info_dict)
available_actions = self._get_available_actions()
chat_history_raw = get_raw_msg_before_timestamp_with_chat(
chat_id=self.chat_id,
timestamp=time.time(),
limit=int(global_config.chat.max_context_size),
)
chat_history = [DatabaseMessages(**msg) for msg in chat_history_raw]
available_actions = self._get_available_actions(mode, chat_history)
plan = Plan(
@@ -75,41 +75,36 @@ class PlanGenerator:
)
return plan
def _get_available_actions(self, mode: ChatMode, chat_history: List[DatabaseMessages]) -> Dict[str, "ActionInfo"]:
def _get_available_actions(self) -> Dict[str, "ActionInfo"]:
"""
根据当前的聊天模式和激活类型,筛选出可用的动作。
从 ActionManager 和组件注册表中获取当前所有可用的动作。
它会合并已注册的动作和系统级动作(如 "no_reply"
并以字典形式返回。
Returns:
Dict[str, "ActionInfo"]: 一个字典,键是动作名称,值是 ActionInfo 对象。
"""
all_actions: Dict[str, ActionInfo] = component_registry.get_components_by_type(ComponentType.ACTION) # type: ignore
available_actions = {}
latest_message_text = chat_history[-1].processed_plain_text if chat_history else ""
current_available_actions_dict = self.action_manager.get_using_actions()
all_registered_actions: Dict[str, ActionInfo] = component_registry.get_components_by_type( # type: ignore
ComponentType.ACTION
)
current_available_actions = {}
for action_name in current_available_actions_dict:
if action_name in all_registered_actions:
current_available_actions[action_name] = all_registered_actions[action_name]
for name, info in all_actions.items():
# 根据当前模式选择对应的激活类型
activation_type = info.focus_activation_type if mode == ChatMode.FOCUS else info.normal_activation_type
if activation_type in [ActionActivationType.ALWAYS, ActionActivationType.LLM_JUDGE]:
available_actions[name] = info
elif activation_type == ActionActivationType.KEYWORD:
if any(kw.lower() in latest_message_text.lower() for kw in info.activation_keywords):
available_actions[name] = info
elif activation_type == ActionActivationType.KEYWORD_OR_LLM_JUDGE:
if any(kw.lower() in latest_message_text.lower() for kw in info.activation_keywords):
available_actions[name] = info
else:
# 即使关键词不匹配也将其添加到可用动作中交由LLM判断
available_actions[name] = info
elif activation_type == ActionActivationType.NEVER:
pass # 永不激活
else:
logger.warning(f"未知的激活类型: {activation_type},跳过处理")
# 添加系统级动作
no_reply_info = ActionInfo(
name="no_reply",
component_type=ComponentType.ACTION,
description="系统级动作:选择不回复消息的决策",
action_parameters={},
activation_keywords=[],
plugin_name="SYSTEM",
enabled=True,
parallel_action=False,
)
available_actions["no_reply"] = no_reply_info
return available_actions
current_available_actions["no_reply"] = no_reply_info
return current_available_actions