Variable Substitution
Dynamic variable substitution with double-brace placeholders and dot-notation
Variable substitution enables dynamic content replacement using {{variable}} placeholders that resolve at runtime, supporting both flat variables and dot-notation for nested data structures.
from praisonaiagents import Agent
agent = Agent(name="Daily Agent", instructions="Answer using today's date when asked.")
agent.start("Summarise the top AI news for {{today}}")The user includes placeholders in the prompt; the agent resolves variables before answering.
graph LR
subgraph "Variable Resolution"
A[📝 {{user.name}}] --> B[🔍 Look up]
B --> C[📊 variables["user"]["name"]]
C --> D[✅ "Alice"]
end
classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
classDef process fill:#F59E0B,stroke:#7C90A0,color:#fff
classDef output fill:#10B981,stroke:#7C90A0,color:#fff
class A input
class B,C process
class D outputHow It Works
Quick Start
- Dynamic placeholders in prompts
Built-in providers such as
{{today}},{{now}}, and{{uuid}}resolve when the agent runs:python from praisonaiagents import Agent agent = Agent(name="Daily Agent", instructions="Answer concisely.") agent.start("What is today's date? Reply with {{today}} only.") - Dot notation for nested data
Pass a variables dict when substituting text in workflows or templates:
python from praisonaiagents.utils.variables import substitute_variables text = substitute_variables( "Welcome {{user.name}}! Your role is {{user.role}}", {"user": {"name": "Alice", "role": "admin", "settings": {"theme": "dark"}}}, ) # "Welcome Alice! Your role is admin" - Graceful fallback for missing keys
Missing segments stay as literal placeholders — no error is raised:
python from praisonaiagents.utils.variables import substitute_variables text = substitute_variables( "Process {{data.value}} or use {{fallback}} if missing", {"data": {"other": "something"}, "fallback": "default"}, ) # "Process {{data.value}} or use default if missing"
How It Works
graph TB
A[📝 Input Text] --> B{🔍 Has {{var}}?}
B -->|No| C[✅ Return as-is]
B -->|Yes| D{📊 Dotted name?}
D -->|No| E[🔍 Provider Registry]
D -->|Yes| F[🗂️ Dict Traversal]
E --> G{✅ Found?}
F --> G
G -->|Yes| H[🔄 Replace]
G -->|No| I[📝 Keep Original]
H --> J[✅ Result]
I --> J
classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
classDef process fill:#F59E0B,stroke:#7C90A0,color:#fff
classDef output fill:#10B981,stroke:#7C90A0,color:#fff
class A input
class B,D,E,F,G process
class C,H,I,J output| Resolution Order | Description |
|---|---|
| 1. Provider Registry | Dynamic providers (for non-dotted names only) |
| 2. Dict Traversal | Navigate nested dicts using dot notation |
| 3. Graceful Fallback | Keep original {{...}} if not found |
Configuration Options
| Pattern | Type | Description |
|---|---|---|
{{name}} | str | Flat variable name – checks providers then static variables |
{{obj.prop}} | str | Dot notation – traverses nested dicts only |
{{missing}} | str | Missing variables remain as literal text (graceful fallback) |
Traversal Rules:
- Only dict objects are traversed (not lists, objects, or attributes)
- Provider registry is consulted only for non-dotted names
- Missing segments result in graceful fallback (no errors)
Common Patterns
Pattern 1: User configuration
from praisonaiagents.utils.variables import substitute_variables
instructions = substitute_variables(
"Hello {{user.name}}! Theme: {{user.settings.theme}}",
{
"user": {
"name": "Bob",
"settings": {"theme": "light", "notifications": True},
}
},
)Pattern 2: API Endpoint Templates
variables = {
"api": {
"base_url": "https://api.example.com",
"version": "v1",
"endpoints": {
"users": "/users",
"posts": "/posts"
}
}
}
template = "{{api.base_url}}/{{api.version}}{{api.endpoints.users}}"
# Resolves to: "https://api.example.com/v1/users"Pattern 3: Multi-Level Data Access
variables = {
"company": {
"departments": {
"engineering": {
"team_lead": "Charlie",
"budget": 100000
}
}
}
}
instruction = "Contact {{company.departments.engineering.team_lead}}"
# Resolves to: "Contact Charlie"Best Practices
Use Descriptive Variable Names
Choose clear, descriptive names that make templates self-documenting.
# Good
variables = {
"user": {"name": "Alice", "role": "admin"},
"project": {"name": "Dashboard", "deadline": "2024-01-15"}
}
# Avoid
variables = {
"u": {"n": "Alice", "r": "admin"},
"p": {"n": "Dashboard", "d": "2024-01-15"}
}Structure Data Hierarchically
Organize related data into logical groupings for cleaner templates.
# Organized structure
variables = {
"user": {
"profile": {"name": "Bob", "email": "bob@example.com"},
"preferences": {"theme": "dark", "language": "en"}
},
"system": {
"version": "1.2.0",
"environment": "production"
}
}Handle Missing Data Gracefully
Design templates that degrade gracefully when optional data is missing.
# Template with fallback
template = "Welcome {{user.name}}! {{user.title}} is optional."
# Works with partial data
variables = {"user": {"name": "Carol"}}
# Result: "Welcome Carol! {{user.title}} is optional."Avoid Deep Nesting
Keep dot-notation paths reasonably short for maintainability.
# Reasonable depth
{{config.database.host}}
# Too deep - consider flattening
{{app.modules.auth.providers.oauth.google.client_id}}