重构Kokoro Flow Chatter:移除已弃用的响应后处理器和会话管理器

- 删除了`response_post_processor.py`和`session_manager.py`,因为它们已不再需要。
- 更新了`__init__.py`文件,移除了对`ActionExecutor`的引用。
- 删除了`action_executor.py`,并将动作执行直接集成到`chatter.py`和`proactive_thinker.py`中。
- 在`KokoroFlowChatterV2`中重构了动作执行逻辑,以直接使用`ChatterActionManager`。
- 增强了主动思考逻辑,以简化操作执行,而无需依赖已移除的`ActionExecutor`。
This commit is contained in:
Windpicker-owo
2025-11-30 13:40:59 +08:00
parent 0fe15dac52
commit c68bf4ad4f
23 changed files with 319 additions and 6634 deletions

View File

@@ -4,12 +4,10 @@ import traceback
from typing import Any, TYPE_CHECKING
from src.chat.message_receive.chat_stream import get_chat_manager
from src.chat.utils.timer_calculator import Timer
from src.common.data_models.database_data_model import DatabaseMessages
from src.common.logger import get_logger
from src.config.config import global_config
from src.person_info.person_info import get_person_info_manager
from src.plugin_system.apis import database_api, generator_api, message_api, send_api
from src.plugin_system.apis import database_api
from src.plugin_system.base.base_action import BaseAction
from src.plugin_system.base.component_types import ActionInfo, ComponentType
from src.plugin_system.core.component_registry import component_registry
@@ -160,6 +158,8 @@ class ChatterActionManager:
) -> Any:
"""
执行单个动作的通用函数
所有动作(包括 reply/respond都通过 BaseAction.execute() 执行
Args:
action_name: 动作名称
@@ -169,14 +169,13 @@ class ChatterActionManager:
action_data: 动作数据
thinking_id: 思考ID
log_prefix: 日志前缀
clear_unread_messages: 是否清除未读消息
Returns:
执行结果
"""
chat_stream = None
try:
logger.debug(f"🎯 [ActionManager] execute_action接收到 target_message: {target_message}")
# 通过chat_id获取chat_stream
chat_manager = get_chat_manager()
chat_stream = await chat_manager.get_stream(chat_id)
@@ -193,149 +192,33 @@ class ChatterActionManager:
# 设置正在回复的状态
chat_stream.context.is_replying = True
# no_action 特殊处理
if action_name == "no_action":
return {"action_type": "no_action", "success": True, "reply_text": "", "command": ""}
if action_name == "no_reply":
# 直接处理no_reply逻辑不再通过动作系统
reason = reasoning or "选择不回复"
logger.info(f"{log_prefix} 选择不回复,原因: {reason}")
# 统一通过 _handle_action 执行所有动作
success, reply_text, command = await self._handle_action(
chat_stream,
action_name,
reasoning,
action_data or {},
{}, # cycle_timers
thinking_id,
target_message,
)
# 存储no_reply信息到数据库支持批量存储
if self._batch_storage_enabled:
self.add_action_to_batch(
action_name="no_reply",
action_data={"reason": reason},
thinking_id=thinking_id or "",
action_done=True,
action_build_into_prompt=False,
action_prompt_display=reason,
)
else:
asyncio.create_task(database_api.store_action_info(
chat_stream=chat_stream,
action_build_into_prompt=False,
action_prompt_display=reason,
action_done=True,
thinking_id=thinking_id or "",
action_data={"reason": reason},
action_name="no_reply",
))
# 记录执行的动作到目标消息
if success:
asyncio.create_task(self._record_action_to_message(chat_stream, action_name, target_message, action_data))
# 重置打断计数
await self._reset_interruption_count_after_action(chat_stream.stream_id)
return {"action_type": "no_reply", "success": True, "reply_text": "", "command": ""}
elif action_name != "reply" and action_name != "respond" and action_name != "no_action":
# 执行普通动作
success, reply_text, command = await self._handle_action(
chat_stream,
action_name,
reasoning,
action_data or {},
{}, # cycle_timers
thinking_id,
target_message,
)
# 记录执行的动作到目标消息
if success:
asyncio.create_task(self._record_action_to_message(chat_stream, action_name, target_message, action_data))
# 重置打断计数
await self._reset_interruption_count_after_action(chat_stream.stream_id)
return {
"action_type": action_name,
"success": success,
"reply_text": reply_text,
"command": command,
}
else:
# 检查目标消息是否为表情包消息以及配置是否允许回复表情包
if target_message and getattr(target_message, "is_emoji", False):
# 如果是表情包消息且配置不允许回复表情包,则跳过回复
if not getattr(global_config.chat, "allow_reply_to_emoji", True):
logger.info(f"{log_prefix} 目标消息为表情包且配置不允许回复表情包,跳过回复")
return {"action_type": action_name, "success": True, "reply_text": "", "skip_reason": "emoji_not_allowed"}
# 生成回复 (reply 或 respond)
# reply: 针对单条消息的回复,使用 s4u 模板
# respond: 对未读消息的统一回应,使用 normal 模板
try:
# 根据动作类型确定提示词模式
prompt_mode = "s4u" if action_name == "reply" else "normal"
# 将prompt_mode传递给generate_reply
action_data_with_mode = (action_data or {}).copy()
action_data_with_mode["prompt_mode"] = prompt_mode
# 只传递当前正在执行的动作,而不是所有可用动作
# 这样可以让LLM明确知道"已决定执行X动作",而不是"有这些动作可用"
current_action_info = self._using_actions.get(action_name)
current_actions: dict[str, Any] = {action_name: current_action_info} if current_action_info else {}
# 附加目标消息信息(如果存在)
if target_message:
# 提取目标消息的关键信息
target_msg_info = {
"message_id": getattr(target_message, "message_id", ""),
"sender": getattr(target_message.user_info, "user_nickname", "") if hasattr(target_message, "user_info") else "",
"content": getattr(target_message, "processed_plain_text", ""),
"time": getattr(target_message, "time", 0),
}
current_actions["_target_message"] = target_msg_info
success, response_set, _ = await generator_api.generate_reply(
chat_stream=chat_stream,
reply_message=target_message,
action_data=action_data_with_mode,
available_actions=current_actions, # type: ignore
enable_tool=global_config.tool.enable_tool,
request_type="chat.replyer",
from_plugin=False,
)
if not success or not response_set:
# 安全地获取 processed_plain_text
if target_message:
msg_text = target_message.processed_plain_text or "未知消息"
else:
msg_text = "未知消息"
logger.info(f"{msg_text} 的回复生成失败")
return {"action_type": action_name, "success": False, "reply_text": "", "loop_info": None}
except asyncio.CancelledError:
logger.debug(f"{log_prefix} 并行执行:回复生成任务已被取消")
return {"action_type": action_name, "success": False, "reply_text": "", "loop_info": None}
# 从action_data中提取should_quote_reply参数
should_quote_reply = None
if action_data and isinstance(action_data, dict):
should_quote_reply = action_data.get("should_quote_reply", None)
# respond动作默认不引用回复保持对话流畅
if action_name == "respond" and should_quote_reply is None:
should_quote_reply = False
async def _after_reply():
# 发送并存储回复
reply_text, cycle_timers_reply = await self._send_and_store_reply(
chat_stream,
response_set,
asyncio.get_event_loop().time(),
target_message,
{}, # cycle_timers
thinking_id,
[], # actions
should_quote_reply, # 传递should_quote_reply参数
)
# 记录回复动作到目标消息
await self._record_action_to_message(chat_stream, action_name, target_message, action_data)
# 回复成功,重置打断计数
await self._reset_interruption_count_after_action(chat_stream.stream_id)
return reply_text
asyncio.create_task(_after_reply())
return {"action_type": action_name, "success": True}
return {
"action_type": action_name,
"success": success,
"reply_text": reply_text,
"command": command,
}
except Exception as e:
logger.error(f"{log_prefix} 执行动作时出错: {e}")

