"""Beaver 内置 skill_view tool。 这个工具对应显式 skill loading path: 1. skill 正文默认不会长期塞进 system prompt 2. 模型若想查看某个 skill 的完整正文或支持文件,必须显式调用 `skill_view` 这样 skill 的按需展开路径会保持显式,而不是依赖 prompt 里长期堆目录信息。 """ from __future__ import annotations import json from dataclasses import dataclass, field from typing import Any from beaver.skills.catalog.loader import SkillsLoader SKILL_VIEW_TOOL_DESCRIPTION = ( "Load the full content of a skill or one of its supporting files. " "Use this when you want to inspect a skill in detail." ) SKILL_VIEW_TOOL_PARAMETERS: dict[str, Any] = { "type": "object", "properties": { "name": { "type": "string", "description": "The skill name to inspect.", }, "file_path": { "type": "string", "description": ( "Optional relative path to a supporting file inside the skill directory, " "for example 'references/usage.md'. Omit to load SKILL.md itself." ), }, }, "required": ["name"], } def skill_view(*, name: str, file_path: str | None = None, loader: SkillsLoader | None = None) -> str: """读取 skill 正文或支持文件,并返回结构化 JSON。""" if loader is None: return json.dumps({"success": False, "error": "Skills loader is not available."}, ensure_ascii=False) try: viewed = loader.view_skill(name, file_path=file_path) except FileNotFoundError as exc: return json.dumps({"success": False, "error": str(exc)}, ensure_ascii=False) except ValueError as exc: return json.dumps({"success": False, "error": str(exc)}, ensure_ascii=False) if viewed is None: return json.dumps({"success": False, "error": f"Unknown skill '{name}'."}, ensure_ascii=False) display_name, content = viewed support_files = loader.list_skill_supporting_files(name) return json.dumps( { "success": True, "name": name, "file": display_name, "content": content, "supporting_files": support_files, }, ensure_ascii=False, ) @dataclass(slots=True) class SkillViewTool: """面向 runtime 的 skill_view 工具封装。""" loader: SkillsLoader name: str = "skill_view" description: str = SKILL_VIEW_TOOL_DESCRIPTION toolset: str = "skills" always_available: bool = True parameters: dict[str, Any] = field(default_factory=lambda: dict(SKILL_VIEW_TOOL_PARAMETERS)) async def execute(self, **kwargs: Any) -> str: return skill_view(loader=self.loader, **kwargs)