D
Dev SOPKnowledge Base
Search
← All topics

SignalWire Voice AI Agents: SWML, SWAIG, and the Python Agents SDK

SignalWire voice agent development — SWML verb structure, SWAIG tool-calling functions, data_map serverless handlers, Python AgentBase SDK, Call Flow Builder nodes, and IVR-to-AI migration patterns.

signalwirevoice-aiswmlswaigpythontelephonyagents
Agent trigger phrases: SignalWire · SWML · SWAIG · voice AI agent · SignalWire call flow · AgentBase · voice agent · IVR replacement · signalwire-agents

Overview

SignalWire provides three layers for building voice AI: the Call Flow Builder (visual, no-code), SWML (YAML/JSON script), and the Python Agents SDK (code-first). All three converge on the same runtime — SWML is the intermediate representation.

SWML Structure

version: 1.0.0
sections:
  main:
    - answer
    - ai:
        prompt:
          text: |
            You are a helpful receptionist for Acme Corp.
            Be friendly, concise, and professional.
        SWAIG:
          functions:
            - function: get_hours
              description: "Get business hours. Call when user asks when we're open."
              parameters:
                type: object
                properties: {}
              web_hook_url: https://api.example.com/swaig

SWAIG Function Schema

SWAIG (SignalWire AI Gateway) is the tool-calling mechanism. Functions are defined inside ai.SWAIG.functions[].

{
  "function": "get_balance",
  "description": "Get customer account balance. Call when user asks about balance.",
  "active": true,
  "parameters": {
    "type": "object",
    "properties": {
      "account_id": {
        "type": "string",
        "description": "Customer account ID"
      }
    },
    "required": ["account_id"]
  },
  "web_hook_url": "https://api.example.com/swaig",
  "fillers": {
    "en-US": ["Let me check on that...", "One moment please..."]
  }
}

When the AI invokes a function, it POSTs to web_hook_url. Response must include:

{
  "response": "Your balance is $127.50.",
  "action": []
}

data_map: Serverless Function Handler

For simple API passthrough without a webhook server, use data_map:

- function: get_weather
  description: "Get current weather. Call when user asks about weather."
  parameters:
    type: object
    properties:
      city:
        type: string
  data_map:
    webhooks:
      - url: "https://api.weather.com/v1/current?city=${args.city}&key=API_KEY"
        method: GET
        output:
          response: "The weather in ${args.city} is ${response.condition}, ${response.temp_f}°F."

data_map evaluates expressions inline — no server required.

Python Agents SDK (AgentBase)

from signalwire import AgentBase

class MyAgent(AgentBase):
    def __init__(self):
        super().__init__(
            name="receptionist",
            route="/",
            host="0.0.0.0",
            port=3000,
        )
        self.set_prompt_text(
            "You are a helpful receptionist for Acme Corp. Be concise."
        )

        @self.tool(
            name="get_hours",
            description="Get business hours. Call when user asks when we're open.",
        )
        def get_hours():
            return FunctionResult("We're open Monday–Friday, 9am to 5pm PST.")

agent = MyAgent()
agent.run()

Key AgentBase Methods

| Method | Purpose | |--------|---------| | set_prompt_text(text) | Set main system prompt | | set_temperature(n) | AI temperature (0.0–2.0) | | set_max_tokens(n) | Response length limit | | set_language("en-US") | Voice language | | set_voice("en-US-Neural2-F") | TTS voice | | set_record_call(True) | Enable call recording | | add_language("es-US", ...) | Multi-language support |

SWAIG Defaults

self.set_swaig_defaults(
    web_hook_url="https://api.example.com/swaig",
    web_hook_auth_user="user",
    web_hook_auth_password="secret",
)

Applies authentication to all functions that don't override it.

Call Flow Builder Nodes

| Node | Description | |------|-------------| | Handle Call | Entry point (mandatory, one per flow) | | AI Agent | Connects to an AI Agent resource | | Gather Input | Collects DTMF or speech | | Forward to Phone | Transfer to number or SIP endpoint | | Play Audio or TTS | Play file, silence, or synthesized speech | | Request | HTTP GET/POST to external API | | Conditions | JavaScript if/else logic | | Set Variables | Create %{vars.key} variables | | Execute SWML | Fetch and run remote SWML document | | Voicemail Recording | Async voicemail capture |

Variables in CFB

%{call.from}        # Caller's phone number
%{call.to}          # Dialed number
%{vars.account_id}  # Custom variable set via Set Variables node
%{request_response.balance}  # Field from a Request node response
%{record_call_url}  # URL of recorded call

IVR to AI Migration Pattern

Before (IVR tree):

Press 1 for Hours → Play hours message
Press 2 for Location → Play address
Press 3 for Agent → Transfer to queue

After (AI agent):

ai:
  prompt:
    text: |
      You handle calls for Acme Corp.
      For hours, say: Monday–Friday 9am–5pm PST.
      For location, say: 123 Main St, Portland OR.
      For agent requests, use transfer_to_agent function.
  SWAIG:
    functions:
      - function: transfer_to_agent
        description: "Transfer caller to live agent when requested."
        parameters:
          type: object
          properties: {}
        data_map:
          output:
            action:
              - transfer: "+15551234567"

Webhook Auth

SignalWire signs SWAIG webhook requests with HMAC-SHA256. Verify in your handler:

import hmac, hashlib

def verify_swaig_signature(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

Multi-Agent Server

from signalwire import AgentServer

server = AgentServer(host="0.0.0.0", port=3000)
server.add_agent(ReceptionistAgent(), route="/reception")
server.add_agent(SupportAgent(), route="/support")
server.run()