Best Practices
Best Practices
Summary: Guidelines and recommendations for building production-quality SignalWire voice AI agents.
Overview
| Category | Focus Area |
|---|---|
| Prompt Design | Effective prompts and POM structure |
| Function Design | Well-structured SWAIG functions |
| Error Handling | Graceful failure and recovery |
| Security | Authentication and data protection |
| Performance | Optimization and efficiency |
| Testing | Validation and quality assurance |
| Monitoring | Logging and observability |
Prompt Design
Use POM (Prompt Object Model)
Structure prompts with clear sections:
from signalwire_agents import AgentBase
agent = AgentBase(name="service", route="/service")
## Good: Structured sections
agent.prompt_add_section("Role", """
You are a customer service representative for Acme Corp.
""")
agent.prompt_add_section("Guidelines", body="Follow these rules:", bullets=[
"Be professional and courteous",
"Verify customer identity before account access",
"Never share sensitive information",
"Escalate complex issues to human agents"
])
agent.add_language("English", "en-US", "rime.spore")
Be Specific About Behavior
## Good: Specific instructions
agent.prompt_add_section("Response Style", """
- Keep responses under 3 sentences for simple questions
- Ask one question at a time
- Confirm understanding before taking action
- Use the customer's name when known
""")
Function Design
Clear Descriptions
from signalwire_agents import AgentBase
from signalwire_agents.core.function_result import SwaigFunctionResult
agent = AgentBase(name="accounts", route="/accounts")
## Good: Descriptive with parameter details
@agent.tool(
description="Look up customer account by account number. "
"Returns account status, balance, and last activity date."
)
def lookup_account(
account_number: str # The 8-digit account number
) -> SwaigFunctionResult:
pass
Return Actionable Information
@agent.tool(description="Check product availability")
def check_availability(product_id: str) -> SwaigFunctionResult:
stock = get_stock(product_id)
if stock > 10:
return SwaigFunctionResult(
f"Product {product_id} is in stock with {stock} units available. "
"The customer can place an order."
)
elif stock > 0:
return SwaigFunctionResult(
f"Product {product_id} has limited stock ({stock} units). "
"Suggest ordering soon."
)
else:
return SwaigFunctionResult(
f"Product {product_id} is out of stock. "
"Expected restock date: next week."
)
Error Handling
Graceful Degradation
@agent.tool(description="Look up order status")
def order_status(order_id: str) -> SwaigFunctionResult:
try:
order = fetch_order(order_id)
return SwaigFunctionResult(
f"Order {order_id}: Status is {order['status']}"
)
except OrderNotFoundError:
return SwaigFunctionResult(
f"Order {order_id} was not found. "
"Please verify the order number and try again."
)
except ServiceUnavailableError:
return SwaigFunctionResult(
"The order system is temporarily unavailable. "
"Please try again in a few minutes."
)
Security
Use Authentication
import os
agent = AgentBase(
name="secure",
route="/secure",
basic_auth=(
os.environ.get("AGENT_USER", "agent"),
os.environ.get("AGENT_PASSWORD")
)
)
Secure Function Tokens
## Require token authentication for sensitive functions
@agent.tool(
description="Process payment",
secure=True # Requires valid token
)
def process_payment(amount: float, card_last_four: str) -> SwaigFunctionResult:
pass
Environment Variables
| Variable | Purpose |
|---|---|
SWML_BASIC_AUTH_USER | Basic auth username |
SWML_BASIC_AUTH_PASSWORD | Basic auth password (required for production) |
SWML_SSL_ENABLED | Enable HTTPS |
SWML_SSL_CERT_PATH | SSL certificate path |
SWML_SSL_KEY_PATH | SSL key path |
Performance
Use DataMap for Simple API Calls
from signalwire_agents.core.data_map import DataMap
## Good: DataMap for simple lookups (no webhook roundtrip)
weather_map = DataMap(
name="get_weather",
description="Get weather for a city"
)
weather_map.add_parameter("city", "string", "City name", required=True)
weather_map.add_webhook(
url="https://api.weather.com/v1/current?q=${enc:args.city}",
method="GET",
output_map={"response": "Weather: ${response.temp}F, ${response.condition}"}
)
agent.add_data_map_tool(weather_map)
Use Fillers for Long Operations
@agent.tool(
description="Search database",
fillers=["Searching...", "This may take a moment..."]
)
def search_db(query: str) -> SwaigFunctionResult:
# Long-running search
results = search_database(query)
return SwaigFunctionResult(f"Found {len(results)} matching orders.")
Testing
Use swaig-test
## Validate agent configuration
swaig-test agent.py --dump-swml
## List available functions
swaig-test agent.py --list-tools
## Test specific function
swaig-test agent.py --exec lookup_account --account_number "12345678"
Monitoring
Use Structured Logging
import structlog
logger = structlog.get_logger()
@agent.tool(description="Process refund")
def process_refund(order_id: str, amount: float) -> SwaigFunctionResult:
logger.info(
"refund_requested",
order_id=order_id,
amount=amount
)
# Process refund
return SwaigFunctionResult(f"Refund of ${amount} processed.")
Production Readiness Checklist
- Authentication configured (basic_auth or environment variables)
- SSL/HTTPS enabled for production
- Sensitive functions marked as secure
- Error handling in all functions
- Input validation for user-provided data
- Logging configured (no sensitive data in logs)
- All functions tested with swaig-test
- Edge cases and error scenarios tested
- Prompts reviewed for clarity and completeness
- Transfer/escalation paths defined
- Timeout values appropriate for use case
- Summary handling for call analytics