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