迁移:3804124,9e9e796
(feat:将no_reply内置、fix:优化reply,填补缺失值)
This commit is contained in:
122
FIXES_SUMMARY.md
122
FIXES_SUMMARY.md
@@ -1,122 +0,0 @@
|
|||||||
# MoFox_Bot 修复总结
|
|
||||||
|
|
||||||
## 修复的问题
|
|
||||||
|
|
||||||
### 1. Action组件可用性问题
|
|
||||||
**问题描述**: 用户反馈"no_reply动作还是不可用",并且可用动作列表中缺少 `reply` 和 `no_reply` 动作。
|
|
||||||
|
|
||||||
**根本原因**:
|
|
||||||
- `reply` 动作没有在 `core_actions` 插件中注册
|
|
||||||
- `_manifest.json` 文件缺少 `reply` 动作的声明
|
|
||||||
- `config.toml` 配置文件没有 `enable_reply` 选项
|
|
||||||
|
|
||||||
**修复内容**:
|
|
||||||
1. **plugin.py**: 添加了 `ReplyAction` 的导入和注册
|
|
||||||
```python
|
|
||||||
from src.plugins.built_in.core_actions.reply import ReplyAction
|
|
||||||
# 在配置schema中添加
|
|
||||||
"enable_reply": ConfigField(type=bool, default=True, description="是否启用基本回复动作")
|
|
||||||
# 在组件注册中添加
|
|
||||||
if self.get_config("components.enable_reply", True):
|
|
||||||
components.append((ReplyAction.get_action_info(), ReplyAction))
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **_manifest.json**: 添加了 `reply` 动作的组件声明
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "action",
|
|
||||||
"name": "reply",
|
|
||||||
"description": "执行基本回复动作"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **config.toml**: 添加了完整的组件配置
|
|
||||||
```toml
|
|
||||||
enable_no_reply = true
|
|
||||||
enable_reply = true
|
|
||||||
enable_emoji = true
|
|
||||||
enable_anti_injector_manager = true
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 思考循环触发机制问题
|
|
||||||
**问题描述**:
|
|
||||||
- 用户反馈"思考间隔明显太短了,才1秒左右,应该等到有新的消息才进行下一个思考循环"
|
|
||||||
- 系统使用固定0.1秒间隔无论是否有新消息都进行思考循环,造成资源浪费
|
|
||||||
|
|
||||||
**根本原因**:
|
|
||||||
- 主聊天循环使用固定的短间隔轮询
|
|
||||||
- 不区分是否有新消息,即使没有新消息也会进行思考循环
|
|
||||||
- 违反了"消息驱动"的设计理念
|
|
||||||
|
|
||||||
**修复内容**:
|
|
||||||
1. **消息驱动机制**: 修改为只有在有新消息时才触发思考循环
|
|
||||||
```python
|
|
||||||
# 只有在有新消息时才进行思考循环处理
|
|
||||||
if has_new_messages:
|
|
||||||
# 根据聊天模式处理新消息
|
|
||||||
if self.context.loop_mode == ChatMode.FOCUS:
|
|
||||||
for message in recent_messages:
|
|
||||||
await self.cycle_processor.observe(message)
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **优化等待策略**:
|
|
||||||
- 有新消息时: 0.1秒快速检查后续消息
|
|
||||||
- 无新消息时: 1.0秒轻量级状态检查
|
|
||||||
- 完全避免无意义的思考循环
|
|
||||||
|
|
||||||
3. **保持主动思考独立性**: 主动思考系统有自己的时间间隔,不受此修改影响
|
|
||||||
|
|
||||||
## 修复验证
|
|
||||||
|
|
||||||
### 已验证的修复项目
|
|
||||||
✅ **reply 动作注册**: manifest、config和plugin.py中都已正确配置
|
|
||||||
✅ **no_reply 动作注册**: 配置完整且可用
|
|
||||||
✅ **循环间隔优化**: 动态间隔逻辑已实现
|
|
||||||
✅ **配置文件完整性**: 所有必需的配置项都已添加
|
|
||||||
|
|
||||||
### 预期效果
|
|
||||||
1. **Action系统**:
|
|
||||||
- `no_reply` 和 `reply` 动作将出现在可用动作列表中
|
|
||||||
- Action回退机制将正常工作
|
|
||||||
- 不再出现"未找到Action组件"错误
|
|
||||||
|
|
||||||
2. **思考循环性能**:
|
|
||||||
- **消息驱动机制**: 只有新消息到达时才触发思考循环
|
|
||||||
- **无消息时仅状态检查**: 避免无意义的思考处理
|
|
||||||
- **CPU使用率大幅降低**: 消除连续的高频思考循环
|
|
||||||
- **快速消息响应**: 有新消息时仍保持0.1秒响应速度
|
|
||||||
- **主动思考独立**: 不影响主动思考系统的时间间隔机制
|
|
||||||
|
|
||||||
## 技术细节
|
|
||||||
|
|
||||||
### Action注册流程
|
|
||||||
```
|
|
||||||
plugin.py 导入 → _manifest.json 声明 → config.toml 启用 → 运行时注册
|
|
||||||
```
|
|
||||||
|
|
||||||
### 消息驱动思考策略
|
|
||||||
```
|
|
||||||
消息状态 → 系统行为
|
|
||||||
有新消息 → 0.1秒快速响应 + 思考循环处理
|
|
||||||
无新消息 → 1.0秒状态检查 + 跳过思考循环
|
|
||||||
主动思考 → 独立时间间隔(1500秒) + 独立触发机制
|
|
||||||
```
|
|
||||||
|
|
||||||
## 部署建议
|
|
||||||
|
|
||||||
1. **重启服务**: 修改了核心循环逻辑,建议重启MaiBot服务
|
|
||||||
2. **监控性能**: 观察CPU使用率是否有明显下降
|
|
||||||
3. **测试Action**: 验证no_reply和reply动作是否在可用列表中出现
|
|
||||||
4. **检查日志**: 确认不再出现Action组件错误
|
|
||||||
|
|
||||||
## 后续优化建议
|
|
||||||
|
|
||||||
1. **消息事件驱动**: 考虑使用事件驱动机制完全消除轮询
|
|
||||||
2. **配置化间隔**: 将循环间隔参数添加到配置文件中
|
|
||||||
3. **性能监控**: 添加循环性能指标收集
|
|
||||||
4. **Action热重载**: 实现Action组件的热重载机制
|
|
||||||
|
|
||||||
---
|
|
||||||
**修复日期**: 2025年1月17日
|
|
||||||
**修复范围**: Action系统 + 聊天循环优化
|
|
||||||
**预计效果**: 大幅减少CPU使用率,解决Action可用性问题
|
|
||||||
2
bot.py
2
bot.py
@@ -24,8 +24,6 @@ else:
|
|||||||
from src.common.logger import initialize_logging, get_logger, shutdown_logging
|
from src.common.logger import initialize_logging, get_logger, shutdown_logging
|
||||||
|
|
||||||
# UI日志适配器
|
# UI日志适配器
|
||||||
|
|
||||||
|
|
||||||
initialize_logging()
|
initialize_logging()
|
||||||
|
|
||||||
from src.main import MainSystem # noqa
|
from src.main import MainSystem # noqa
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ from rich.panel import Panel
|
|||||||
# from rich.text import Text
|
# from rich.text import Text
|
||||||
|
|
||||||
from src.common.database.database import db
|
from src.common.database.database import db
|
||||||
from src.common.database.database_model import (
|
from src.common.database.sqlalchemy_models import (
|
||||||
ChatStreams,
|
ChatStreams,
|
||||||
Emoji,
|
Emoji,
|
||||||
Messages,
|
Messages,
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Optional, Dict, Any
|
from typing import Optional, Dict, Any, Tuple
|
||||||
|
|
||||||
|
from src.chat.message_receive.chat_stream import get_chat_manager
|
||||||
from src.chat.utils.timer_calculator import Timer
|
from src.chat.utils.timer_calculator import Timer
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
from src.chat.planner_actions.planner import ActionPlanner
|
from src.chat.planner_actions.planner import ActionPlanner
|
||||||
from src.chat.planner_actions.action_modifier import ActionModifier
|
from src.chat.planner_actions.action_modifier import ActionModifier
|
||||||
|
from src.person_info.person_info import get_person_info_manager
|
||||||
|
from src.plugin_system.apis import database_api
|
||||||
from src.plugin_system.base.component_types import ChatMode
|
from src.plugin_system.base.component_types import ChatMode
|
||||||
from src.mais4u.constant_s4u import ENABLE_S4U
|
from src.mais4u.constant_s4u import ENABLE_S4U
|
||||||
from src.chat.chat_loop.hfc_utils import send_typing, stop_typing
|
from src.chat.chat_loop.hfc_utils import send_typing, stop_typing
|
||||||
@@ -28,6 +31,8 @@ class CycleProcessor:
|
|||||||
response_handler: 响应处理器,负责生成和发送回复
|
response_handler: 响应处理器,负责生成和发送回复
|
||||||
cycle_tracker: 循环跟踪器,负责记录和管理每次思考循环的信息
|
cycle_tracker: 循环跟踪器,负责记录和管理每次思考循环的信息
|
||||||
"""
|
"""
|
||||||
|
self.log_prefix = f"[{get_chat_manager().get_stream_name(self.stream_id) or self.stream_id}]"
|
||||||
|
|
||||||
self.context = context
|
self.context = context
|
||||||
self.response_handler = response_handler
|
self.response_handler = response_handler
|
||||||
self.cycle_tracker = cycle_tracker
|
self.cycle_tracker = cycle_tracker
|
||||||
@@ -35,7 +40,54 @@ class CycleProcessor:
|
|||||||
self.action_modifier = ActionModifier(
|
self.action_modifier = ActionModifier(
|
||||||
action_manager=self.context.action_manager, chat_id=self.context.stream_id
|
action_manager=self.context.action_manager, chat_id=self.context.stream_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def _send_and_store_reply(
|
||||||
|
self,
|
||||||
|
response_set,
|
||||||
|
reply_to_str,
|
||||||
|
loop_start_time,
|
||||||
|
action_message,
|
||||||
|
cycle_timers: Dict[str, float],
|
||||||
|
thinking_id,
|
||||||
|
plan_result,
|
||||||
|
) -> Tuple[Dict[str, Any], str, Dict[str, float]]:
|
||||||
|
with Timer("回复发送", cycle_timers):
|
||||||
|
reply_text = await self._send_response(response_set, reply_to_str, loop_start_time, action_message)
|
||||||
|
|
||||||
|
# 存储reply action信息
|
||||||
|
person_info_manager = get_person_info_manager()
|
||||||
|
person_id = person_info_manager.get_person_id(
|
||||||
|
action_message.get("chat_info_platform", ""),
|
||||||
|
action_message.get("user_id", ""),
|
||||||
|
)
|
||||||
|
person_name = await person_info_manager.get_value(person_id, "person_name")
|
||||||
|
action_prompt_display = f"你对{person_name}进行了回复:{reply_text}"
|
||||||
|
|
||||||
|
await database_api.store_action_info(
|
||||||
|
chat_stream=self.chat_stream,
|
||||||
|
action_build_into_prompt=False,
|
||||||
|
action_prompt_display=action_prompt_display,
|
||||||
|
action_done=True,
|
||||||
|
thinking_id=thinking_id,
|
||||||
|
action_data={"reply_text": reply_text, "reply_to": reply_to_str},
|
||||||
|
action_name="reply",
|
||||||
|
)
|
||||||
|
|
||||||
|
# 构建循环信息
|
||||||
|
loop_info: Dict[str, Any] = {
|
||||||
|
"loop_plan_info": {
|
||||||
|
"action_result": plan_result.get("action_result", {}),
|
||||||
|
},
|
||||||
|
"loop_action_info": {
|
||||||
|
"action_taken": True,
|
||||||
|
"reply_text": reply_text,
|
||||||
|
"command": "",
|
||||||
|
"taken_time": time.time(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return loop_info, reply_text, cycle_timers
|
||||||
|
|
||||||
async def observe(self, message_data: Optional[Dict[str, Any]] = None) -> bool:
|
async def observe(self, message_data: Optional[Dict[str, Any]] = None) -> bool:
|
||||||
"""
|
"""
|
||||||
观察和处理单次思考循环的核心方法
|
观察和处理单次思考循环的核心方法
|
||||||
@@ -79,34 +131,24 @@ class CycleProcessor:
|
|||||||
at_bot_mentioned = (global_config.chat.mentioned_bot_inevitable_reply and is_mentioned_bot) or (
|
at_bot_mentioned = (global_config.chat.mentioned_bot_inevitable_reply and is_mentioned_bot) or (
|
||||||
global_config.chat.at_bot_inevitable_reply and is_mentioned_bot
|
global_config.chat.at_bot_inevitable_reply and is_mentioned_bot
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 专注模式下提及bot必定回复
|
||||||
if self.context.loop_mode == ChatMode.FOCUS and at_bot_mentioned and "no_reply" in available_actions:
|
if self.context.loop_mode == ChatMode.FOCUS and at_bot_mentioned and "no_reply" in available_actions:
|
||||||
available_actions = {k: v for k, v in available_actions.items() if k != "no_reply"}
|
available_actions = {k: v for k, v in available_actions.items() if k != "no_reply"}
|
||||||
|
|
||||||
# 检查是否在normal模式下没有可用动作(除了reply相关动作)
|
# 检查是否在normal模式下没有可用动作(除了reply相关动作)
|
||||||
skip_planner = False
|
skip_planner = False
|
||||||
gen_task = None
|
|
||||||
if self.context.loop_mode == ChatMode.NORMAL:
|
if self.context.loop_mode == ChatMode.NORMAL:
|
||||||
non_reply_actions = {
|
non_reply_actions = {
|
||||||
k: v for k, v in available_actions.items() if k not in ["reply", "no_reply", "no_action"]
|
k: v for k, v in available_actions.items() if k not in ["reply", "no_reply", "no_action"]
|
||||||
}
|
}
|
||||||
if not non_reply_actions:
|
if not non_reply_actions:
|
||||||
skip_planner = True
|
skip_planner = True
|
||||||
logger.info(f"{self.log_prefix} Normal模式下没有可用动作,直接回复")
|
logger.info(f"Normal模式下没有可用动作,直接回复")
|
||||||
plan_result = self._get_direct_reply_plan(loop_start_time)
|
plan_result = self._get_direct_reply_plan(loop_start_time)
|
||||||
target_message = message_data
|
target_message = message_data
|
||||||
|
|
||||||
# 如果normal模式且不跳过规划器,开始一个回复生成进程,先准备好回复(其实是和planer同时进行的)
|
|
||||||
if not skip_planner:
|
|
||||||
reply_to_str = await self._build_reply_to_str(message_data)
|
|
||||||
gen_task = asyncio.create_task(
|
|
||||||
self.response_handler.generate_response(
|
|
||||||
message_data=message_data,
|
|
||||||
available_actions=available_actions,
|
|
||||||
reply_to=reply_to_str,
|
|
||||||
request_type="chat.replyer.normal",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Focus模式
|
# Focus模式
|
||||||
if not skip_planner:
|
if not skip_planner:
|
||||||
@@ -123,57 +165,237 @@ class CycleProcessor:
|
|||||||
with Timer("规划器", cycle_timers):
|
with Timer("规划器", cycle_timers):
|
||||||
plan_result, target_message = await self.action_planner.plan(mode=self.context.loop_mode)
|
plan_result, target_message = await self.action_planner.plan(mode=self.context.loop_mode)
|
||||||
|
|
||||||
action_result = plan_result.get("action_result", {}) if isinstance(plan_result, dict) else {}
|
action_result = plan_result.get("action_result", {})
|
||||||
if not isinstance(action_result, dict):
|
|
||||||
action_result = {}
|
|
||||||
action_type = action_result.get("action_type", "error")
|
action_type = action_result.get("action_type", "error")
|
||||||
action_data = action_result.get("action_data", {})
|
action_data = action_result.get("action_data", {})
|
||||||
reasoning = action_result.get("reasoning", "未提供理由")
|
reasoning = action_result.get("reasoning", "未提供理由")
|
||||||
is_parallel = action_result.get("is_parallel", True)
|
is_parallel = action_result.get("is_parallel", True)
|
||||||
|
|
||||||
action_data["loop_start_time"] = loop_start_time
|
action_data["loop_start_time"] = loop_start_time
|
||||||
|
action_message = message_data or target_message
|
||||||
|
|
||||||
is_private_chat = self.context.chat_stream.group_info is None if self.context.chat_stream else False
|
# is_private_chat = self.context.chat_stream.group_info is None if self.context.chat_stream else False
|
||||||
if self.context.loop_mode == ChatMode.FOCUS and is_private_chat and action_type == "no_reply":
|
|
||||||
action_type = "reply"
|
# 重构后的动作处理逻辑:先汇总所有动作,然后并行执行
|
||||||
|
actions = []
|
||||||
|
|
||||||
if action_type == "reply":
|
# 1. 添加Planner取得的动作
|
||||||
# 使用 action_planner 获取的 target_message,如果为空则使用原始 message_data
|
actions.append({
|
||||||
actual_message = target_message or message_data
|
"action_type": action_type,
|
||||||
await self._handle_reply_action(
|
"reasoning": reasoning,
|
||||||
actual_message, available_actions, gen_task, loop_start_time, cycle_timers, thinking_id, plan_result
|
"action_data": action_data,
|
||||||
|
"action_message": action_message,
|
||||||
|
"available_actions": available_actions # 添加这个字段
|
||||||
|
})
|
||||||
|
|
||||||
|
# 2. 如果不是reply动作且需要并行执行,额外添加reply动作
|
||||||
|
if action_type != "reply" and is_parallel:
|
||||||
|
actions.append({
|
||||||
|
"action_type": "reply",
|
||||||
|
"action_message": action_message,
|
||||||
|
"available_actions": available_actions
|
||||||
|
})
|
||||||
|
|
||||||
|
async def execute_action(action_info):
|
||||||
|
"""执行单个动作的通用函数"""
|
||||||
|
try:
|
||||||
|
if action_info["action_type"] == "no_reply":
|
||||||
|
# 直接处理no_reply逻辑,不再通过动作系统
|
||||||
|
reason = action_info.get("reasoning", "选择不回复")
|
||||||
|
logger.info(f"{self.log_prefix} 选择不回复,原因: {reason}")
|
||||||
|
|
||||||
|
# 存储no_reply信息到数据库
|
||||||
|
await database_api.store_action_info(
|
||||||
|
chat_stream=self.context.chat_stream,
|
||||||
|
action_build_into_prompt=False,
|
||||||
|
action_prompt_display=reason,
|
||||||
|
action_done=True,
|
||||||
|
thinking_id=thinking_id,
|
||||||
|
action_data={"reason": reason},
|
||||||
|
action_name="no_reply",
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"action_type": "no_reply",
|
||||||
|
"success": True,
|
||||||
|
"reply_text": "",
|
||||||
|
"command": ""
|
||||||
|
}
|
||||||
|
elif action_info["action_type"] != "reply":
|
||||||
|
# 执行普通动作
|
||||||
|
with Timer("动作执行", cycle_timers):
|
||||||
|
success, reply_text, command = await self._handle_action(
|
||||||
|
action_info["action_type"],
|
||||||
|
action_info["reasoning"],
|
||||||
|
action_info["action_data"],
|
||||||
|
cycle_timers,
|
||||||
|
thinking_id,
|
||||||
|
action_info["action_message"]
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"action_type": action_info["action_type"],
|
||||||
|
"success": success,
|
||||||
|
"reply_text": reply_text,
|
||||||
|
"command": command
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# 执行回复动作
|
||||||
|
reply_to_str = await self._build_reply_to_str(action_info["action_message"])
|
||||||
|
request_type = "chat.replyer"
|
||||||
|
|
||||||
|
# 生成回复
|
||||||
|
gather_timeout = global_config.chat.thinking_timeout
|
||||||
|
try:
|
||||||
|
response_set = await asyncio.wait_for(
|
||||||
|
self.response_handler._generate_response(
|
||||||
|
message_data=action_info["action_message"],
|
||||||
|
available_actions=action_info["available_actions"],
|
||||||
|
reply_to=reply_to_str,
|
||||||
|
request_type=request_type,
|
||||||
|
),
|
||||||
|
timeout=gather_timeout
|
||||||
|
)
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
logger.warning(
|
||||||
|
f"{self.log_prefix} 并行执行:回复生成超时>{global_config.chat.thinking_timeout}s,已跳过"
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"action_type": "reply",
|
||||||
|
"success": False,
|
||||||
|
"reply_text": "",
|
||||||
|
"loop_info": None
|
||||||
|
}
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
logger.debug(f"{self.log_prefix} 并行执行:回复生成任务已被取消")
|
||||||
|
return {
|
||||||
|
"action_type": "reply",
|
||||||
|
"success": False,
|
||||||
|
"reply_text": "",
|
||||||
|
"loop_info": None
|
||||||
|
}
|
||||||
|
|
||||||
|
if not response_set:
|
||||||
|
logger.warning(f"{self.log_prefix} 模型超时或生成回复内容为空")
|
||||||
|
return {
|
||||||
|
"action_type": "reply",
|
||||||
|
"success": False,
|
||||||
|
"reply_text": "",
|
||||||
|
"loop_info": None
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO: Where is my fucking _send_and_store_reply?
|
||||||
|
loop_info, reply_text, cycle_timers_reply = await self._send_and_store_reply(
|
||||||
|
response_set,
|
||||||
|
reply_to_str,
|
||||||
|
loop_start_time,
|
||||||
|
action_info["action_message"],
|
||||||
|
cycle_timers,
|
||||||
|
thinking_id,
|
||||||
|
plan_result,
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"action_type": "reply",
|
||||||
|
"success": True,
|
||||||
|
"reply_text": reply_text,
|
||||||
|
"loop_info": loop_info
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"{self.log_prefix} 执行动作时出错: {e}")
|
||||||
|
return {
|
||||||
|
"action_type": action_info["action_type"],
|
||||||
|
"success": False,
|
||||||
|
"reply_text": "",
|
||||||
|
"loop_info": None,
|
||||||
|
"error": str(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
# 创建所有动作的后台任务
|
||||||
|
action_tasks = [asyncio.create_task(execute_action(action)) for action in actions]
|
||||||
|
|
||||||
|
# 并行执行所有任务
|
||||||
|
results = await asyncio.gather(*action_tasks, return_exceptions=True)
|
||||||
|
|
||||||
|
# 处理执行结果
|
||||||
|
reply_loop_info = None
|
||||||
|
reply_text_from_reply = ""
|
||||||
|
action_success = False
|
||||||
|
action_reply_text = ""
|
||||||
|
action_command = ""
|
||||||
|
|
||||||
|
for i, result in enumerate(results):
|
||||||
|
if isinstance(result, BaseException):
|
||||||
|
logger.error(f"{self.log_prefix} 动作执行异常: {result}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
action_info = actions[i]
|
||||||
|
if result["action_type"] != "reply":
|
||||||
|
action_success = result["success"]
|
||||||
|
action_reply_text = result["reply_text"]
|
||||||
|
action_command = result.get("command", "")
|
||||||
|
elif result["action_type"] == "reply":
|
||||||
|
if result["success"]:
|
||||||
|
reply_loop_info = result["loop_info"]
|
||||||
|
reply_text_from_reply = result["reply_text"]
|
||||||
|
else:
|
||||||
|
logger.warning(f"{self.log_prefix} 回复动作执行失败")
|
||||||
|
|
||||||
|
# 构建最终的循环信息
|
||||||
|
if reply_loop_info:
|
||||||
|
# 如果有回复信息,使用回复的loop_info作为基础
|
||||||
|
loop_info = reply_loop_info
|
||||||
|
# 更新动作执行信息
|
||||||
|
loop_info["loop_action_info"].update(
|
||||||
|
{
|
||||||
|
"action_taken": action_success,
|
||||||
|
"command": action_command,
|
||||||
|
"taken_time": time.time(),
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
reply_text = reply_text_from_reply
|
||||||
else:
|
else:
|
||||||
await self._handle_other_actions(
|
# 没有回复信息,构建纯动作的loop_info
|
||||||
action_type,
|
loop_info = {
|
||||||
reasoning,
|
"loop_plan_info": {
|
||||||
action_data,
|
"action_result": plan_result.get("action_result", {}),
|
||||||
is_parallel,
|
},
|
||||||
gen_task,
|
"loop_action_info": {
|
||||||
target_message or message_data,
|
"action_taken": action_success,
|
||||||
cycle_timers,
|
"reply_text": action_reply_text,
|
||||||
thinking_id,
|
"command": action_command,
|
||||||
plan_result,
|
"taken_time": time.time(),
|
||||||
loop_start_time,
|
},
|
||||||
)
|
}
|
||||||
|
reply_text = action_reply_text
|
||||||
self.context.last_action = action_type
|
|
||||||
|
self.last_action = action_type
|
||||||
# 处理no_reply相关的逻辑
|
|
||||||
if action_type != "no_reply":
|
|
||||||
self.context.no_reply_consecutive = 0
|
|
||||||
if hasattr(self.context, 'chat_instance') and self.context.chat_instance:
|
|
||||||
self.context.chat_instance.recent_interest_records.clear()
|
|
||||||
logger.info(f"{self.context.log_prefix} 执行了{action_type}动作,重置no_reply计数器和兴趣度记录")
|
|
||||||
|
|
||||||
if action_type == "no_reply":
|
|
||||||
self.context.no_reply_consecutive += 1
|
|
||||||
# 调用HeartFChatting中的_determine_form_type方法
|
|
||||||
if hasattr(self.context, 'chat_instance') and self.context.chat_instance:
|
|
||||||
self.context.chat_instance._determine_form_type()
|
|
||||||
|
|
||||||
if ENABLE_S4U:
|
if ENABLE_S4U:
|
||||||
await stop_typing()
|
await stop_typing()
|
||||||
|
|
||||||
|
self.context.chat_instance.cycle_tracker.end_cycle(loop_info, cycle_timers)
|
||||||
|
self.context.chat_instance.cycle_tracker.print_cycle_info(cycle_timers)
|
||||||
|
|
||||||
|
if self.context.loop_mode == ChatMode.NORMAL:
|
||||||
|
await self.context.chat_instance.willing_manager.after_generate_reply_handle(message_data.get("message_id", ""))
|
||||||
|
|
||||||
|
# 管理no_reply计数器:当执行了非no_reply动作时,重置计数器
|
||||||
|
if action_type != "no_reply" and action_type != "no_action":
|
||||||
|
# no_reply逻辑已集成到heartFC_chat.py中,直接重置计数器
|
||||||
|
self.context.chat_instance.recent_interest_records.clear()
|
||||||
|
self.context.no_reply_consecutive = 0
|
||||||
|
logger.info(f"{self.log_prefix} 执行了{action_type}动作,重置no_reply计数器")
|
||||||
|
return True
|
||||||
|
elif action_type == "no_action":
|
||||||
|
# 当执行回复动作时,也重置no_reply计数
|
||||||
|
self.context.chat_instance.recent_interest_records.clear()
|
||||||
|
self.context.no_reply_consecutive = 0
|
||||||
|
logger.info(f"{self.log_prefix} 执行了回复动作,重置no_reply计数器")
|
||||||
|
|
||||||
|
if action_type == "no_reply":
|
||||||
|
self.context.no_reply_consecutive += 1
|
||||||
|
self.context.chat_instance._determine_form_type()
|
||||||
|
|
||||||
# 在一轮动作执行完毕后,增加睡眠压力
|
# 在一轮动作执行完毕后,增加睡眠压力
|
||||||
if self.context.energy_manager and global_config.sleep_system.enable_insomnia_system:
|
if self.context.energy_manager and global_config.sleep_system.enable_insomnia_system:
|
||||||
if action_type not in ["no_reply", "no_action"]:
|
if action_type not in ["no_reply", "no_action"]:
|
||||||
|
|||||||
@@ -1,380 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
Action组件诊断和修复脚本
|
|
||||||
检查no_reply等核心Action是否正确注册,并尝试修复相关问题
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
from typing import Dict, Any
|
|
||||||
|
|
||||||
# 添加项目路径
|
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), "../../../"))
|
|
||||||
|
|
||||||
from src.common.logger import get_logger
|
|
||||||
from src.plugin_system.core.component_registry import component_registry
|
|
||||||
from src.plugin_system.core.plugin_manager import plugin_manager
|
|
||||||
from src.plugin_system.base.component_types import ComponentType
|
|
||||||
|
|
||||||
logger = get_logger("action_diagnostics")
|
|
||||||
|
|
||||||
|
|
||||||
class ActionDiagnostics:
|
|
||||||
"""Action组件诊断器"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.required_actions = ["no_reply", "reply", "emoji", "at_user"]
|
|
||||||
|
|
||||||
def check_plugin_loading(self) -> Dict[str, Any]:
|
|
||||||
"""检查插件加载状态"""
|
|
||||||
logger.info("开始检查插件加载状态...")
|
|
||||||
|
|
||||||
result = {
|
|
||||||
"plugins_loaded": False,
|
|
||||||
"total_plugins": 0,
|
|
||||||
"loaded_plugins": [],
|
|
||||||
"failed_plugins": [],
|
|
||||||
"core_actions_plugin": None,
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 加载所有插件
|
|
||||||
plugin_manager.load_all_plugins()
|
|
||||||
|
|
||||||
# 获取插件统计信息
|
|
||||||
stats = plugin_manager.get_stats()
|
|
||||||
result["plugins_loaded"] = True
|
|
||||||
result["total_plugins"] = stats.get("total_plugins", 0)
|
|
||||||
|
|
||||||
# 检查是否有core_actions插件
|
|
||||||
for plugin_name in plugin_manager.loaded_plugins:
|
|
||||||
result["loaded_plugins"].append(plugin_name)
|
|
||||||
if "core_actions" in plugin_name.lower():
|
|
||||||
result["core_actions_plugin"] = plugin_name
|
|
||||||
|
|
||||||
logger.info(f"插件加载成功,总数: {result['total_plugins']}")
|
|
||||||
logger.info(f"已加载插件: {result['loaded_plugins']}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"插件加载失败: {e}")
|
|
||||||
result["error"] = str(e)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def check_action_registry(self) -> Dict[str, Any]:
|
|
||||||
"""检查Action注册状态"""
|
|
||||||
logger.info("开始检查Action组件注册状态...")
|
|
||||||
|
|
||||||
result = {"registered_actions": [], "missing_actions": [], "default_actions": {}, "total_actions": 0}
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 获取所有注册的Action
|
|
||||||
all_components = component_registry.get_all_components(ComponentType.ACTION)
|
|
||||||
result["total_actions"] = len(all_components)
|
|
||||||
|
|
||||||
for name, info in all_components.items():
|
|
||||||
result["registered_actions"].append(name)
|
|
||||||
logger.debug(f"已注册Action: {name} (插件: {info.plugin_name})")
|
|
||||||
|
|
||||||
# 检查必需的Action是否存在
|
|
||||||
for required_action in self.required_actions:
|
|
||||||
if required_action not in all_components:
|
|
||||||
result["missing_actions"].append(required_action)
|
|
||||||
logger.warning(f"缺失必需Action: {required_action}")
|
|
||||||
else:
|
|
||||||
logger.info(f"找到必需Action: {required_action}")
|
|
||||||
|
|
||||||
# 获取默认Action
|
|
||||||
default_actions = component_registry.get_default_actions()
|
|
||||||
result["default_actions"] = {name: info.plugin_name for name, info in default_actions.items()}
|
|
||||||
|
|
||||||
logger.info(f"总注册Action数量: {result['total_actions']}")
|
|
||||||
logger.info(f"缺失Action: {result['missing_actions']}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Action注册检查失败: {e}")
|
|
||||||
result["error"] = str(e)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def check_specific_action(self, action_name: str) -> Dict[str, Any]:
|
|
||||||
"""检查特定Action的详细信息"""
|
|
||||||
logger.info(f"检查Action详细信息: {action_name}")
|
|
||||||
|
|
||||||
result = {
|
|
||||||
"exists": False,
|
|
||||||
"component_info": None,
|
|
||||||
"component_class": None,
|
|
||||||
"is_default": False,
|
|
||||||
"plugin_name": None,
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 检查组件信息
|
|
||||||
component_info = component_registry.get_component_info(action_name, ComponentType.ACTION)
|
|
||||||
if component_info:
|
|
||||||
result["exists"] = True
|
|
||||||
result["component_info"] = {
|
|
||||||
"name": component_info.name,
|
|
||||||
"description": component_info.description,
|
|
||||||
"plugin_name": component_info.plugin_name,
|
|
||||||
"version": component_info.version,
|
|
||||||
}
|
|
||||||
result["plugin_name"] = component_info.plugin_name
|
|
||||||
logger.info(f"找到Action组件信息: {action_name}")
|
|
||||||
else:
|
|
||||||
logger.warning(f"未找到Action组件信息: {action_name}")
|
|
||||||
return result
|
|
||||||
|
|
||||||
# 检查组件类
|
|
||||||
component_class = component_registry.get_component_class(action_name, ComponentType.ACTION)
|
|
||||||
if component_class:
|
|
||||||
result["component_class"] = component_class.__name__
|
|
||||||
logger.info(f"找到Action组件类: {component_class.__name__}")
|
|
||||||
else:
|
|
||||||
logger.warning(f"未找到Action组件类: {action_name}")
|
|
||||||
|
|
||||||
# 检查是否为默认Action
|
|
||||||
default_actions = component_registry.get_default_actions()
|
|
||||||
result["is_default"] = action_name in default_actions
|
|
||||||
|
|
||||||
logger.info(f"Action {action_name} 检查完成: 存在={result['exists']}, 默认={result['is_default']}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"检查Action {action_name} 失败: {e}")
|
|
||||||
result["error"] = str(e)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def attempt_fix_missing_actions(self) -> Dict[str, Any]:
|
|
||||||
"""尝试修复缺失的Action"""
|
|
||||||
logger.info("尝试修复缺失的Action组件...")
|
|
||||||
|
|
||||||
result = {"fixed_actions": [], "still_missing": [], "errors": []}
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 重新加载插件
|
|
||||||
plugin_manager.load_all_plugins()
|
|
||||||
|
|
||||||
# 再次检查Action注册状态
|
|
||||||
registry_check = self.check_action_registry()
|
|
||||||
|
|
||||||
for required_action in self.required_actions:
|
|
||||||
if required_action in registry_check["missing_actions"]:
|
|
||||||
try:
|
|
||||||
# 尝试手动注册核心Action
|
|
||||||
if required_action == "no_reply":
|
|
||||||
self._register_no_reply_action()
|
|
||||||
result["fixed_actions"].append(required_action)
|
|
||||||
else:
|
|
||||||
result["still_missing"].append(required_action)
|
|
||||||
except Exception as e:
|
|
||||||
error_msg = f"修复Action {required_action} 失败: {e}"
|
|
||||||
logger.error(error_msg)
|
|
||||||
result["errors"].append(error_msg)
|
|
||||||
result["still_missing"].append(required_action)
|
|
||||||
|
|
||||||
logger.info(f"Action修复完成: 已修复={result['fixed_actions']}, 仍缺失={result['still_missing']}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
error_msg = f"Action修复过程失败: {e}"
|
|
||||||
logger.error(error_msg)
|
|
||||||
result["errors"].append(error_msg)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _register_no_reply_action(self):
|
|
||||||
"""手动注册no_reply Action"""
|
|
||||||
try:
|
|
||||||
from src.plugins.built_in.core_actions.no_reply import NoReplyAction
|
|
||||||
from src.plugin_system.base.component_types import ActionInfo
|
|
||||||
|
|
||||||
# 创建Action信息
|
|
||||||
action_info = ActionInfo(
|
|
||||||
name="no_reply", description="暂时不回复消息", plugin_name="built_in.core_actions", version="1.0.0"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 注册Action
|
|
||||||
success = component_registry._register_action_component(action_info, NoReplyAction)
|
|
||||||
if success:
|
|
||||||
logger.info("手动注册no_reply Action成功")
|
|
||||||
else:
|
|
||||||
raise Exception("注册失败")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
raise Exception(f"手动注册no_reply Action失败: {e}") from e
|
|
||||||
|
|
||||||
def run_full_diagnosis(self) -> Dict[str, Any]:
|
|
||||||
"""运行完整诊断"""
|
|
||||||
logger.info("🔧 开始Action组件完整诊断")
|
|
||||||
logger.info("=" * 60)
|
|
||||||
|
|
||||||
diagnosis_result = {
|
|
||||||
"plugin_status": {},
|
|
||||||
"registry_status": {},
|
|
||||||
"action_details": {},
|
|
||||||
"fix_attempts": {},
|
|
||||||
"summary": {},
|
|
||||||
}
|
|
||||||
|
|
||||||
# 1. 检查插件加载
|
|
||||||
logger.info("\n📦 步骤1: 检查插件加载状态")
|
|
||||||
diagnosis_result["plugin_status"] = self.check_plugin_loading()
|
|
||||||
|
|
||||||
# 2. 检查Action注册
|
|
||||||
logger.info("\n📋 步骤2: 检查Action注册状态")
|
|
||||||
diagnosis_result["registry_status"] = self.check_action_registry()
|
|
||||||
|
|
||||||
# 3. 检查特定Action详细信息
|
|
||||||
logger.info("\n🔍 步骤3: 检查特定Action详细信息")
|
|
||||||
diagnosis_result["action_details"] = {}
|
|
||||||
for action in self.required_actions:
|
|
||||||
diagnosis_result["action_details"][action] = self.check_specific_action(action)
|
|
||||||
|
|
||||||
# 4. 尝试修复缺失的Action
|
|
||||||
if diagnosis_result["registry_status"].get("missing_actions"):
|
|
||||||
logger.info("\n🔧 步骤4: 尝试修复缺失的Action")
|
|
||||||
diagnosis_result["fix_attempts"] = self.attempt_fix_missing_actions()
|
|
||||||
|
|
||||||
# 5. 生成诊断摘要
|
|
||||||
logger.info("\n📊 步骤5: 生成诊断摘要")
|
|
||||||
diagnosis_result["summary"] = self._generate_summary(diagnosis_result)
|
|
||||||
|
|
||||||
self._print_diagnosis_results(diagnosis_result)
|
|
||||||
|
|
||||||
return diagnosis_result
|
|
||||||
|
|
||||||
def _generate_summary(self, diagnosis_result: Dict[str, Any]) -> Dict[str, Any]:
|
|
||||||
"""生成诊断摘要"""
|
|
||||||
summary = {"overall_status": "unknown", "critical_issues": [], "recommendations": []}
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 检查插件加载状态
|
|
||||||
if not diagnosis_result["plugin_status"].get("plugins_loaded"):
|
|
||||||
summary["critical_issues"].append("插件加载失败")
|
|
||||||
summary["recommendations"].append("检查插件系统配置")
|
|
||||||
|
|
||||||
# 检查必需Action
|
|
||||||
missing_actions = diagnosis_result["registry_status"].get("missing_actions", [])
|
|
||||||
if "no_reply" in missing_actions:
|
|
||||||
summary["critical_issues"].append("缺失no_reply Action")
|
|
||||||
summary["recommendations"].append("检查core_actions插件是否正确加载")
|
|
||||||
|
|
||||||
# 检查修复结果
|
|
||||||
if diagnosis_result.get("fix_attempts"):
|
|
||||||
still_missing = diagnosis_result["fix_attempts"].get("still_missing", [])
|
|
||||||
if still_missing:
|
|
||||||
summary["critical_issues"].append(f"修复后仍缺失Action: {still_missing}")
|
|
||||||
summary["recommendations"].append("需要手动修复插件注册问题")
|
|
||||||
|
|
||||||
# 确定整体状态
|
|
||||||
if not summary["critical_issues"]:
|
|
||||||
summary["overall_status"] = "healthy"
|
|
||||||
elif len(summary["critical_issues"]) <= 2:
|
|
||||||
summary["overall_status"] = "warning"
|
|
||||||
else:
|
|
||||||
summary["overall_status"] = "critical"
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
summary["critical_issues"].append(f"摘要生成失败: {e}")
|
|
||||||
summary["overall_status"] = "error"
|
|
||||||
|
|
||||||
return summary
|
|
||||||
|
|
||||||
def _print_diagnosis_results(self, diagnosis_result: Dict[str, Any]):
|
|
||||||
"""打印诊断结果"""
|
|
||||||
logger.info("\n" + "=" * 60)
|
|
||||||
logger.info("📈 诊断结果摘要")
|
|
||||||
logger.info("=" * 60)
|
|
||||||
|
|
||||||
summary = diagnosis_result.get("summary", {})
|
|
||||||
overall_status = summary.get("overall_status", "unknown")
|
|
||||||
|
|
||||||
# 状态指示器
|
|
||||||
status_indicators = {
|
|
||||||
"healthy": "✅ 系统健康",
|
|
||||||
"warning": "⚠️ 存在警告",
|
|
||||||
"critical": "❌ 存在严重问题",
|
|
||||||
"error": "💥 诊断出错",
|
|
||||||
"unknown": "❓ 状态未知",
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(f"🎯 整体状态: {status_indicators.get(overall_status, overall_status)}")
|
|
||||||
|
|
||||||
# 关键问题
|
|
||||||
critical_issues = summary.get("critical_issues", [])
|
|
||||||
if critical_issues:
|
|
||||||
logger.info("\n🚨 关键问题:")
|
|
||||||
for issue in critical_issues:
|
|
||||||
logger.info(f" • {issue}")
|
|
||||||
|
|
||||||
# 建议
|
|
||||||
recommendations = summary.get("recommendations", [])
|
|
||||||
if recommendations:
|
|
||||||
logger.info("\n💡 建议:")
|
|
||||||
for rec in recommendations:
|
|
||||||
logger.info(f" • {rec}")
|
|
||||||
|
|
||||||
# 详细状态
|
|
||||||
plugin_status = diagnosis_result.get("plugin_status", {})
|
|
||||||
if plugin_status.get("plugins_loaded"):
|
|
||||||
logger.info(f"\n📦 插件状态: 已加载 {plugin_status.get('total_plugins', 0)} 个插件")
|
|
||||||
else:
|
|
||||||
logger.info("\n📦 插件状态: ❌ 插件加载失败")
|
|
||||||
|
|
||||||
registry_status = diagnosis_result.get("registry_status", {})
|
|
||||||
total_actions = registry_status.get("total_actions", 0)
|
|
||||||
missing_actions = registry_status.get("missing_actions", [])
|
|
||||||
logger.info(f"📋 Action状态: 已注册 {total_actions} 个,缺失 {len(missing_actions)} 个")
|
|
||||||
|
|
||||||
if missing_actions:
|
|
||||||
logger.info(f" 缺失的Action: {missing_actions}")
|
|
||||||
|
|
||||||
logger.info("\n" + "=" * 60)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""主函数"""
|
|
||||||
diagnostics = ActionDiagnostics()
|
|
||||||
|
|
||||||
try:
|
|
||||||
result = diagnostics.run_full_diagnosis()
|
|
||||||
|
|
||||||
# 保存诊断结果
|
|
||||||
import orjson
|
|
||||||
|
|
||||||
with open("action_diagnosis_results.json", "w", encoding="utf-8") as f:
|
|
||||||
f.write(orjson.dumps(result, option=orjson.OPT_INDENT_2).decode("utf-8"))
|
|
||||||
|
|
||||||
logger.info("📄 诊断结果已保存到: action_diagnosis_results.json")
|
|
||||||
|
|
||||||
# 根据诊断结果返回适当的退出代码
|
|
||||||
summary = result.get("summary", {})
|
|
||||||
overall_status = summary.get("overall_status", "unknown")
|
|
||||||
|
|
||||||
if overall_status == "healthy":
|
|
||||||
return 0
|
|
||||||
elif overall_status == "warning":
|
|
||||||
return 1
|
|
||||||
else:
|
|
||||||
return 2
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
logger.info("❌ 诊断被用户中断")
|
|
||||||
return 3
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"❌ 诊断执行失败: {e}")
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
traceback.print_exc()
|
|
||||||
return 4
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import logging
|
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
|
||||||
|
|
||||||
exit_code = main()
|
|
||||||
sys.exit(exit_code)
|
|
||||||
@@ -159,8 +159,11 @@ class ActionModifier:
|
|||||||
if all_removals:
|
if all_removals:
|
||||||
removals_summary = " | ".join([f"{name}({reason})" for name, reason in all_removals])
|
removals_summary = " | ".join([f"{name}({reason})" for name, reason in all_removals])
|
||||||
|
|
||||||
|
available_actions = list(self.action_manager.get_using_actions().keys())
|
||||||
|
available_actions_text = "、".join(available_actions) if available_actions else "无"
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{self.log_prefix} 动作修改流程结束,最终可用动作: {list(self.action_manager.get_using_actions().keys())}||移除记录: {removals_summary}"
|
f"{self.log_prefix} 当前可用动作: {available_actions_text}||移除: {removals_summary}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def _check_action_associated_types(self, all_actions: Dict[str, ActionInfo], chat_context: ChatMessageContext):
|
def _check_action_associated_types(self, all_actions: Dict[str, ActionInfo], chat_context: ChatMessageContext):
|
||||||
|
|||||||
@@ -495,7 +495,7 @@ class ActionPlanner:
|
|||||||
by_what = "聊天内容"
|
by_what = "聊天内容"
|
||||||
target_prompt = '\n "target_message_id":"触发action的消息id"'
|
target_prompt = '\n "target_message_id":"触发action的消息id"'
|
||||||
no_action_block = f"""重要说明:
|
no_action_block = f"""重要说明:
|
||||||
- 'no_reply' 表示只进行不进行回复,等待合适的回复时机
|
- 'no_reply' 表示只进行不进行回复,等待合适的回复时机(由系统直接处理)
|
||||||
- 当你刚刚发送了消息,没有人回复时,选择no_reply
|
- 当你刚刚发送了消息,没有人回复时,选择no_reply
|
||||||
- 当你一次发送了太多消息,为了避免打扰聊天节奏,选择no_reply
|
- 当你一次发送了太多消息,为了避免打扰聊天节奏,选择no_reply
|
||||||
|
|
||||||
|
|||||||
@@ -99,13 +99,12 @@ def init_prompt():
|
|||||||
{identity}
|
{identity}
|
||||||
|
|
||||||
{action_descriptions}
|
{action_descriptions}
|
||||||
你现在的主要任务是和 {sender_name} 聊天。同时,也有其他用户会参与你们的聊天,你可以参考他们的回复内容,但是你主要还是关注你和{sender_name}的聊天内容。
|
|
||||||
|
|
||||||
{background_dialogue_prompt}
|
|
||||||
--------------------------------
|
--------------------------------
|
||||||
{time_block}
|
{time_block}
|
||||||
这是你和{sender_name}的对话,你们正在交流中:
|
你现在的主要任务是和 {sender_name} 聊天。同时,也有其他用户会参与聊天,你可以参考他们的回复内容,但是你现在想回复{sender_name}的发言。
|
||||||
|
|
||||||
|
{background_dialogue_prompt}
|
||||||
{core_dialogue_prompt}
|
{core_dialogue_prompt}
|
||||||
|
|
||||||
{reply_target_block}
|
{reply_target_block}
|
||||||
@@ -678,7 +677,7 @@ class DefaultReplyer:
|
|||||||
return name, result, duration
|
return name, result, duration
|
||||||
|
|
||||||
def build_s4u_chat_history_prompts(
|
def build_s4u_chat_history_prompts(
|
||||||
self, message_list_before_now: List[Dict[str, Any]], target_user_id: str
|
self, message_list_before_now: List[Dict[str, Any]], target_user_id: str, sender: str
|
||||||
) -> Tuple[str, str]:
|
) -> Tuple[str, str]:
|
||||||
"""
|
"""
|
||||||
构建 s4u 风格的分离对话 prompt
|
构建 s4u 风格的分离对话 prompt
|
||||||
@@ -691,7 +690,6 @@ class DefaultReplyer:
|
|||||||
Tuple[str, str]: (核心对话prompt, 背景对话prompt)
|
Tuple[str, str]: (核心对话prompt, 背景对话prompt)
|
||||||
"""
|
"""
|
||||||
core_dialogue_list = []
|
core_dialogue_list = []
|
||||||
background_dialogue_list = []
|
|
||||||
bot_id = str(global_config.bot.qq_account)
|
bot_id = str(global_config.bot.qq_account)
|
||||||
|
|
||||||
# 过滤消息:分离bot和目标用户的对话 vs 其他用户的对话
|
# 过滤消息:分离bot和目标用户的对话 vs 其他用户的对话
|
||||||
@@ -703,41 +701,53 @@ class DefaultReplyer:
|
|||||||
if (msg_user_id == bot_id and reply_to_user_id == target_user_id) or msg_user_id == target_user_id:
|
if (msg_user_id == bot_id and reply_to_user_id == target_user_id) or msg_user_id == target_user_id:
|
||||||
# bot 和目标用户的对话
|
# bot 和目标用户的对话
|
||||||
core_dialogue_list.append(msg_dict)
|
core_dialogue_list.append(msg_dict)
|
||||||
else:
|
|
||||||
# 其他用户的对话
|
|
||||||
background_dialogue_list.append(msg_dict)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"处理消息记录时出错: {msg_dict}, 错误: {e}")
|
logger.error(f"处理消息记录时出错: {msg_dict}, 错误: {e}")
|
||||||
|
|
||||||
# 构建背景对话 prompt
|
# 构建背景对话 prompt
|
||||||
background_dialogue_prompt = ""
|
all_dialogue_prompt = ""
|
||||||
if background_dialogue_list:
|
if message_list_before_now:
|
||||||
latest_25_msgs = background_dialogue_list[-int(global_config.chat.max_context_size * 0.5) :]
|
latest_25_msgs = message_list_before_now[-int(global_config.chat.max_context_size) :]
|
||||||
background_dialogue_prompt_str = build_readable_messages(
|
all_dialogue_prompt_str = build_readable_messages(
|
||||||
latest_25_msgs,
|
latest_25_msgs,
|
||||||
replace_bot_name=True,
|
replace_bot_name=True,
|
||||||
timestamp_mode="normal",
|
timestamp_mode="normal",
|
||||||
truncate=True,
|
truncate=True,
|
||||||
)
|
)
|
||||||
background_dialogue_prompt = f"这是其他用户的发言:\n{background_dialogue_prompt_str}"
|
all_dialogue_prompt = f"所有用户的发言:\n{all_dialogue_prompt_str}"
|
||||||
|
|
||||||
# 构建核心对话 prompt
|
# 构建核心对话 prompt
|
||||||
core_dialogue_prompt = ""
|
core_dialogue_prompt = ""
|
||||||
if core_dialogue_list:
|
if core_dialogue_list:
|
||||||
core_dialogue_list = core_dialogue_list[-int(global_config.chat.max_context_size * 2) :] # 限制消息数量
|
# 检查最新五条消息中是否包含bot自己说的消息
|
||||||
|
latest_5_messages = core_dialogue_list[-5:] if len(core_dialogue_list) >= 5 else core_dialogue_list
|
||||||
|
has_bot_message = any(str(msg.get("user_id")) == bot_id for msg in latest_5_messages)
|
||||||
|
|
||||||
|
# logger.info(f"最新五条消息:{latest_5_messages}")
|
||||||
|
# logger.info(f"最新五条消息中是否包含bot自己说的消息:{has_bot_message}")
|
||||||
|
|
||||||
|
# 如果最新五条消息中不包含bot的消息,则返回空字符串
|
||||||
|
if not has_bot_message:
|
||||||
|
core_dialogue_prompt = ""
|
||||||
|
else:
|
||||||
|
core_dialogue_list = core_dialogue_list[-int(global_config.chat.max_context_size * 2) :] # 限制消息数量
|
||||||
|
|
||||||
|
core_dialogue_prompt_str = build_readable_messages(
|
||||||
|
core_dialogue_list,
|
||||||
|
replace_bot_name=True,
|
||||||
|
merge_messages=False,
|
||||||
|
timestamp_mode="normal_no_YMD",
|
||||||
|
read_mark=0.0,
|
||||||
|
truncate=True,
|
||||||
|
show_actions=True,
|
||||||
|
)
|
||||||
|
core_dialogue_prompt = f"""--------------------------------
|
||||||
|
这是你和{sender}的对话,你们正在交流中:
|
||||||
|
{core_dialogue_prompt_str}
|
||||||
|
--------------------------------
|
||||||
|
"""
|
||||||
|
|
||||||
core_dialogue_prompt_str = build_readable_messages(
|
return core_dialogue_prompt, all_dialogue_prompt
|
||||||
core_dialogue_list,
|
|
||||||
replace_bot_name=True,
|
|
||||||
merge_messages=False,
|
|
||||||
timestamp_mode="normal",
|
|
||||||
read_mark=0.0,
|
|
||||||
truncate=True,
|
|
||||||
show_actions=True,
|
|
||||||
)
|
|
||||||
core_dialogue_prompt = core_dialogue_prompt_str
|
|
||||||
|
|
||||||
return core_dialogue_prompt, background_dialogue_prompt
|
|
||||||
|
|
||||||
def build_mai_think_context(
|
def build_mai_think_context(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -1250,6 +1250,10 @@ async def get_person_id_list(messages: List[Dict[str, Any]]) -> List[str]:
|
|||||||
# 检查必要信息是否存在 且 不是机器人自己
|
# 检查必要信息是否存在 且 不是机器人自己
|
||||||
if not all([platform, user_id]) or user_id == global_config.bot.qq_account:
|
if not all([platform, user_id]) or user_id == global_config.bot.qq_account:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 添加空值检查,防止 platform 为 None 时出错
|
||||||
|
if platform is None:
|
||||||
|
platform = "unknown"
|
||||||
|
|
||||||
if person_id := PersonInfoManager.get_person_id(platform, user_id):
|
if person_id := PersonInfoManager.get_person_id(platform, user_id):
|
||||||
person_ids_set.add(person_id)
|
person_ids_set.add(person_id)
|
||||||
|
|||||||
@@ -194,6 +194,7 @@ class SmartPromptBuilder:
|
|||||||
core_dialogue, background_dialogue = await self._build_s4u_chat_history_prompts(
|
core_dialogue, background_dialogue = await self._build_s4u_chat_history_prompts(
|
||||||
params.message_list_before_now_long,
|
params.message_list_before_now_long,
|
||||||
params.target_user_info.get("user_id") if params.target_user_info else "",
|
params.target_user_info.get("user_id") if params.target_user_info else "",
|
||||||
|
params.sender
|
||||||
)
|
)
|
||||||
|
|
||||||
context_data["core_dialogue_prompt"] = core_dialogue
|
context_data["core_dialogue_prompt"] = core_dialogue
|
||||||
@@ -208,11 +209,10 @@ class SmartPromptBuilder:
|
|||||||
{params.chat_talking_prompt_short}"""
|
{params.chat_talking_prompt_short}"""
|
||||||
|
|
||||||
async def _build_s4u_chat_history_prompts(
|
async def _build_s4u_chat_history_prompts(
|
||||||
self, message_list_before_now: List[Dict[str, Any]], target_user_id: str
|
self, message_list_before_now: List[Dict[str, Any]], target_user_id: str, sender: str
|
||||||
) -> Tuple[str, str]:
|
) -> Tuple[str, str]:
|
||||||
"""构建S4U风格的分离对话prompt - 完整实现"""
|
"""构建S4U风格的分离对话prompt - 完整实现"""
|
||||||
core_dialogue_list = []
|
core_dialogue_list = []
|
||||||
background_dialogue_list = []
|
|
||||||
bot_id = str(global_config.bot.qq_account)
|
bot_id = str(global_config.bot.qq_account)
|
||||||
|
|
||||||
# 过滤消息:分离bot和目标用户的对话 vs 其他用户的对话
|
# 过滤消息:分离bot和目标用户的对话 vs 其他用户的对话
|
||||||
@@ -224,41 +224,53 @@ class SmartPromptBuilder:
|
|||||||
if (msg_user_id == bot_id and reply_to_user_id == target_user_id) or msg_user_id == target_user_id:
|
if (msg_user_id == bot_id and reply_to_user_id == target_user_id) or msg_user_id == target_user_id:
|
||||||
# bot 和目标用户的对话
|
# bot 和目标用户的对话
|
||||||
core_dialogue_list.append(msg_dict)
|
core_dialogue_list.append(msg_dict)
|
||||||
else:
|
|
||||||
# 其他用户的对话
|
|
||||||
background_dialogue_list.append(msg_dict)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"处理消息记录时出错: {msg_dict}, 错误: {e}")
|
logger.error(f"处理消息记录时出错: {msg_dict}, 错误: {e}")
|
||||||
|
|
||||||
# 构建背景对话 prompt
|
# 构建背景对话 prompt
|
||||||
background_dialogue_prompt = ""
|
all_dialogue_prompt = ""
|
||||||
if background_dialogue_list:
|
if message_list_before_now:
|
||||||
latest_25_msgs = background_dialogue_list[-int(global_config.chat.max_context_size * 0.5) :]
|
latest_25_msgs = message_list_before_now[-int(global_config.chat.max_context_size) :]
|
||||||
background_dialogue_prompt_str = build_readable_messages(
|
all_dialogue_prompt_str = build_readable_messages(
|
||||||
latest_25_msgs,
|
latest_25_msgs,
|
||||||
replace_bot_name=True,
|
replace_bot_name=True,
|
||||||
timestamp_mode="normal",
|
timestamp_mode="normal",
|
||||||
truncate=True,
|
truncate=True,
|
||||||
)
|
)
|
||||||
background_dialogue_prompt = f"这是其他用户的发言:\n{background_dialogue_prompt_str}"
|
all_dialogue_prompt = f"所有用户的发言:\n{all_dialogue_prompt_str}"
|
||||||
|
|
||||||
# 构建核心对话 prompt
|
# 构建核心对话 prompt
|
||||||
core_dialogue_prompt = ""
|
core_dialogue_prompt = ""
|
||||||
if core_dialogue_list:
|
if core_dialogue_list:
|
||||||
core_dialogue_list = core_dialogue_list[-int(global_config.chat.max_context_size * 2) :] # 限制消息数量
|
# 检查最新五条消息中是否包含bot自己说的消息
|
||||||
|
latest_5_messages = core_dialogue_list[-5:] if len(core_dialogue_list) >= 5 else core_dialogue_list
|
||||||
|
has_bot_message = any(str(msg.get("user_id")) == bot_id for msg in latest_5_messages)
|
||||||
|
|
||||||
|
# logger.info(f"最新五条消息:{latest_5_messages}")
|
||||||
|
# logger.info(f"最新五条消息中是否包含bot自己说的消息:{has_bot_message}")
|
||||||
|
|
||||||
|
# 如果最新五条消息中不包含bot的消息,则返回空字符串
|
||||||
|
if not has_bot_message:
|
||||||
|
core_dialogue_prompt = ""
|
||||||
|
else:
|
||||||
|
core_dialogue_list = core_dialogue_list[-int(global_config.chat.max_context_size * 2) :] # 限制消息数量
|
||||||
|
|
||||||
|
core_dialogue_prompt_str = build_readable_messages(
|
||||||
|
core_dialogue_list,
|
||||||
|
replace_bot_name=True,
|
||||||
|
merge_messages=False,
|
||||||
|
timestamp_mode="normal_no_YMD",
|
||||||
|
read_mark=0.0,
|
||||||
|
truncate=True,
|
||||||
|
show_actions=True,
|
||||||
|
)
|
||||||
|
core_dialogue_prompt = f"""--------------------------------
|
||||||
|
这是你和{sender}的对话,你们正在交流中:
|
||||||
|
{core_dialogue_prompt_str}
|
||||||
|
--------------------------------
|
||||||
|
"""
|
||||||
|
|
||||||
core_dialogue_prompt_str = build_readable_messages(
|
return core_dialogue_prompt, all_dialogue_prompt
|
||||||
core_dialogue_list,
|
|
||||||
replace_bot_name=True,
|
|
||||||
merge_messages=False,
|
|
||||||
timestamp_mode="normal",
|
|
||||||
read_mark=0.0,
|
|
||||||
truncate=True,
|
|
||||||
show_actions=True,
|
|
||||||
)
|
|
||||||
core_dialogue_prompt = core_dialogue_prompt_str
|
|
||||||
|
|
||||||
return core_dialogue_prompt, background_dialogue_prompt
|
|
||||||
|
|
||||||
async def _build_mai_think_context(self, params: SmartPromptParameters) -> Any:
|
async def _build_mai_think_context(self, params: SmartPromptParameters) -> Any:
|
||||||
"""构建mai_think上下文 - 完全继承DefaultReplyer功能"""
|
"""构建mai_think上下文 - 完全继承DefaultReplyer功能"""
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 1,
|
"manifest_version": 1,
|
||||||
"name": "核心动作插件 (Core Actions)",
|
"name": "Emoji插件 (Emoji Actions)",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "系统核心动作插件,提供基础聊天交互功能,包括回复、不回复、表情包发送和聊天模式切换等核心功能。",
|
"description": "可以发送和管理Emoji",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "MaiBot团队",
|
"name": "SengokuCola",
|
||||||
"url": "https://github.com/MaiM-with-u"
|
"url": "https://github.com/MaiM-with-u"
|
||||||
},
|
},
|
||||||
"license": "GPL-v3.0-or-later",
|
"license": "GPL-v3.0-or-later",
|
||||||
|
|
||||||
"host_application": {
|
"host_application": {
|
||||||
"min_version": "0.8.0"
|
"min_version": "0.10.0"
|
||||||
},
|
},
|
||||||
"homepage_url": "https://github.com/MaiM-with-u/maibot",
|
"homepage_url": "https://github.com/MaiM-with-u/maibot",
|
||||||
"repository_url": "https://github.com/MaiM-with-u/maibot",
|
"repository_url": "https://github.com/MaiM-with-u/maibot",
|
||||||
"keywords": ["core", "chat", "reply", "emoji", "action", "built-in"],
|
"keywords": ["emoji", "action", "built-in"],
|
||||||
"categories": ["Core System", "Chat Management"],
|
"categories": ["Emoji"],
|
||||||
|
|
||||||
"default_locale": "zh-CN",
|
"default_locale": "zh-CN",
|
||||||
"locales_path": "_locales",
|
"locales_path": "_locales",
|
||||||
@@ -24,26 +24,11 @@
|
|||||||
"is_built_in": true,
|
"is_built_in": true,
|
||||||
"plugin_type": "action_provider",
|
"plugin_type": "action_provider",
|
||||||
"components": [
|
"components": [
|
||||||
{
|
|
||||||
"type": "action",
|
|
||||||
"name": "no_reply",
|
|
||||||
"description": "暂时不回复消息,等待新消息或超时"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "action",
|
|
||||||
"name": "reply",
|
|
||||||
"description": "执行基本回复动作"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "action",
|
"type": "action",
|
||||||
"name": "emoji",
|
"name": "emoji",
|
||||||
"description": "发送表情包辅助表达情绪"
|
"description": "发送表情包辅助表达情绪"
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "action",
|
|
||||||
"name": "anti_injector_manager",
|
|
||||||
"description": "管理和监控反注入系统"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
from typing import Tuple
|
|
||||||
|
|
||||||
# 导入新插件系统
|
|
||||||
from src.plugin_system import BaseAction, ActionActivationType, ChatMode
|
|
||||||
|
|
||||||
# 导入依赖的系统组件
|
|
||||||
from src.common.logger import get_logger
|
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger("no_reply_action")
|
|
||||||
|
|
||||||
|
|
||||||
class NoReplyAction(BaseAction):
|
|
||||||
"""不回复动作,支持waiting和breaking两种形式."""
|
|
||||||
|
|
||||||
focus_activation_type = ActionActivationType.ALWAYS # 修复:在focus模式下应该始终可用
|
|
||||||
normal_activation_type = ActionActivationType.ALWAYS # 修复:在normal模式下应该始终可用
|
|
||||||
mode_enable = ChatMode.FOCUS # 修复:只在专注模式下有用
|
|
||||||
parallel_action = False
|
|
||||||
|
|
||||||
# 动作基本信息
|
|
||||||
action_name = "no_reply"
|
|
||||||
action_description = "暂时不回复消息"
|
|
||||||
|
|
||||||
# 动作参数定义
|
|
||||||
action_parameters = {
|
|
||||||
"reason": "不回复的原因",
|
|
||||||
}
|
|
||||||
|
|
||||||
# 动作使用场景
|
|
||||||
action_require = [""]
|
|
||||||
|
|
||||||
# 关联类型
|
|
||||||
associated_types = []
|
|
||||||
|
|
||||||
async def execute(self) -> Tuple[bool, str]:
|
|
||||||
"""执行不回复动作"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
reason = self.action_data.get("reason", "")
|
|
||||||
|
|
||||||
logger.info(f"{self.log_prefix} 选择不回复,原因: {reason}")
|
|
||||||
|
|
||||||
await self.store_action_info(
|
|
||||||
action_build_into_prompt=False,
|
|
||||||
action_prompt_display=reason,
|
|
||||||
action_done=True,
|
|
||||||
)
|
|
||||||
return True, reason
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"{self.log_prefix} 不回复动作执行失败: {e}")
|
|
||||||
exit_reason = f"执行异常: {str(e)}"
|
|
||||||
full_prompt = f"no_reply执行异常: {exit_reason},你思考是否要进行回复"
|
|
||||||
await self.store_action_info(
|
|
||||||
action_build_into_prompt=True,
|
|
||||||
action_prompt_display=full_prompt,
|
|
||||||
action_done=True,
|
|
||||||
)
|
|
||||||
return False, f"不回复动作执行失败: {e}"
|
|
||||||
@@ -16,7 +16,6 @@ from src.plugin_system.base.config_types import ConfigField
|
|||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
|
|
||||||
# 导入API模块 - 标准Python包方式
|
# 导入API模块 - 标准Python包方式
|
||||||
from src.plugins.built_in.core_actions.no_reply import NoReplyAction
|
|
||||||
from src.plugins.built_in.core_actions.reply import ReplyAction
|
from src.plugins.built_in.core_actions.reply import ReplyAction
|
||||||
from src.plugins.built_in.core_actions.emoji import EmojiAction
|
from src.plugins.built_in.core_actions.emoji import EmojiAction
|
||||||
from src.plugins.built_in.core_actions.anti_injector_manager import AntiInjectorStatusCommand
|
from src.plugins.built_in.core_actions.anti_injector_manager import AntiInjectorStatusCommand
|
||||||
@@ -53,10 +52,9 @@ class CoreActionsPlugin(BasePlugin):
|
|||||||
config_schema: dict = {
|
config_schema: dict = {
|
||||||
"plugin": {
|
"plugin": {
|
||||||
"enabled": ConfigField(type=bool, default=True, description="是否启用插件"),
|
"enabled": ConfigField(type=bool, default=True, description="是否启用插件"),
|
||||||
"config_version": ConfigField(type=str, default="0.5.0", description="配置文件版本"),
|
"config_version": ConfigField(type=str, default="0.6.0", description="配置文件版本"),
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
"enable_no_reply": ConfigField(type=bool, default=True, description="是否启用不回复动作"),
|
|
||||||
"enable_reply": ConfigField(type=bool, default=True, description="是否启用基本回复动作"),
|
"enable_reply": ConfigField(type=bool, default=True, description="是否启用基本回复动作"),
|
||||||
"enable_emoji": ConfigField(type=bool, default=True, description="是否启用发送表情/图片动作"),
|
"enable_emoji": ConfigField(type=bool, default=True, description="是否启用发送表情/图片动作"),
|
||||||
"enable_anti_injector_manager": ConfigField(
|
"enable_anti_injector_manager": ConfigField(
|
||||||
@@ -70,8 +68,6 @@ class CoreActionsPlugin(BasePlugin):
|
|||||||
|
|
||||||
# --- 根据配置注册组件 ---
|
# --- 根据配置注册组件 ---
|
||||||
components = []
|
components = []
|
||||||
if self.get_config("components.enable_no_reply", True):
|
|
||||||
components.append((NoReplyAction.get_action_info(), NoReplyAction))
|
|
||||||
if self.get_config("components.enable_reply", True):
|
if self.get_config("components.enable_reply", True):
|
||||||
components.append((ReplyAction.get_action_info(), ReplyAction))
|
components.append((ReplyAction.get_action_info(), ReplyAction))
|
||||||
if self.get_config("components.enable_emoji", True):
|
if self.get_config("components.enable_emoji", True):
|
||||||
|
|||||||
Reference in New Issue
Block a user