By Complexity
Examples by Complexity
Summary: Progressive examples from simple to advanced, helping you build increasingly sophisticated agents.
Beginner Examples
Hello World Agent
The simplest possible agent:
#!/usr/bin/env python3
## hello_world_agent.py - Simplest possible agent
from signalwire_agents import AgentBase
agent = AgentBase(name="hello", route="/hello")
agent.prompt_add_section("Role", "Say hello and have a friendly conversation.")
agent.add_language("English", "en-US", "rime.spore")
if __name__ == "__main__":
agent.run()
FAQ Agent
Agent that answers questions from a knowledge base:
#!/usr/bin/env python3
## faq_agent.py - Agent with knowledge base
from signalwire_agents import AgentBase
agent = AgentBase(name="faq", route="/faq")
agent.prompt_add_section("Role", "Answer questions about our company.")
agent.prompt_add_section("Information", """
Our hours are Monday to Friday, 9 AM to 5 PM.
We are located at 123 Main Street.
Contact us at support@example.com.
""")
agent.add_language("English", "en-US", "rime.spore")
if __name__ == "__main__":
agent.run()
Greeting Agent
Agent with a custom greeting:
#!/usr/bin/env python3
## greeting_agent.py - Agent with custom greeting
from signalwire_agents import AgentBase
agent = AgentBase(name="greeter", route="/greeter")
agent.prompt_add_section("Role", "You are a friendly receptionist.")
agent.prompt_add_section("Greeting", """
Always start by saying: "Thank you for calling Acme Corporation. How may I help you today?"
""")
agent.add_language("English", "en-US", "rime.spore")
if __name__ == "__main__":
agent.run()
Intermediate Examples
Account Lookup Agent
Agent with database lookup:
#!/usr/bin/env python3
## account_lookup_agent.py - Agent with database lookup
from signalwire_agents import AgentBase
from signalwire_agents.core.function_result import SwaigFunctionResult
## Simulated database
ACCOUNTS = {
"12345": {"name": "John Doe", "balance": 150.00, "status": "active"},
"67890": {"name": "Jane Smith", "balance": 500.00, "status": "active"},
}
agent = AgentBase(name="accounts", route="/accounts")
agent.prompt_add_section("Role", "You help customers check their account status.")
agent.prompt_add_section("Guidelines", """
- Always verify the account ID before providing information
- Be helpful and professional
- Never share information about other accounts
""")
agent.add_language("English", "en-US", "rime.spore")
@agent.tool(description="Look up account information by ID")
def lookup_account(account_id: str) -> SwaigFunctionResult:
account = ACCOUNTS.get(account_id)
if account:
return SwaigFunctionResult(
f"Account for {account['name']}: Status is {account['status']}, "
f"balance is ${account['balance']:.2f}"
)
return SwaigFunctionResult("Account not found. Please check the ID and try again.")
if __name__ == "__main__":
agent.run()
Appointment Scheduler
Agent that books appointments with confirmation:
#!/usr/bin/env python3
## appointment_scheduler_agent.py - Agent that books appointments
from signalwire_agents import AgentBase
from signalwire_agents.core.function_result import SwaigFunctionResult
from datetime import datetime
appointments = []
agent = AgentBase(name="scheduler", route="/scheduler")
agent.prompt_add_section("Role", "You help customers schedule appointments.")
agent.prompt_add_section("Guidelines", """
- Collect customer name, date, and preferred time
- Confirm all details before booking
- Send SMS confirmation when booking is complete
""")
agent.add_language("English", "en-US", "rime.spore")
@agent.tool(description="Check if a time slot is available")
def check_availability(date: str, time: str) -> SwaigFunctionResult:
# Check against existing appointments
for apt in appointments:
if apt["date"] == date and apt["time"] == time:
return SwaigFunctionResult(f"Sorry, {date} at {time} is not available.")
return SwaigFunctionResult(f"{date} at {time} is available.")
@agent.tool(description="Book an appointment")
def book_appointment(
name: str,
phone: str,
date: str,
time: str
) -> SwaigFunctionResult:
appointments.append({
"name": name,
"phone": phone,
"date": date,
"time": time,
"booked_at": datetime.now().isoformat()
})
return (
SwaigFunctionResult(f"Appointment booked for {name} on {date} at {time}.")
.send_sms(
to_number=phone,
from_number="+15559876543",
body=f"Your appointment is confirmed for {date} at {time}."
)
)
if __name__ == "__main__":
agent.run()
Department Router
Agent that routes calls to the right department:
#!/usr/bin/env python3
## department_router_agent.py - Agent that routes calls
from signalwire_agents import AgentBase
from signalwire_agents.core.function_result import SwaigFunctionResult
DEPARTMENTS = {
"sales": "+15551001001",
"support": "+15551001002",
"billing": "+15551001003",
"hr": "+15551001004"
}
agent = AgentBase(name="router", route="/router")
agent.prompt_add_section("Role", "You are a receptionist routing calls.")
agent.prompt_add_section("Departments", """
Available departments:
- Sales: Product inquiries, pricing, quotes
- Support: Technical help, troubleshooting
- Billing: Payments, invoices, refunds
- HR: Employment, benefits, careers
""")
agent.add_language("English", "en-US", "rime.spore")
@agent.tool(description="Transfer to a specific department")
def transfer_to_department(department: str) -> SwaigFunctionResult:
dept_lower = department.lower()
if dept_lower in DEPARTMENTS:
return (
SwaigFunctionResult(f"Transferring you to {department} now.")
.connect(DEPARTMENTS[dept_lower], final=True)
)
return SwaigFunctionResult(
f"I don't have a {department} department. "
"Available departments are: sales, support, billing, and HR."
)
if __name__ == "__main__":
agent.run()
Advanced Examples
Multi-Skill Agent
Agent combining multiple skills:
#!/usr/bin/env python3
## multi_skill_agent.py - Agent with multiple skills
from signalwire_agents import AgentBase
from signalwire_agents.core.function_result import SwaigFunctionResult
agent = AgentBase(name="assistant", route="/assistant")
agent.prompt_add_section("Role", "You are a comprehensive assistant.")
agent.prompt_add_section("Capabilities", """
You can:
- Tell the current time and date
- Search our knowledge base
- Look up weather information
- Transfer to support if needed
""")
agent.add_language("English", "en-US", "rime.spore")
## Add built-in skills
agent.add_skill("datetime")
agent.add_skill("native_vector_search", {
"index_path": "./knowledge.swsearch",
"tool_name": "search_kb"
})
## Custom function
@agent.tool(description="Transfer to human support")
def transfer_support() -> SwaigFunctionResult:
return (
SwaigFunctionResult("Connecting you to a support representative.")
.connect("+15551234567", final=True)
)
if __name__ == "__main__":
agent.run()
Order Processing Agent
Complete order management system:
#!/usr/bin/env python3
## order_processing_agent.py - Complete order management system
from signalwire_agents import AgentBase
from signalwire_agents.core.function_result import SwaigFunctionResult
from datetime import datetime
import uuid
## Simulated databases
orders = {}
products = {
"widget": {"price": 29.99, "stock": 100},
"gadget": {"price": 49.99, "stock": 50},
"device": {"price": 99.99, "stock": 25}
}
agent = AgentBase(name="orders", route="/orders")
agent.prompt_add_section("Role", "You help customers with orders.")
agent.prompt_add_section("Products", """
Available products:
- Widget: $29.99
- Gadget: $49.99
- Device: $99.99
""")
agent.prompt_add_section("Guidelines", """
- Verify product availability before placing orders
- Collect customer name and phone for orders
- Confirm order details before finalizing
- Provide order ID for tracking
""")
agent.add_language("English", "en-US", "rime.spore")
agent.set_global_data({"current_order": None})
@agent.tool(description="Check product availability")
def check_product(product: str) -> SwaigFunctionResult:
prod = products.get(product.lower())
if prod:
return SwaigFunctionResult(
f"{product.title()}: ${prod['price']}, {prod['stock']} in stock."
)
return SwaigFunctionResult(f"Product '{product}' not found.")
@agent.tool(description="Place an order")
def place_order(
product: str,
quantity: int,
customer_name: str,
customer_phone: str
) -> SwaigFunctionResult:
prod = products.get(product.lower())
if not prod:
return SwaigFunctionResult(f"Product '{product}' not found.")
if prod["stock"] < quantity:
return SwaigFunctionResult(f"Insufficient stock. Only {prod['stock']} available.")
order_id = str(uuid.uuid4())[:8].upper()
total = prod["price"] * quantity
orders[order_id] = {
"product": product,
"quantity": quantity,
"total": total,
"customer": customer_name,
"phone": customer_phone,
"status": "confirmed",
"created": datetime.now().isoformat()
}
prod["stock"] -= quantity
return (
SwaigFunctionResult(
f"Order {order_id} confirmed! {quantity}x {product} for ${total:.2f}."
)
.update_global_data({"last_order_id": order_id})
.send_sms(
to_number=customer_phone,
from_number="+15559876543",
body=f"Order {order_id} confirmed: {quantity}x {product}, ${total:.2f}"
)
)
@agent.tool(description="Check order status")
def order_status(order_id: str) -> SwaigFunctionResult:
order = orders.get(order_id.upper())
if order:
return SwaigFunctionResult(
f"Order {order_id}: {order['quantity']}x {order['product']}, "
f"${order['total']:.2f}, Status: {order['status']}"
)
return SwaigFunctionResult(f"Order {order_id} not found.")
if __name__ == "__main__":
agent.run()
Multi-Agent Server
Server hosting multiple specialized agents:
#!/usr/bin/env python3
## multi_agent_server.py - Server hosting multiple agents
from signalwire_agents import AgentBase, AgentServer
from signalwire_agents.core.function_result import SwaigFunctionResult
class SalesAgent(AgentBase):
def __init__(self):
super().__init__(name="sales", route="/sales")
self.prompt_add_section("Role", "You are a sales specialist.")
self.add_language("English", "en-US", "rime.spore")
@AgentBase.tool(description="Get product pricing")
def get_pricing(self, product: str) -> SwaigFunctionResult:
return SwaigFunctionResult(f"Pricing for {product}: Starting at $99.")
class SupportAgent(AgentBase):
def __init__(self):
super().__init__(name="support", route="/support")
self.prompt_add_section("Role", "You are a support specialist.")
self.add_language("English", "en-US", "rime.spore")
self.add_skill("native_vector_search", {
"index_path": "./support_docs.swsearch"
})
@AgentBase.tool(description="Create support ticket")
def create_ticket(self, issue: str) -> SwaigFunctionResult:
return SwaigFunctionResult(f"Ticket created for: {issue}")
class RouterAgent(AgentBase):
def __init__(self):
super().__init__(name="router", route="/")
self.prompt_add_section("Role", "Route callers to the right agent.")
self.add_language("English", "en-US", "rime.spore")
@AgentBase.tool(description="Transfer to sales")
def transfer_sales(self) -> SwaigFunctionResult:
return SwaigFunctionResult("Transferring to sales.").connect(
"https://agent.example.com/sales", final=True
)
@AgentBase.tool(description="Transfer to support")
def transfer_support(self) -> SwaigFunctionResult:
return SwaigFunctionResult("Transferring to support.").connect(
"https://agent.example.com/support", final=True
)
if __name__ == "__main__":
server = AgentServer(host="0.0.0.0", port=8080)
server.register(RouterAgent())
server.register(SalesAgent())
server.register(SupportAgent())
server.run()
Expert Examples
Code-Driven LLM Architecture
The most robust agents use code-driven architecture where business logic lives in SWAIG functions, not prompts. The LLM becomes a natural language translator while code handles all validation, state, and business rules.
┌─────────────────────────────────────────────────────────────┐
│ Code-Driven Approach │
│ │
│ User ──► LLM ──► SWAIG Function ──► Response to LLM │
│ │ │
│ ▼ │
│ Code enforces: │
│ • Business rules │
│ • State management │
│ • Calculations │
│ • Validation │
│ • UI updates │
│ │
│ Advantage: LLM only translates intent, code does the rest │
└─────────────────────────────────────────────────────────────┘
Core principles:
| Traditional Approach | Code-Driven Approach |
|---|---|
| Rules in prompts | Rules in functions |
| LLM does math | Code does math |
| LLM tracks state | Global data tracks state |
| Hope LLM follows rules | Code enforces rules |
Order-Taking Agent (Code-Driven)
Complete example demonstrating code-driven patterns:
#!/usr/bin/env python3
## code_driven_order_agent.py - Code-driven LLM architecture example
from signalwire_agents import AgentBase
from signalwire_agents.core.function_result import SwaigFunctionResult
## Menu data lives in code, not prompts
MENU = {
"tacos": {
"T001": {"name": "Beef Taco", "price": 3.49},
"T002": {"name": "Chicken Taco", "price": 3.49},
"T003": {"name": "Fish Taco", "price": 4.29},
},
"sides": {
"S001": {"name": "Chips & Salsa", "price": 2.99},
"S002": {"name": "Guacamole", "price": 3.49},
},
"drinks": {
"D001": {"name": "Soda", "price": 1.99},
"D002": {"name": "Iced Tea", "price": 1.99},
},
"combos": {
"C001": {"name": "Taco Combo", "price": 9.99,
"includes": ["taco", "chips", "drink"], "savings": 1.97},
}
}
## Aliases handle natural speech variations
MENU_ALIASES = {
"D001": ["soda", "coke", "pop", "soft drink"],
"S001": ["chips", "chips and salsa", "nachos"],
}
TAX_RATE = 0.10
MAX_ITEMS_PER_ADD = 10
MAX_ORDER_VALUE = 500.00
class OrderAgent(AgentBase):
def __init__(self):
super().__init__(name="order-agent", route="/order")
self.add_language("English", "en-US", "rime.spore")
# Minimal prompt - personality only, not rules
self.prompt_add_section("Role",
"You are a friendly drive-thru order taker. "
"Keep responses brief and natural."
)
# State machine controls conversation flow
self._setup_contexts()
# Initialize order state
self.set_global_data({
"order_state": {
"items": [],
"subtotal": 0.00,
"tax": 0.00,
"total": 0.00,
"item_count": 0
}
})
def _setup_contexts(self):
"""Define state machine for conversation flow."""
contexts = self.define_contexts()
ctx = contexts.add_context("default")
# Greeting state - limited actions
ctx.add_step("greeting") \
.add_section("Task", "Welcome the customer and take their order.") \
.set_functions(["add_item"]) \
.set_valid_steps(["taking_order"])
# Order state - full ordering capabilities
ctx.add_step("taking_order") \
.add_section("Task", "Continue taking the order.") \
.add_bullets("Info", [
"Current total: $${global_data.order_state.total}",
"Items: ${global_data.order_state.item_count}"
]) \
.set_functions(["add_item", "remove_item", "finalize_order"]) \
.set_valid_steps(["confirming"])
# Confirmation state
ctx.add_step("confirming") \
.add_section("Task", "Confirm the order with the customer.") \
.set_functions(["confirm_order", "add_item", "remove_item"]) \
.set_valid_steps(["complete"])
def _find_menu_item(self, item_name):
"""Find item by name or alias - code handles fuzzy matching."""
item_lower = item_name.lower().strip()
# Check exact matches first
for category, items in MENU.items():
for sku, data in items.items():
if item_lower == data["name"].lower():
return sku, data, category
# Check aliases
for sku, aliases in MENU_ALIASES.items():
if item_lower in [a.lower() for a in aliases]:
for category, items in MENU.items():
if sku in items:
return sku, items[sku], category
return None, None, None
def _calculate_totals(self, items):
"""Code does all math - LLM never calculates."""
subtotal = sum(item["price"] * item["quantity"] for item in items)
tax = round(subtotal * TAX_RATE, 2)
total = round(subtotal + tax, 2)
return subtotal, tax, total
def _check_combo_opportunity(self, items):
"""Code detects upsells - no prompt rules needed."""
item_names = [i["name"].lower() for i in items]
has_taco = any("taco" in n for n in item_names)
has_chips = any("chip" in n for n in item_names)
has_drink = any(n in ["soda", "iced tea"] for n in item_names)
# Check if already has combo
if any("combo" in n for n in item_names):
return None
if has_taco and has_chips and has_drink:
return "Great news! I can upgrade you to a Taco Combo and save you $1.97!"
return None
@AgentBase.tool(
name="add_item",
description="Add an item to the order",
parameters={
"type": "object",
"properties": {
"item_name": {"type": "string", "description": "Name of the menu item"},
"quantity": {"type": "integer", "description": "How many (default 1)",
"minimum": 1, "maximum": 10}
},
"required": ["item_name"]
}
)
def add_item(self, args, raw_data):
"""Add item - code enforces all limits and rules."""
item_name = args.get("item_name", "")
quantity = args.get("quantity", 1)
# Code enforces limits (LLM doesn't need to know)
if quantity > MAX_ITEMS_PER_ADD:
quantity = MAX_ITEMS_PER_ADD
# Get order state
global_data = raw_data.get("global_data", {})
order_state = global_data.get("order_state", {
"items": [], "subtotal": 0, "tax": 0, "total": 0, "item_count": 0
})
# Find the item (code handles fuzzy matching)
sku, item_data, category = self._find_menu_item(item_name)
if not item_data:
return SwaigFunctionResult(
f"I couldn't find '{item_name}' on the menu. "
"We have tacos, chips, guacamole, and drinks."
)
# Check order value limit
potential = order_state["subtotal"] + (item_data["price"] * quantity)
if potential > MAX_ORDER_VALUE:
return SwaigFunctionResult(
f"That would exceed our ${MAX_ORDER_VALUE:.2f} order limit."
)
# Add to order
order_state["items"].append({
"sku": sku,
"name": item_data["name"],
"quantity": quantity,
"price": item_data["price"]
})
order_state["item_count"] += quantity
# Code calculates totals (LLM never does math)
subtotal, tax, total = self._calculate_totals(order_state["items"])
order_state["subtotal"] = subtotal
order_state["tax"] = tax
order_state["total"] = total
# Build response that guides LLM behavior
response = f"Added {quantity}x {item_data['name']} (${item_data['price']:.2f} each)."
# Check for upsell (code decides, not LLM)
combo_suggestion = self._check_combo_opportunity(order_state["items"])
if combo_suggestion:
response += f"\n\n{combo_suggestion}"
# Update state and transition
global_data["order_state"] = order_state
result = SwaigFunctionResult(response)
result.update_global_data(global_data)
result.swml_change_step("taking_order")
# Push UI update (frontend stays in sync without LLM)
result.swml_user_event({
"type": "item_added",
"item": {"name": item_data["name"], "quantity": quantity,
"price": item_data["price"]},
"total": total
})
return result
@AgentBase.tool(
name="remove_item",
description="Remove an item from the order",
parameters={
"type": "object",
"properties": {
"item_name": {"type": "string", "description": "Item to remove"},
"quantity": {"type": "integer", "description": "How many (-1 for all)"}
},
"required": ["item_name"]
}
)
def remove_item(self, args, raw_data):
"""Remove item - code handles all edge cases."""
item_name = args.get("item_name", "").lower()
quantity = args.get("quantity", 1)
global_data = raw_data.get("global_data", {})
order_state = global_data.get("order_state", {"items": []})
# Find matching item in order
for i, item in enumerate(order_state["items"]):
if item_name in item["name"].lower():
if quantity == -1 or quantity >= item["quantity"]:
removed = order_state["items"].pop(i)
order_state["item_count"] -= removed["quantity"]
else:
item["quantity"] -= quantity
order_state["item_count"] -= quantity
# Recalculate
subtotal, tax, total = self._calculate_totals(order_state["items"])
order_state["subtotal"] = subtotal
order_state["tax"] = tax
order_state["total"] = total
global_data["order_state"] = order_state
result = SwaigFunctionResult(f"Removed {item_name} from your order.")
result.update_global_data(global_data)
return result
return SwaigFunctionResult(f"I don't see {item_name} in your order.")
@AgentBase.tool(
name="finalize_order",
description="Finalize and review the order",
parameters={"type": "object", "properties": {}}
)
def finalize_order(self, args, raw_data):
"""Finalize - code builds the summary."""
global_data = raw_data.get("global_data", {})
order_state = global_data.get("order_state", {})
if not order_state.get("items"):
return SwaigFunctionResult("Your order is empty. What can I get you?")
# Code builds accurate summary (LLM just relays it)
items_text = ", ".join(
f"{i['quantity']}x {i['name']}" for i in order_state["items"]
)
result = SwaigFunctionResult(
f"Your order: {items_text}. "
f"Total is ${order_state['total']:.2f} including tax. "
"Does that look correct?"
)
result.swml_change_step("confirming")
return result
@AgentBase.tool(
name="confirm_order",
description="Confirm the order is complete",
parameters={"type": "object", "properties": {}}
)
def confirm_order(self, args, raw_data):
"""Confirm - code handles completion."""
global_data = raw_data.get("global_data", {})
order_state = global_data.get("order_state", {})
# Generate order number
import random
order_num = random.randint(100, 999)
result = SwaigFunctionResult(
f"Order #{order_num} confirmed! "
f"Your total is ${order_state['total']:.2f}. "
"Please pull forward. Thank you!"
)
result.swml_change_step("complete")
# Final UI update
result.swml_user_event({
"type": "order_complete",
"order_number": order_num,
"total": order_state["total"]
})
return result
if __name__ == "__main__":
agent = OrderAgent()
agent.run()
Key patterns demonstrated:
-
Response-guided behavior: Functions return text that guides LLM responses. The combo upsell suggestion appears in the response, so the LLM naturally offers it.
-
Code-enforced limits:
MAX_ITEMS_PER_ADDandMAX_ORDER_VALUEare enforced in code. The LLM cannot bypass them. -
State machine control:
set_functions()restricts what the LLM can do in each state. Impossible actions are literally unavailable. -
Dynamic prompt injection:
${global_data.order_state.total}injects current state into prompts without LLM tracking. -
UI synchronization:
swml_user_event()pushes updates to frontends in real-time. -
Fuzzy input handling:
_find_menu_item()handles variations like "coke" → "Soda" without prompt rules.
Complexity Progression
Beginner
- Create basic agent with prompt
- Add language configuration
- Test with swaig-test
Intermediate
- Add SWAIG functions
- Use global data for state
- Add skills
- Implement call transfers
Advanced
- Use DataMap for API integration
- Implement context workflows
- Build multi-agent systems
- Deploy to production
Expert
- Code-driven LLM architecture
- State machine conversation control
- Response-guided LLM behavior
- Real-time UI synchronization