Duration 2.5 hours
Day 2 of 2

Learning Objectives

By the end of this module, students will be able to:

  • Create SWAIG functions using the @tool decorator
  • Define parameters with proper types
  • Handle function execution flow
  • Return appropriate responses

Topics

1. What Are SWAIG Functions? (20 min)

Definition

SWAIG (SignalWire AI Gateway) functions extend your agent’s capabilities beyond conversation. They allow the AI to:

  • Look up information
  • Perform actions
  • Integrate with external systems
  • Make decisions based on data

How It Works

┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│   Caller     │    │  SignalWire  │    │  Your Agent  │
│              │    │     AI       │    │              │
└──────┬───────┘    └──────┬───────┘    └──────┬───────┘
       │                   │                   │
       │ "What's my        │                   │
       │  order status?"   │                   │
       │──────────────────>│                   │
       │                   │                   │
       │                   │ Call function:    │
       │                   │ get_order_status  │
       │                   │──────────────────>│
       │                   │                   │
       │                   │ "Order shipped"   │
       │                   │<──────────────────│
       │                   │                   │
       │ "Your order       │                   │
       │  shipped Tuesday" │                   │
       │<──────────────────│                   │

Function vs Conversation

Conversation Function
AI generates response AI triggers your code
No external data needed Can access databases/APIs
Immediate May take time
Stateless Can have side effects

2. The @tool Decorator (30 min)

Basic Syntax

from signalwire_agents import AgentBase, SwaigFunctionResult

agent = AgentBase(name="my-agent")

