Files
2026-08-21 16:00:13 +08:00

84 lines
2.6 KiB
Python

"""声明式 Skill 激活:匹配、提示注入、Run 级工具别名和事件。"""
from __future__ import annotations
from typing import TYPE_CHECKING
from app.application.agent.prompt_composer import compose_system_messages
from app.application.agent.skill_loader import load_skill_manifests
from app.modules.runtime.events import Event
if TYPE_CHECKING:
from app.application.tool_runtime.view import RunScopedToolView
from app.modules.runtime.events import EventStore
class SkillExecutor:
"""把数据库 Skill 激活到当前 Run 的工具视图。"""
def __init__(
self,
tool_view: RunScopedToolView | None = None,
event_store: EventStore | None = None,
) -> None:
self._tool_view = tool_view
self._event_store = event_store
async def match_and_activate(
self,
context_text: str,
system_messages: list[str],
*,
run_id: str | None = None,
character_id: str | None = None,
skill_refs: tuple[str, ...] = (),
) -> list[str]:
"""匹配技能并把提示与别名写入当前 Run。"""
if not context_text:
return system_messages
manifests = await load_skill_manifests(context_text, skill_refs=skill_refs)
system_messages.extend(compose_system_messages([], None, manifests))
activated_names: list[str] = []
for manifest in manifests:
if self._tool_view is not None:
for tool_name in manifest.allowed_tools:
self._tool_view.bind_alias(
f"skill:{manifest.name}/{tool_name}",
tool_name,
description_prefix=f"[{manifest.name}] ",
)
activated_names.append(manifest.name)
if activated_names and self._event_store:
await self._emit_activation_event(
activated_names,
run_id=run_id,
character_id=character_id,
)
return system_messages
async def _emit_activation_event(
self,
skill_names: list[str],
*,
run_id: str | None = None,
character_id: str | None = None,
) -> None:
event_store = self._event_store
if event_store is None:
return
await event_store.append(
Event(
type="skill.activated",
source="skill_executor",
visibility="public",
run_id=run_id,
character_id=character_id,
payload={"skills": skill_names},
)
)
__all__ = ["SkillExecutor"]