This commit is contained in:
Windpicker-owo
2025-11-12 13:38:12 +08:00
36 changed files with 934 additions and 626 deletions

View File

@@ -362,7 +362,7 @@ class ChatterPlanFilter:
return "最近没有聊天内容。", "没有未读消息。", []
stream_context = chat_stream.context_manager
# 获取真正的已读和未读消息
read_messages = stream_context.context.history_messages # 已读消息存储在history_messages中
if not read_messages:
@@ -652,30 +652,30 @@ class ChatterPlanFilter:
if not action_info:
logger.debug(f"动作 {action_name} 不在可用动作列表中,保留所有参数")
return action_data
# 获取该动作定义的合法参数
defined_params = set(action_info.action_parameters.keys())
# 合法参数集合
valid_params = defined_params
# 过滤参数
filtered_data = {}
removed_params = []
for key, value in action_data.items():
if key in valid_params:
filtered_data[key] = value
else:
removed_params.append(key)
# 记录被移除的参数
if removed_params:
logger.info(
f"🧹 [参数过滤] 动作 '{action_name}' 移除了多余参数: {removed_params}. "
f"合法参数: {sorted(valid_params)}"
)
return filtered_data
def _filter_no_actions(self, action_list: list[ActionPlannerInfo]) -> list[ActionPlannerInfo]:

View File

@@ -545,14 +545,14 @@ async def execute_proactive_thinking(stream_id: str):
# 获取或创建该聊天流的执行锁
if stream_id not in _execution_locks:
_execution_locks[stream_id] = asyncio.Lock()
lock = _execution_locks[stream_id]
# 尝试获取锁,如果已被占用则跳过本次执行(防止重复)
if lock.locked():
logger.warning(f"⚠️ 主动思考跳过:聊天流 {stream_id} 已有正在执行的主动思考任务")
return
async with lock:
logger.debug(f"🤔 开始主动思考 {stream_id}")
@@ -563,13 +563,13 @@ async def execute_proactive_thinking(stream_id: str):
from src.chat.message_receive.chat_stream import get_chat_manager
chat_manager = get_chat_manager()
chat_stream = await chat_manager.get_stream(stream_id)
if chat_stream and chat_stream.context_manager.context.is_chatter_processing:
logger.warning(f"⚠️ 主动思考跳过:聊天流 {stream_id} 的 chatter 正在处理消息")
return
except Exception as e:
logger.warning(f"检查 chatter 处理状态时出错: {e},继续执行")
# 0.1 检查白名单/黑名单
# 从 stream_id 获取 stream_config 字符串进行验证
try:

View File

