Static vs Dynamic Agents

View as MarkdownOpen in Claude

Understanding the Difference

AspectStatic AgentDynamic Agent
ConfigurationSet once at startupPer-request based on call data
BehaviorSame for all callersDifferent for different callers

Use Static When:

  • Same prompt for everyone
  • Generic assistant
  • Simple IVR
  • FAQ bot

Use Dynamic When:

  • Personalized greetings
  • Caller-specific data
  • Account-based routing
  • Multi-tenant applications

Static Agents

Static agents have fixed configuration determined at instantiation time.

Example: Static Customer Service Agent

1from signalwire import AgentBase, FunctionResult
2
3class StaticSupportAgent(AgentBase):
4 """Same behavior for all callers."""
5
6 def __init__(self):
7 super().__init__(name="static-support")
8
9 self.add_language("English", "en-US", "rime.spore")
10
11 self.prompt_add_section(
12 "Role",
13 "You are a customer service agent for Acme Corp. "
14 "Help callers with general inquiries about our products."
15 )
16
17 self.prompt_add_section(
18 "Guidelines",
19 bullets=[
20 "Be helpful and professional",
21 "Answer questions about products",
22 "Transfer complex issues to support"
23 ]
24 )
25
26 self.define_tool(
27 name="get_store_hours",
28 description="Get store hours",
29 parameters={},
30 handler=self.get_store_hours
31 )
32
33 def get_store_hours(self, args, raw_data):
34 return FunctionResult(
35 "We're open Monday through Friday, 9 AM to 5 PM."
36 )
37
38if __name__ == "__main__":
39 agent = StaticSupportAgent()
40 agent.run()

Dynamic Agents

Dynamic agents customize their behavior based on the incoming request using the on_swml_request method.

The on_swml_request Method

LanguageHook Method
Pythondef on_swml_request(self, request_data=None, callback_path=None, request=None)
TypeScriptonSwmlRequest(requestData?, callbackPath?, request?)
1def on_swml_request(self, request_data=None, callback_path=None, request=None):
2 """
3 Called before SWML is generated for each request.
4
5 Args:
6 request_data: Optional dict containing the parsed POST body from SignalWire.
7 Call information is nested under the 'call' key.
8 callback_path: Optional callback path for routing
9 request: Optional FastAPI Request object
10 """
11 pass

Example: Dynamic Personalized Agent

1from signalwire import AgentBase, FunctionResult
2
3class DynamicPersonalizedAgent(AgentBase):
4 """Customizes greeting based on caller."""
5
6 CUSTOMERS = {
7 "+15551234567": {"name": "John Smith", "tier": "gold", "account": "A001"},
8 "+15559876543": {"name": "Jane Doe", "tier": "platinum", "account": "A002"},
9 }
10
11 def __init__(self):
12 super().__init__(name="dynamic-agent")
13 self.add_language("English", "en-US", "rime.spore")
14 self.set_params({"end_of_speech_timeout": 500, "attention_timeout": 15000})
15 self.define_tool(name="get_account_status", description="Get the caller's account status", parameters={}, handler=self.get_account_status)
16 self._current_caller = None
17
18 def on_swml_request(self, request_data=None, callback_path=None, request=None):
19 call_data = (request_data or {}).get("call", {})
20 caller_num = call_data.get("from") or call_data.get("from_number", "")
21 customer = self.CUSTOMERS.get(caller_num)
22
23 if customer:
24 self._current_caller = customer
25 self.prompt_add_section("Role",
26 f"You are a premium support agent for Acme Corp. "
27 f"You are speaking with {customer['name']}, a {customer['tier']} member.")
28 self.prompt_add_section("Context",
29 f"Customer account: {customer['account']}\nMembership tier: {customer['tier'].upper()}")
30 if customer["tier"] == "platinum":
31 self.prompt_add_section("Special Treatment",
32 "This is a platinum customer. Prioritize their requests and offer expedited service on all issues.")
33 else:
34 self._current_caller = None
35 self.prompt_add_section("Role",
36 "You are a customer service agent for Acme Corp. Help the caller with their inquiry and offer to create an account.")
37
38 def get_account_status(self, args, raw_data):
39 if self._current_caller:
40 return FunctionResult(f"Account {self._current_caller['account']} is active. Tier: {self._current_caller['tier'].upper()}")
41 return FunctionResult("No account found. Would you like to create one?")
42
43if __name__ == "__main__":
44 agent = DynamicPersonalizedAgent()
45 agent.run()

Request Data Fields

The request_data dictionary is the parsed POST body from SignalWire. Call information is nested under the call key:

FieldDescriptionExample
call["call_id"]Unique call identifier"a1b2c3d4-..."
call["from"]Caller’s phone number"+15551234567"
call["from_number"]Alternative caller number field"+15551234567"
call["to"]Number that was called"+15559876543"
call["direction"]Call direction"inbound"

Always use defensive access when working with request_data.

1def on_swml_request(self, request_data=None, callback_path=None, request=None):
2 call_data = (request_data or {}).get("call", {})
3 caller_num = call_data.get("from") or call_data.get("from_number", "")
4 call_id = call_data.get("call_id", "")

Dynamic Function Registration

You can also register functions dynamically based on the caller:

1class DynamicFunctionsAgent(AgentBase):
2 """Different functions for different callers."""
3
4 ADMIN_NUMBERS = ["+15551111111", "+15552222222"]
5
6 def __init__(self):
7 super().__init__(name="dynamic-functions")
8 self.add_language("English", "en-US", "rime.spore")
9 self.define_tool(name="get_info", description="Get general information", parameters={}, handler=self.get_info)
10
11 def on_swml_request(self, request_data=None, callback_path=None, request=None):
12 call_data = (request_data or {}).get("call", {})
13 caller_num = call_data.get("from") or call_data.get("from_number", "")
14
15 self.prompt_add_section("Role", "You are a helpful assistant.")
16
17 if caller_num in self.ADMIN_NUMBERS:
18 self.prompt_add_section("Admin Access",
19 "This caller has administrator privileges. They can access system administration functions.")
20 self.define_tool(name="admin_reset", description="Reset system configuration (admin only)", parameters={}, handler=self.admin_reset)
21 self.define_tool(name="admin_report", description="Generate system report (admin only)", parameters={}, handler=self.admin_report)
22
23 def get_info(self, args, raw_data):
24 return FunctionResult("General information...")
25
26 def admin_reset(self, args, raw_data):
27 return FunctionResult("System reset initiated.")
28
29 def admin_report(self, args, raw_data):
30 return FunctionResult("Report generated: All systems operational.")

Multi-Tenant Applications

Dynamic agents are ideal for multi-tenant scenarios:

1class MultiTenantAgent(AgentBase):
2 """Different branding per tenant."""
3
4 TENANTS = {
5 "+15551111111": {"company": "Acme Corp", "voice": "rime.spore", "greeting": "Welcome to Acme Corp support!"},
6 "+15552222222": {"company": "Beta Industries", "voice": "rime.marsh", "greeting": "Thank you for calling Beta Industries!"}
7 }
8
9 def __init__(self):
10 super().__init__(name="multi-tenant")
11
12 def on_swml_request(self, request_data=None, callback_path=None, request=None):
13 call_data = (request_data or {}).get("call", {})
14 called_num = call_data.get("to", "")
15
16 tenant = self.TENANTS.get(called_num, {"company": "Default Company", "voice": "rime.spore", "greeting": "Hello!"})
17
18 self.add_language("English", "en-US", tenant["voice"])
19 self.prompt_add_section("Role",
20 f"You are a customer service agent for {tenant['company']}. Start by saying: {tenant['greeting']}")

Comparison Summary

AspectStaticDynamic
ConfigurationOnce at startupPer-request
PerformanceSlightly fasterMinimal overhead
Use CaseGeneric assistantsPersonalized experiences
ComplexitySimplerMore complex
TestingEasierRequires more scenarios
Method__init__ onlyon_swml_request

Best Practices

  1. Start static, go dynamic when needed - Don’t over-engineer
  2. Cache expensive lookups - Database calls in on_swml_request add latency
  3. Clear prompts between calls - Use self.pom.clear() if reusing sections
  4. Log caller info - Helps with debugging dynamic behavior
  5. Test multiple scenarios - Each caller path needs testing
1def on_swml_request(self, request_data=None, callback_path=None, request=None):
2 # Clear previous dynamic configuration
3 self.pom.clear()
4
5 # Extract call data
6 call_data = (request_data or {}).get("call", {})
7
8 # Log for debugging
9 self.log.info("request_received",
10 caller=call_data.get("from") or call_data.get("from_number"),
11 called=call_data.get("to")
12 )
13
14 # Configure based on request
15 self._configure_for_caller(request_data)