106 lines
3.7 KiB
Python
106 lines
3.7 KiB
Python
"""SkillLoader - 从 YAML/SKILL.md 目录批量加载 Skill"""
|
||
|
||
import glob
|
||
import logging
|
||
import os
|
||
|
||
from agentkit.skills.base import Skill, SkillConfig
|
||
from agentkit.skills.registry import SkillRegistry
|
||
from agentkit.tools.registry import ToolRegistry
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class SkillLoader:
|
||
"""从 YAML/SKILL.md 目录批量加载 Skill 并注册到 SkillRegistry"""
|
||
|
||
def __init__(
|
||
self,
|
||
skill_registry: SkillRegistry,
|
||
tool_registry: ToolRegistry | None = None,
|
||
):
|
||
self._skill_registry = skill_registry
|
||
self._tool_registry = tool_registry
|
||
|
||
def load_from_directory(self, directory: str) -> list[Skill]:
|
||
"""加载目录下所有 YAML 和 SKILL.md 文件为 Skill,并注册到 SkillRegistry
|
||
|
||
无效的文件会被跳过并记录警告。
|
||
"""
|
||
skills: list[Skill] = []
|
||
|
||
# 加载 YAML 文件
|
||
yaml_pattern = os.path.join(directory, "*.yaml")
|
||
yaml_files = sorted(glob.glob(yaml_pattern))
|
||
for yaml_path in yaml_files:
|
||
try:
|
||
skill = self._load_skill_from_file(yaml_path)
|
||
skills.append(skill)
|
||
except Exception as e:
|
||
logger.warning(f"Skipping invalid YAML file '{yaml_path}': {e}")
|
||
|
||
# 加载 SKILL.md 文件
|
||
md_pattern = os.path.join(directory, "*.md")
|
||
md_files = sorted(glob.glob(md_pattern))
|
||
for md_path in md_files:
|
||
try:
|
||
skill = self.load_from_skill_md(md_path)
|
||
skills.append(skill)
|
||
except Exception as e:
|
||
logger.warning(f"Skipping invalid SKILL.md file '{md_path}': {e}")
|
||
|
||
return skills
|
||
|
||
def load_from_file(self, path: str) -> Skill:
|
||
"""加载单个 YAML 文件为 Skill,并注册到 SkillRegistry"""
|
||
skill = self._load_skill_from_file(path)
|
||
return skill
|
||
|
||
def _load_skill_from_file(self, path: str) -> Skill:
|
||
"""从 YAML 文件加载 SkillConfig,创建 Skill,绑定工具,注册"""
|
||
config = SkillConfig.from_yaml(path)
|
||
tools = self._bind_tools(config)
|
||
skill = Skill(config, tools=tools)
|
||
self._skill_registry.register(skill)
|
||
logger.info(f"Loaded skill '{skill.name}' from '{path}'")
|
||
return skill
|
||
|
||
def load_from_skill_md(self, path: str, disclosure_level: int = 1) -> Skill:
|
||
"""加载 SKILL.md 文件为 Skill,并注册到 SkillRegistry
|
||
|
||
Args:
|
||
path: SKILL.md 文件路径
|
||
disclosure_level: 渐进式加载层级(0=概要, 1=完整, 2=参考)
|
||
|
||
Returns:
|
||
加载的 Skill 实例
|
||
"""
|
||
from agentkit.skills.skill_md import SkillMdParser
|
||
|
||
frontmatter, sections, body = SkillMdParser.parse(path)
|
||
config = SkillMdParser.to_skill_config(
|
||
frontmatter, sections, path, disclosure_level=disclosure_level,
|
||
)
|
||
tools = self._bind_tools(config)
|
||
skill = Skill(config, tools=tools)
|
||
self._skill_registry.register(skill)
|
||
logger.info(f"Loaded skill '{skill.name}' from SKILL.md '{path}' (level={disclosure_level})")
|
||
return skill
|
||
|
||
def _bind_tools(self, config: SkillConfig) -> list:
|
||
"""根据配置中的 tools 列表绑定工具"""
|
||
if not self._tool_registry or not config.tools:
|
||
return []
|
||
|
||
tools = []
|
||
for tool_name in config.tools:
|
||
try:
|
||
tool = self._tool_registry.get(tool_name)
|
||
tools.append(tool)
|
||
logger.info(f"Bound tool '{tool_name}' to skill '{config.name}'")
|
||
except Exception as e:
|
||
logger.warning(
|
||
f"Failed to bind tool '{tool_name}' to skill '{config.name}': {e}"
|
||
)
|
||
return tools
|