View File

@@ -57,6 +57,7 @@ class ActionModifier:
async def modify_actions(
self,
message_content: str = "",
chatter_name: str = "",
): # sourcery skip: use-named-expression
"""
动作修改流程,整合传统观察处理和新的激活类型判定
@@ -66,6 +67,10 @@ class ActionModifier:
2. 基于激活类型的智能动作判定,最终确定可用动作集
处理后ActionManager 将包含最终的可用动作集,供规划器直接使用
Args:
message_content: 消息内容
chatter_name: 当前使用的 Chatter 名称,用于过滤只允许特定 Chatter 使用的动作
"""
# 初始化log_prefix
await self._initialize_log_prefix()
@@ -82,13 +87,14 @@ class ActionModifier:
logger.debug(f"{self.log_prefix}开始完整动作修改流程")
removals_s0: list[tuple[str, str]] = [] # 第0阶段聊天类型和Chatter过滤
removals_s1: list[tuple[str, str]] = []
removals_s2: list[tuple[str, str]] = []
removals_s3: list[tuple[str, str]] = []
all_actions = self.action_manager.get_using_actions()
# === 第0阶段根据聊天类型过滤动作 ===
# === 第0阶段根据聊天类型和Chatter过滤动作 ===
from src.chat.utils.utils import get_chat_type_and_target_info
from src.plugin_system.base.component_types import ChatType, ComponentType
from src.plugin_system.core.component_registry import component_registry
@@ -97,26 +103,35 @@ class ActionModifier:
is_group_chat, _ = await get_chat_type_and_target_info(self.chat_id)
all_registered_actions = component_registry.get_components_by_type(ComponentType.ACTION)
chat_type_removals = []
for action_name in list(all_actions.keys()):
if action_name in all_registered_actions:
action_info = all_registered_actions[action_name]
# 检查聊天类型限制
chat_type_allow = getattr(action_info, "chat_type_allow", ChatType.ALL)
# 检查是否符合聊天类型限制
should_keep = (
should_keep_chat_type = (
chat_type_allow == ChatType.ALL
or (chat_type_allow == ChatType.GROUP and is_group_chat)
or (chat_type_allow == ChatType.PRIVATE and not is_group_chat)
)
if not should_keep:
chat_type_removals.append((action_name, f"不支持{'群聊' if is_group_chat else '私聊'}"))
if not should_keep_chat_type:
removals_s0.append((action_name, f"不支持{'群聊' if is_group_chat else '私聊'}"))
self.action_manager.remove_action_from_using(action_name)
continue
# 检查 Chatter 限制
chatter_allow = getattr(action_info, "chatter_allow", [])
if chatter_allow and chatter_name:
# 如果设置了 chatter_allow 且提供了 chatter_name则检查是否匹配
if chatter_name not in chatter_allow:
removals_s0.append((action_name, f"仅限 {', '.join(chatter_allow)} 使用"))
self.action_manager.remove_action_from_using(action_name)
continue
if chat_type_removals:
logger.info(f"{self.log_prefix} 第0阶段根据聊天类型过滤 - 移除了 {len(chat_type_removals)} 个动作")
for action_name, reason in chat_type_removals:
if removals_s0:
logger.info(f"{self.log_prefix} 第0阶段类型/Chatter过滤 - 移除了 {len(removals_s0)} 个动作")
for action_name, reason in removals_s0:
logger.debug(f"{self.log_prefix} - 移除 {action_name}: {reason}")
message_list_before_now_half = await get_raw_msg_before_timestamp_with_chat(