Quick Start: Your First Agent

View as MarkdownOpen in Claude

The Minimal Agent

my_first_agent.py

1#!/usr/bin/env python3
2## my_first_agent.py - A simple voice AI agent
3"""
4My First SignalWire Agent
5
6A simple voice AI agent that greets callers and has conversations.
7"""
8
9from signalwire import AgentBase
10
11class MyFirstAgent(AgentBase):
12 def __init__(self):
13 super().__init__(name="my-first-agent")
14
15 # Set the voice
16 self.add_language("English", "en-US", "rime.spore")
17
18 # Define the AI's personality and behavior
19 self.prompt_add_section(
20 "Role",
21 "You are a friendly and helpful assistant. Greet the caller warmly "
22 "and help them with any questions they have. Keep responses concise "
23 "and conversational."
24 )
25
26 self.prompt_add_section(
27 "Guidelines",
28 body="Follow these rules:",
29 bullets=[
30 "Be friendly and professional",
31 "Keep responses brief (1-2 sentences when possible)",
32 "If you don't know something, say so honestly",
33 "End conversations politely when the caller is done"
34 ]
35 )
36
37if __name__ == "__main__":
38 agent = MyFirstAgent()
39 print("Starting My First Agent...")
40 print("Server running at: http://localhost:3000")
41 print("Press Ctrl+C to stop")
42 agent.run()

Run the Agent

LanguageCommand
Pythonpython my_first_agent.py
TypeScriptnpx tsx my_first_agent.ts

You’ll see output like:

Agent startup output showing security configuration, service initialization, basic auth credentials, and server startup information.
Agent startup output

The SDK shows:

  • Security configuration (SSL, CORS, rate limits)
  • Service initialization details
  • Basic auth credentials (username and password)
  • Server startup information

Test the Agent

Open a new terminal and test with curl. Use the Basic Auth credentials shown in the agent output:

$## Get the SWML document (what SignalWire receives)
$## Replace the password with the one from your agent's output
>curl -u signalwire:7vVZ8iMTOWL0Y7-BG6xaN3qhjmcm4Sf59nORNdlF9bs \
> http://localhost:3000/

The -u flag provides Basic Auth credentials in the format username:password. Use the exact password shown in your agent’s startup output.

You’ll see JSON output like:

1{
2 "version": "1.0.0",
3 "sections": {
4 "main": [
5 {"answer": {}},
6 {
7 "ai": {
8 "prompt": {
9 "text": "# Role\nYou are a friendly and helpful assistant..."
10 },
11 "languages": [
12 {"name": "English", "code": "en-US", "voice": "rime.spore"}
13 ]
14 }
15 }
16 ]
17 }
18}

What Just Happened?

1. You run: python my_first_agent.py

Agent starts a web server on port 3000.

2. SignalWire (or curl) sends: GET http://localhost:3000/

Agent returns SWML document (JSON).

3. SWML tells SignalWire:

  • Answer the call
  • Use this AI prompt (your personality config)
  • Use this voice (rime.spore)
  • Use English language

4. SignalWire’s AI:

  • Converts caller’s speech to text (STT)
  • Sends text to AI model (GPT-4, etc.)
  • Gets AI response
  • Converts response to speech (TTS)

Adding a Custom Function

Let’s add a function the AI can call:

1#!/usr/bin/env python3
2## my_first_agent_with_function.py - Agent with custom function
3"""
4My First SignalWire Agent - With Custom Function
5"""
6
7from signalwire import AgentBase, FunctionResult
8
9class MyFirstAgent(AgentBase):
10 def __init__(self):
11 super().__init__(name="my-first-agent")
12
13 # Set the voice
14 self.add_language("English", "en-US", "rime.spore")
15
16 # Define the AI's personality
17 self.prompt_add_section(
18 "Role",
19 "You are a friendly assistant who can tell jokes. "
20 "When someone asks for a joke, use your tell_joke function."
21 )
22
23 # Register the custom function
24 self.define_tool(
25 name="tell_joke",
26 description="Tell a joke to the caller",
27 parameters={"type": "object", "properties": {}},
28 handler=self.tell_joke
29 )
30
31 def tell_joke(self, args, raw_data):
32 """Return a joke for the AI to tell."""
33 import random
34 jokes = [
35 "Why do programmers prefer dark mode? Because light attracts bugs!",
36 "Why did the Python programmer need glasses? Because they couldn't C!",
37 "What's a programmer's favorite hangout spot? Foo Bar!",
38 ]
39 joke = random.choice(jokes)
40 return FunctionResult(f"Here's a joke: {joke}")
41
42if __name__ == "__main__":
43 agent = MyFirstAgent()
44 print("Starting My First Agent (with jokes!)...")
45 print("Server running at: http://localhost:3000")
46 agent.run()

