Merge branch 'dev' of https://github.com/MoFox-Studio/MoFox_Bot into dev
This commit is contained in:
@@ -194,6 +194,7 @@ class WeatherPrompt(BasePrompt):
|
||||
async def execute(self) -> str:
|
||||
# 在实际应用中,这里可以调用天气API
|
||||
# 为了演示,我们返回一个固定的天气信息
|
||||
logger.info(self.target_prompt_name)
|
||||
return "当前天气:晴朗,温度25°C。"
|
||||
|
||||
|
||||
@@ -202,7 +203,7 @@ class HelloWorldPlugin(BasePlugin):
|
||||
"""一个包含四大核心组件和高级配置功能的入门示例插件。"""
|
||||
|
||||
plugin_name = "hello_world_plugin"
|
||||
enable_plugin = False
|
||||
enable_plugin = True
|
||||
dependencies: ClassVar = []
|
||||
python_dependencies: ClassVar = []
|
||||
config_file_name = "config.toml"
|
||||
|
||||
@@ -1028,8 +1028,8 @@
|
||||
console.log(`准备更新图形: ${nodeCount} 个节点, ${edgeCount} 条边`);
|
||||
|
||||
// 对于超大数据集,进一步限制
|
||||
const MAX_RENDERABLE_NODES = 5000;
|
||||
const MAX_RENDERABLE_EDGES = 10000;
|
||||
const MAX_RENDERABLE_NODES = 10000;
|
||||
const MAX_RENDERABLE_EDGES = 20000;
|
||||
|
||||
let nodesToRender = data.nodes;
|
||||
let edgesToRender = data.edges;
|
||||
|
||||
@@ -2,6 +2,7 @@ import asyncio
|
||||
import copy
|
||||
import re
|
||||
from collections.abc import Awaitable, Callable
|
||||
from typing import List
|
||||
|
||||
from src.chat.utils.prompt_params import PromptParameters
|
||||
from src.common.logger import get_logger
|
||||
@@ -68,7 +69,9 @@ class PromptComponentManager:
|
||||
logger.warning(f"无法为 '{prompt_name}' 加载静态规则,因为它不是一个有效的 Prompt 组件。")
|
||||
continue
|
||||
|
||||
def create_provider(cls: type[BasePrompt]) -> Callable[[PromptParameters], Awaitable[str]]:
|
||||
def create_provider(
|
||||
cls: type[BasePrompt],
|
||||
) -> Callable[[PromptParameters, str], Awaitable[str]]:
|
||||
"""
|
||||
为静态组件创建一个内容提供者闭包 (Content Provider Closure)。
|
||||
|
||||
@@ -80,10 +83,10 @@ class PromptComponentManager:
|
||||
cls (type[BasePrompt]): 需要为其创建提供者的 Prompt 组件类。
|
||||
|
||||
Returns:
|
||||
Callable[[PromptParameters], Awaitable[str]]: 一个符合管理器标准的异步内容提供者。
|
||||
Callable[[PromptParameters, str], Awaitable[str]]: 一个符合管理器标准的异步内容提供者。
|
||||
"""
|
||||
|
||||
async def content_provider(params: PromptParameters) -> str:
|
||||
async def content_provider(params: PromptParameters, target_prompt_name: str) -> str:
|
||||
"""实际执行内容生成的异步函数。"""
|
||||
try:
|
||||
# 从注册表获取最新的组件信息,包括插件配置
|
||||
@@ -92,8 +95,8 @@ class PromptComponentManager:
|
||||
if isinstance(p_info, PromptInfo):
|
||||
plugin_config = component_registry.get_plugin_config(p_info.plugin_name)
|
||||
|
||||
# 实例化组件并执行
|
||||
instance = cls(params=params, plugin_config=plugin_config)
|
||||
# 实例化组件并执行,传入 target_prompt_name
|
||||
instance = cls(params=params, plugin_config=plugin_config, target_prompt_name=target_prompt_name)
|
||||
result = await instance.execute()
|
||||
return str(result) if result is not None else ""
|
||||
except Exception as e:
|
||||
@@ -116,28 +119,29 @@ class PromptComponentManager:
|
||||
async def add_injection_rule(
|
||||
self,
|
||||
prompt_name: str,
|
||||
rule: InjectionRule,
|
||||
rules: List[InjectionRule],
|
||||
content_provider: Callable[..., Awaitable[str]],
|
||||
source: str = "runtime",
|
||||
) -> bool:
|
||||
"""
|
||||
动态添加或更新一条注入规则。
|
||||
动态添加或更新注入规则。
|
||||
|
||||
此方法允许在系统运行时,由外部逻辑(如插件、命令)向管理器中添加新的注入行为。
|
||||
如果已存在同名组件针对同一目标的规则,此方法会覆盖旧规则。
|
||||
|
||||
Args:
|
||||
prompt_name (str): 动态注入组件的唯一名称。
|
||||
rule (InjectionRule): 描述注入行为的规则对象。
|
||||
rules (List[InjectionRule]): 描述注入行为的规则对象列表。
|
||||
content_provider (Callable[..., Awaitable[str]]):
|
||||
一个异步函数,用于在应用注入时动态生成内容。
|
||||
函数签名应为: `async def provider(params: "PromptParameters") -> str`
|
||||
函数签名应为: `async def provider(params: "PromptParameters", target_prompt_name: str) -> str`
|
||||
source (str, optional): 规则的来源标识,默认为 "runtime"。
|
||||
|
||||
Returns:
|
||||
bool: 如果成功添加或更新,则返回 True。
|
||||
"""
|
||||
async with self._lock:
|
||||
for rule in rules:
|
||||
target_rules = self._dynamic_rules.setdefault(rule.target_prompt, {})
|
||||
target_rules[prompt_name] = (rule, content_provider, source)
|
||||
logger.info(f"成功添加/更新注入规则: '{prompt_name}' -> '{rule.target_prompt}' (来源: {source})")
|
||||
@@ -207,7 +211,7 @@ class PromptComponentManager:
|
||||
# 对于非 REMOVE 类型的注入,需要先获取内容
|
||||
if rule.injection_type != InjectionType.REMOVE:
|
||||
try:
|
||||
content = await provider(params)
|
||||
content = await provider(params, target_prompt_name)
|
||||
except Exception as e:
|
||||
logger.error(f"执行规则 '{rule}' (来源: {source}) 的内容提供者时失败: {e}", exc_info=True)
|
||||
continue # 跳过失败的 provider,不中断整个流程
|
||||
@@ -287,53 +291,44 @@ class PromptComponentManager:
|
||||
components = component_registry.get_components_by_type(ComponentType.PROMPT).values()
|
||||
return [info for info in components if isinstance(info, PromptInfo)]
|
||||
|
||||
async def get_full_injection_map(self) -> dict[str, list[dict]]:
|
||||
async def get_injection_info(
|
||||
self,
|
||||
target_prompt: str | None = None,
|
||||
detailed: bool = False,
|
||||
) -> dict[str, list[dict]]:
|
||||
"""
|
||||
获取当前完整的注入映射图。
|
||||
获取注入信息的映射图,可按目标筛选,并可控制信息的详细程度。
|
||||
|
||||
此方法提供了一个系统全局的注入视图,展示了每个核心提示词(target)
|
||||
被哪些注入组件(source)以何种优先级注入。
|
||||
- `get_injection_info()` 返回所有目标的摘要注入信息。
|
||||
- `get_injection_info(target_prompt="...")` 返回指定目标的摘要注入信息。
|
||||
- `get_injection_info(detailed=True)` 返回所有目标的详细注入信息。
|
||||
- `get_injection_info(target_prompt="...", detailed=True)` 返回指定目标的详细注入信息。
|
||||
|
||||
Args:
|
||||
target_prompt (str, optional): 如果指定,仅返回该目标的注入信息。
|
||||
detailed (bool, optional): 如果为 True,则返回包含注入类型和内容的详细信息。
|
||||
默认为 False,返回摘要信息。
|
||||
|
||||
Returns:
|
||||
dict[str, list[dict]]: 一个字典,键是目标提示词名称,
|
||||
值是按优先级排序的注入信息列表。
|
||||
`[{"name": str, "priority": int, "source": str}]`
|
||||
"""
|
||||
injection_map = {}
|
||||
info_map = {}
|
||||
async with self._lock:
|
||||
# 合并所有动态规则的目标和所有核心提示词,确保所有潜在目标都被包含
|
||||
all_targets = set(self._dynamic_rules.keys()) | set(self.get_core_prompts())
|
||||
for target in sorted(all_targets):
|
||||
|
||||
# 如果指定了目标,则只处理该目标
|
||||
targets_to_process = [target_prompt] if target_prompt and target_prompt in all_targets else sorted(all_targets)
|
||||
|
||||
for target in targets_to_process:
|
||||
rules = self._dynamic_rules.get(target, {})
|
||||
if not rules:
|
||||
injection_map[target] = []
|
||||
info_map[target] = []
|
||||
continue
|
||||
|
||||
info_list = []
|
||||
for prompt_name, (rule, _, source) in rules.items():
|
||||
info_list.append({"name": prompt_name, "priority": rule.priority, "source": source})
|
||||
|
||||
# 按优先级排序后存入 map
|
||||
info_list.sort(key=lambda x: x["priority"])
|
||||
injection_map[target] = info_list
|
||||
return injection_map
|
||||
|
||||
async def get_injections_for_prompt(self, target_prompt_name: str) -> list[dict]:
|
||||
"""
|
||||
获取指定核心提示词模板的所有注入信息(包含详细规则)。
|
||||
|
||||
Args:
|
||||
target_prompt_name (str): 目标核心提示词的名称。
|
||||
|
||||
Returns:
|
||||
list[dict]: 一个包含注入规则详细信息的列表,已按优先级排序。
|
||||
"""
|
||||
rules_for_target = self._dynamic_rules.get(target_prompt_name, {})
|
||||
if not rules_for_target:
|
||||
return []
|
||||
|
||||
info_list = []
|
||||
for prompt_name, (rule, _, source) in rules_for_target.items():
|
||||
if detailed:
|
||||
info_list.append(
|
||||
{
|
||||
"name": prompt_name,
|
||||
@@ -343,53 +338,69 @@ class PromptComponentManager:
|
||||
"target_content": rule.target_content,
|
||||
}
|
||||
)
|
||||
else:
|
||||
info_list.append({"name": prompt_name, "priority": rule.priority, "source": source})
|
||||
|
||||
info_list.sort(key=lambda x: x["priority"])
|
||||
return info_list
|
||||
info_map[target] = info_list
|
||||
return info_map
|
||||
|
||||
def get_all_dynamic_rules(self) -> dict[str, dict[str, "InjectionRule"]]:
|
||||
def get_injection_rules(
|
||||
self,
|
||||
target_prompt: str | None = None,
|
||||
component_name: str | None = None,
|
||||
) -> dict[str, dict[str, "InjectionRule"]]:
|
||||
"""
|
||||
获取所有当前的动态注入规则,以 InjectionRule 对象形式返回。
|
||||
获取动态注入规则,可通过目标或组件名称进行筛选。
|
||||
|
||||
此方法返回一个深拷贝的规则副本,隐藏了 `content_provider` 等内部实现细节。
|
||||
适合用于展示或序列化当前的规则配置。
|
||||
- 不提供任何参数时,返回所有规则。
|
||||
- 提供 `target_prompt` 时,仅返回注入到该目标的规则。
|
||||
- 提供 `component_name` 时,仅返回由该组件定义的所有规则。
|
||||
- 同时提供 `target_prompt` 和 `component_name` 时,返回满足两个条件的规则。
|
||||
|
||||
Args:
|
||||
target_prompt (str, optional): 按目标核心提示词名称筛选。
|
||||
component_name (str, optional): 按注入组件名称筛选。
|
||||
|
||||
Returns:
|
||||
dict[str, dict[str, InjectionRule]]: 一个深拷贝的规则字典。
|
||||
结构: { "target_prompt": { "component_name": InjectionRule } }
|
||||
"""
|
||||
rules_copy = {}
|
||||
for target, rules in self._dynamic_rules.items():
|
||||
target_copy = {name: rule for name, (rule, _, _) in rules.items()}
|
||||
# 筛选目标
|
||||
targets_to_check = [target_prompt] if target_prompt else self._dynamic_rules.keys()
|
||||
|
||||
for target in targets_to_check:
|
||||
if target not in self._dynamic_rules:
|
||||
continue
|
||||
|
||||
rules_for_target = self._dynamic_rules[target]
|
||||
target_copy = {}
|
||||
|
||||
# 筛选组件
|
||||
if component_name:
|
||||
if component_name in rules_for_target:
|
||||
rule, _, _ = rules_for_target[component_name]
|
||||
target_copy[component_name] = rule
|
||||
else:
|
||||
for name, (rule, _, _) in rules_for_target.items():
|
||||
target_copy[name] = rule
|
||||
|
||||
if target_copy:
|
||||
rules_copy[target] = target_copy
|
||||
return copy.deepcopy(rules_copy)
|
||||
|
||||
def get_rules_for_target(self, target_prompt: str) -> dict[str, InjectionRule]:
|
||||
"""
|
||||
获取所有注入到指定核心提示词的动态规则。
|
||||
|
||||
Args:
|
||||
target_prompt (str): 目标核心提示词的名称。
|
||||
|
||||
Returns:
|
||||
dict[str, InjectionRule]: 一个字典,键是注入组件的名称,值是 `InjectionRule` 对象。
|
||||
如果找不到任何注入到该目标的规则,则返回一个空字典。
|
||||
"""
|
||||
target_rules = self._dynamic_rules.get(target_prompt, {})
|
||||
return {name: copy.deepcopy(rule_info[0]) for name, rule_info in target_rules.items()}
|
||||
|
||||
def get_rules_by_component(self, component_name: str) -> dict[str, InjectionRule]:
|
||||
"""
|
||||
获取由指定的单个注入组件定义的所有动态规则。
|
||||
|
||||
Args:
|
||||
component_name (str): 注入组件的名称。
|
||||
|
||||
Returns:
|
||||
dict[str, InjectionRule]: 一个字典,键是目标核心提示词的名称,值是 `InjectionRule` 对象。
|
||||
如果该组件没有定义任何注入规则,则返回一个空字典。
|
||||
"""
|
||||
# 如果是按组件筛选且未指定目标,则需遍历所有目标
|
||||
if component_name and not target_prompt:
|
||||
found_rules = {}
|
||||
for target, rules in self._dynamic_rules.items():
|
||||
if component_name in rules:
|
||||
rule_info = rules[component_name]
|
||||
found_rules[target] = copy.deepcopy(rule_info[0])
|
||||
return found_rules
|
||||
rule, _, _ = rules[component_name]
|
||||
if target not in found_rules:
|
||||
found_rules[target] = {}
|
||||
found_rules[target][component_name] = rule
|
||||
return copy.deepcopy(found_rules)
|
||||
|
||||
return copy.deepcopy(rules_copy)
|
||||
|
||||
|
||||
# 创建全局单例 (Singleton)
|
||||
|
||||
@@ -411,6 +411,7 @@ class StatisticOutputTask(AsyncTask):
|
||||
(REQ_CNT_BY_USER, "user"),
|
||||
(REQ_CNT_BY_MODEL, "model"),
|
||||
(REQ_CNT_BY_MODULE, "module"),
|
||||
(REQ_CNT_BY_PROVIDER, "provider"),
|
||||
]:
|
||||
time_cost_key = f"TIME_COST_BY_{items.upper()}"
|
||||
avg_key = f"AVG_TIME_COST_BY_{items.upper()}"
|
||||
|
||||
@@ -84,12 +84,12 @@ def format_memory_for_prompt(memory: Memory, include_metadata: bool = False) ->
|
||||
if object_node is not None:
|
||||
# 有完整的主谓宾
|
||||
if core_relation:
|
||||
parts.append(f"{subject_text}{topic_text}{core_relation}{object_node.content}")
|
||||
parts.append(f"{subject_text}-{topic_text}{core_relation}{object_node.content}")
|
||||
else:
|
||||
parts.append(f"{subject_text}{topic_text}{object_node.content}")
|
||||
parts.append(f"{subject_text}-{topic_text}{object_node.content}")
|
||||
else:
|
||||
# 只有主谓
|
||||
parts.append(f"{subject_text}{topic_text}")
|
||||
parts.append(f"{subject_text}-{topic_text}")
|
||||
|
||||
# 添加属性信息
|
||||
if attributes:
|
||||
|
||||
@@ -27,22 +27,24 @@ class BasePrompt(ABC):
|
||||
# 定义此组件希望如何注入到核心Prompt中
|
||||
# 这是一个 InjectionRule 对象的列表,可以实现复杂的注入逻辑
|
||||
# 例如: [InjectionRule(target_prompt="planner_prompt", injection_type=InjectionType.APPEND, priority=50)]
|
||||
injection_rules: ClassVar[list[InjectionRule] ] = []
|
||||
injection_rules: ClassVar[list[InjectionRule]] = []
|
||||
"""定义注入规则的列表"""
|
||||
|
||||
# 旧的注入点定义,用于向后兼容。如果定义了这个,它将被自动转换为 injection_rules。
|
||||
injection_point: str | list[str] | None = None
|
||||
"""[已废弃] 要注入的目标Prompt名称或列表,请使用 injection_rules"""
|
||||
|
||||
def __init__(self, params: PromptParameters, plugin_config: dict | None = None):
|
||||
def __init__(self, params: PromptParameters, plugin_config: dict | None = None, target_prompt_name: str | None = None):
|
||||
"""初始化Prompt组件
|
||||
|
||||
Args:
|
||||
params: 统一提示词参数,包含所有构建提示词所需的上下文信息。
|
||||
plugin_config: 插件配置字典。
|
||||
target_prompt_name: 在应用注入时,当前注入的目标提示词名称。
|
||||
"""
|
||||
self.params = params
|
||||
self.plugin_config = plugin_config or {}
|
||||
self.target_prompt_name = target_prompt_name
|
||||
self.log_prefix = "[PromptComponent]"
|
||||
|
||||
logger.debug(f"{self.log_prefix} Prompt组件 '{self.prompt_name}' 初始化完成")
|
||||
|
||||
@@ -156,9 +156,6 @@ class ChatterActionPlanner:
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Focus模式 - 处理消息 {message.message_id} 失败: {e}")
|
||||
message.interest_value = 0.0
|
||||
message.should_reply = False
|
||||
message.should_act = False
|
||||
|
||||
# 2. 检查兴趣度是否达到非回复动作阈值
|
||||
non_reply_action_interest_threshold = global_config.affinity_flow.non_reply_action_interest_threshold
|
||||
@@ -202,9 +199,9 @@ class ChatterActionPlanner:
|
||||
filtered_plan = await plan_filter.filter(initial_plan)
|
||||
|
||||
# 检查reply动作是否可用
|
||||
reply_action_available = "reply" in available_actions or "respond" in available_actions
|
||||
if filtered_plan.decided_actions and not reply_action_available:
|
||||
logger.info("Focus模式 - 回复动作不可用,移除所有回复相关动作")
|
||||
has_reply_action = "reply" in available_actions or "respond" in available_actions
|
||||
if filtered_plan.decided_actions and has_reply_action and reply_not_available:
|
||||
logger.info("Focus模式 - 未达到回复动作阈值,移除所有回复相关动作")
|
||||
filtered_plan.decided_actions = [
|
||||
action for action in filtered_plan.decided_actions
|
||||
if action.action_type not in ["reply", "respond"]
|
||||
|
||||
@@ -6,6 +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,
|
||||
@@ -13,6 +14,7 @@ from src.plugin_system.apis import (
|
||||
from src.plugin_system.apis.logging_api import get_logger
|
||||
from src.plugin_system.apis.permission_api import permission_api
|
||||
from src.plugin_system.apis.plugin_register_api import register_plugin
|
||||
from src.plugin_system.apis.unified_scheduler import TriggerType, unified_scheduler
|
||||
from src.plugin_system.base.base_plugin import BasePlugin
|
||||
from src.plugin_system.base.command_args import CommandArgs
|
||||
from src.plugin_system.base.component_types import (
|
||||
@@ -23,7 +25,6 @@ from src.plugin_system.base.component_types import (
|
||||
from src.plugin_system.base.config_types import ConfigField
|
||||
from src.plugin_system.base.plus_command import PlusCommand
|
||||
from src.plugin_system.utils.permission_decorators import require_permission
|
||||
from src.plugin_system.apis.unified_scheduler import TriggerType, unified_scheduler
|
||||
|
||||
logger = get_logger("SystemManagement")
|
||||
|
||||
@@ -59,6 +60,8 @@ class SystemCommand(PlusCommand):
|
||||
await self._handle_schedule_commands(remaining_args)
|
||||
elif subcommand in ["help", "帮助"]:
|
||||
await self._show_help("all")
|
||||
elif subcommand in ["prompt","提示词"]:
|
||||
await self._handle_prompt_commands(remaining_args)
|
||||
else:
|
||||
await self.send_text(f"❌ 未知的子命令: {subcommand}\n使用 /system help 查看帮助")
|
||||
|
||||
@@ -266,7 +269,7 @@ class SystemCommand(PlusCommand):
|
||||
@require_permission("prompt.view", deny_message="❌ 你没有查看提示词注入信息的权限")
|
||||
async def _show_injection_map(self):
|
||||
"""显示全局注入关系图"""
|
||||
injection_map = await prompt_component_manager.get_full_injection_map()
|
||||
injection_map = await prompt_component_manager.get_injection_info()
|
||||
if not injection_map:
|
||||
await self.send_text("📊 当前没有任何提示词注入关系")
|
||||
return
|
||||
@@ -312,7 +315,8 @@ class SystemCommand(PlusCommand):
|
||||
@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)
|
||||
injection_info = await prompt_component_manager.get_injection_info(target_prompt=target_name, detailed=True)
|
||||
injections = injection_info.get(target_name, [])
|
||||
|
||||
core_prompts = prompt_component_manager.get_core_prompts()
|
||||
if target_name not in core_prompts:
|
||||
|
||||
Reference in New Issue
Block a user