Refactor skills tool integration and enhance system prompt

- Removed the skills_categories tool from the skills toolset, streamlining the skills functionality to focus on skills_list and skill_view.
- Updated the system prompt to dynamically build a compact skills index, allowing the model to quickly reference available skills without additional tool calls.
- Cleaned up related code and documentation to reflect the removal of skills_categories, ensuring clarity and consistency across the codebase.
This commit is contained in:
teknium1 2026-02-10 19:48:38 -08:00
parent 669545f551
commit 153cd5bb44
4 changed files with 94 additions and 55 deletions

View file

@ -41,7 +41,7 @@ from tools.terminal_hecate import terminal_hecate_tool, check_hecate_requirement
from tools.vision_tools import vision_analyze_tool, check_vision_requirements
from tools.mixture_of_agents_tool import mixture_of_agents_tool, check_moa_requirements
from tools.image_generation_tool import image_generate_tool, check_image_generation_requirements
from tools.skills_tool import skills_categories, skills_list, skill_view, check_skills_requirements, SKILLS_TOOL_DESCRIPTION
from tools.skills_tool import skills_list, skill_view, check_skills_requirements, SKILLS_TOOL_DESCRIPTION
# RL Training tools (Tinker-Atropos)
from tools.rl_training_tool import (
rl_list_environments,
@ -143,7 +143,7 @@ TOOLSET_REQUIREMENTS = {
"env_vars": [], # Just needs skills directory
"check_fn": check_skills_requirements,
"setup_url": None,
"tools": ["skills_categories", "skills_list", "skill_view"],
"tools": ["skills_list", "skill_view"],
},
"rl": {
"name": "RL Training (Tinker-Atropos)",
@ -432,24 +432,7 @@ def get_skills_tool_definitions() -> List[Dict[str, Any]]:
"properties": {
"category": {
"type": "string",
"description": "Optional category filter (from skills_categories)"
}
},
"required": []
}
}
},
{
"type": "function",
"function": {
"name": "skills_categories",
"description": "List available skill categories. Call this first to discover what skill categories exist, then use skills_list(category) to see skills in a category.",
"parameters": {
"type": "object",
"properties": {
"verbose": {
"type": "boolean",
"description": "If true, include skill counts per category. Default: false."
"description": "Optional category filter to narrow results"
}
},
"required": []
@ -910,7 +893,7 @@ def get_all_tool_names() -> List[str]:
# Skills tools
if check_skills_requirements():
tool_names.extend(["skills_categories", "skills_list", "skill_view"])
tool_names.extend(["skills_list", "skill_view"])
# Browser automation tools
if check_browser_requirements():
@ -957,7 +940,6 @@ TOOL_TO_TOOLSET_MAP = {
"mixture_of_agents": "moa_tools",
"image_generate": "image_tools",
# Skills tools
"skills_categories": "skills_tools",
"skills_list": "skills_tools",
"skill_view": "skills_tools",
# Browser automation tools
@ -1109,7 +1091,7 @@ def get_tool_definitions(
"vision_tools": ["vision_analyze"],
"moa_tools": ["mixture_of_agents"],
"image_tools": ["image_generate"],
"skills_tools": ["skills_categories", "skills_list", "skill_view"],
"skills_tools": ["skills_list", "skill_view"],
"browser_tools": [
"browser_navigate", "browser_snapshot", "browser_click",
"browser_type", "browser_scroll", "browser_back",
@ -1162,7 +1144,7 @@ def get_tool_definitions(
"vision_tools": ["vision_analyze"],
"moa_tools": ["mixture_of_agents"],
"image_tools": ["image_generate"],
"skills_tools": ["skills_categories", "skills_list", "skill_view"],
"skills_tools": ["skills_list", "skill_view"],
"browser_tools": [
"browser_navigate", "browser_snapshot", "browser_click",
"browser_type", "browser_scroll", "browser_back",
@ -1391,11 +1373,7 @@ def handle_skills_function_call(function_name: str, function_args: Dict[str, Any
Returns:
str: Function result as JSON string
"""
if function_name == "skills_categories":
verbose = function_args.get("verbose", False)
return skills_categories(verbose=verbose)
elif function_name == "skills_list":
if function_name == "skills_list":
category = function_args.get("category")
return skills_list(category=category)
@ -1686,7 +1664,7 @@ def handle_function_call(
return handle_image_function_call(function_name, function_args)
# Route skills tools
elif function_name in ["skills_categories", "skills_list", "skill_view"]:
elif function_name in ["skills_list", "skill_view"]:
return handle_skills_function_call(function_name, function_args)
# Route browser automation tools
@ -1767,7 +1745,7 @@ def get_available_toolsets() -> Dict[str, Dict[str, Any]]:
},
"skills_tools": {
"available": check_skills_requirements(),
"tools": ["skills_categories", "skills_list", "skill_view"],
"tools": ["skills_list", "skill_view"],
"description": "Access skill documents that provide specialized instructions, guidelines, or knowledge the agent can load on demand",
"requirements": ["skills/ directory in repo root"]
},

View file

@ -550,14 +550,79 @@ def apply_anthropic_cache_control(
# Default System Prompt Components
# =============================================================================
# Skills guidance - instructs the model to check skills before technical tasks
SKILLS_SYSTEM_PROMPT = """## Skills
Before answering technical questions about tools, frameworks, or workflows:
1. Check skills_categories to see if a relevant category exists
2. If a category matches your task, use skills_list with that category
3. If a skill matches, load it with skill_view and follow its instructions
Skills contain vetted, up-to-date instructions for specific tools and workflows."""
# Skills guidance - embeds a compact skill index in the system prompt so
# the model can match skills at a glance without extra tool calls.
def build_skills_system_prompt() -> str:
"""
Build a dynamic skills system prompt by scanning the skills/ directory.
Returns a prompt section that lists all skill categories (with descriptions
from DESCRIPTION.md) and their skill names inline, so the model can
immediately see if a relevant skill exists and load it with a single
skill_view(name) call -- no discovery tool calls needed.
Returns:
str: The skills system prompt section, or empty string if no skills found.
"""
import re
from pathlib import Path
skills_dir = Path(__file__).parent / "skills"
if not skills_dir.exists():
return ""
# Scan for SKILL.md files grouped by category
skills_by_category = {}
for skill_file in skills_dir.rglob("SKILL.md"):
rel_path = skill_file.relative_to(skills_dir)
parts = rel_path.parts
if len(parts) >= 2:
category = parts[0]
skill_name = parts[-2] # Folder containing SKILL.md
else:
category = "general"
skill_name = skill_file.parent.name
skills_by_category.setdefault(category, []).append(skill_name)
if not skills_by_category:
return ""
# Load category descriptions from DESCRIPTION.md files (YAML frontmatter)
category_descriptions = {}
for category in skills_by_category:
desc_file = skills_dir / category / "DESCRIPTION.md"
if desc_file.exists():
try:
content = desc_file.read_text(encoding="utf-8")
# Parse description from YAML frontmatter: ---\ndescription: ...\n---
match = re.search(r"^---\s*\n.*?description:\s*(.+?)\s*\n.*?^---", content, re.MULTILINE | re.DOTALL)
if match:
category_descriptions[category] = match.group(1).strip()
except Exception:
pass
# Build compact index: category with description + skill names
index_lines = []
for category in sorted(skills_by_category.keys()):
desc = category_descriptions.get(category, "")
names = ", ".join(sorted(skills_by_category[category]))
if desc:
index_lines.append(f" {category}: {desc}")
else:
index_lines.append(f" {category}:")
index_lines.append(f" skills: {names}")
return (
"## Skills (mandatory)\n"
"Before replying, scan the skills below. If one clearly matches your task, "
"load it with skill_view(name) and follow its instructions.\n"
"\n"
"<available_skills>\n"
+ "\n".join(index_lines) + "\n"
"</available_skills>\n"
"\n"
"If none match, proceed normally without loading a skill."
)
class KawaiiSpinner:
@ -1054,10 +1119,6 @@ class AIAgent:
return f"{face} 🎨 creating '{prompt}'... {time_str}"
# Skills - use large pool for variety
elif tool_name == "skills_categories":
face = random.choice(self.KAWAII_SKILL)
return f"{face} 📚 listing categories... {time_str}"
elif tool_name == "skills_list":
category = args.get("category", "skills")
face = random.choice(self.KAWAII_SKILL)
@ -1635,12 +1696,15 @@ class AIAgent:
base_system_prompt = system_message if system_message is not None else self.ephemeral_system_prompt
# Auto-include skills guidance if skills tools are available
has_skills_tools = any(name in self.valid_tool_names for name in ['skills_list', 'skills_categories', 'skill_view'])
if has_skills_tools:
# Embeds a compact category:names index so the model can match skills
# at a glance and load with a single skill_view(name) call.
has_skills_tools = any(name in self.valid_tool_names for name in ['skills_list', 'skill_view'])
skills_prompt = build_skills_system_prompt() if has_skills_tools else ""
if skills_prompt:
if base_system_prompt:
active_system_prompt = f"{base_system_prompt}\n\n{SKILLS_SYSTEM_PROMPT}"
active_system_prompt = f"{base_system_prompt}\n\n{skills_prompt}"
else:
active_system_prompt = SKILLS_SYSTEM_PROMPT
active_system_prompt = skills_prompt
else:
active_system_prompt = base_system_prompt
@ -2277,7 +2341,6 @@ class AIAgent:
'image_generate': ('sparkle', ['🎨', '', '🖼️', '🌟']),
'skill_view': ('star', ['📚', '📖', '🎓', '']),
'skills_list': ('pulse', ['📋', '📝', '📑', '📜']),
'skills_categories': ('pulse', ['📂', '🗂️', '📁', '🏷️']),
'moa_query': ('brain', ['🧠', '💭', '🤔', '💡']),
'analyze_image': ('sparkle', ['👁️', '🔍', '📷', '']),
}

View file

@ -59,7 +59,6 @@ from .image_generation_tool import (
)
from .skills_tool import (
skills_categories,
skills_list,
skill_view,
check_skills_requirements,
@ -158,7 +157,6 @@ __all__ = [
'image_generate_tool',
'check_image_generation_requirements',
# Skills tools
'skills_categories',
'skills_list',
'skill_view',
'check_skills_requirements',

View file

@ -69,7 +69,7 @@ TOOLSETS = {
"skills": {
"description": "Access skill documents with specialized instructions and knowledge",
"tools": ["skills_categories", "skills_list", "skill_view"],
"tools": ["skills_list", "skill_view"],
"includes": []
},
@ -142,7 +142,7 @@ TOOLSETS = {
# MoA
"mixture_of_agents",
# Skills
"skills_categories", "skills_list", "skill_view",
"skills_list", "skill_view",
# Browser
"browser_navigate", "browser_snapshot", "browser_click",
"browser_type", "browser_scroll", "browser_back",
@ -170,7 +170,7 @@ TOOLSETS = {
# Vision - analyze images sent by users
"vision_analyze",
# Skills - access knowledge base
"skills_categories", "skills_list", "skill_view",
"skills_list", "skill_view",
# Cronjob management - let users schedule tasks
"schedule_cronjob", "list_cronjobs", "remove_cronjob"
],
@ -185,7 +185,7 @@ TOOLSETS = {
# Vision - analyze images
"vision_analyze",
# Skills - access knowledge base
"skills_categories", "skills_list", "skill_view",
"skills_list", "skill_view",
# Cronjob - let users schedule reminders
"schedule_cronjob", "list_cronjobs", "remove_cronjob"
],
@ -204,7 +204,7 @@ TOOLSETS = {
# Vision
"vision_analyze",
# Skills
"skills_categories", "skills_list", "skill_view",
"skills_list", "skill_view",
# Cronjob management
"schedule_cronjob", "list_cronjobs", "remove_cronjob"
],