feat(plugin): 引入Prompt组件系统以实现动态Prompt注入
引入了一个新的插件组件类型 `BasePrompt`,允许插件动态地向核心Prompt模板中注入额外的上下文信息。该系统旨在提高Prompt的可扩展性和可定制性,使得开发者可以在不修改核心代码的情况下,通过插件来丰富和调整模型的行为。 主要变更包括: - **`BasePrompt` 基类**: 定义了Prompt组件的标准接口,包括 `execute` 方法用于生成注入内容,以及 `injection_point` 属性用于指定目标Prompt。 - **`PromptComponentManager`**: 一个新的管理器,负责注册、分类和执行所有 `BasePrompt` 组件。它会在构建核心Prompt时,自动查找并执行相关组件,将其输出拼接到主Prompt内容之前。 - **核心Prompt逻辑更新**: `src.chat.utils.prompt.Prompt` 类现在会调用 `PromptComponentManager` 来获取并注入组件内容。 - **插件系统集成**: `ComponentRegistry` 和 `PluginManager` 已更新,以支持 `BasePrompt` 组件的注册、管理和统计。 - **示例插件更新**: `hello_world_plugin` 中增加了一个 `WeatherPrompt` 示例,演示了如何创建和注册一个新的Prompt组件。 - **代码重构**: 将 `PromptParameters` 类从 `prompt.py` 移动到独立的 `prompt_params.py` 文件中,以改善模块化和解决循环依赖问题。
This commit is contained in:
committed by
Windpicker-owo
parent
baefca2115
commit
0917318cbd
@@ -26,6 +26,7 @@ from .base import (
|
||||
ActionInfo,
|
||||
BaseAction,
|
||||
BaseCommand,
|
||||
BasePrompt,
|
||||
BaseEventHandler,
|
||||
BasePlugin,
|
||||
BaseTool,
|
||||
@@ -64,6 +65,7 @@ __all__ = [
|
||||
"BaseEventHandler",
|
||||
# 基础类
|
||||
"BasePlugin",
|
||||
"BasePrompt",
|
||||
"BaseTool",
|
||||
"ChatMode",
|
||||
"ChatType",
|
||||
|
||||
@@ -8,6 +8,7 @@ from .base_action import BaseAction
|
||||
from .base_command import BaseCommand
|
||||
from .base_events_handler import BaseEventHandler
|
||||
from .base_plugin import BasePlugin
|
||||
from .base_prompt import BasePrompt
|
||||
from .base_tool import BaseTool
|
||||
from .command_args import CommandArgs
|
||||
from .component_types import (
|
||||
@@ -37,6 +38,7 @@ __all__ = [
|
||||
"BaseCommand",
|
||||
"BaseEventHandler",
|
||||
"BasePlugin",
|
||||
"BasePrompt",
|
||||
"BaseTool",
|
||||
"ChatMode",
|
||||
"ChatType",
|
||||
|
||||
@@ -8,6 +8,7 @@ from src.plugin_system.base.component_types import (
|
||||
EventHandlerInfo,
|
||||
InterestCalculatorInfo,
|
||||
PlusCommandInfo,
|
||||
PromptInfo,
|
||||
ToolInfo,
|
||||
)
|
||||
|
||||
@@ -15,6 +16,7 @@ from .base_action import BaseAction
|
||||
from .base_command import BaseCommand
|
||||
from .base_events_handler import BaseEventHandler
|
||||
from .base_interest_calculator import BaseInterestCalculator
|
||||
from .base_prompt import BasePrompt
|
||||
from .base_tool import BaseTool
|
||||
from .plugin_base import PluginBase
|
||||
from .plus_command import PlusCommand
|
||||
@@ -80,6 +82,13 @@ class BasePlugin(PluginBase):
|
||||
logger.warning("EventHandler的get_info逻辑尚未实现")
|
||||
return None
|
||||
|
||||
elif component_type == ComponentType.PROMPT:
|
||||
if hasattr(component_class, "get_prompt_info"):
|
||||
return component_class.get_prompt_info()
|
||||
else:
|
||||
logger.warning(f"Prompt类 {component_class.__name__} 缺少 get_prompt_info 方法")
|
||||
return None
|
||||
|
||||
else:
|
||||
logger.error(f"不支持的组件类型: {component_type}")
|
||||
return None
|
||||
@@ -109,6 +118,7 @@ class BasePlugin(PluginBase):
|
||||
| tuple[EventHandlerInfo, type[BaseEventHandler]]
|
||||
| tuple[ToolInfo, type[BaseTool]]
|
||||
| tuple[InterestCalculatorInfo, type[BaseInterestCalculator]]
|
||||
| tuple[PromptInfo, type[BasePrompt]]
|
||||
]:
|
||||
"""获取插件包含的组件列表
|
||||
|
||||
|
||||
95
src/plugin_system/base/base_prompt.py
Normal file
95
src/plugin_system/base/base_prompt.py
Normal file
@@ -0,0 +1,95 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any
|
||||
|
||||
from src.chat.utils.prompt_params import PromptParameters
|
||||
from src.common.logger import get_logger
|
||||
from src.plugin_system.base.component_types import ComponentType, PromptInfo
|
||||
|
||||
logger = get_logger("base_prompt")
|
||||
|
||||
|
||||
class BasePrompt(ABC):
|
||||
"""Prompt组件基类
|
||||
|
||||
Prompt是插件的一种组件类型,用于动态地向现有的核心Prompt模板中注入额外的上下文信息。
|
||||
它的主要作用是在不修改核心代码的情况下,扩展和定制模型的行为。
|
||||
|
||||
子类可以通过类属性定义其行为:
|
||||
- prompt_name: Prompt组件的唯一名称。
|
||||
- injection_point: 指定要注入的目标Prompt名称(或名称列表)。
|
||||
"""
|
||||
|
||||
prompt_name: str = ""
|
||||
"""Prompt组件的名称"""
|
||||
prompt_description: str = ""
|
||||
"""Prompt组件的描述"""
|
||||
|
||||
# 定义此组件希望注入到哪个或哪些核心Prompt中
|
||||
# 可以是一个字符串(单个目标)或字符串列表(多个目标)
|
||||
# 例如: "planner_prompt" 或 ["s4u_style_prompt", "normal_style_prompt"]
|
||||
injection_point: str | list[str] = ""
|
||||
"""要注入的目标Prompt名称或列表"""
|
||||
|
||||
def __init__(self, params: PromptParameters, plugin_config: dict | None = None):
|
||||
"""初始化Prompt组件
|
||||
|
||||
Args:
|
||||
params: 统一提示词参数,包含所有构建提示词所需的上下文信息。
|
||||
plugin_config: 插件配置字典。
|
||||
"""
|
||||
self.params = params
|
||||
self.plugin_config = plugin_config or {}
|
||||
self.log_prefix = "[PromptComponent]"
|
||||
|
||||
logger.debug(f"{self.log_prefix} Prompt组件 '{self.prompt_name}' 初始化完成")
|
||||
|
||||
@abstractmethod
|
||||
async def execute(self) -> str:
|
||||
"""执行Prompt生成的抽象方法,子类必须实现。
|
||||
|
||||
此方法应根据初始化时传入的 `self.params` 来构建并返回一个字符串。
|
||||
返回的字符串将被拼接到目标Prompt的最前面。
|
||||
|
||||
Returns:
|
||||
str: 生成的文本内容。
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_config(self, key: str, default: Any = None) -> Any:
|
||||
"""获取插件配置值,支持嵌套键访问。
|
||||
|
||||
Args:
|
||||
key: 配置键名,使用点号进行嵌套访问,如 "section.subsection.key"。
|
||||
default: 未找到键时返回的默认值。
|
||||
|
||||
Returns:
|
||||
Any: 配置值或默认值。
|
||||
"""
|
||||
if not self.plugin_config:
|
||||
return default
|
||||
|
||||
keys = key.split(".")
|
||||
current = self.plugin_config
|
||||
for k in keys:
|
||||
if isinstance(current, dict) and k in current:
|
||||
current = current[k]
|
||||
else:
|
||||
return default
|
||||
return current
|
||||
|
||||
@classmethod
|
||||
def get_prompt_info(cls) -> "PromptInfo":
|
||||
"""从类属性生成PromptInfo,用于组件注册和管理。
|
||||
|
||||
Returns:
|
||||
PromptInfo: 生成的Prompt信息对象。
|
||||
"""
|
||||
if not cls.prompt_name:
|
||||
raise ValueError("Prompt组件必须定义 'prompt_name' 类属性。")
|
||||
|
||||
return PromptInfo(
|
||||
name=cls.prompt_name,
|
||||
component_type=ComponentType.PROMPT,
|
||||
description=cls.prompt_description,
|
||||
injection_point=cls.injection_point,
|
||||
)
|
||||
@@ -20,6 +20,7 @@ class ComponentType(Enum):
|
||||
EVENT_HANDLER = "event_handler" # 事件处理组件
|
||||
CHATTER = "chatter" # 聊天处理器组件
|
||||
INTEREST_CALCULATOR = "interest_calculator" # 兴趣度计算组件
|
||||
PROMPT = "prompt" # Prompt组件
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.value
|
||||
@@ -266,6 +267,18 @@ class EventInfo(ComponentInfo):
|
||||
self.component_type = ComponentType.EVENT_HANDLER
|
||||
|
||||
|
||||
@dataclass
|
||||
class PromptInfo(ComponentInfo):
|
||||
"""Prompt组件信息"""
|
||||
|
||||
injection_point: str | list[str] = ""
|
||||
"""要注入的目标Prompt名称或列表"""
|
||||
|
||||
def __post_init__(self):
|
||||
super().__post_init__()
|
||||
self.component_type = ComponentType.PROMPT
|
||||
|
||||
|
||||
@dataclass
|
||||
class PluginInfo:
|
||||
"""插件信息"""
|
||||
|
||||
@@ -11,6 +11,7 @@ from src.plugin_system.base.base_chatter import BaseChatter
|
||||
from src.plugin_system.base.base_command import BaseCommand
|
||||
from src.plugin_system.base.base_events_handler import BaseEventHandler
|
||||
from src.plugin_system.base.base_interest_calculator import BaseInterestCalculator
|
||||
from src.plugin_system.base.base_prompt import BasePrompt
|
||||
from src.plugin_system.base.base_tool import BaseTool
|
||||
from src.plugin_system.base.component_types import (
|
||||
ActionInfo,
|
||||
@@ -22,6 +23,7 @@ from src.plugin_system.base.component_types import (
|
||||
InterestCalculatorInfo,
|
||||
PluginInfo,
|
||||
PlusCommandInfo,
|
||||
PromptInfo,
|
||||
ToolInfo,
|
||||
)
|
||||
from src.plugin_system.base.plus_command import PlusCommand
|
||||
@@ -37,6 +39,7 @@ ComponentClassType = (
|
||||
| type[PlusCommand]
|
||||
| type[BaseChatter]
|
||||
| type[BaseInterestCalculator]
|
||||
| type[BasePrompt]
|
||||
)
|
||||
|
||||
|
||||
@@ -183,6 +186,10 @@ class ComponentRegistry:
|
||||
assert isinstance(component_info, InterestCalculatorInfo)
|
||||
assert issubclass(component_class, BaseInterestCalculator)
|
||||
ret = self._register_interest_calculator_component(component_info, component_class)
|
||||
case ComponentType.PROMPT:
|
||||
assert isinstance(component_info, PromptInfo)
|
||||
assert issubclass(component_class, BasePrompt)
|
||||
ret = self._register_prompt_component(component_info, component_class)
|
||||
case _:
|
||||
logger.warning(f"未知组件类型: {component_type}")
|
||||
ret = False
|
||||
@@ -346,6 +353,31 @@ class ComponentRegistry:
|
||||
logger.debug(f"已注册InterestCalculator组件: {calculator_name}")
|
||||
return True
|
||||
|
||||
def _register_prompt_component(
|
||||
self, prompt_info: PromptInfo, prompt_class: "ComponentClassType"
|
||||
) -> bool:
|
||||
"""注册Prompt组件到Prompt特定注册表"""
|
||||
prompt_name = prompt_info.name
|
||||
if not prompt_name:
|
||||
logger.error(f"Prompt组件 {prompt_class.__name__} 必须指定名称")
|
||||
return False
|
||||
|
||||
if not hasattr(self, "_prompt_registry"):
|
||||
self._prompt_registry: dict[str, type[BasePrompt]] = {}
|
||||
if not hasattr(self, "_enabled_prompt_registry"):
|
||||
self._enabled_prompt_registry: dict[str, type[BasePrompt]] = {}
|
||||
|
||||
_assign_plugin_attrs(
|
||||
prompt_class, prompt_info.plugin_name, self.get_plugin_config(prompt_info.plugin_name) or {}
|
||||
)
|
||||
self._prompt_registry[prompt_name] = prompt_class # type: ignore
|
||||
|
||||
if prompt_info.enabled:
|
||||
self._enabled_prompt_registry[prompt_name] = prompt_class # type: ignore
|
||||
|
||||
logger.debug(f"已注册Prompt组件: {prompt_name}")
|
||||
return True
|
||||
|
||||
# === 组件移除相关 ===
|
||||
|
||||
async def remove_component(self, component_name: str, component_type: ComponentType, plugin_name: str) -> bool:
|
||||
@@ -580,7 +612,17 @@ class ComponentRegistry:
|
||||
component_name: str,
|
||||
component_type: ComponentType | None = None,
|
||||
) -> (
|
||||
type[BaseCommand | BaseAction | BaseEventHandler | BaseTool | PlusCommand | BaseChatter | BaseInterestCalculator] | None
|
||||
type[
|
||||
BaseCommand
|
||||
| BaseAction
|
||||
| BaseEventHandler
|
||||
| BaseTool
|
||||
| PlusCommand
|
||||
| BaseChatter
|
||||
| BaseInterestCalculator
|
||||
| BasePrompt
|
||||
]
|
||||
| None
|
||||
):
|
||||
"""获取组件类,支持自动命名空间解析
|
||||
|
||||
@@ -830,6 +872,7 @@ class ComponentRegistry:
|
||||
events_handlers: int = 0
|
||||
plus_command_components: int = 0
|
||||
chatter_components: int = 0
|
||||
prompt_components: int = 0
|
||||
for component in self._components.values():
|
||||
if component.component_type == ComponentType.ACTION:
|
||||
action_components += 1
|
||||
@@ -843,6 +886,8 @@ class ComponentRegistry:
|
||||
plus_command_components += 1
|
||||
elif component.component_type == ComponentType.CHATTER:
|
||||
chatter_components += 1
|
||||
elif component.component_type == ComponentType.PROMPT:
|
||||
prompt_components += 1
|
||||
return {
|
||||
"action_components": action_components,
|
||||
"command_components": command_components,
|
||||
@@ -850,6 +895,7 @@ class ComponentRegistry:
|
||||
"event_handlers": events_handlers,
|
||||
"plus_command_components": plus_command_components,
|
||||
"chatter_components": chatter_components,
|
||||
"prompt_components": prompt_components,
|
||||
"total_components": len(self._components),
|
||||
"total_plugins": len(self._plugins),
|
||||
"components_by_type": {
|
||||
|
||||
@@ -358,13 +358,14 @@ class PluginManager:
|
||||
event_handler_count = stats.get("event_handlers", 0)
|
||||
plus_command_count = stats.get("plus_command_components", 0)
|
||||
chatter_count = stats.get("chatter_components", 0)
|
||||
prompt_count = stats.get("prompt_components", 0)
|
||||
total_components = stats.get("total_components", 0)
|
||||
|
||||
# 📋 显示插件加载总览
|
||||
if total_registered > 0:
|
||||
logger.info("🎉 插件系统加载完成!")
|
||||
logger.info(
|
||||
f"📊 总览: {total_registered}个插件, {total_components}个组件 (Action: {action_count}, Command: {command_count}, Tool: {tool_count}, PlusCommand: {plus_command_count}, EventHandler: {event_handler_count}, Chatter: {chatter_count})"
|
||||
f"📊 总览: {total_registered}个插件, {total_components}个组件 (Action: {action_count}, Command: {command_count}, Tool: {tool_count}, PlusCommand: {plus_command_count}, EventHandler: {event_handler_count}, Chatter: {chatter_count}, Prompt: {prompt_count})"
|
||||
)
|
||||
|
||||
# 显示详细的插件列表
|
||||
@@ -402,6 +403,9 @@ class PluginManager:
|
||||
plus_command_components = [
|
||||
c for c in plugin_info.components if c.component_type == ComponentType.PLUS_COMMAND
|
||||
]
|
||||
prompt_components = [
|
||||
c for c in plugin_info.components if c.component_type == ComponentType.PROMPT
|
||||
]
|
||||
|
||||
if action_components:
|
||||
action_details = [format_component(c) for c in action_components]
|
||||
@@ -425,6 +429,9 @@ class PluginManager:
|
||||
if event_handler_components:
|
||||
event_handler_details = [format_component(c) for c in event_handler_components]
|
||||
logger.info(f" 📢 EventHandler组件: {', '.join(event_handler_details)}")
|
||||
if prompt_components:
|
||||
prompt_details = [format_component(c) for c in prompt_components]
|
||||
logger.info(f" 📝 Prompt组件: {', '.join(prompt_details)}")
|
||||
|
||||
# 权限节点信息
|
||||
if plugin_instance := self.loaded_plugins.get(plugin_name):
|
||||
|
||||
Reference in New Issue
Block a user