@@ -31,4 +31,4 @@ __plugin_meta__ = PluginMetadata(
# 导入插件主类
from .plugin import AntiInjectionPlugin
__all__ = ["__plugin_meta__", "AntiInjectionPlugin"]
__all__ = ["AntiInjectionPlugin", "__plugin_meta__"]

View File

@@ -8,8 +8,8 @@ import time
from src.chat.security.interfaces import (
SecurityAction,
SecurityCheckResult,
SecurityChecker,
SecurityCheckResult,
SecurityLevel,
)
from src.common.logger import get_logger

View File

@@ -4,7 +4,7 @@
处理检测结果,执行相应的动作(允许/监控/加盾/阻止/反击)。
"""
from src.chat.security.interfaces import SecurityAction, SecurityCheckResult
from src.chat.security.interfaces import SecurityCheckResult
from src.common.logger import get_logger
from .counter_attack import CounterAttackGenerator

View File

@@ -64,15 +64,15 @@ class CoreActionsPlugin(BasePlugin):
# --- 根据配置注册组件 ---
components: ClassVar = []
# 注册 reply 动作
if self.get_config("components.enable_reply", True):
components.append((ReplyAction.get_action_info(), ReplyAction))
# 注册 respond 动作
if self.get_config("components.enable_respond", True):
components.append((RespondAction.get_action_info(), RespondAction))
# 注册 emoji 动作
if self.get_config("components.enable_emoji", True):
components.append((EmojiAction.get_action_info(), EmojiAction))

View File

@@ -22,23 +22,23 @@ class ReplyAction(BaseAction):
- 专注于理解和回应单条消息的具体内容
- 适合 Focus 模式下的精准回复
"""
# 动作基本信息
action_name = "reply"
action_description = "针对特定消息进行精准回复。深度理解并回应单条消息的具体内容。需要指定目标消息ID。"
# 激活设置
activation_type = ActionActivationType.ALWAYS # 回复动作总是可用
mode_enable = ChatMode.ALL # 在所有模式下都可用
parallel_action = False # 回复动作不能与其他动作并行
# 动作参数定义
action_parameters: ClassVar = {
"target_message_id": "要回复的目标消息ID必需来自未读消息的 <m...> 标签)",
"content": "回复的具体内容可选由LLM生成",
"should_quote_reply": "是否引用原消息可选true/false默认false。群聊中回复较早消息或需要明确指向时使用true",
}
# 动作使用场景
action_require: ClassVar = [
"需要针对特定消息进行精准回复时使用",
@@ -48,10 +48,10 @@ class ReplyAction(BaseAction):
"群聊中需要明确回应某个特定用户或问题时使用",
"关注单条消息的具体内容和上下文细节",
]
# 关联类型
associated_types: ClassVar[list[str]] = ["text"]
async def execute(self) -> tuple[bool, str]:
"""执行reply动作
@@ -70,21 +70,21 @@ class RespondAction(BaseAction):
- 适合对于群聊消息下的宏观回应
- 避免与单一用户深度对话而忽略其他用户的消息
"""
# 动作基本信息
action_name = "respond"
action_description = "统一回应所有未读消息。理解整体对话动态和话题走向,生成连贯的回复。无需指定目标消息。"
# 激活设置
activation_type = ActionActivationType.ALWAYS # 回应动作总是可用
mode_enable = ChatMode.ALL # 在所有模式下都可用
parallel_action = False # 回应动作不能与其他动作并行
# 动作参数定义
action_parameters: ClassVar = {
"content": "回复的具体内容可选由LLM生成",
}
# 动作使用场景
action_require: ClassVar = [
"需要统一回应多条未读消息时使用Normal 模式专用)",
@@ -94,10 +94,10 @@ class RespondAction(BaseAction):
"适合群聊中的自然对话流,无需精确指向特定消息",
"可以同时回应多个话题或参与者",
]
# 关联类型
associated_types: ClassVar[list[str]] = ["text"]
async def execute(self) -> tuple[bool, str]:
"""执行respond动作

View File

@@ -6,10 +6,10 @@
import asyncio
import base64
import datetime
import filetype
from collections.abc import Callable
import aiohttp
import filetype
from maim_message import UserInfo
from src.chat.message_receive.chat_stream import get_chat_manager

View File

@@ -6,7 +6,7 @@
import re
from typing import ClassVar
from src.chat.utils.prompt_component_manager import prompt_component_manager
from src.plugin_system.apis import (
plugin_manage_api,
)
@@ -74,6 +74,7 @@ class SystemCommand(PlusCommand):
• `/system permission` - 权限管理
• `/system plugin` - 插件管理
• `/system schedule` - 定时任务管理
• `/system prompt` - 提示词注入管理
"""
elif target == "schedule":
help_text = """📅 定时任务管理帮助
@@ -113,8 +114,17 @@ class SystemCommand(PlusCommand):
• /system permission nodes [插件名] - 查看权限节点
• /system permission allnodes - 查看所有权限节点详情
"""
await self.send_text(help_text)
elif target == "prompt":
help_text = """📝 提示词注入管理帮助
🔎 查询命令 (需要 `system.prompt.view` 权限):
• `/system prompt help` - 显示此帮助
• `/system prompt map` - 查看全局注入关系图
• `/system prompt targets` - 列出所有可被注入的核心提示词
• `/system prompt components` - 列出所有已注册的提示词组件
• `/system prompt info <目标名>` - 查看特定核心提示词的注入详情
"""
await self.send_text(help_text)
# =================================================================
# Plugin Management Section
@@ -231,6 +241,101 @@ class SystemCommand(PlusCommand):
else:
await self.send_text(f"❌ 恢复任务失败: `{schedule_id}`")
# =================================================================
# Prompt Management Section
# =================================================================
async def _handle_prompt_commands(self, args: list[str]):
"""处理提示词管理相关命令"""
if not args or args[0].lower() in ["help", "帮助"]:
await self._show_help("prompt")
return
action = args[0].lower()
remaining_args = args[1:]
if action in ["map", "关系图"]:
await self._show_injection_map()
elif action in ["targets", "目标"]:
await self._list_core_prompts()
elif action in ["components", "组件"]:
await self._list_prompt_components()
elif action in ["info", "详情"] and remaining_args:
await self._get_prompt_injection_info(remaining_args[0])
else:
await self.send_text("❌ 提示词管理命令不合法\n使用 /system prompt help 查看帮助")
@require_permission("prompt.view", deny_message="❌ 你没有查看提示词注入信息的权限")
async def _show_injection_map(self):
"""显示全局注入关系图"""
injection_map = await prompt_component_manager.get_full_injection_map()
if not injection_map:
await self.send_text("📊 当前没有任何提示词注入关系")
return
response_parts = ["📊 全局提示词注入关系图:\n"]
for target, injections in injection_map.items():
if injections:
response_parts.append(f"🎯 **{target}** (注入源):")
for inj in injections:
source_tag = f"({inj['source']})" if inj['source'] != 'static_default' else ''
response_parts.append(f" ⎿ `{inj['name']}` (优先级: {inj['priority']}) {source_tag}")
else:
response_parts.append(f"🎯 **{target}** (无注入)")
await self._send_long_message("\n".join(response_parts))
@require_permission("prompt.view", deny_message="❌ 你没有查看提示词注入信息的权限")
async def _list_core_prompts(self):
"""列出所有可注入的核心提示词"""
targets = prompt_component_manager.get_core_prompts()
if not targets:
await self.send_text("🎯 当前没有可注入的核心提示词")
return
response = "🎯 所有可注入的核心提示词:\n" + "\n".join([f"• `{name}`" for name in targets])
await self.send_text(response)
@require_permission("prompt.view", deny_message="❌ 你没有查看提示词注入信息的权限")
async def _list_prompt_components(self):
"""列出所有已注册的提示词组件"""
components = prompt_component_manager.get_registered_prompt_component_info()
if not components:
await self.send_text("🧩 当前没有已注册的提示词组件")
return
response_parts = [f"🧩 已注册的提示词组件 (共 {len(components)} 个):"]
for comp in components:
response_parts.append(f"• `{comp.name}` (来自: `{comp.plugin_name}`)")
await self._send_long_message("\n".join(response_parts))
@require_permission("prompt.view", deny_message="❌ 你没有查看提示词注入信息的权限")
async def _get_prompt_injection_info(self, target_name: str):
"""获取特定核心提示词的注入详情"""
injections = await prompt_component_manager.get_injections_for_prompt(target_name)
core_prompts = prompt_component_manager.get_core_prompts()
if target_name not in core_prompts:
await self.send_text(f"❌ 找不到核心提示词: `{target_name}`")
return
if not injections:
await self.send_text(f"🎯 核心提示词 `{target_name}` 当前没有被任何组件注入。")
return
response_parts = [f"🔎 核心提示词 `{target_name}` 的注入详情:"]
for inj in injections:
response_parts.append(
f" • **`{inj['name']}`** (优先级: {inj['priority']})"
)
response_parts.append(f" - 来源: `{inj['source']}`")
response_parts.append(f" - 类型: `{inj['injection_type']}`")
if inj.get('target_content'):
response_parts.append(f" - 操作目标: `{inj['target_content']}`")
await self.send_text("\n".join(response_parts))
# =================================================================
# Permission Management Section
# =================================================================