***

title: Static vs Dynamic Agents
description: Choose between static agents with fixed configuration and dynamic agents that customize behavior per-call based on caller data.
slug: /guides/static-vs-dynamic
max-toc-depth: 3
---------------------

For a complete index of all SignalWire documentation pages, fetch https://signalwire.com/docs/llms.txt

### Understanding the Difference

| Aspect            | Static Agent         | Dynamic Agent                   |
| ----------------- | -------------------- | ------------------------------- |
| **Configuration** | Set once at startup  | Per-request based on call data  |
| **Behavior**      | Same for all callers | Different 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

<Tabs>
  <Tab title="Python">
    ```python
    from signalwire import AgentBase, FunctionResult

    class StaticSupportAgent(AgentBase):
        """Same behavior for all callers."""

        def __init__(self):
            super().__init__(name="static-support")

            self.add_language("English", "en-US", "rime.spore")

            self.prompt_add_section(
                "Role",
                "You are a customer service agent for Acme Corp. "
                "Help callers with general inquiries about our products."
            )

            self.prompt_add_section(
                "Guidelines",
                bullets=[
                    "Be helpful and professional",
                    "Answer questions about products",
                    "Transfer complex issues to support"
                ]
            )

            self.define_tool(
                name="get_store_hours",
                description="Get store hours",
                parameters={},
                handler=self.get_store_hours
            )

        def get_store_hours(self, args, raw_data):
            return FunctionResult(
                "We're open Monday through Friday, 9 AM to 5 PM."
            )

    if __name__ == "__main__":
        agent = StaticSupportAgent()
        agent.run()
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript
    import { AgentBase, FunctionResult } from 'signalwire-agents';
    const agent = new AgentBase({ name: "static-support" });
    agent.addLanguage({ name: 'English', code: 'en-US', voice: 'rime.spore' });
    agent.promptAddSection('Role', 'You are a customer service agent for Acme Corp. ' + 'Help callers with general inquiries about our products.');
    agent.promptAddSection('Guidelines', { bullets: ['Be helpful and professional', 'Answer questions about products', 'Transfer complex issues to support'] });
    agent.defineTool({ name: 'get_store_hours', description: 'Get store hours', parameters: {}, handler: async () => new FunctionResult("We're open Monday through Friday, 9 AM to 5 PM.") });
    agent.run();
    ```
  </Tab>
</Tabs>

### Dynamic Agents

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

#### The on\_swml\_request Method

| Language   | Hook Method                                                                      |
| ---------- | -------------------------------------------------------------------------------- |
| Python     | `def on_swml_request(self, request_data=None, callback_path=None, request=None)` |
| TypeScript | `onSwmlRequest(requestData?, callbackPath?, request?)`                           |

```python
def on_swml_request(self, request_data=None, callback_path=None, request=None):
    """
    Called before SWML is generated for each request.

    Args:
        request_data: Optional dict containing the parsed POST body from SignalWire.
                     Call information is nested under the 'call' key.
        callback_path: Optional callback path for routing
        request: Optional FastAPI Request object
    """
    pass
```

#### Example: Dynamic Personalized Agent

```python
from signalwire import AgentBase, FunctionResult

class DynamicPersonalizedAgent(AgentBase):
    """Customizes greeting based on caller."""

    CUSTOMERS = {
        "+15551234567": {"name": "John Smith", "tier": "gold", "account": "A001"},
        "+15559876543": {"name": "Jane Doe", "tier": "platinum", "account": "A002"},
    }

    def __init__(self):
        super().__init__(name="dynamic-agent")
        self.add_language("English", "en-US", "rime.spore")
        self.set_params({"end_of_speech_timeout": 500, "attention_timeout": 15000})
        self.define_tool(name="get_account_status", description="Get the caller's account status", parameters={}, handler=self.get_account_status)
        self._current_caller = None

    def on_swml_request(self, request_data=None, callback_path=None, request=None):
        call_data = (request_data or {}).get("call", {})
        caller_num = call_data.get("from") or call_data.get("from_number", "")
        customer = self.CUSTOMERS.get(caller_num)

        if customer:
            self._current_caller = customer
            self.prompt_add_section("Role",
                f"You are a premium support agent for Acme Corp. "
                f"You are speaking with {customer['name']}, a {customer['tier']} member.")
            self.prompt_add_section("Context",
                f"Customer account: {customer['account']}\nMembership tier: {customer['tier'].upper()}")
            if customer["tier"] == "platinum":
                self.prompt_add_section("Special Treatment",
                    "This is a platinum customer. Prioritize their requests and offer expedited service on all issues.")
        else:
            self._current_caller = None
            self.prompt_add_section("Role",
                "You are a customer service agent for Acme Corp. Help the caller with their inquiry and offer to create an account.")

    def get_account_status(self, args, raw_data):
        if self._current_caller:
            return FunctionResult(f"Account {self._current_caller['account']} is active. Tier: {self._current_caller['tier'].upper()}")
        return FunctionResult("No account found. Would you like to create one?")

if __name__ == "__main__":
    agent = DynamicPersonalizedAgent()
    agent.run()
```

