Contexts and Steps Guide
Overview
The Contexts and Steps system enhances traditional Prompt Object Model (POM) prompts in SignalWire AI agents by adding structured workflows on top of your base prompt. Instead of just defining a single prompt, you create workflows with explicit steps, navigation rules, and completion criteria.
Key Benefits
- Structured Workflows: Define clear, step-by-step processes
- Explicit Navigation: Control exactly where users can go next
- Function Restrictions: Limit AI tool access per step
- Completion Criteria: Define clear progression requirements
- Context Isolation: Separate different conversation flows
- Debugging: Easier to trace and debug complex interactions
When to Use Contexts vs Traditional Prompts
Use Contexts and Steps when:
- Building multi-step workflows (onboarding, support tickets, applications)
- Need explicit navigation control between conversation states
- Want to restrict function access based on conversation stage
- Building complex customer service or troubleshooting flows
- Creating guided experiences with clear progression
Use Traditional Prompts when:
- Building simple, freeform conversational agents
- Want maximum flexibility in conversation flow
- Creating general-purpose assistants
- Prototyping or building simple proof-of-concepts
Core Concepts
Contexts
A Context represents a conversation state or workflow area. Contexts can be:
- Workflow Container: Simple step organization without state changes
- Context Switch: Triggers conversation state changes when entered
Each context can define:
- Steps: Individual workflow stages within the context
- Context Prompts: Guidance that applies to all steps in the context
- Entry Parameters: Control conversation state when context is entered
- Navigation Rules: Which other contexts can be accessed
Context Entry Parameters
When entering a context, these parameters control conversation behavior:
post_prompt
: Override the agent's post prompt for this contextsystem_prompt
: Trigger conversation reset with new instructionsconsolidate
: Summarize previous conversation in new promptfull_reset
: Complete system prompt replacement vs injectionuser_prompt
: Inject user message for context establishment
Important: If system_prompt
is present, the context becomes a "Context Switch Context" that processes entry parameters like a context_switch
SWAIG action. Without system_prompt
, it's a "Workflow Container Context" that only organizes steps.
Context Prompts
Contexts can have their own prompts (separate from entry parameters):
# Simple string prompt
context.set_prompt("Context-specific guidance")
# POM-style sections
context.add_section("Department", "Billing Department")
context.add_bullets("Services", ["Payments", "Refunds", "Account inquiries"])
Context prompts provide guidance that applies to all steps within that context, creating a prompt hierarchy: Base Agent Prompt → Context Prompt → Step Prompt.
Steps
A Step is a specific stage within a context. Each step defines:
- Prompt Content: What the AI says/does (text or POM sections)
- Completion Criteria: When the step is considered complete
- Navigation Rules: Where the user can go next
- Function Access: Which AI tools are available
Navigation Control
The system provides fine-grained control over conversation flow:
- Valid Steps: Control movement within a context
- Valid Contexts: Control switching between contexts
- Implicit Navigation: Automatic "next" step progression
- Explicit Navigation: User must explicitly choose next step
Getting Started
Basic Single-Context Workflow
from signalwire_agents import AgentBase
class OnboardingAgent(AgentBase):
def __init__(self):
super().__init__(name="Onboarding Assistant", route="/onboarding")
# Define contexts (replaces traditional prompt setup)
contexts = self.define_contexts()
# Single context must be named "default"
workflow = contexts.add_context("default")
# Step 1: Welcome
workflow.add_step("welcome") \
.set_text("Welcome to our service! Let's get you set up. What's your name?") \
.set_step_criteria("User has provided their name") \
.set_valid_steps(["collect_email"])
# Step 2: Collect Email
workflow.add_step("collect_email") \
.set_text("Thanks! Now I need your email address to create your account.") \
.set_step_criteria("Valid email address has been provided") \
.set_valid_steps(["confirm_details"])
# Step 3: Confirmation
workflow.add_step("confirm_details") \
.set_text("Perfect! Let me confirm your details before we proceed.") \
.set_step_criteria("User has confirmed their information") \
.set_valid_steps(["complete"])
# Step 4: Completion
workflow.add_step("complete") \
.set_text("All set! Your account has been created successfully.")
# No valid_steps = end of workflow
agent = OnboardingAgent()
agent.run()
if __name__ == "__main__":
main()
Multi-Context Workflow
class CustomerServiceAgent(AgentBase):
def __init__(self):
super().__init__(name="Customer Service", route="/service")
# Add skills for enhanced capabilities
self.add_skill("datetime")
self.add_skill("web_search", {
"api_key": "your-api-key",
"search_engine_id": "your-engine-id"
})
contexts = self.define_contexts()
# Main triage context
triage = contexts.add_context("triage")
triage.add_step("greeting") \
.add_section("Current Task", "Understand the customer's need and route appropriately") \
.add_bullets("Required Information", [
"Type of issue they're experiencing",
"Urgency level of the problem",
"Previous troubleshooting attempts"
]) \
.set_step_criteria("Customer's need has been identified") \
.set_valid_contexts(["technical", "billing", "general"])
# Technical support context
tech = contexts.add_context("technical")
tech.add_step("technical_help") \
.add_section("Current Task", "Help diagnose and resolve technical issues") \
.add_section("Available Tools", "Use web search and datetime functions for technical solutions") \
.set_functions(["web_search", "datetime"]) \
.set_step_criteria("Issue is resolved or escalated") \
.set_valid_contexts(["triage"])
# Billing context (restricted functions for security)
billing = contexts.add_context("billing")
billing.add_step("billing_help") \
.set_text("I'll help with your billing question. For security, please provide your account verification.") \
.set_functions("none") \
.set_step_criteria("Billing issue is addressed") \
.set_valid_contexts(["triage"])
# General inquiries context
general = contexts.add_context("general")
general.add_step("general_help") \
.set_text("I'm here to help with general questions. What can I assist you with?") \
.set_functions(["web_search", "datetime"]) \
.set_step_criteria("Question has been answered") \
.set_valid_contexts(["triage"])
agent = CustomerServiceAgent()
agent.run()
if __name__ == "__main__":
main()
API Reference
ContextBuilder
The main entry point for defining contexts and steps.
# Get the builder
contexts = self.define_contexts()
# Create contexts
context = contexts.add_context(name: str) -> Context
Context
Represents a conversation context or workflow state.
class Context:
def add_step(self, name: str) -> Step
"""Create a new step in this context"""
def set_valid_contexts(self, contexts: List[str]) -> Context
"""Set which contexts can be accessed from this context"""
# Context entry parameters
def set_post_prompt(self, post_prompt: str) -> Context
"""Override post prompt for this context"""
def set_system_prompt(self, system_prompt: str) -> Context
"""Trigger context switch with new system prompt"""
def set_consolidate(self, consolidate: bool) -> Context
"""Consolidate conversation history when entering"""
def set_full_reset(self, full_reset: bool) -> Context
"""Full system prompt replacement vs injection"""
def set_user_prompt(self, user_prompt: str) -> Context
"""Inject user message for context"""
# Context prompts
def set_prompt(self, prompt: str) -> Context
"""Set simple string prompt for context"""
def add_section(self, title: str, body: str) -> Context
"""Add POM section to context prompt"""
def add_bullets(self, title: str, bullets: List[str]) -> Context
"""Add POM bullet section to context prompt"""
Methods
add_step(name)
: Create and return a new Stepset_valid_contexts(contexts)
: Allow navigation to specified contextsset_post_prompt(prompt)
: Override agent's post prompt for this contextset_system_prompt(prompt)
: Trigger context switch behavior (makes this a Context Switch Context)set_consolidate(bool)
: Whether to consolidate conversation when enteringset_full_reset(bool)
: Complete vs partial context resetset_user_prompt(prompt)
: User message to inject when entering contextset_prompt(text)
: Simple string prompt for contextadd_section(title, body)
: Add POM section to context promptadd_bullets(title, list)
: Add POM bullet section to context prompt
Step
Represents a single step within a context workflow.
class Step:
# Content definition (choose one approach)
def set_text(self, text: str) -> Step
"""Set direct text prompt (mutually exclusive with POM sections)"""
def add_section(self, title: str, body: str = "") -> Step
"""Add a POM-style section (mutually exclusive with set_text)"""
def add_bullets(self, bullets: List[str], numbered: bool = False) -> Step
"""Add bullets to the current or most recent section"""
# Flow control
def set_step_criteria(self, criteria: str) -> Step
"""Define completion criteria for this step"""
def set_valid_steps(self, steps: List[str]) -> Step
"""Set which steps can be accessed next in same context"""
def set_valid_contexts(self, contexts: List[str]) -> Step
"""Set which contexts can be accessed from this step"""
# Function restrictions
def set_functions(self, functions: Union[List[str], str]) -> Step
"""Restrict available functions ('none' or list of function names)"""
Content Methods
Option 1: Direct Text
step.set_text("Direct prompt text for the AI")
Option 2: POM-Style Sections
step.add_section("Role", "You are a helpful assistant") \
.add_section("Instructions", "Help users with their questions") \
.add_bullets(["Be friendly", "Ask clarifying questions"])
Note: You cannot mix set_text()
with add_section()
in the same step.
Navigation Methods
# Control step progression within context
step.set_valid_steps(["step1", "step2"]) # Can go to step1 or step2
step.set_valid_steps([]) # Cannot progress (dead end)
# No set_valid_steps() call = implicit "next" step
# Control context switching
step.set_valid_contexts(["context1", "context2"]) # Can switch contexts
step.set_valid_contexts([]) # Trapped in current context
# No set_valid_contexts() call = inherit from context level
Function Restriction Methods
# Allow specific functions only
step.set_functions(["datetime", "math"])
# Block all functions
step.set_functions("none")
# No restriction (default - all agent functions available)
# step.set_functions() # Don't call this method
Navigation and Flow Control
Step Navigation Rules
The set_valid_steps()
method controls movement within a context:
# Explicit step list - can only go to these steps
step.set_valid_steps(["review", "edit", "cancel"])
# Empty list - dead end, cannot progress
step.set_valid_steps([])
# Not called - implicit "next" step progression
# (will go to the next step defined in the context)
Context Navigation Rules
The set_valid_contexts()
method controls switching between contexts:
# Can switch to these contexts
step.set_valid_contexts(["billing", "technical", "general"])
# Trapped in current context
step.set_valid_contexts([])
# Not called - inherit from context-level settings
Navigation Inheritance
Context-level navigation settings are inherited by steps:
# Set at context level
context.set_valid_contexts(["main", "help"])
# All steps in this context can access main and help contexts
# unless overridden at step level
step.set_valid_contexts(["main"]) # Override - only main allowed
Complete Navigation Example
contexts = self.define_contexts()
# Main context
main = contexts.add_context("main")
main.set_valid_contexts(["help", "settings"]) # Context-level setting
main.add_step("welcome") \
.set_text("Welcome! How can I help you?") \
.set_valid_steps(["menu"]) # Must go to menu
# Inherits context-level valid_contexts
main.add_step("menu") \
.set_text("Choose an option: 1) Help 2) Settings 3) Continue") \
.set_valid_contexts(["help", "settings", "main"]) # Override context setting
# No valid_steps = this is a branching point
# Help context
help_ctx = contexts.add_context("help")
help_ctx.add_step("help_info") \
.set_text("Here's how to use the system...") \
.set_valid_contexts(["main"]) # Can return to main
# Settings context
settings = contexts.add_context("settings")
settings.add_step("settings_menu") \
.set_text("Choose a setting to modify...") \
.set_valid_contexts(["main"]) # Can return to main
Function Restrictions
Control which AI tools/functions are available in each step for enhanced security and user experience.
Function Restriction Levels
# No restrictions (default) - all agent functions available
step # Don't call set_functions()
# Allow specific functions only
step.set_functions(["datetime", "math", "web_search"])
# Block all functions
step.set_functions("none")
Security-Focused Example
class SecureBankingAgent(AgentBase):
def __init__(self):
super().__init__(name="Banking Assistant", route="/banking")
# Add potentially sensitive functions
self.add_skill("web_search", {"api_key": "key", "search_engine_id": "id"})
self.add_skill("datetime")
contexts = self.define_contexts()
# Public context - full access
public = contexts.add_context("public")
public.add_step("welcome") \
.set_text("Welcome to banking support. Are you an existing customer?") \
.set_functions(["datetime", "web_search"]) # Safe functions only \
.set_valid_contexts(["authenticated", "public"])
# Authenticated context - restricted for security
auth = contexts.add_context("authenticated")
auth.add_step("account_access") \
.set_text("I can help with your account. What do you need assistance with?") \
.set_functions("none") # No external functions for account data \
.set_valid_contexts(["public"]) # Can log out
Function Access Patterns
# Progressive function access based on trust level
contexts = self.define_contexts()
# Low trust - limited functions
public = contexts.add_context("public")
public.add_step("initial_contact") \
.set_functions(["datetime"]) # Only safe functions
# Medium trust - more functions
verified = contexts.add_context("verified")
verified.add_step("verified_user") \
.set_functions(["datetime", "web_search"]) # Add search capability
# High trust - full access
authenticated = contexts.add_context("authenticated")
authenticated.add_step("full_access") \
# No set_functions() call = all functions available
Real-World Examples
Example 1: Technical Support Troubleshooting
class TechnicalSupportAgent(AgentBase):
def __init__(self):
super().__init__(name="Tech Support", route="/tech-support")
# Add diagnostic tools
self.add_skill("web_search", {"api_key": "key", "search_engine_id": "id"})
self.add_skill("datetime")
contexts = self.define_contexts()
# Initial triage
triage = contexts.add_context("triage")
triage.add_step("problem_identification") \
.add_section("Current Task", "Identify the type of technical issue") \
.add_bullets("Information to Gather", [
"Description of the specific problem",
"When did the issue start occurring?",
"What steps has the customer already tried?",
"Rate the severity level (critical/high/medium/low)"
]) \
.set_step_criteria("Issue type and severity determined") \
.set_valid_contexts(["hardware", "software", "network"])
# Hardware troubleshooting
hardware = contexts.add_context("hardware")
hardware.add_step("hardware_diagnosis") \
.add_section("Current Task", "Guide user through hardware diagnostics") \
.add_section("Available Tools", "Use web search to find hardware specifications and troubleshooting guides") \
.set_functions(["web_search"]) # Can search for hardware info \
.set_step_criteria("Hardware issue diagnosed") \
.set_valid_steps(["hardware_solution"])
hardware.add_step("hardware_solution") \
.set_text("Based on the diagnosis, here's how to resolve the hardware issue...") \
.set_step_criteria("Solution provided and tested") \
.set_valid_contexts(["triage"]) # Can start over if needed
# Software troubleshooting
software = contexts.add_context("software")
software.add_step("software_diagnosis") \
.add_section("Current Task", "Diagnose software-related issues") \
.add_section("Available Tools", "Use web search for software updates and datetime to check for recent changes") \
.set_functions(["web_search", "datetime"]) # Can check for updates \
.set_step_criteria("Software issue identified") \
.set_valid_steps(["software_fix", "escalation"])
software.add_step("software_fix") \
.set_text("Let's try these software troubleshooting steps...") \
.set_step_criteria("Fix attempted and result confirmed") \
.set_valid_steps(["escalation", "resolution"])
software.add_step("escalation") \
.set_text("I'll escalate this to our specialist team.") \
.set_functions("none") # No tools needed for escalation \
.set_step_criteria("Escalation ticket created")
software.add_step("resolution") \
.set_text("Great! The issue has been resolved.") \
.set_step_criteria("Customer confirms resolution") \
.set_valid_contexts(["triage"])
# Network troubleshooting
network = contexts.add_context("network")
network.add_step("network_diagnosis") \
.add_section("Current Task", "Diagnose network and connectivity issues") \
.add_section("Available Tools", "Use web search to check service status and datetime for outage windows") \
.set_functions(["web_search", "datetime"]) # Check service status \
.set_step_criteria("Network issue diagnosed") \
.set_valid_steps(["network_fix"])
network.add_step("network_fix") \
.set_text("Let's resolve your connectivity issue with these steps...") \
.set_step_criteria("Network connectivity restored") \
.set_valid_contexts(["triage"])
agent = TechnicalSupportAgent()
agent.run()
if __name__ == "__main__":
main()
Example 2: Multi-Step Application Process
class LoanApplicationAgent(AgentBase):
def __init__(self):
super().__init__(name="Loan Application", route="/loan-app")
# Add verification tools
self.add_skill("datetime") # For date validation
contexts = self.define_contexts()
# Single workflow context
application = contexts.add_context("default")
# Step 1: Introduction and eligibility
application.add_step("introduction") \
.add_section("Current Task", "Guide customers through the loan application process") \
.add_bullets("Information to Provide", [
"Explain the process clearly",
"Outline what information will be needed",
"Set expectations for timeline and next steps"
]) \
.set_step_criteria("Customer understands process and wants to continue") \
.set_valid_steps(["personal_info"])
# Step 2: Personal information
application.add_step("personal_info") \
.add_section("Instructions", "Collect personal information") \
.add_bullets([
"Full legal name",
"Date of birth",
"Social Security Number",
"Phone number and email"
]) \
.set_functions(["datetime"]) # Can validate dates \
.set_step_criteria("All personal information collected and verified") \
.set_valid_steps(["employment_info", "personal_info"]) # Can review/edit
# Step 3: Employment information
application.add_step("employment_info") \
.set_text("Now I need information about your employment and income.") \
.set_step_criteria("Employment and income information complete") \
.set_valid_steps(["financial_info", "personal_info"]) # Can go back
# Step 4: Financial information
application.add_step("financial_info") \
.set_text("Let's review your financial situation including assets and debts.") \
.set_step_criteria("Financial information complete") \
.set_valid_steps(["review", "employment_info"]) # Can go back
# Step 5: Review all information
application.add_step("review") \
.add_section("Instructions", "Review all collected information") \
.add_bullets([
"Confirm personal details",
"Verify employment information",
"Review financial data",
"Ensure accuracy before submission"
]) \
.set_step_criteria("Customer has reviewed and confirmed all information") \
.set_valid_steps(["submit", "personal_info", "employment_info", "financial_info"])
# Step 6: Submission
application.add_step("submit") \
.set_text("Thank you! Your loan application has been submitted successfully. You'll receive a decision within 2-3 business days.") \
.set_functions("none") # No tools needed for final message \
.set_step_criteria("Application submitted and confirmation provided")
# No valid_steps = end of process
agent = LoanApplicationAgent()
agent.run()
if __name__ == "__main__":
main()
Example 3: E-commerce Customer Service
class EcommerceServiceAgent(AgentBase):
def __init__(self):
super().__init__(name="E-commerce Support", route="/ecommerce")
# Add tools for order management
self.add_skill("web_search", {"api_key": "key", "search_engine_id": "id"})
self.add_skill("datetime")
contexts = self.define_contexts()
# Main service menu
main = contexts.add_context("main")
main.add_step("service_menu") \
.add_section("Current Task", "Help customers with their orders and questions") \
.add_bullets("Service Areas Available", [
"Order status, modifications, and tracking",
"Returns and refunds",
"Product information and specifications",
"Account-related questions"
]) \
.set_step_criteria("Customer's need has been identified") \
.set_valid_contexts(["orders", "returns", "products", "account"])
# Order management context
orders = contexts.add_context("orders")
orders.add_step("order_assistance") \
.add_section("Current Task", "Help with order status, modifications, and tracking") \
.add_section("Available Tools", "Use datetime to check delivery dates and processing times") \
.set_functions(["datetime"]) # Can check delivery dates \
.set_step_criteria("Order issue resolved or escalated") \
.set_valid_contexts(["main"])
# Returns and refunds context
returns = contexts.add_context("returns")
returns.add_step("return_process") \
.add_section("Current Task", "Guide customers through return process") \
.add_bullets("Return Process Steps", [
"Verify return eligibility",
"Explain return policy",
"Provide return instructions",
"Process refund if applicable"
]) \
.set_functions("none") # Sensitive financial operations \
.set_step_criteria("Return request processed") \
.set_valid_contexts(["main"])
# Product information context
products = contexts.add_context("products")
products.add_step("product_help") \
.add_section("Current Task", "Help customers with product questions") \
.add_section("Available Tools", "Use web search to find detailed product information and specifications") \
.set_functions(["web_search"]) # Can search for product info \
.set_step_criteria("Product question answered") \
.set_valid_contexts(["main"])
# Account management context
account = contexts.add_context("account")
account.add_step("account_help") \
.set_text("I can help with account-related questions. Please verify your identity first.") \
.set_functions("none") # Security-sensitive context \
.set_step_criteria("Account issue resolved") \
.set_valid_contexts(["main"])
agent = EcommerceServiceAgent()
agent.run()
if __name__ == "__main__":
main()
Best Practices
1. Clear Step Naming
Use descriptive step names that indicate purpose:
# Good
.add_step("collect_shipping_address")
.add_step("verify_payment_method")
.add_step("confirm_order_details")
# Avoid
.add_step("step1")
.add_step("next")
.add_step("continue")
2. Meaningful Completion Criteria
Define clear, testable completion criteria:
# Good - specific and measurable
.set_step_criteria("User has provided valid email address and confirmed subscription preferences")
.set_step_criteria("All required fields completed and payment method verified")
# Avoid - vague or subjective
.set_step_criteria("User is ready")
.set_step_criteria("Everything is good")
3. Logical Navigation Flow
Design intuitive navigation that matches user expectations:
# Allow users to go back and review
.set_valid_steps(["review_info", "edit_details", "confirm_submission"])
# Provide escape routes
.set_valid_contexts(["main_menu", "help"])
# Consider dead ends carefully
.set_valid_steps([]) # Only if this is truly the end
4. Progressive Function Access
Restrict functions based on security and context needs:
# Public areas - limited functions
public_step.set_functions(["datetime", "web_search"])
# Authenticated areas - more functions allowed
auth_step.set_functions(["datetime", "web_search", "user_profile"])
# Sensitive operations - minimal functions
billing_step.set_functions("none")
5. Context Organization
Organize contexts by functional area or user journey:
# By functional area
contexts = ["triage", "technical_support", "billing", "account_management"]
# By user journey stage
contexts = ["onboarding", "verification", "configuration", "completion"]
# By security level
contexts = ["public", "authenticated", "admin"]
6. Error Handling and Recovery
Provide recovery paths for common issues:
# Allow users to retry failed steps
.set_valid_steps(["retry_payment", "choose_different_method", "contact_support"])
# Provide help context access
.set_valid_contexts(["help", "main"])
# Include validation steps
verification_step.add_step("validation") \
.set_step_criteria("Data validation passed") \
.set_valid_steps(["proceed", "edit_data"])
7. Content Strategy
Choose the right content approach for each step:
# Use set_text() for simple, direct instructions
step.set_text("Please provide your email address")
# Use POM sections for complex, structured content
step.add_section("Role", "You are a technical specialist") \
.add_section("Context", "Customer is experiencing network issues") \
.add_section("Instructions", "Follow diagnostic protocol") \
.add_bullets(["Check connectivity", "Test speed", "Verify settings"])
Troubleshooting
Common Issues
1. "Single context must be named 'default'"
Error: When using a single context with a name other than "default"
# Wrong
context = contexts.add_context("main") # Error!
# Correct
context = contexts.add_context("default")
2. "Cannot mix set_text with add_section"
Error: Using both direct text and POM sections in the same step
# Wrong
step.set_text("Welcome!") \
.add_section("Role", "Assistant") # Error!
# Correct - choose one approach
step.set_text("Welcome! I'm your assistant.")
# OR
step.add_section("Role", "Assistant") \
.add_section("Message", "Welcome!")
3. Navigation Issues
Problem: Users getting stuck or unable to navigate
# Check your navigation rules
step.set_valid_steps([]) # Dead end - is this intended?
step.set_valid_contexts([]) # Trapped in context - is this intended?
# Add appropriate navigation
step.set_valid_steps(["next_step", "previous_step"])
step.set_valid_contexts(["main", "help"])
4. Function Access Problems
Problem: Functions not available when expected
# Check function restrictions
step.set_functions("none") # All functions blocked
step.set_functions(["datetime"]) # Only datetime allowed
# Verify function names match your agent's functions
self.add_skill("web_search") # Function name is "web_search"
step.set_functions(["web_search"]) # Must match exactly
Debugging Tips
1. Trace Navigation Flow
Add logging to understand flow:
def create_step_with_logging(self, name):
step = context.add_step(name)
print(f"Created step: {name}")
return step
2. Validate Navigation Rules
Check that all referenced steps/contexts exist:
# Ensure referenced steps exist
.set_valid_steps(["review", "edit"]) # Both "review" and "edit" steps must exist
# Ensure referenced contexts exist
.set_valid_contexts(["main", "help"]) # Both "main" and "help" contexts must exist
3. Test Function Restrictions
Verify functions are properly restricted:
# Test with all functions
# step # No set_functions() call
# Test with restrictions
step.set_functions(["datetime"])
# Test with no functions
step.set_functions("none")
Migration from POM
Converting Traditional Prompts
Before (Traditional POM):
class TraditionalAgent(AgentBase):
def __init__(self):
super().__init__(name="assistant", route="/assistant")
self.prompt_add_section("Role", "You are a helpful assistant")
self.prompt_add_section("Instructions", "Help users with questions")
self.prompt_add_section("Guidelines", bullets=[
"Be friendly",
"Ask clarifying questions",
"Provide accurate information"
])
After (Contexts and Steps):
class ContextsAgent(AgentBase):
def __init__(self):
super().__init__(name="assistant", route="/assistant")
contexts = self.define_contexts()
main = contexts.add_context("default")
main.add_step("assistance") \
.add_section("Role", "You are a helpful assistant") \
.add_section("Instructions", "Help users with questions") \
.add_section("Guidelines", bullets=[
"Be friendly",
"Ask clarifying questions",
"Provide accurate information"
]) \
.set_step_criteria("User's question has been answered")
Hybrid Approach
You can use both traditional prompts and contexts in the same agent:
class HybridAgent(AgentBase):
def __init__(self):
super().__init__(name="hybrid", route="/hybrid")
# Traditional prompt sections (from skills, global settings, etc.)
# These will coexist with contexts
# Define contexts for structured workflows
contexts = self.define_contexts()
workflow = contexts.add_context("default")
workflow.add_step("structured_process") \
.set_text("Following the structured workflow...") \
.set_step_criteria("Workflow complete")
Migration Strategy
- Start Simple: Convert one workflow at a time
- Preserve Existing: Keep traditional prompts for simple interactions
- Add Structure: Use contexts for complex, multi-step processes
- Test Thoroughly: Verify navigation and function access work as expected
- Iterate: Refine step criteria and navigation based on testing
Conclusion
The Contexts and Steps system provides powerful workflow control for building sophisticated AI agents. By combining structured navigation, function restrictions, and clear completion criteria, you can create predictable, user-friendly agent experiences that guide users through complex processes while maintaining security and control.
Start with simple single-context workflows and gradually build more complex multi-context systems as your requirements grow. The system is designed to be flexible and scalable, supporting both simple linear workflows and complex branching conversation trees.
Context Inheritance
Contexts can inherit from other contexts to create hierarchical structures:
from signalwire_agents import AgentBase
from signalwire_agents.core.context import Context
class CustomerServiceAgent(AgentBase):
def __init__(self):
super().__init__(name="customer-service", route="/support")
# Base context for all customer interactions
base_context = Context(
name="customer_base",
description="Base context for all customer interactions",
instructions=[
"Always be polite and professional",
"Verify customer identity before accessing account information",
"Document all interactions in the customer record"
]
)
# Billing context inherits from base
billing_context = Context(
name="billing_support",
description="Handle billing inquiries and payment issues",
parent=base_context, # Inherits from base_context
instructions=[
"Check payment history before suggesting solutions",
"Offer payment plan options for overdue accounts",
"Escalate disputes over $500 to billing manager"
]
)
# Technical support context also inherits from base
tech_context = Context(
name="technical_support",
description="Provide technical assistance and troubleshooting",
parent=base_context, # Also inherits from base_context
instructions=[
"Start with basic troubleshooting steps",
"Document error messages and symptoms",
"Create support tickets for unresolved issues"
]
)
self.add_context(base_context)
self.add_context(billing_context)
self.add_context(tech_context)
def main():
agent = CustomerServiceAgent()
agent.run()
if __name__ == "__main__":
main()
Dynamic Context Switching
Contexts can be switched dynamically during conversations based on user input or business logic:
from signalwire_agents import AgentBase
from signalwire_agents.core.context import Context
from signalwire_agents.core.function_result import SwaigFunctionResult
class AdaptiveAgent(AgentBase):
def __init__(self):
super().__init__(name="adaptive", route="/adaptive")
# Define multiple contexts
self.setup_contexts()
# Start with general context
self.set_active_context("general")
def setup_contexts(self):
general = Context(
name="general",
description="General conversation and routing",
instructions=[
"Determine what the user needs help with",
"Route to appropriate specialized context",
"Be helpful and friendly"
]
)
sales = Context(
name="sales",
description="Sales and product information",
instructions=[
"Focus on product benefits and features",
"Understand customer needs and budget",
"Provide pricing and availability information"
]
)
support = Context(
name="support",
description="Technical support and troubleshooting",
instructions=[
"Diagnose technical issues systematically",
"Provide step-by-step solutions",
"Escalate complex problems to specialists"
]
)
self.add_context(general)
self.add_context(sales)
self.add_context(support)
@AgentBase.tool(
name="switch_to_sales",
description="Switch to sales context for product inquiries",
parameters={}
)
def switch_to_sales(self, args, raw_data):
self.set_active_context("sales")
return SwaigFunctionResult("Switching to sales mode. How can I help you with our products?")
@AgentBase.tool(
name="switch_to_support",
description="Switch to technical support context",
parameters={}
)
def switch_to_support(self, args, raw_data):
self.set_active_context("support")
return SwaigFunctionResult("Switching to technical support. What issue are you experiencing?")
def main():
agent = AdaptiveAgent()
agent.run()
if __name__ == "__main__":
main()
Context-Aware Function Behavior
Functions can behave differently based on the active context:
from signalwire_agents import AgentBase
from signalwire_agents.core.context import Context
from signalwire_agents.core.function_result import SwaigFunctionResult
class ContextAwareAgent(AgentBase):
def __init__(self):
super().__init__(name="context-aware", route="/context")
# Setup contexts
self.setup_contexts()
self.set_active_context("customer")
def setup_contexts(self):
customer = Context(
name="customer",
description="Customer-facing interactions",
instructions=["Be friendly and helpful", "Use simple language"]
)
internal = Context(
name="internal",
description="Internal staff interactions",
instructions=["Be direct and technical", "Include detailed information"]
)
self.add_context(customer)
self.add_context(internal)
@AgentBase.tool(
name="get_account_info",
description="Get account information",
parameters={
"account_id": {
"type": "string",
"description": "Account identifier"
}
}
)
def get_account_info(self, args, raw_data):
account_id = args.get("account_id")
# Get the current context
current_context = self.get_active_context()
if current_context.name == "customer":
# Customer-friendly response
return SwaigFunctionResult(
f"Your account {account_id} is in good standing. "
"Your next billing date is March 15th."
)
elif current_context.name == "internal":
# Detailed internal response
return SwaigFunctionResult(
f"Account {account_id}: Status=ACTIVE, Balance=$125.50, "
"Last_Payment=2024-02-15, Next_Bill=2024-03-15, "
"Plan=Premium, Usage=85% of limit"
)
else:
return SwaigFunctionResult("Account information retrieved.")
def main():
agent = ContextAwareAgent()
agent.run()
if __name__ == "__main__":
main()