feat:统一normal和focus的动作调整,emoji统一可选随机激活或llm激活
This commit is contained in:
@@ -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.info_processors.base_processor import BaseProcessor
|
||||
from src.chat.focus_chat.planners.planner_simple import ActionPlanner
|
||||
from src.chat.focus_chat.planners.modify_actions import ActionModifier
|
||||
from src.chat.focus_chat.planners.action_manager import ActionManager
|
||||
from src.chat.planner_actions.planner_focus import ActionPlanner
|
||||
from src.chat.planner_actions.action_modifier import ActionModifier
|
||||
from src.chat.planner_actions.action_manager import ActionManager
|
||||
from src.config.config import global_config
|
||||
from src.chat.focus_chat.hfc_performance_logger import HFCPerformanceLogger
|
||||
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
|
||||
|
||||
|
||||
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:
|
||||
"""
|
||||
@@ -80,7 +62,6 @@ class HeartFChatting:
|
||||
self,
|
||||
chat_id: str,
|
||||
on_stop_focus_chat: Optional[Callable[[], Awaitable[None]]] = None,
|
||||
performance_version: str = None,
|
||||
):
|
||||
"""
|
||||
HeartFChatting 初始化函数
|
||||
@@ -122,7 +103,7 @@ class HeartFChatting:
|
||||
self.action_planner = ActionPlanner(
|
||||
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.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)
|
||||
|
||||
logger.info(
|
||||
@@ -287,7 +268,6 @@ class HeartFChatting:
|
||||
|
||||
# 初始化周期状态
|
||||
cycle_timers = {}
|
||||
loop_cycle_start_time = time.monotonic()
|
||||
|
||||
# 执行规划和处理阶段
|
||||
try:
|
||||
@@ -370,11 +350,6 @@ class HeartFChatting:
|
||||
|
||||
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._cycle_history.append(self._current_cycle_detail)
|
||||
@@ -407,7 +382,7 @@ class HeartFChatting:
|
||||
self.performance_logger.record_cycle(cycle_performance_data)
|
||||
except Exception as perf_e:
|
||||
logger.warning(f"{self.log_prefix} 记录性能数据失败: {perf_e}")
|
||||
|
||||
|
||||
await asyncio.sleep(global_config.focus_chat.think_interval)
|
||||
|
||||
except asyncio.CancelledError:
|
||||
@@ -543,6 +518,7 @@ class HeartFChatting:
|
||||
# 调用完整的动作修改流程
|
||||
await self.action_modifier.modify_actions(
|
||||
observations=self.observations,
|
||||
mode="focus",
|
||||
)
|
||||
|
||||
await self.action_observation.observe()
|
||||
@@ -567,7 +543,7 @@ class HeartFChatting:
|
||||
logger.debug(f"{self.log_prefix} 并行阶段完成,准备进入规划器,plan_info数量: {len(all_plan_info)}")
|
||||
|
||||
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 = {
|
||||
"action_result": plan_result.get("action_result", {}),
|
||||
|
||||
@@ -1,327 +0,0 @@
|
||||
from typing import Dict, List, Optional, Type, Any
|
||||
from src.plugin_system.base.base_action import BaseAction
|
||||
from src.chat.message_receive.chat_stream import ChatStream
|
||||
from src.common.logger import get_logger
|
||||
from src.plugin_system.core.component_registry import component_registry
|
||||
from src.plugin_system.base.component_types import ComponentType
|
||||
|
||||
logger = get_logger("action_manager")
|
||||
|
||||
# 定义动作信息类型
|
||||
ActionInfo = Dict[str, Any]
|
||||
|
||||
|
||||
class ActionManager:
|
||||
"""
|
||||
动作管理器,用于管理各种类型的动作
|
||||
|
||||
现在统一使用新插件系统,简化了原有的新旧兼容逻辑。
|
||||
"""
|
||||
|
||||
# 类常量
|
||||
DEFAULT_RANDOM_PROBABILITY = 0.3
|
||||
DEFAULT_MODE = "all"
|
||||
DEFAULT_ACTIVATION_TYPE = "always"
|
||||
|
||||
def __init__(self):
|
||||
"""初始化动作管理器"""
|
||||
# 所有注册的动作集合
|
||||
self._registered_actions: Dict[str, ActionInfo] = {}
|
||||
# 当前正在使用的动作集合,默认加载默认动作
|
||||
self._using_actions: Dict[str, ActionInfo] = {}
|
||||
|
||||
# 默认动作集,仅作为快照,用于恢复默认
|
||||
self._default_actions: Dict[str, ActionInfo] = {}
|
||||
|
||||
# 加载插件动作
|
||||
self._load_plugin_actions()
|
||||
|
||||
# 初始化时将默认动作加载到使用中的动作
|
||||
self._using_actions = self._default_actions.copy()
|
||||
|
||||
def _load_plugin_actions(self) -> None:
|
||||
"""
|
||||
加载所有插件系统中的动作
|
||||
"""
|
||||
try:
|
||||
# 从新插件系统获取Action组件
|
||||
self._load_plugin_system_actions()
|
||||
logger.debug("从插件系统加载Action组件成功")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"加载插件动作失败: {e}")
|
||||
|
||||
def _load_plugin_system_actions(self) -> None:
|
||||
"""从插件系统的component_registry加载Action组件"""
|
||||
try:
|
||||
from src.plugin_system.core.component_registry import component_registry
|
||||
from src.plugin_system.base.component_types import ComponentType
|
||||
|
||||
# 获取所有Action组件
|
||||
action_components = component_registry.get_components_by_type(ComponentType.ACTION)
|
||||
|
||||
for action_name, action_info in action_components.items():
|
||||
if action_name in self._registered_actions:
|
||||
logger.debug(f"Action组件 {action_name} 已存在,跳过")
|
||||
continue
|
||||
|
||||
# 将插件系统的ActionInfo转换为ActionManager格式
|
||||
converted_action_info = {
|
||||
"description": action_info.description,
|
||||
"parameters": getattr(action_info, "action_parameters", {}),
|
||||
"require": getattr(action_info, "action_require", []),
|
||||
"associated_types": getattr(action_info, "associated_types", []),
|
||||
"enable_plugin": action_info.enabled,
|
||||
# 激活类型相关
|
||||
"focus_activation_type": action_info.focus_activation_type.value,
|
||||
"normal_activation_type": action_info.normal_activation_type.value,
|
||||
"random_activation_probability": action_info.random_activation_probability,
|
||||
"llm_judge_prompt": action_info.llm_judge_prompt,
|
||||
"activation_keywords": action_info.activation_keywords,
|
||||
"keyword_case_sensitive": action_info.keyword_case_sensitive,
|
||||
# 模式和并行设置
|
||||
"mode_enable": action_info.mode_enable.value,
|
||||
"parallel_action": action_info.parallel_action,
|
||||
# 插件信息
|
||||
"_plugin_name": getattr(action_info, "plugin_name", ""),
|
||||
}
|
||||
|
||||
self._registered_actions[action_name] = converted_action_info
|
||||
|
||||
# 如果启用,也添加到默认动作集
|
||||
if action_info.enabled:
|
||||
self._default_actions[action_name] = converted_action_info
|
||||
|
||||
logger.debug(
|
||||
f"从插件系统加载Action组件: {action_name} (插件: {getattr(action_info, 'plugin_name', 'unknown')})"
|
||||
)
|
||||
|
||||
logger.info(f"从插件系统加载了 {len(action_components)} 个Action组件")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"从插件系统加载Action组件失败: {e}")
|
||||
import traceback
|
||||
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
def create_action(
|
||||
self,
|
||||
action_name: str,
|
||||
action_data: dict,
|
||||
reasoning: str,
|
||||
cycle_timers: dict,
|
||||
thinking_id: str,
|
||||
chat_stream: ChatStream,
|
||||
log_prefix: str,
|
||||
shutting_down: bool = False,
|
||||
) -> Optional[BaseAction]:
|
||||
"""
|
||||
创建动作处理器实例
|
||||
|
||||
Args:
|
||||
action_name: 动作名称
|
||||
action_data: 动作数据
|
||||
reasoning: 执行理由
|
||||
cycle_timers: 计时器字典
|
||||
thinking_id: 思考ID
|
||||
chat_stream: 聊天流
|
||||
log_prefix: 日志前缀
|
||||
shutting_down: 是否正在关闭
|
||||
|
||||
Returns:
|
||||
Optional[BaseAction]: 创建的动作处理器实例,如果动作名称未注册则返回None
|
||||
"""
|
||||
try:
|
||||
# 获取组件类 - 明确指定查询Action类型
|
||||
component_class = component_registry.get_component_class(action_name, ComponentType.ACTION)
|
||||
if not component_class:
|
||||
logger.warning(f"{log_prefix} 未找到Action组件: {action_name}")
|
||||
return None
|
||||
|
||||
# 获取组件信息
|
||||
component_info = component_registry.get_component_info(action_name, ComponentType.ACTION)
|
||||
if not component_info:
|
||||
logger.warning(f"{log_prefix} 未找到Action组件信息: {action_name}")
|
||||
return None
|
||||
|
||||
# 获取插件配置
|
||||
plugin_config = component_registry.get_plugin_config(component_info.plugin_name)
|
||||
|
||||
# 创建动作实例
|
||||
instance = component_class(
|
||||
action_data=action_data,
|
||||
reasoning=reasoning,
|
||||
cycle_timers=cycle_timers,
|
||||
thinking_id=thinking_id,
|
||||
chat_stream=chat_stream,
|
||||
log_prefix=log_prefix,
|
||||
shutting_down=shutting_down,
|
||||
plugin_config=plugin_config,
|
||||
)
|
||||
|
||||
logger.debug(f"创建Action实例成功: {action_name}")
|
||||
return instance
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"创建Action实例失败 {action_name}: {e}")
|
||||
import traceback
|
||||
|
||||
logger.error(traceback.format_exc())
|
||||
return None
|
||||
|
||||
def get_registered_actions(self) -> Dict[str, ActionInfo]:
|
||||
"""获取所有已注册的动作集"""
|
||||
return self._registered_actions.copy()
|
||||
|
||||
def get_default_actions(self) -> Dict[str, ActionInfo]:
|
||||
"""获取默认动作集"""
|
||||
return self._default_actions.copy()
|
||||
|
||||
def get_using_actions(self) -> Dict[str, ActionInfo]:
|
||||
"""获取当前正在使用的动作集合"""
|
||||
return self._using_actions.copy()
|
||||
|
||||
def get_using_actions_for_mode(self, mode: str) -> Dict[str, ActionInfo]:
|
||||
"""
|
||||
根据聊天模式获取可用的动作集合
|
||||
|
||||
Args:
|
||||
mode: 聊天模式 ("focus", "normal", "all")
|
||||
|
||||
Returns:
|
||||
Dict[str, ActionInfo]: 在指定模式下可用的动作集合
|
||||
"""
|
||||
filtered_actions = {}
|
||||
|
||||
for action_name, action_info in self._using_actions.items():
|
||||
action_mode = action_info.get("mode_enable", "all")
|
||||
|
||||
# 检查动作是否在当前模式下启用
|
||||
if action_mode == "all" or action_mode == mode:
|
||||
filtered_actions[action_name] = action_info
|
||||
logger.debug(f"动作 {action_name} 在模式 {mode} 下可用 (mode_enable: {action_mode})")
|
||||
|
||||
logger.debug(f"模式 {mode} 下可用动作: {list(filtered_actions.keys())}")
|
||||
return filtered_actions
|
||||
|
||||
def add_action_to_using(self, action_name: str) -> bool:
|
||||
"""
|
||||
添加已注册的动作到当前使用的动作集
|
||||
|
||||
Args:
|
||||
action_name: 动作名称
|
||||
|
||||
Returns:
|
||||
bool: 添加是否成功
|
||||
"""
|
||||
if action_name not in self._registered_actions:
|
||||
logger.warning(f"添加失败: 动作 {action_name} 未注册")
|
||||
return False
|
||||
|
||||
if action_name in self._using_actions:
|
||||
logger.info(f"动作 {action_name} 已经在使用中")
|
||||
return True
|
||||
|
||||
self._using_actions[action_name] = self._registered_actions[action_name]
|
||||
logger.info(f"添加动作 {action_name} 到使用集")
|
||||
return True
|
||||
|
||||
def remove_action_from_using(self, action_name: str) -> bool:
|
||||
"""
|
||||
从当前使用的动作集中移除指定动作
|
||||
|
||||
Args:
|
||||
action_name: 动作名称
|
||||
|
||||
Returns:
|
||||
bool: 移除是否成功
|
||||
"""
|
||||
if action_name not in self._using_actions:
|
||||
logger.warning(f"移除失败: 动作 {action_name} 不在当前使用的动作集中")
|
||||
return False
|
||||
|
||||
del self._using_actions[action_name]
|
||||
logger.debug(f"已从使用集中移除动作 {action_name}")
|
||||
return True
|
||||
|
||||
def add_action(self, action_name: str, description: str, parameters: Dict = None, require: List = None) -> bool:
|
||||
"""
|
||||
添加新的动作到注册集
|
||||
|
||||
Args:
|
||||
action_name: 动作名称
|
||||
description: 动作描述
|
||||
parameters: 动作参数定义,默认为空字典
|
||||
require: 动作依赖项,默认为空列表
|
||||
|
||||
Returns:
|
||||
bool: 添加是否成功
|
||||
"""
|
||||
if action_name in self._registered_actions:
|
||||
return False
|
||||
|
||||
if parameters is None:
|
||||
parameters = {}
|
||||
if require is None:
|
||||
require = []
|
||||
|
||||
action_info = {"description": description, "parameters": parameters, "require": require}
|
||||
|
||||
self._registered_actions[action_name] = action_info
|
||||
return True
|
||||
|
||||
def remove_action(self, action_name: str) -> bool:
|
||||
"""从注册集移除指定动作"""
|
||||
if action_name not in self._registered_actions:
|
||||
return False
|
||||
del self._registered_actions[action_name]
|
||||
# 如果在使用集中也存在,一并移除
|
||||
if action_name in self._using_actions:
|
||||
del self._using_actions[action_name]
|
||||
return True
|
||||
|
||||
def temporarily_remove_actions(self, actions_to_remove: List[str]) -> None:
|
||||
"""临时移除使用集中的指定动作"""
|
||||
for name in actions_to_remove:
|
||||
self._using_actions.pop(name, None)
|
||||
|
||||
def restore_actions(self) -> None:
|
||||
"""恢复到默认动作集"""
|
||||
logger.debug(
|
||||
f"恢复动作集: 从 {list(self._using_actions.keys())} 恢复到默认动作集 {list(self._default_actions.keys())}"
|
||||
)
|
||||
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:
|
||||
"""
|
||||
根据需要添加系统动作到使用集
|
||||
|
||||
Args:
|
||||
action_name: 动作名称
|
||||
|
||||
Returns:
|
||||
bool: 是否成功添加
|
||||
"""
|
||||
if action_name in self._registered_actions and action_name not in self._using_actions:
|
||||
self._using_actions[action_name] = self._registered_actions[action_name]
|
||||
logger.info(f"临时添加系统动作到使用集: {action_name}")
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_action(self, action_name: str) -> Optional[Type[BaseAction]]:
|
||||
"""
|
||||
获取指定动作的处理器类
|
||||
|
||||
Args:
|
||||
action_name: 动作名称
|
||||
|
||||
Returns:
|
||||
Optional[Type[BaseAction]]: 动作处理器类,如果不存在则返回None
|
||||
"""
|
||||
from src.plugin_system.core.component_registry import component_registry
|
||||
|
||||
return component_registry.get_component_class(action_name)
|
||||
@@ -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
|
||||
@@ -1,619 +0,0 @@
|
||||
from typing import List, Optional, Any, Dict
|
||||
from src.chat.heart_flow.observation.observation import Observation
|
||||
from src.common.logger import get_logger
|
||||
from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation
|
||||
from src.chat.heart_flow.observation.chatting_observation import ChattingObservation
|
||||
from src.chat.message_receive.chat_stream import get_chat_manager
|
||||
from src.config.config import global_config
|
||||
from src.llm_models.utils_model import LLMRequest
|
||||
import random
|
||||
import asyncio
|
||||
import hashlib
|
||||
import time
|
||||
from src.chat.focus_chat.planners.action_manager import ActionManager
|
||||
|
||||
logger = get_logger("action_manager")
|
||||
|
||||
|
||||
class ActionModifier:
|
||||
"""动作处理器
|
||||
|
||||
用于处理Observation对象和根据激活类型处理actions。
|
||||
集成了原有的modify_actions功能和新的激活类型处理功能。
|
||||
支持并行判定和智能缓存优化。
|
||||
"""
|
||||
|
||||
log_prefix = "动作处理"
|
||||
|
||||
def __init__(self, action_manager: ActionManager):
|
||||
"""初始化动作处理器"""
|
||||
self.action_manager = action_manager
|
||||
self.all_actions = self.action_manager.get_using_actions_for_mode("focus")
|
||||
|
||||
# 用于LLM判定的小模型
|
||||
self.llm_judge = LLMRequest(
|
||||
model=global_config.model.utils_small,
|
||||
request_type="action.judge",
|
||||
)
|
||||
|
||||
# 缓存相关属性
|
||||
self._llm_judge_cache = {} # 缓存LLM判定结果
|
||||
self._cache_expiry_time = 30 # 缓存过期时间(秒)
|
||||
self._last_context_hash = None # 上次上下文的哈希值
|
||||
|
||||
async def modify_actions(
|
||||
self,
|
||||
observations: Optional[List[Observation]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
"""
|
||||
完整的动作修改流程,整合传统观察处理和新的激活类型判定
|
||||
|
||||
这个方法处理完整的动作管理流程:
|
||||
1. 基于观察的传统动作修改(循环历史分析、类型匹配等)
|
||||
2. 基于激活类型的智能动作判定,最终确定可用动作集
|
||||
|
||||
处理后,ActionManager 将包含最终的可用动作集,供规划器直接使用
|
||||
"""
|
||||
logger.debug(f"{self.log_prefix}开始完整动作修改流程")
|
||||
|
||||
# === 第一阶段:传统观察处理 ===
|
||||
chat_content = None
|
||||
|
||||
if observations:
|
||||
hfc_obs = None
|
||||
chat_obs = None
|
||||
|
||||
# 收集所有观察对象
|
||||
for obs in observations:
|
||||
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模式的动作
|
||||
all_actions = self.all_actions
|
||||
action_changes = await self.analyze_loop_actions(obs)
|
||||
if action_changes["add"] or action_changes["remove"]:
|
||||
# 合并动作变更
|
||||
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
|
||||
type_mismatched_actions = []
|
||||
|
||||
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:
|
||||
# 合并到移除列表中
|
||||
merged_action_changes["remove"].extend(type_mismatched_actions)
|
||||
reasons.append("基于关联类型检查")
|
||||
|
||||
# 应用传统的动作变更到ActionManager
|
||||
for action_name in merged_action_changes["add"]:
|
||||
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)
|
||||
logger.debug(f"{self.log_prefix}应用移除动作: {action_name},原因集合: {reasons}")
|
||||
|
||||
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:
|
||||
logger.debug(f"{self.log_prefix}开始激活类型判定阶段")
|
||||
|
||||
# 获取当前使用的动作集(经过第一阶段处理,且适用于FOCUS模式)
|
||||
current_using_actions = self.action_manager.get_using_actions()
|
||||
all_registered_actions = self.action_manager.get_registered_actions()
|
||||
|
||||
# 构建完整的动作信息
|
||||
current_actions_with_info = {}
|
||||
for action_name in current_using_actions.keys():
|
||||
if action_name in all_registered_actions:
|
||||
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,
|
||||
)
|
||||
|
||||
# 更新ActionManager,移除未激活的动作
|
||||
actions_to_remove = []
|
||||
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)
|
||||
reason = removal_reasons.get(action_name, "未知原因")
|
||||
logger.info(f"{self.log_prefix}移除动作: {action_name},原因: {reason}")
|
||||
|
||||
# 注释:已完全移除exit_focus_chat动作
|
||||
|
||||
logger.info(f"{self.log_prefix}激活类型判定完成,最终可用动作: {list(final_activated_actions.keys())}")
|
||||
|
||||
logger.info(
|
||||
f"{self.log_prefix}完整动作修改流程结束,最终动作集: {list(self.action_manager.get_using_actions().keys())}"
|
||||
)
|
||||
|
||||
async def _apply_activation_type_filtering(
|
||||
self,
|
||||
actions_with_info: Dict[str, Any],
|
||||
chat_content: str = "",
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
应用激活类型过滤逻辑,支持四种激活类型的并行处理
|
||||
|
||||
Args:
|
||||
actions_with_info: 带完整信息的动作字典
|
||||
chat_content: 聊天内容
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: 过滤后激活的actions字典
|
||||
"""
|
||||
activated_actions = {}
|
||||
|
||||
# 分类处理不同激活类型的actions
|
||||
always_actions = {}
|
||||
random_actions = {}
|
||||
llm_judge_actions = {}
|
||||
keyword_actions = {}
|
||||
|
||||
for action_name, action_info in actions_with_info.items():
|
||||
activation_type = action_info.get("focus_activation_type", "always")
|
||||
|
||||
# print(f"action_name: {action_name}, activation_type: {activation_type}")
|
||||
|
||||
# 现在统一是字符串格式的激活类型值
|
||||
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,
|
||||
)
|
||||
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}激活类型过滤完成: {list(activated_actions.keys())}")
|
||||
return activated_actions
|
||||
|
||||
async def process_actions_for_planner(
|
||||
self, observed_messages_str: str = "", chat_context: Optional[str] = None, extra_context: Optional[str] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
[已废弃] 此方法现在已被整合到 modify_actions() 中
|
||||
|
||||
为了保持向后兼容性而保留,但建议直接使用 ActionManager.get_using_actions()
|
||||
规划器应该直接从 ActionManager 获取最终的可用动作集,而不是调用此方法
|
||||
|
||||
新的架构:
|
||||
1. 主循环调用 modify_actions() 处理完整的动作管理流程
|
||||
2. 规划器直接使用 ActionManager.get_using_actions() 获取最终动作集
|
||||
"""
|
||||
logger.warning(
|
||||
f"{self.log_prefix}process_actions_for_planner() 已废弃,建议规划器直接使用 ActionManager.get_using_actions()"
|
||||
)
|
||||
|
||||
# 为了向后兼容,仍然返回当前使用的动作集
|
||||
current_using_actions = self.action_manager.get_using_actions()
|
||||
all_registered_actions = self.action_manager.get_registered_actions()
|
||||
|
||||
# 构建完整的动作信息
|
||||
result = {}
|
||||
for action_name in current_using_actions.keys():
|
||||
if action_name in all_registered_actions:
|
||||
result[action_name] = all_registered_actions[action_name]
|
||||
|
||||
return result
|
||||
|
||||
def _generate_context_hash(self, chat_content: str) -> str:
|
||||
"""生成上下文的哈希值用于缓存"""
|
||||
context_content = f"{chat_content}"
|
||||
return hashlib.md5(context_content.encode("utf-8")).hexdigest()
|
||||
|
||||
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 _cleanup_expired_cache(self, current_time: float):
|
||||
"""清理过期的缓存条目"""
|
||||
expired_keys = []
|
||||
for cache_key, cache_data in self._llm_judge_cache.items():
|
||||
if current_time - cache_data["timestamp"] > self._cache_expiry_time:
|
||||
expired_keys.append(cache_key)
|
||||
|
||||
for key in expired_keys:
|
||||
del self._llm_judge_cache[key]
|
||||
|
||||
if expired_keys:
|
||||
logger.debug(f"{self.log_prefix}清理了 {len(expired_keys)} 个过期缓存条目")
|
||||
|
||||
async def _llm_judge_action(
|
||||
self,
|
||||
action_name: str,
|
||||
action_info: Dict[str, Any],
|
||||
chat_content: str = "",
|
||||
) -> bool:
|
||||
"""
|
||||
使用LLM判定是否应该激活某个action
|
||||
|
||||
Args:
|
||||
action_name: 动作名称
|
||||
action_info: 动作信息
|
||||
observed_messages_str: 观察到的聊天消息
|
||||
chat_context: 聊天上下文
|
||||
extra_context: 额外上下文
|
||||
|
||||
Returns:
|
||||
bool: 是否应该激活此action
|
||||
"""
|
||||
|
||||
try:
|
||||
# 构建判定提示词
|
||||
action_description = action_info.get("description", "")
|
||||
action_require = action_info.get("require", [])
|
||||
custom_prompt = action_info.get("llm_judge_prompt", "")
|
||||
|
||||
# 构建基础判定提示词
|
||||
base_prompt = f"""
|
||||
你需要判断在当前聊天情况下,是否应该激活名为"{action_name}"的动作。
|
||||
|
||||
动作描述:{action_description}
|
||||
|
||||
动作使用场景:
|
||||
"""
|
||||
for req in action_require:
|
||||
base_prompt += f"- {req}\n"
|
||||
|
||||
if custom_prompt:
|
||||
base_prompt += f"\n额外判定条件:\n{custom_prompt}\n"
|
||||
|
||||
if chat_content:
|
||||
base_prompt += f"\n当前聊天记录:\n{chat_content}\n"
|
||||
|
||||
base_prompt += """
|
||||
请根据以上信息判断是否应该激活这个动作。
|
||||
只需要回答"是"或"否",不要有其他内容。
|
||||
"""
|
||||
|
||||
# 调用LLM进行判定
|
||||
response, _ = await self.llm_judge.generate_response_async(prompt=base_prompt)
|
||||
|
||||
# 解析响应
|
||||
response = response.strip().lower()
|
||||
|
||||
# print(base_prompt)
|
||||
# print(f"LLM判定动作 {action_name}:响应='{response}'")
|
||||
|
||||
should_activate = "是" in response or "yes" in response or "true" in response
|
||||
|
||||
logger.debug(
|
||||
f"{self.log_prefix}LLM判定动作 {action_name}:响应='{response}',结果={'激活' if should_activate else '不激活'}"
|
||||
)
|
||||
return should_activate
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix}LLM判定动作 {action_name} 时出错: {e}")
|
||||
# 出错时默认不激活
|
||||
return False
|
||||
|
||||
def _check_keyword_activation(
|
||||
self,
|
||||
action_name: str,
|
||||
action_info: Dict[str, Any],
|
||||
chat_content: str = "",
|
||||
) -> bool:
|
||||
"""
|
||||
检查是否匹配关键词触发条件
|
||||
|
||||
Args:
|
||||
action_name: 动作名称
|
||||
action_info: 动作信息
|
||||
observed_messages_str: 观察到的聊天消息
|
||||
chat_context: 聊天上下文
|
||||
extra_context: 额外上下文
|
||||
|
||||
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 = ""
|
||||
if chat_content:
|
||||
search_text += chat_content
|
||||
# if chat_context:
|
||||
# search_text += f" {chat_context}"
|
||||
# if extra_context:
|
||||
# search_text += f" {extra_context}"
|
||||
|
||||
# 如果不区分大小写,转换为小写
|
||||
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)
|
||||
|
||||
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 analyze_loop_actions(self, obs: HFCloopObservation) -> Dict[str, List[str]]:
|
||||
"""分析最近的循环内容并决定动作的增减
|
||||
|
||||
Returns:
|
||||
Dict[str, List[str]]: 包含要增加和删除的动作
|
||||
{
|
||||
"add": ["action1", "action2"],
|
||||
"remove": ["action3"]
|
||||
}
|
||||
"""
|
||||
result = {"add": [], "remove": []}
|
||||
|
||||
# 获取最近10次循环
|
||||
recent_cycles = obs.history_loop[-10:] if len(obs.history_loop) > 10 else obs.history_loop
|
||||
if not recent_cycles:
|
||||
return result
|
||||
|
||||
reply_sequence = [] # 记录最近的动作序列
|
||||
|
||||
for cycle in recent_cycles:
|
||||
action_result = cycle.loop_plan_info.get("action_result", {})
|
||||
action_type = action_result.get("action_type", "unknown")
|
||||
reply_sequence.append(action_type == "reply")
|
||||
|
||||
# 计算连续回复的相关阈值
|
||||
|
||||
max_reply_num = int(global_config.focus_chat.consecutive_replies * 3.2)
|
||||
sec_thres_reply_num = int(global_config.focus_chat.consecutive_replies * 2)
|
||||
one_thres_reply_num = int(global_config.focus_chat.consecutive_replies * 1.5)
|
||||
|
||||
# 获取最近max_reply_num次的reply状态
|
||||
if len(reply_sequence) >= max_reply_num:
|
||||
last_max_reply_num = reply_sequence[-max_reply_num:]
|
||||
else:
|
||||
last_max_reply_num = reply_sequence[:]
|
||||
|
||||
# 详细打印阈值和序列信息,便于调试
|
||||
logger.info(
|
||||
f"连续回复阈值: max={max_reply_num}, sec={sec_thres_reply_num}, one={one_thres_reply_num},"
|
||||
f"最近reply序列: {last_max_reply_num}"
|
||||
)
|
||||
# print(f"consecutive_replies: {consecutive_replies}")
|
||||
|
||||
# 根据最近的reply情况决定是否移除reply动作
|
||||
if len(last_max_reply_num) >= max_reply_num and all(last_max_reply_num):
|
||||
# 如果最近max_reply_num次都是reply,直接移除
|
||||
result["remove"].append("reply")
|
||||
# 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:]):
|
||||
# 如果最近sec_thres_reply_num次都是reply,40%概率移除
|
||||
removal_probability = 0.4 / global_config.focus_chat.consecutive_replies
|
||||
if random.random() < removal_probability:
|
||||
result["remove"].append("reply")
|
||||
logger.info(
|
||||
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:]):
|
||||
# 如果最近one_thres_reply_num次都是reply,20%概率移除
|
||||
removal_probability = 0.2 / global_config.focus_chat.consecutive_replies
|
||||
if random.random() < removal_probability:
|
||||
result["remove"].append("reply")
|
||||
logger.info(
|
||||
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:
|
||||
logger.debug(f"{self.log_prefix}连续回复检测:无需移除reply动作,最近回复模式正常")
|
||||
|
||||
return result
|
||||
@@ -1,369 +0,0 @@
|
||||
import json # <--- 确保导入 json
|
||||
import traceback
|
||||
from typing import List, Dict, Any, Optional
|
||||
from rich.traceback import install
|
||||
from src.llm_models.utils_model import LLMRequest
|
||||
from src.config.config import global_config
|
||||
from src.chat.focus_chat.info.info_base import InfoBase
|
||||
from src.chat.focus_chat.info.obs_info import ObsInfo
|
||||
from src.chat.focus_chat.info.action_info import ActionInfo
|
||||
from src.common.logger import get_logger
|
||||
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
|
||||
from src.chat.focus_chat.planners.action_manager import ActionManager
|
||||
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 datetime import datetime
|
||||
|
||||
logger = get_logger("planner")
|
||||
|
||||
install(extra_lines=3)
|
||||
|
||||
|
||||
def init_prompt():
|
||||
Prompt(
|
||||
"""
|
||||
{time_block}
|
||||
{indentify_block}
|
||||
你现在需要根据聊天内容,选择的合适的action来参与聊天。
|
||||
{chat_context_description},以下是具体的聊天内容:
|
||||
{chat_content_block}
|
||||
{moderation_prompt}
|
||||
|
||||
现在请你根据聊天内容选择合适的action:
|
||||
|
||||
{action_options_text}
|
||||
|
||||
请根据动作示例,以严格的 JSON 格式输出,且仅包含 JSON 内容:
|
||||
""",
|
||||
"simple_planner_prompt",
|
||||
)
|
||||
|
||||
Prompt(
|
||||
"""
|
||||
{time_block}
|
||||
{indentify_block}
|
||||
你现在需要根据聊天内容,选择的合适的action来参与聊天。
|
||||
{chat_context_description},以下是具体的聊天内容:
|
||||
{chat_content_block}
|
||||
{moderation_prompt}
|
||||
现在请你选择合适的action:
|
||||
|
||||
{action_options_text}
|
||||
|
||||
请根据动作示例,以严格的 JSON 格式输出,且仅包含 JSON 内容:
|
||||
""",
|
||||
"simple_planner_prompt_private",
|
||||
)
|
||||
|
||||
Prompt(
|
||||
"""
|
||||
动作:{action_name}
|
||||
动作描述:{action_description}
|
||||
{action_require}
|
||||
{{
|
||||
"action": "{action_name}",{action_parameters}
|
||||
}}
|
||||
""",
|
||||
"action_prompt",
|
||||
)
|
||||
|
||||
|
||||
class ActionPlanner(BasePlanner):
|
||||
def __init__(self, log_prefix: str, action_manager: ActionManager):
|
||||
super().__init__(log_prefix, action_manager)
|
||||
# LLM规划器配置
|
||||
self.planner_llm = LLMRequest(
|
||||
model=global_config.model.planner,
|
||||
request_type="focus.planner", # 用于动作规划
|
||||
)
|
||||
|
||||
self.utils_llm = LLMRequest(
|
||||
model=global_config.model.utils_small,
|
||||
request_type="focus.planner", # 用于动作规划
|
||||
)
|
||||
|
||||
async def plan(
|
||||
self, all_plan_info: List[InfoBase], running_memorys: List[Dict[str, Any]], loop_start_time: float
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
规划器 (Planner): 使用LLM根据上下文决定做出什么动作。
|
||||
|
||||
参数:
|
||||
all_plan_info: 所有计划信息
|
||||
running_memorys: 回忆信息
|
||||
loop_start_time: 循环开始时间
|
||||
"""
|
||||
|
||||
action = "no_reply" # 默认动作
|
||||
reasoning = "规划器初始化默认"
|
||||
action_data = {}
|
||||
|
||||
try:
|
||||
# 获取观察信息
|
||||
extra_info: list[str] = []
|
||||
|
||||
extra_info = []
|
||||
observed_messages = []
|
||||
observed_messages_str = ""
|
||||
chat_type = "group"
|
||||
is_group_chat = True
|
||||
chat_id = None # 添加chat_id变量
|
||||
|
||||
for info in all_plan_info:
|
||||
if isinstance(info, ObsInfo):
|
||||
observed_messages = info.get_talking_message()
|
||||
observed_messages_str = info.get_talking_message_str_truncate_short()
|
||||
chat_type = info.get_chat_type()
|
||||
is_group_chat = chat_type == "group"
|
||||
# 从ObsInfo中获取chat_id
|
||||
chat_id = info.get_chat_id()
|
||||
else:
|
||||
extra_info.append(info.get_processed_info())
|
||||
|
||||
# 获取聊天类型和目标信息
|
||||
chat_target_info = None
|
||||
if chat_id:
|
||||
try:
|
||||
# 重新获取更准确的聊天信息
|
||||
is_group_chat_updated, chat_target_info = get_chat_type_and_target_info(chat_id)
|
||||
# 如果获取成功,更新is_group_chat
|
||||
if is_group_chat_updated is not None:
|
||||
is_group_chat = is_group_chat_updated
|
||||
logger.debug(
|
||||
f"{self.log_prefix}获取到聊天信息 - 群聊: {is_group_chat}, 目标信息: {chat_target_info}"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"{self.log_prefix}获取聊天目标信息失败: {e}")
|
||||
chat_target_info = None
|
||||
|
||||
# 获取经过modify_actions处理后的最终可用动作集
|
||||
# 注意:动作的激活判定现在在主循环的modify_actions中完成
|
||||
# 使用Focus模式过滤动作
|
||||
current_available_actions_dict = self.action_manager.get_using_actions_for_mode("focus")
|
||||
|
||||
# 获取完整的动作信息
|
||||
all_registered_actions = self.action_manager.get_registered_actions()
|
||||
current_available_actions = {}
|
||||
for action_name in current_available_actions_dict.keys():
|
||||
if action_name in all_registered_actions:
|
||||
current_available_actions[action_name] = all_registered_actions[action_name]
|
||||
else:
|
||||
logger.warning(f"{self.log_prefix}使用中的动作 {action_name} 未在已注册动作中找到")
|
||||
|
||||
# 如果没有可用动作或只有no_reply动作,直接返回no_reply
|
||||
if not current_available_actions or (
|
||||
len(current_available_actions) == 1 and "no_reply" in current_available_actions
|
||||
):
|
||||
action = "no_reply"
|
||||
reasoning = "没有可用的动作" if not current_available_actions else "只有no_reply动作可用,跳过规划"
|
||||
logger.info(f"{self.log_prefix}{reasoning}")
|
||||
self.action_manager.restore_actions()
|
||||
logger.debug(
|
||||
f"{self.log_prefix}[focus]沉默后恢复到默认动作集, 当前可用: {list(self.action_manager.get_using_actions().keys())}"
|
||||
)
|
||||
return {
|
||||
"action_result": {"action_type": action, "action_data": action_data, "reasoning": reasoning},
|
||||
"observed_messages": observed_messages,
|
||||
}
|
||||
|
||||
# --- 构建提示词 (调用修改后的 PromptBuilder 方法) ---
|
||||
prompt = await self.build_planner_prompt(
|
||||
is_group_chat=is_group_chat, # <-- Pass HFC state
|
||||
chat_target_info=chat_target_info, # <-- 传递获取到的聊天目标信息
|
||||
observed_messages_str=observed_messages_str, # <-- Pass local variable
|
||||
current_available_actions=current_available_actions, # <-- Pass determined actions
|
||||
)
|
||||
|
||||
# --- 调用 LLM (普通文本生成) ---
|
||||
llm_content = None
|
||||
try:
|
||||
prompt = f"{prompt}"
|
||||
llm_content, (reasoning_content, _) = await self.planner_llm.generate_response_async(prompt=prompt)
|
||||
|
||||
logger.info(f"{self.log_prefix}规划器原始提示词: {prompt}")
|
||||
logger.info(f"{self.log_prefix}规划器原始响应: {llm_content}")
|
||||
if reasoning_content:
|
||||
logger.info(f"{self.log_prefix}规划器推理: {reasoning_content}")
|
||||
|
||||
except Exception as req_e:
|
||||
logger.error(f"{self.log_prefix}LLM 请求执行失败: {req_e}")
|
||||
reasoning = f"LLM 请求失败,你的模型出现问题: {req_e}"
|
||||
action = "no_reply"
|
||||
|
||||
if llm_content:
|
||||
try:
|
||||
fixed_json_string = repair_json(llm_content)
|
||||
if isinstance(fixed_json_string, str):
|
||||
try:
|
||||
parsed_json = json.loads(fixed_json_string)
|
||||
except json.JSONDecodeError as decode_error:
|
||||
logger.error(f"JSON解析错误: {str(decode_error)}")
|
||||
parsed_json = {}
|
||||
else:
|
||||
# 如果repair_json直接返回了字典对象,直接使用
|
||||
parsed_json = fixed_json_string
|
||||
|
||||
# 处理repair_json可能返回列表的情况
|
||||
if isinstance(parsed_json, list):
|
||||
if parsed_json:
|
||||
# 取列表中最后一个元素(通常是最完整的)
|
||||
parsed_json = parsed_json[-1]
|
||||
logger.warning(f"{self.log_prefix}LLM返回了多个JSON对象,使用最后一个: {parsed_json}")
|
||||
else:
|
||||
parsed_json = {}
|
||||
|
||||
# 确保parsed_json是字典
|
||||
if not isinstance(parsed_json, dict):
|
||||
logger.error(f"{self.log_prefix}解析后的JSON不是字典类型: {type(parsed_json)}")
|
||||
parsed_json = {}
|
||||
|
||||
# 提取决策,提供默认值
|
||||
extracted_action = parsed_json.get("action", "no_reply")
|
||||
extracted_reasoning = ""
|
||||
|
||||
# 将所有其他属性添加到action_data
|
||||
action_data = {}
|
||||
for key, value in parsed_json.items():
|
||||
if key not in ["action", "reasoning"]:
|
||||
action_data[key] = value
|
||||
|
||||
action_data["loop_start_time"] = loop_start_time
|
||||
|
||||
# 对于reply动作不需要额外处理,因为相关字段已经在上面的循环中添加到action_data
|
||||
|
||||
if extracted_action not in current_available_actions:
|
||||
logger.warning(
|
||||
f"{self.log_prefix}LLM 返回了当前不可用或无效的动作: '{extracted_action}' (可用: {list(current_available_actions.keys())}),将强制使用 'no_reply'"
|
||||
)
|
||||
action = "no_reply"
|
||||
reasoning = f"LLM 返回了当前不可用的动作 '{extracted_action}' (可用: {list(current_available_actions.keys())})。原始理由: {extracted_reasoning}"
|
||||
else:
|
||||
# 动作有效且可用
|
||||
action = extracted_action
|
||||
reasoning = extracted_reasoning
|
||||
|
||||
except Exception as json_e:
|
||||
logger.warning(f"{self.log_prefix}解析LLM响应JSON失败 {json_e}. LLM原始输出: '{llm_content}'")
|
||||
traceback.print_exc()
|
||||
reasoning = f"解析LLM响应JSON失败: {json_e}. 将使用默认动作 'no_reply'."
|
||||
action = "no_reply"
|
||||
|
||||
except Exception as outer_e:
|
||||
logger.error(f"{self.log_prefix}Planner 处理过程中发生意外错误,规划失败,将执行 no_reply: {outer_e}")
|
||||
traceback.print_exc()
|
||||
action = "no_reply"
|
||||
reasoning = f"Planner 内部处理错误: {outer_e}"
|
||||
|
||||
# 恢复到默认动作集
|
||||
self.action_manager.restore_actions()
|
||||
logger.debug(
|
||||
f"{self.log_prefix}规划后恢复到默认动作集, 当前可用: {list(self.action_manager.get_using_actions().keys())}"
|
||||
)
|
||||
|
||||
action_result = {"action_type": action, "action_data": action_data, "reasoning": reasoning}
|
||||
|
||||
plan_result = {
|
||||
"action_result": action_result,
|
||||
"observed_messages": observed_messages,
|
||||
"action_prompt": prompt,
|
||||
}
|
||||
|
||||
return plan_result
|
||||
|
||||
async def build_planner_prompt(
|
||||
self,
|
||||
is_group_chat: bool, # Now passed as argument
|
||||
chat_target_info: Optional[dict], # Now passed as argument
|
||||
observed_messages_str: str,
|
||||
current_available_actions: Dict[str, ActionInfo],
|
||||
) -> str:
|
||||
"""构建 Planner LLM 的提示词 (获取模板并填充数据)"""
|
||||
try:
|
||||
chat_context_description = "你现在正在一个群聊中"
|
||||
chat_target_name = None # Only relevant for private
|
||||
if not is_group_chat and chat_target_info:
|
||||
chat_target_name = (
|
||||
chat_target_info.get("person_name") or chat_target_info.get("user_nickname") or "对方"
|
||||
)
|
||||
chat_context_description = f"你正在和 {chat_target_name} 私聊"
|
||||
|
||||
chat_content_block = ""
|
||||
if observed_messages_str:
|
||||
chat_content_block = f"\n{observed_messages_str}"
|
||||
else:
|
||||
chat_content_block = "你还未开始聊天"
|
||||
|
||||
action_options_block = ""
|
||||
# 根据聊天类型选择不同的动作prompt模板
|
||||
action_template_name = "action_prompt_private" if not is_group_chat else "action_prompt"
|
||||
|
||||
for using_actions_name, using_actions_info in current_available_actions.items():
|
||||
using_action_prompt = await global_prompt_manager.get_prompt_async(action_template_name)
|
||||
|
||||
if using_actions_info["parameters"]:
|
||||
param_text = "\n"
|
||||
for param_name, param_description in using_actions_info["parameters"].items():
|
||||
param_text += f' "{param_name}":"{param_description}"\n'
|
||||
param_text = param_text.rstrip("\n")
|
||||
else:
|
||||
param_text = ""
|
||||
|
||||
require_text = ""
|
||||
for require_item in using_actions_info["require"]:
|
||||
require_text += f"- {require_item}\n"
|
||||
require_text = require_text.rstrip("\n")
|
||||
|
||||
# 根据模板类型决定是否包含description参数
|
||||
if action_template_name == "action_prompt_private":
|
||||
# 私聊模板不包含description参数
|
||||
using_action_prompt = using_action_prompt.format(
|
||||
action_name=using_actions_name,
|
||||
action_parameters=param_text,
|
||||
action_require=require_text,
|
||||
)
|
||||
else:
|
||||
# 群聊模板包含description参数
|
||||
using_action_prompt = using_action_prompt.format(
|
||||
action_name=using_actions_name,
|
||||
action_description=using_actions_info["description"],
|
||||
action_parameters=param_text,
|
||||
action_require=require_text,
|
||||
)
|
||||
|
||||
action_options_block += using_action_prompt
|
||||
|
||||
# moderation_prompt_block = "请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。"
|
||||
moderation_prompt_block = ""
|
||||
|
||||
# 获取当前时间
|
||||
time_block = f"当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
||||
|
||||
bot_name = global_config.bot.nickname
|
||||
if global_config.bot.alias_names:
|
||||
bot_nickname = f",也有人叫你{','.join(global_config.bot.alias_names)}"
|
||||
else:
|
||||
bot_nickname = ""
|
||||
bot_core_personality = global_config.personality.personality_core
|
||||
indentify_block = f"你的名字是{bot_name}{bot_nickname},你{bot_core_personality}:"
|
||||
|
||||
# 根据聊天类型选择不同的prompt模板
|
||||
template_name = "simple_planner_prompt_private" if not is_group_chat else "simple_planner_prompt"
|
||||
planner_prompt_template = await global_prompt_manager.get_prompt_async(template_name)
|
||||
prompt = planner_prompt_template.format(
|
||||
time_block=time_block,
|
||||
chat_context_description=chat_context_description,
|
||||
chat_content_block=chat_content_block,
|
||||
action_options_text=action_options_block,
|
||||
moderation_prompt=moderation_prompt_block,
|
||||
indentify_block=indentify_block,
|
||||
)
|
||||
return prompt
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"构建 Planner 提示词时出错: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
return "构建 Planner Prompt 时出错"
|
||||
|
||||
|
||||
init_prompt()
|
||||
Reference in New Issue
Block a user