Now when a caller asks for a joke, the AI will call your tell_joke function!

Using the Debug Endpoint

The agent provides a debug endpoint to inspect its configuration:

$## Use the Basic Auth credentials from your agent's startup output
>## (or set via SWML_BASIC_AUTH_USER and SWML_BASIC_AUTH_PASSWORD env vars)
>curl -u "$SWML_BASIC_AUTH_USER:$SWML_BASIC_AUTH_PASSWORD" http://localhost:3000/debug

This shows detailed information about:

  • Registered functions
  • Prompt configuration
  • Voice settings
  • Authentication credentials

Test with swaig-test CLI

The SDK includes a CLI tool for testing:

$## Show the SWML document
$swaig-test my_first_agent.py --dump-swml
$
$## List available functions
$swaig-test my_first_agent.py --list-tools
$
$## Test a function
$swaig-test my_first_agent.py --exec tell_joke

Complete Example with Multiple Features

Here’s a more complete example showing common patterns — voice, hints, prompts, and multiple tools.

1#!/usr/bin/env python3
2## complete_first_agent.py - Complete agent example with multiple features
3"""
4Complete First Agent Example
5
6Demonstrates: voice config, AI parameters, prompt sections, custom functions, speech hints.
7"""
8
9from signalwire import AgentBase, FunctionResult
10
11class CompleteFirstAgent(AgentBase):
12 def __init__(self):
13 super().__init__(
14 name="complete-agent",
15 auto_answer=True,
16 record_call=False
17 )
18
19 # Voice and language
20 self.add_language("English", "en-US", "rime.spore")
21
22 # AI behavior parameters
23 self.set_params({
24 "end_of_speech_timeout": 500, # Wait 500ms for speaker to finish
25 "attention_timeout": 15000 # 15 second attention span
26 })
27
28 # Speech recognition hints (improves accuracy)
29 self.add_hints([
30 "SignalWire",
31 "SWML",
32 "AI agent"
33 ])
34
35 # Prompt sections
36 self.prompt_add_section(
37 "Identity",
38 "You are Alex, a helpful AI assistant created by SignalWire."
39 )
40
41 self.prompt_add_section(
42 "Capabilities",
43 body="You can help callers with:",
44 bullets=[
45 "Answering general questions",
46 "Telling jokes",
47 "Providing the current time",
48 "Basic conversation"
49 ]
50 )
51
52 self.prompt_add_section(
53 "Style",
54 "Keep responses brief and friendly. Use a conversational tone."
55 )
56
57 # Register functions
58 self.define_tool(
59 name="get_current_time",
60 description="Get the current time",
61 parameters={"type": "object", "properties": {}},
62 handler=self.get_current_time
63 )
64
65 self.define_tool(
66 name="tell_joke",
67 description="Tell a random joke",
68 parameters={"type": "object", "properties": {}},
69 handler=self.tell_joke
70 )
71
72 def get_current_time(self, args, raw_data):
73 """Return the current time."""
74 from datetime import datetime
75 now = datetime.now()
76 return FunctionResult(f"The current time is {now.strftime('%I:%M %p')}")
77
78 def tell_joke(self, args, raw_data):
79 """Return a random joke."""
80 import random
81 jokes = [
82 "Why do programmers prefer dark mode? Because light attracts bugs!",
83 "Why did the developer go broke? Because they used up all their cache!",
84 "There are only 10 types of people: those who understand binary and those who don't.",
85 ]
86 return FunctionResult(random.choice(jokes))
87
88if __name__ == "__main__":
89 agent = CompleteFirstAgent()
90 print("Complete First Agent running at http://localhost:3000")
91 agent.run()

Next Steps

Your agent is running locally, but SignalWire can’t reach localhost. You need to expose it to the internet.

Continue to:

  1. Development Environment - Set up a professional project structure with environment variables and testing
  2. Exposing Your Agent - Make your agent accessible via ngrok so SignalWire can reach it

Or jump ahead to: