This commit is contained in:
tt-P607
2025-11-13 19:43:18 +08:00
8 changed files with 124 additions and 108 deletions

View File

@@ -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"

View File

@@ -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;

View File

@@ -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)

View File

@@ -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()}"

View File

@@ -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:

View File

@@ -34,15 +34,17 @@ class BasePrompt(ABC):
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}' 初始化完成")

View File

@@ -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"]

View File

@@ -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: