Understanding Skills

View as Markdown

Skills

Skills are modular, reusable capabilities that add functions, prompts, and integrations to your agents without custom code.

What You’ll Learn

This chapter covers the skills system:

  1. Understanding Skills - What skills are and how they work
  2. Built-in Skills - Pre-built skills available in the SDK
  3. Adding Skills - How to add skills to your agents
  4. Custom Skills - Creating your own skills
  5. Skill Configuration - Parameters and advanced options

What Are Skills?

Skills are pre-packaged capabilities that add:

  • Functions - SWAIG tools the AI can call
  • Prompts - Instructions for how to use the skill
  • Hints - Speech recognition keywords
  • Global Data - Variables available throughout the call
Without SkillsWith Skills
Write weather functionself.add_skill("weather")
Add API integration
Write promptsDone!
Add speech hints
Handle errors

Quick Start

Add a skill in one line:

1from signalwire_agents import AgentBase
2
3class MyAgent(AgentBase):
4 def __init__(self):
5 super().__init__(name="my-agent")
6 self.add_language("English", "en-US", "rime.spore")
7
8 # Add datetime capability
9 self.add_skill("datetime")
10
11 # Add math capability
12 self.add_skill("math")
13
14 self.prompt_add_section(
15 "Role",
16 "You are a helpful assistant that can tell time and do math."
17 )

Available Built-in Skills

SkillDescription
datetimeGet current date and time
mathPerform calculations
web_searchSearch the web (requires API key)
wikipedia_searchSearch Wikipedia
weather_apiGet weather information
jokeTell jokes
play_background_filePlay audio files
swml_transferTransfer calls to SWML endpoints
datasphereSearch DataSphere documents
native_vector_searchLocal vector search

Chapter Contents

SectionDescription
Built-in SkillsReference for included skills
Adding SkillsHow to use skills in your agents
Custom SkillsCreating your own skills
Skill ConfigurationParameters and advanced options

Skills vs Functions

AspectSWAIG FunctionSkill
ScopeSingle functionMultiple functions + prompts + hints
ReusabilityPer-agentAcross all agents
Setupdefine_tool()add_skill()
CustomizationFull controlParameters only
MaintenanceYou maintainSDK maintains

When to Use Skills

Use Built-in Skills When:

  • Standard capability needed (datetime, search, etc.)
  • Want quick setup without custom code
  • Need tested, maintained functionality

Create Custom Skills When:

  • Reusing capability across multiple agents
  • Want to share functionality with team/community
  • Packaging complex integrations

Use SWAIG Functions When:

  • One-off custom logic
  • Agent-specific business rules
  • Need full control over implementation

Complete Example

1#!/usr/bin/env python3
2# assistant_agent.py - Agent with multiple skills
3from signalwire_agents import AgentBase
4
5class AssistantAgent(AgentBase):
6 def __init__(self):
7 super().__init__(name="assistant")
8 self.add_language("English", "en-US", "rime.spore")
9
10 # Add multiple skills
11 self.add_skill("datetime")
12 self.add_skill("math")
13
14 self.prompt_add_section(
15 "Role",
16 "You are a helpful assistant named Alex."
17 )
18
19 self.prompt_add_section(
20 "Capabilities",
21 body="You can help with:",
22 bullets=[
23 "Telling the current date and time",
24 "Performing math calculations"
25 ]
26 )
27
28if __name__ == "__main__":
29 agent = AssistantAgent()
30 agent.run()

Let’s start by understanding how skills work internally.

Skill Architecture

SkillBase (Abstract Base Class)

Required Methods:

  • setup() - Initialize the skill
  • register_tools() - Register SWAIG functions

Optional Methods:

  • get_hints() - Speech recognition hints
  • get_global_data() - Session data
  • get_prompt_sections() - Prompt additions
  • cleanup() - Resource cleanup

SkillRegistry (Discovery & Loading)

  • Discovers skills from directories
  • Loads skills on-demand (lazy loading)
  • Validates requirements (packages, env vars)
  • Supports external skill paths

How Skills Work

Skills are a convenience layer built on top of SWAIG functions. When you add a skill, it registers one or more SWAIG functions with the agent, adds relevant prompts, and configures hints—all from a single add_skill() call.

Understanding this helps when debugging: a skill’s function behaves exactly like a SWAIG function you’d define yourself. The only difference is that the skill packages everything together.

When you call add_skill():

Skill Loading Process.
Skill Loading Process

Skill Directory Structure

Built-in skills live in the SDK:

signalwire_agents
skills
datetime
__init__.py
skill.py
math
web_search
...# more skills

Each skill directory contains:

FilePurpose
skill.pySkill class implementation
__init__.pyPython package marker
requirements.txtOptional extra dependencies

SkillBase Class

All skills inherit from SkillBase:

1from signalwire_agents.skills import SkillBase
2from signalwire_agents.core.function_result import SwaigFunctionResult
3
4
5class MySkill(SkillBase):
6 # Required class attributes
7 SKILL_NAME = "my_skill"
8 SKILL_DESCRIPTION = "Does something useful"
9 SKILL_VERSION = "1.0.0"
10
11 # Optional requirements
12 REQUIRED_PACKAGES = [] # Python packages needed
13 REQUIRED_ENV_VARS = [] # Environment variables needed
14
15 # Multi-instance support
16 SUPPORTS_MULTIPLE_INSTANCES = False
17
18 def setup(self) -> bool:
19 """Initialize the skill. Return True if successful."""
20 return True
21
22 def register_tools(self) -> None:
23 """Register SWAIG tools with the agent."""
24 self.define_tool(
25 name="my_function",
26 description="Does something",
27 parameters={},
28 handler=self.my_handler
29 )
30
31 def my_handler(self, args, raw_data):
32 """Handle function calls."""
33 return SwaigFunctionResult("Result")

Skill Lifecycle

Discover → Load → Setup → Register → Active → Cleanup
StageDescription
DiscoverRegistry finds skill class in directory
LoadSkill class is imported and validated
Setupsetup() validates requirements, initializes resources
Registerregister_tools() adds functions to agent
ActiveSkill is ready, functions can be called
Cleanupcleanup() releases resources on shutdown

Skill Contributions

Skills can contribute to the agent in multiple ways:

1. Tools (Functions)

1def register_tools(self) -> None:
2 self.define_tool(
3 name="get_time",
4 description="Get current time",
5 parameters={
6 "timezone": {
7 "type": "string",
8 "description": "Timezone name"
9 }
10 },
11 handler=self.get_time_handler
12 )

2. Prompt Sections

1def get_prompt_sections(self):
2 return [
3 {
4 "title": "Time Information",
5 "body": "You can tell users the current time.",
6 "bullets": [
7 "Use get_time for time queries",
8 "Support multiple timezones"
9 ]
10 }
11 ]

3. Speech Hints

1def get_hints(self):
2 return ["time", "date", "clock", "timezone"]

4. Global Data

1def get_global_data(self):
2 return {
3 "datetime_enabled": True,
4 "default_timezone": "UTC"
5 }

Skill Discovery Paths

Skills are discovered from multiple locations in priority order:

PrioritySourceExample
1Already registered skills (in memory)-
2Entry points (pip installed packages)entry_points={'signalwire_agents.skills': ['my_skill = pkg:Skill']}
3Built-in skills directorysignalwire_agents/skills/
4External pathsskill_registry.add_skill_directory('/opt/custom_skills')
5Environment variable pathsSIGNALWIRE_SKILL_PATHS=/path1:/path2

Lazy Loading

Skills are loaded on-demand to minimize startup time:

1# Skill NOT loaded yet
2agent = MyAgent()
3
4# Skill loaded when first referenced
5agent.add_skill("datetime") # datetime skill loaded here
6
7# Already loaded, reused
8agent.add_skill("datetime") # Uses cached class

Multi-Instance Skills

Some skills support multiple instances with different configurations:

1class MySkill(SkillBase):
2 SUPPORTS_MULTIPLE_INSTANCES = True
3
4 def get_instance_key(self) -> str:
5 # Unique key for this instance
6 tool_name = self.params.get('tool_name', self.SKILL_NAME)
7 return f"{self.SKILL_NAME}_{tool_name}"

Usage:

1# Add two instances with different configs
2agent.add_skill("web_search", {
3 "tool_name": "search_news",
4 "search_engine_id": "NEWS_ENGINE_ID",
5 "api_key": "KEY"
6})
7
8agent.add_skill("web_search", {
9 "tool_name": "search_docs",
10 "search_engine_id": "DOCS_ENGINE_ID",
11 "api_key": "KEY"
12})

Parameter Passing

Parameters flow through skills in a structured way:

At add_skill() time:

1self.add_skill("web_search", {
2 "api_key": "your-key",
3 "tool_name": "custom_search",
4 "max_results": 5
5})

The skill receives these in self.params during setup:

1def setup(self) -> bool:
2 self.api_key = self.params.get("api_key")
3 self.max_results = self.params.get("max_results", 3)
4 return True

At function call time: The AI calls the function with arguments:

1def search_handler(self, args, raw_data):
2 query = args.get("query") # From AI's function call
3 max_results = self.max_results # From skill config
4 # ...

Result Handling

Skill handlers return SwaigFunctionResult just like regular SWAIG functions:

1def my_handler(self, args, raw_data):
2 # Success case
3 return SwaigFunctionResult("The result is 42")
4
5 # With actions
6 return (
7 SwaigFunctionResult("Updated your preferences")
8 .update_global_data({"preference": "value"})
9 )
10
11 # Error case - still return a result for the AI
12 return SwaigFunctionResult("I couldn't complete that request. Please try again.")

The result goes back to the AI, which uses it to formulate a response to the user.

Error Handling and Propagation

Skills should handle errors gracefully and return meaningful messages:

1def api_handler(self, args, raw_data):
2 try:
3 result = self.call_external_api(args)
4 return SwaigFunctionResult(f"Result: {result}")
5 except requests.Timeout:
6 return SwaigFunctionResult(
7 "The service is taking too long to respond. Please try again."
8 )
9 except requests.RequestException as e:
10 self.agent.log.error(f"API error: {e}")
11 return SwaigFunctionResult(
12 "I'm having trouble connecting to the service right now."
13 )
14 except Exception as e:
15 self.agent.log.error(f"Unexpected error: {e}")
16 return SwaigFunctionResult(
17 "Something went wrong. Please try again."
18 )

Error handling principles:

  • Always return a SwaigFunctionResult, even on errors
  • Make error messages user-friendly (the AI will relay them)
  • Log technical details for debugging
  • Don’t expose internal errors to users

Debugging Skills

When skills don’t work as expected:

1. Check if the skill loaded:

1# In your agent
2print(f"Skills loaded: {list(self._skill_manager._skills.keys())}")

2. Verify functions are registered:

$swaig-test your_agent.py --dump-swml | grep -A 5 "functions"

3. Test the function directly:

$swaig-test your_agent.py --function skill_function_name --args '{"param": "value"}'

4. Check for missing requirements: Skills log warnings if required packages or environment variables are missing. Check your logs during agent startup.

5. Look at skill source: Built-in skills are in the SDK source. Examine them to understand how they work:

$pip show signalwire-agents
$# Find location, then look in signalwire_agents/skills/