@agent.tool(
    description="Look up the current weather",
    parameters={
        "type": "object",
        "properties": {
            "city": {"type": "string", "description": "City name"}
        },
        "required": ["city"]
    }
)
def get_weather(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    city = args.get("city", "")
    # Your logic here
    return SwaigFunctionResult(f"The weather in {city} is sunny and 72°F.")

Decorator Parameters

Parameter Type Purpose
description str Required. What the function does
name str Override function name
parameters dict Explicit JSON Schema
secure bool Require authentication
fillers list Phrases while executing

Name Override

@agent.tool(
    name="check_weather",  # AI sees this name
    description="Get current weather for a city",
    parameters={
        "type": "object",
        "properties": {
            "city": {"type": "string", "description": "City name"}
        },
        "required": ["city"]
    }
)
def get_weather_info(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    city = args.get("city", "")
    # Function name in code can differ
    return SwaigFunctionResult(f"Weather in {city}: Sunny")

Security

@agent.tool(
    description="Update account password",
    secure=True,  # Requires function token
    parameters={
        "type": "object",
        "properties": {
            "new_password": {"type": "string", "description": "New password"}
        },
        "required": ["new_password"]
    }
)
def update_password(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    new_password = args.get("new_password", "")
    return SwaigFunctionResult("Password updated successfully.")

3. Parameters (35 min)

Type Hints (Automatic Schema)

The SDK generates JSON Schema from Python type hints:

@agent.tool(
    description="Search products",
    parameters={
        "type": "object",
        "properties": {
            "query": {"type": "string", "description": "Search query"},
            "category": {"type": "string", "description": "Product category"},
            "limit": {"type": "integer", "description": "Maximum results"}
        },
        "required": ["query"]
    }
)
def search_products(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    query = args.get("query", "")
    category = args.get("category")  # Optional
    limit = args.get("limit", 10)    # Optional with default
    return SwaigFunctionResult(f"Found products matching '{query}'")

Generated Schema:

{
  "type": "object",
  "properties": {
    "query": { "type": "string" },
    "category": { "type": "string" },
    "limit": { "type": "integer" }
  },
  "required": ["query"]
}

Type Mapping

Python Type JSON Schema Type
str "string"
int "integer"
float "number"
bool "boolean"
list "array"
dict "object"

Explicit Parameters

For more control, define the schema explicitly:

@agent.tool(
    description="Record a rating",
    parameters={
        "type": "object",
        "properties": {
            "rating": {
                "type": "integer",
                "description": "Rating from 1 to 5",
                "minimum": 1,
                "maximum": 5
            },
            "feedback": {
                "type": "string",
                "description": "Optional feedback text"
            }
        },
        "required": ["rating"]
    }
)
def record_rating(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    rating = args.get("rating", 0)
    feedback = args.get("feedback", "")
    return SwaigFunctionResult(f"Thanks for the {rating}-star rating!")

Enum Parameters

Limit to specific values:

@agent.tool(
    description="Transfer to department",
    parameters={
        "type": "object",
        "properties": {
            "department": {
                "type": "string",
                "enum": ["sales", "support", "billing"],
                "description": "Target department"
            }
        },
        "required": ["department"]
    }
)
def transfer_to_department(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    department = args.get("department", "")
    return SwaigFunctionResult(f"Transferring to {department}...")

4. Function Execution (25 min)

Return Values

Always return a SwaigFunctionResult:

from signalwire_agents import SwaigFunctionResult

@agent.tool(
    description="Get account balance",
    parameters={
        "type": "object",
        "properties": {
            "account_id": {"type": "string", "description": "Account ID"}
        },
        "required": ["account_id"]
    }
)
def get_balance(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    account_id = args.get("account_id", "")
    # Fetch from database
    balance = 1234.56

    # Return result - AI will speak this
    return SwaigFunctionResult(f"Your account balance is ${balance}")

What Gets Spoken

The string in SwaigFunctionResult becomes part of the AI’s response:

return SwaigFunctionResult("Your order shipped on Tuesday.")
# AI might say: "I found your order. Your order shipped on Tuesday. Is there anything else?"

Raw Data Response

Return structured data for AI to interpret:

@agent.tool(
    description="Get order details",
    parameters={
        "type": "object",
        "properties": {
            "order_id": {"type": "string", "description": "Order ID"}
        },
        "required": ["order_id"]
    }
)
def get_order(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    order_id = args.get("order_id", "")
    order = {
        "id": order_id,
        "status": "shipped",
        "date": "2024-01-15",
        "items": ["Widget A", "Widget B"]
    }
    return SwaigFunctionResult(str(order))

The AI will format this naturally in speech.


5. Fillers and Wait States (20 min)

Function Fillers

For functions that take time:

@agent.tool(
    description="Search the database",
    parameters={
        "type": "object",
        "properties": {
            "query": {"type": "string", "description": "Search query"}
        },
        "required": ["query"]
    },
    fillers=[
        "Searching now...",
        "Looking that up...",
        "One moment while I search..."
    ]
)
def search_database(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    query = args.get("query", "")
    # Slow operation
    import time
    time.sleep(2)
    return SwaigFunctionResult(f"Found 5 results for '{query}'")

Wait Files

Play audio while processing:

@agent.tool(
    description="Process payment",
    parameters={
        "type": "object",
        "properties": {
            "amount": {"type": "number", "description": "Payment amount"}
        },
        "required": ["amount"]
    },
    wait_file="https://example.com/processing.mp3",
    wait_file_loops=3
)
def process_payment(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    amount = args.get("amount", 0.0)
    # Payment processing
    return SwaigFunctionResult("Payment processed successfully.")

6. Testing Functions (20 min)

List Functions

swaig-test agent.py --list-tools

Output:

Available tools:
  get_weather - Look up the current weather
    Parameters:
      city (string, required)

  search_products - Search products
    Parameters:
      query (string, required)
      category (string)
      limit (integer)

Execute Functions

# Execute with arguments
swaig-test agent.py --exec get_weather --city "Seattle"

Output:

Executing function: get_weather
Arguments: {"city": "Seattle"}
Result: The weather in Seattle is sunny and 72°F.

Multiple Arguments

swaig-test agent.py --exec search_products --query "laptop" --limit 5

Complete Example

Click to reveal complete solution
#!/usr/bin/env python3
"""Agent with SWAIG functions."""

from signalwire_agents import AgentBase, SwaigFunctionResult

agent = AgentBase(name="order-agent", route="/orders")

agent.prompt_add_section(
    "Role",
    "You help customers check their order status. "
    "Ask for their order number to look it up."
)

agent.add_language("English", "en-US", "rime.spore")

# Simple function
@agent.tool(
    description="Look up order status by order number",
    parameters={
        "type": "object",
        "properties": {
            "order_number": {"type": "string", "description": "Order number"}
        },
        "required": ["order_number"]
    }
)
def get_order_status(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    order_number = args.get("order_number", "")
    # In reality, query database
    return SwaigFunctionResult(
        f"Order {order_number} shipped yesterday via FedEx. "
        f"Expected delivery is Thursday."
    )

# Function with multiple parameters
@agent.tool(
    description="Search for orders by customer email",
    parameters={
        "type": "object",
        "properties": {
            "email": {"type": "string", "description": "Customer email"},
            "status": {"type": "string", "description": "Order status filter"}
        },
        "required": ["email"]
    },
    fillers=["Searching your orders...", "Looking that up now..."]
)
def search_orders(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    email = args.get("email", "")
    status = args.get("status")  # Optional
    # In reality, query database
    return SwaigFunctionResult(
        f"Found 3 orders for {email}. "
        "The most recent is order 12345, shipped Monday."
    )

if __name__ == "__main__":
    agent.run()

Common Mistakes

1. Missing Return Type

Wrong:

@agent.tool(description="Get info")
def get_info(id: str):  # No return type hint
    return "Info here"  # Returns string, not SwaigFunctionResult

Right:

@agent.tool(
    description="Get info",
    parameters={
        "type": "object",
        "properties": {
            "id": {"type": "string", "description": "ID to lookup"}
        },
        "required": ["id"]
    }
)
def get_info(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    id = args.get("id", "")
    return SwaigFunctionResult("Info here")

2. Missing Description

Wrong:

@agent.tool()  # No description!
def get_data(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    id = args.get("id", "")
    return SwaigFunctionResult("Data")

Right:

@agent.tool(
    description="Retrieve data by ID",
    parameters={
        "type": "object",
        "properties": {
            "id": {"type": "string", "description": "ID to retrieve"}
        },
        "required": ["id"]
    }
)
def get_data(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    id = args.get("id", "")
    return SwaigFunctionResult("Data")

3. Poor Description

Wrong:

@agent.tool(description="Does stuff")  # Too vague

Right:

@agent.tool(description="Look up customer account balance by account number")

The AI uses the description to decide when to call the function!


Key Takeaways

  1. @tool decorator - Easy function registration
  2. Type hints - Automatic parameter schema generation
  3. SwaigFunctionResult - Required return type
  4. Description matters - AI uses it to decide when to call
  5. Fillers help UX - Fill processing time naturally
  6. Test with swaig-test - Verify before live calls

Preparation for Lab 1.7

  • Working agent with prompts and voice
  • Understanding of function purpose
  • Test data ready (fake order numbers, etc.)

Lab Preview

In Lab 1.7, you will:

  1. Add a lookup function to your agent
  2. Define parameters with proper types
  3. Test function execution with swaig-test
  4. Make a live call and use the function

Back to top

SignalWire AI Agents Certification Program