### Request Data Fields

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

| Field                 | Description                     | Example          |
| --------------------- | ------------------------------- | ---------------- |
| `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"`      |

<Note>
  Always use defensive access when working with `request_data`.
</Note>

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

### Dynamic Function Registration

You can also register functions dynamically based on the caller:

```python
class DynamicFunctionsAgent(AgentBase):
    """Different functions for different callers."""

    ADMIN_NUMBERS = ["+15551111111", "+15552222222"]

    def __init__(self):
        super().__init__(name="dynamic-functions")
        self.add_language("English", "en-US", "rime.spore")
        self.define_tool(name="get_info", description="Get general information", parameters={}, handler=self.get_info)

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

        self.prompt_add_section("Role", "You are a helpful assistant.")

        if caller_num in self.ADMIN_NUMBERS:
            self.prompt_add_section("Admin Access",
                "This caller has administrator privileges. They can access system administration functions.")
            self.define_tool(name="admin_reset", description="Reset system configuration (admin only)", parameters={}, handler=self.admin_reset)
            self.define_tool(name="admin_report", description="Generate system report (admin only)", parameters={}, handler=self.admin_report)

    def get_info(self, args, raw_data):
        return FunctionResult("General information...")

    def admin_reset(self, args, raw_data):
        return FunctionResult("System reset initiated.")

    def admin_report(self, args, raw_data):
        return FunctionResult("Report generated: All systems operational.")
```

### Multi-Tenant Applications

Dynamic agents are ideal for multi-tenant scenarios:

```python
class MultiTenantAgent(AgentBase):
    """Different branding per tenant."""

    TENANTS = {
        "+15551111111": {"company": "Acme Corp", "voice": "rime.spore", "greeting": "Welcome to Acme Corp support!"},
        "+15552222222": {"company": "Beta Industries", "voice": "rime.marsh", "greeting": "Thank you for calling Beta Industries!"}
    }

    def __init__(self):
        super().__init__(name="multi-tenant")

    def on_swml_request(self, request_data=None, callback_path=None, request=None):
        call_data = (request_data or {}).get("call", {})
        called_num = call_data.get("to", "")

        tenant = self.TENANTS.get(called_num, {"company": "Default Company", "voice": "rime.spore", "greeting": "Hello!"})

        self.add_language("English", "en-US", tenant["voice"])
        self.prompt_add_section("Role",
            f"You are a customer service agent for {tenant['company']}. Start by saying: {tenant['greeting']}")
```

### Comparison Summary

| Aspect            | Static             | Dynamic                  |
| ----------------- | ------------------ | ------------------------ |
| **Configuration** | Once at startup    | Per-request              |
| **Performance**   | Slightly faster    | Minimal overhead         |
| **Use Case**      | Generic assistants | Personalized experiences |
| **Complexity**    | Simpler            | More complex             |
| **Testing**       | Easier             | Requires more scenarios  |
| **Method**        | `__init__` only    | `on_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

```python
def on_swml_request(self, request_data=None, callback_path=None, request=None):
    # Clear previous dynamic configuration
    self.pom.clear()

    # Extract call data
    call_data = (request_data or {}).get("call", {})

    # Log for debugging
    self.log.info("request_received",
        caller=call_data.get("from") or call_data.get("from_number"),
        called=call_data.get("to")
    )

    # Configure based on request
    self._configure_for_caller(request_data)
```