D
Dev SOPKnowledge Base
Search
← All topics

SignalWire Agents SDK: Building Voice AI Agents with SWAIG

SignalWire Agents SDK reference for building voice AI agents — AgentBase setup, SWAIG tool registration, FunctionResult patterns, DataMap for API passthrough, and multi-agent server deployment.

signalwirevoice-aiagentsswaigpythontelephony
Agent trigger phrases: SignalWire agents · SWAIG · voice AI agent · AgentBase · FunctionResult · DataMap builder · SignalWire SDK · telephony AI

Overview

SignalWire Agents SDK (Python) for building production voice AI agents. The SDK handles telephony transport, speech recognition, TTS, and the SWAIG (SignalWire AI Gateway) function calling protocol.

Core Architecture

Caller → SignalWire → AgentBase → SWAIG Function
                          ↑
              (speech recognition, TTS, routing)

Your agent is a Python class extending AgentBase. You define prompt, personality, and tools (@tool decorated methods). SignalWire handles all telephony complexity.

AgentBase Setup

from signalwire_agents import AgentBase

class CustomerServiceAgent(AgentBase):
    def __init__(self):
        super().__init__(
            name="customer-service",
            route="/customer-service",
            host="0.0.0.0",
            port=3000,
        )

        # AI model configuration
        self.set_params({
            "ai_model": "gpt-4o",
            "temperature": 0.7,
            "max_tokens": 1024,
        })

        # Voice configuration
        self.set_params({
            "voice": "elevenlabs.rachel",
            "language": "en-US",
        })

        # System prompt
        self.set_prompt_text(
            "You are a helpful customer service agent for Acme Corp. "
            "Be professional, concise, and empathetic. "
            "Always verify the customer's account before discussing order details."
        )

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

@tool Decorator — SWAIG Function Registration

from signalwire_agents import AgentBase
from signalwire_agents.core.function_result import SwaigFunctionResult

class SalesAgent(AgentBase):
    def __init__(self):
        super().__init__(name="sales", route="/sales")
        self.set_prompt_text("You are a sales assistant.")

    @AgentBase.tool(
        name="lookup_pricing",
        description="Look up pricing for a product",
        parameters={
            "product_id": {
                "type": "string",
                "description": "The product ID to look up",
            },
            "quantity": {
                "type": "integer",
                "description": "Number of units",
                "default": 1,
            },
        },
        required=["product_id"],
    )
    def lookup_pricing(self, args, raw_data) -> SwaigFunctionResult:
        product_id = args.get("product_id")
        quantity = args.get("quantity", 1)

        # Look up price in database
        price = self.db.get_price(product_id)
        if not price:
            return SwaigFunctionResult(f"Product {product_id} not found.")

        total = price * quantity
        return SwaigFunctionResult(
            f"Product {product_id}: ${price:.2f} each. "
            f"Total for {quantity} units: ${total:.2f}."
        )

FunctionResult Patterns

from signalwire_agents.core.function_result import SwaigFunctionResult

# Simple text response
return SwaigFunctionResult("The order has been placed.")

# Stop the conversation
return SwaigFunctionResult("Goodbye! Have a great day.").stop()

# Transfer to another number
return SwaigFunctionResult("Connecting you to a specialist.").transfer("+15551234567")

# Transfer to another AI agent
return SwaigFunctionResult("Transferring to billing.").transfer_to_agent("/billing")

# Post action: collect information
return SwaigFunctionResult("What is your account number?").need_input()

DataMap Builder — Server-Side API Passthrough

DataMap lets the SignalWire platform call external APIs directly, without your Python server in the loop:

from signalwire_agents.core.data_map import DataMap

class WeatherAgent(AgentBase):
    def __init__(self):
        super().__init__(name="weather", route="/weather")

        # Build a DataMap tool — SignalWire calls the API, not your server
        weather_tool = (
            DataMap("get_weather")
            .description("Get current weather for a city")
            .parameter("city", "string", "City name", required=True)
            .webhook(
                method="GET",
                url="https://api.openweathermap.org/data/2.5/weather",
                params={
                    "q": "${args.city}",
                    "appid": "${env.OPENWEATHER_API_KEY}",
                    "units": "metric",
                },
            )
            .output(
                SwaigFunctionResult(
                    "Current weather in ${args.city}: "
                    "${response.weather[0].description}, "
                    "${response.main.temp}°C"
                )
            )
        )
        self.register_tool(weather_tool)

Multi-Agent Server

from signalwire_agents import AgentServer

from agents.sales import SalesAgent
from agents.support import SupportAgent
from agents.billing import BillingAgent

server = AgentServer(host="0.0.0.0", port=3000)

server.register(SalesAgent())
server.register(SupportAgent())
server.register(BillingAgent())

# Each agent is available at its configured route:
# /sales, /support, /billing
server.run()

Built-In Skills

class MyAgent(AgentBase):
    def __init__(self):
        super().__init__(name="my-agent", route="/agent")

        # Add pre-built capabilities
        self.add_skill("datetime")       # Tell time/date
        self.add_skill("web_search")     # Search the web
        self.add_skill("calculator")     # Math operations
        self.add_skill("send_sms", {     # Send SMS (with config)
            "from_number": "+15550001111",
        })

Anti-Patterns

| Anti-Pattern | Problem | Fix | |--------------|---------|-----| | Blocking I/O in tool handler | Blocks other calls on the thread | Use asyncio or thread pool | | Secrets hardcoded in @tool | Visible in logs/tracebacks | Use ${env.VAR} in DataMap or os.getenv() | | Single agent handling all intents | Prompt bloat, poor accuracy | Split by domain — sales, support, billing | | No FunctionResult.stop() on end | Call may hang waiting for next input | Always .stop() on farewell |

Deployment

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENV PORT=3000
EXPOSE 3000
CMD ["python", "main.py"]

SignalWire must be able to reach your agent's port via HTTP. Use a reverse proxy (nginx) or deploy behind Render/Railway/Fly.io.