Jinja2 Templating in Questions
EDSL uses Jinja2 templating to create dynamic questions. Template variables are enclosed in {{ }} and are rendered at runtime with values from scenarios, agents, or previous answers.
Scenario Templating
Scenarios provide key-value data that gets substituted into questions:
from edsl import QuestionFreeText, Scenario
# Question with scenario variable
q = QuestionFreeText(
question_name="opinion",
question_text="What do you think about {{ scenario.fruit }}?"
)
# Create scenarios
scenarios = [
Scenario({"fruit": "apples"}),
Scenario({"fruit": "oranges"}),
Scenario({"fruit": "bananas"})
]
# Run survey with scenarios - each generates a separate response
results = q.by(scenarios).run()
Multiple Variables
q = QuestionFreeText(
question_name="review",
question_text="Review the {{ scenario.product }} priced at ${{ scenario.price }}."
)
scenario = Scenario({"product": "Widget X", "price": 29.99})
Nested Scenario Data
q = QuestionFreeText(
question_name="address",
question_text="Describe the location: {{ scenario.location.city }}, {{ scenario.location.country }}"
)
scenario = Scenario({
"location": {
"city": "Paris",
"country": "France"
}
})
Full Scenario Access
# Access entire scenario as string
q = QuestionFreeText(
question_name="summary",
question_text="Given this data: {{ scenario }}, provide a summary."
)
Agent Templating
Reference agent traits in questions:
from edsl import QuestionFreeText, Agent
q = QuestionFreeText(
question_name="perspective",
question_text="As a {{ agent.occupation }}, what do you think about remote work?"
)
agent = Agent(traits={"occupation": "software engineer", "age": 35})
results = q.by(agent).run()
Multiple Agent Traits
q = QuestionFreeText(
question_name="intro",
question_text="You are {{ agent.name }}, a {{ agent.age }}-year-old {{ agent.occupation }}. Introduce yourself."
)
agent = Agent(traits={"name": "Alice", "age": 28, "occupation": "teacher"})
Piping (Answer References)
Reference previous answers within a survey:
from edsl import Survey, QuestionFreeText, QuestionMultipleChoice
q1 = QuestionFreeText(
question_name="name",
question_text="What is your name?"
)
q2 = QuestionFreeText(
question_name="greeting",
question_text="Hello {{ name.answer }}! How are you today?"
)
q3 = QuestionMultipleChoice(
question_name="mood",
question_text="{{ name.answer }}, which best describes your current mood?",
question_options=["Happy", "Neutral", "Sad"]
)
survey = Survey([q1, q2, q3])
Piping in Options
q1 = QuestionFreeText(
question_name="hobby",
question_text="What is your favorite hobby?"
)
q2 = QuestionMultipleChoice(
question_name="frequency",
question_text="How often do you engage in {{ hobby.answer }}?",
question_options=["Daily", "Weekly", "Monthly", "Rarely"]
)
Combining Sources
You can combine scenario, agent, and piping references:
q = QuestionFreeText(
question_name="comprehensive",
question_text="""
{{ agent.name }} ({{ agent.occupation }}),
regarding {{ scenario.topic }}:
You previously said "{{ prior_question.answer }}".
Please elaborate on your thoughts.
"""
)
Templating in Question Options
Options can also use templates:
q = QuestionMultipleChoice(
question_name="preference",
question_text="Which {{ scenario.category }} do you prefer?",
question_options=[
"{{ scenario.option_a }}",
"{{ scenario.option_b }}",
"{{ scenario.option_c }}"
]
)
scenario = Scenario({
"category": "fruit",
"option_a": "Apples",
"option_b": "Oranges",
"option_c": "Bananas"
})
Conditional Logic in Templates
Jinja2 supports conditionals and loops:
# Conditional text
q = QuestionFreeText(
question_name="advice",
question_text="""
{% if scenario.age < 18 %}
As a young person, what advice would you give your peers?
{% else %}
As an adult, what advice would you give young people?
{% endif %}
"""
)
# Loop in text
q = QuestionFreeText(
question_name="items_review",
question_text="""
Review these items:
{% for item in scenario.items %}
- {{ item }}
{% endfor %}
"""
)
Template Variables Reference
| Variable | Access Pattern | Example |
|---|---|---|
| Scenario value | {{ scenario.key }} | {{ scenario.fruit }} |
| Nested scenario | {{ scenario.obj.key }} | {{ scenario.location.city }} |
| Full scenario | {{ scenario }} | Converts to string |
| Agent trait | {{ agent.trait }} | {{ agent.occupation }} |
| Prior answer | {{ question_name.answer }} | {{ q1.answer }} |
Best Practices
- •
Use descriptive scenario keys:
{{ scenario.product_name }}over{{ scenario.p }} - •
Test templates: Use
question.render(scenario_dict)to test rendering:pythonq = QuestionFreeText( question_name="test", question_text="Opinion on {{ scenario.topic }}?" ) rendered = q.render({"topic": "AI"}) print(rendered.question_text) # "Opinion on AI?" - •
Handle missing values: Provide defaults with Jinja2 filters:
python"{{ scenario.name | default('Unknown') }}" - •
Avoid forward references: In piping, only reference questions that come BEFORE the current question in the survey.
Common Patterns
Survey with Scenarios
from edsl import Survey, QuestionFreeText, Scenario, ScenarioList
q = QuestionFreeText(
question_name="opinion",
question_text="What do you think of {{ scenario.item }}?"
)
survey = Survey([q])
scenarios = ScenarioList([
Scenario({"item": "electric cars"}),
Scenario({"item": "solar panels"}),
Scenario({"item": "wind turbines"})
])
results = survey.by(scenarios).run()
Personalized Questions with Agents
from edsl import Survey, QuestionFreeText, Agent, AgentList
q = QuestionFreeText(
question_name="view",
question_text="As a {{ agent.role }}, what's your view on automation?"
)
agents = AgentList([
Agent(traits={"role": "factory worker"}),
Agent(traits={"role": "CEO"}),
Agent(traits={"role": "software developer"})
])
results = Survey([q]).by(agents).run()
Dynamic Follow-up with Piping
q1 = QuestionMultipleChoice(
question_name="preference",
question_text="Which do you prefer?",
question_options=["Option A", "Option B", "Option C"]
)
q2 = QuestionFreeText(
question_name="why",
question_text="You chose {{ preference.answer }}. Why do you prefer it?"
)
survey = Survey([q1, q2])