Context Builder — Multi-Step Guided Flows
Use ContextBuilder when an AI agent needs structured, step-by-step conversations rather than free-form prompting. Each context has its own system prompt and a chain of steps. Steps gate progression with criteria, allowed tools, and valid next-steps.
Limits: 50 contexts per builder, 100 steps per context.
Single context, sequential steps
from signalwire import AgentBase, FunctionResult
class OrderAgent(AgentBase):
def __init__(self):
super().__init__(name="order-agent")
self.add_language("English", "en-US", "rime.spore")
self.prompt_add_section("Role", "You help customers place orders.")
contexts = self.define_contexts()
order = contexts.add_context("default")
order.add_step("get_item") \
.set_text("Ask what item they want to order.") \
.set_step_criteria("Customer has specified an item") \
.set_valid_steps(["get_quantity"])
order.add_step("get_quantity") \
.set_text("Ask how many they want.") \
.set_step_criteria("Customer has specified a quantity") \
.set_valid_steps(["confirm"])
order.add_step("confirm") \
.set_text("Confirm order details and thank them.") \
.set_step_criteria("Order confirmed") \
.set_end(True)
The agent advances when step_criteria is satisfied. set_valid_steps([...]) limits which steps are reachable next; out-of-list jumps are blocked.
Multi-context flow with cross-context transitions
contexts = self.define_contexts()
# Triage / menu context
main = contexts.add_context("default")
main.add_step("menu") \
.set_text("Ask whether they need sales, support, or billing.") \
.set_functions("none") \
.set_valid_contexts(["sales", "support", "billing"])
# Sales context
sales = contexts.add_context("sales")
sales.set_system_prompt("You are a friendly sales representative.")
sales.add_step("qualify") \
.set_text("Understand what product the caller is interested in.") \
.set_functions(["check_inventory", "get_pricing"]) \
.set_valid_steps(["close"])
sales.add_step("close") \
.set_text("Close the sale or schedule a follow-up.") \
.set_valid_contexts(["default"])
# Support context
support = contexts.add_context("support")
support.set_system_prompt("You are a tier-2 support engineer.")
support.add_step("triage").set_text("Get problem details, system info, and urgency.")
# Billing context
billing = contexts.add_context("billing")
billing.set_system_prompt("You are a billing specialist. Verify account before answering.")
billing.add_step("verify").set_text("Ask for the last four digits of the account.")
Crossing contexts requires set_valid_contexts([...]) on the source step. The SDK blocks the transition otherwise.
Step configuration table
| Setter | Purpose |
|---|---|
| .set_text("...") | The step's prompt instruction (what the AI should do in this step). |
| .set_step_criteria("...") | The exit condition. The AI evaluates whether this is met. |
| .set_valid_steps([...]) | Whitelist of step names reachable from here. |
| .set_valid_contexts([...]) | Whitelist of contexts reachable from here. |
| .set_functions([...]) | SWAIG functions usable inside this step. "none" disables tools entirely. |
| .set_end(True) | Mark this as the terminal step of the context. |
Context configuration
| Setter | Purpose |
|---|---|
| .set_system_prompt("...") | Override the agent's base prompt for this context. |
| .add_step("name") | Add a new step. Returns the step builder for fluent chaining. |
create_simple_context — standalone, no builder
For one-off contexts without the full builder pattern:
from signalwire import create_simple_context
ctx = create_simple_context() # name defaults to "default"
ctx.add_step("greet").set_text("Say hello to the caller.")
ctx.add_step("help").set_text("Ask how you can help today.")
Use this when you have a single context and don't need cross-context transitions.
Triggering transitions from SWAIG handlers
A SWAIG function can advance the flow programmatically:
from signalwire import FunctionResult
@agent.tool(description="Caller wants sales")
def route_to_sales(args, raw_data=None):
return FunctionResult("Connecting you to sales.").swml_change_context("sales")
@agent.tool(description="Move to confirmation step")
def proceed_to_confirm(args, raw_data=None):
return FunctionResult("Let me confirm your order.").swml_change_step("confirm")
.swml_change_step(name) jumps within the active context. .swml_change_context(name) jumps to a different context (must be allowed via set_valid_contexts).
Context vs free-form prompting — when to use which
| Pattern | Use this |
|---|---|
| Open-ended chat with optional tools | Free-form prompt (no ContextBuilder). |
| 2-4 step ordered process (intake, quote, schedule) | Single context with sequential steps. |
| Multi-persona handoff (triage → specialist) | Multi-context with set_valid_contexts. |
| Stateful flow with strict gating | ContextBuilder with step_criteria and set_valid_steps. |
Anti-patterns
- Defining 30+ steps in one context — split into multiple contexts.
- Skipping
set_valid_stepsorset_valid_contexts— the AI may jump anywhere, defeating the structure. - Setting
set_functions("none")in a step that needs tools — the AI will improvise instead of calling SWAIG. - Re-using step names across contexts — fine, but confuses logs. Prefix with context name (
sales_qualify,support_qualify). - Manually editing
prompt.textand a context'ssystem_promptto overlap — the context prompt wins inside that context.