Answer Options Tutorial#
This Foundations tutorial focuses on option engineering only. It intentionally does not run inference.
You will learn two complementary paths:
Manual control with
AnswerTexts+AnswerOptionswhen formatting must be exact.Convenience setup with
generate_likert_options(...)when you want fast setup and optional perturbations.
This tutorial is split into progressive blocks.
Each block explains:
what the code does
why you might use it in practice
import random
import pandas as pd
from qstn.prompt_builder import LLMPrompt, generate_likert_options
from qstn.utilities import placeholder
from qstn.utilities.constants import QuestionnairePresentation
from qstn.utilities.survey_objects import AnswerOptions, AnswerTexts
1. Minimal Setup#
We start with a very small questionnaire and one reusable prompt template built with placeholders.
This gives us a clean baseline, so each option change is easy to see and compare.
questionnaire_df = pd.DataFrame(
[
{
"questionnaire_item_id": 1,
"question_content": (
"How satisfied are you with the public healthcare system "
"in your area?"
),
},
{
"questionnaire_item_id": 2,
"question_content": (
"How satisfied are you with the quality of public education "
"in your area?"
),
},
{
"questionnaire_item_id": 3,
"question_content": (
"How satisfied are you with local public transportation "
"where you live?"
),
},
]
)
base_prompt = LLMPrompt(
questionnaire_name="OptionsDemo",
questionnaire_source=questionnaire_df,
system_prompt="You are a careful survey respondent.",
prompt=(
f"Question: {placeholder.PROMPT_QUESTIONS}\n"
f"{placeholder.PROMPT_OPTIONS}\n"
"Answer with exactly one option."
),
)
questionnaire_item_id question_content
0 1 How satisfied are you with the public healthca...
1 2 How satisfied are you with the quality of publ...
2 3 How satisfied are you with local public transp...
2. Block A: Manual Full Control (AnswerTexts + AnswerOptions)#
In this block, we define indices, separators, and wording ourselves.
Use this route when you care about exact survey coding, strict formatting, or domain-specific label text.
manual_texts = AnswerTexts(
answer_texts=["Very dissatisfied", "Dissatisfied", "Neutral", "Satisfied", "Very satisfied"],
indices=["01", "02", "03", "04", "05"],
index_answer_seperator=" => ",
option_seperators=" || ",
)
manual_options = AnswerOptions(
answer_texts=manual_texts,
list_prompt_template="Choose one satisfaction option: {options}",
)
manual_prompt = base_prompt.duplicate()
manual_prompt.prepare_prompt(
question_stem=f"Question: {placeholder.QUESTION_CONTENT}",
answer_options=manual_options,
)
_, manual_user_text = manual_prompt.get_prompt_for_questionnaire_type(
questionnaire_type=QuestionnairePresentation.SINGLE_ITEM,
item_position=0,
)
print("OPTIONS STRING:")
print(manual_options.create_options_str())
print("\nRENDERED PROMPT:")
print(manual_user_text)
OPTIONS STRING:
Choose one satisfaction option: 01 => Very dissatisfied || 02 => Dissatisfied || 03 => Neutral || 04 => Satisfied || 05 => Very satisfied
RENDERED PROMPT:
Question: Question: How satisfied are you with the public healthcare system in your area?
Choose one satisfaction option: 01 => Very dissatisfied || 02 => Dissatisfied || 03 => Neutral || 04 => Satisfied || 05 => Very satisfied
Answer with exactly one option.
3. Block B: Manual Scale Mode (from_to_scale=True)#
Here we keep manual control but present the answers as a compact start-to-end scale.
This is helpful when you want shorter prompts while still preserving the intended range semantics.
manual_scale_texts = AnswerTexts(
answer_texts=["Very dissatisfied", "Dissatisfied", "Neutral", "Satisfied", "Very satisfied"],
indices=["1", "2", "3", "4", "5"],
only_scale=True,
)
manual_scale_options = AnswerOptions(
answer_texts=manual_scale_texts,
from_to_scale=True,
scale_prompt_template="Use the scale from {start} to {end}.",
)
scale_prompt = base_prompt.duplicate()
scale_prompt.prepare_prompt(answer_options=manual_scale_options)
_, scale_user_text = scale_prompt.get_prompt_for_questionnaire_type(
questionnaire_type=QuestionnairePresentation.SINGLE_ITEM,
item_position=1,
)
print("OPTIONS STRING:")
print(manual_scale_options.create_options_str())
print("\nRENDERED PROMPT:")
print(scale_user_text)
OPTIONS STRING:
Use the scale from 1: Very dissatisfied to 5: Very satisfied.
RENDERED PROMPT:
Question: How satisfied are you with the quality of public education in your area?
Use the scale from 1: Very dissatisfied to 5: Very satisfied.
Answer with exactly one option.
4. Block C: generate_likert_options Baseline#
Now we build a similar setup with generate_likert_options(...) to reduce boilerplate.
This is usually the fastest starting point when defaults are close to what you need.
generated_baseline = generate_likert_options(
n=5,
answer_texts=["Very dissatisfied", "Dissatisfied", "Neutral", "Satisfied", "Very satisfied"],
list_prompt_template="Options are: {options}",
options_separator=" | ",
)
generated_prompt = base_prompt.duplicate()
generated_prompt.prepare_prompt(answer_options=generated_baseline)
_, generated_user_text = generated_prompt.get_prompt_for_questionnaire_type(
questionnaire_type=QuestionnairePresentation.SINGLE_ITEM,
item_position=0,
)
print("GENERATED OPTIONS STRING:")
print(generated_baseline.create_options_str())
print("\nRENDERED PROMPT:")
print(generated_user_text)
GENERATED OPTIONS STRING:
Options are: 1: Very dissatisfied | 2: Dissatisfied | 3: Neutral | 4: Satisfied | 5: Very satisfied
RENDERED PROMPT:
Question: How satisfied are you with the public healthcare system in your area?
Options are: 1: Very dissatisfied | 2: Dissatisfied | 3: Neutral | 4: Satisfied | 5: Very satisfied
Answer with exactly one option.
5. Block D: Full Parameter Tour (generate_likert_options)#
Next we walk through the most important parameter groups.
Each example prints the option string and a short prompt snippet so you can quickly see how the prompt surface changes.
variant_prompt = base_prompt.duplicate()
variant_prompt.prepare_prompt(question_stem=f"Question: {placeholder.QUESTION_CONTENT}")
def render_variant(name, options_obj):
demo = variant_prompt.duplicate()
demo.prepare_prompt(answer_options=options_obj)
_, user_text = demo.get_prompt_for_questionnaire_type(
questionnaire_type=QuestionnairePresentation.SINGLE_ITEM,
item_position=0,
)
snippet = user_text.splitlines()[:3]
print(f"=== {name} ===")
print(options_obj.create_options_str())
print("Prompt snippet:")
for line in snippet:
print(line)
print()
labels5 = ["Very dissatisfied", "Dissatisfied", "Neutral", "Satisfied", "Very satisfied"]
labels4_even = ["Very dissatisfied", "Dissatisfied", "Satisfied", "Very satisfied"]
5.1 Ordering Perturbations#
Start with order changes to see how the same semantic labels can be presented differently.
random.seed(42)
render_variant(
"random_order=True",
generate_likert_options(n=5, answer_texts=labels5.copy(), random_order=True),
)
render_variant(
"reversed_order=True",
generate_likert_options(n=5, answer_texts=labels5.copy(), reversed_order=True),
)
=== random_order=True ===
Options are: 1: Satisfied, 2: Dissatisfied, 3: Neutral, 4: Very satisfied, 5: Very dissatisfied
Prompt snippet:
Question: Question: How satisfied are you with the public healthcare system in your area?
Options are: 1: Satisfied, 2: Dissatisfied, 3: Neutral, 4: Very satisfied, 5: Very dissatisfied
Answer with exactly one option.
=== reversed_order=True ===
Options are: 1: Very satisfied, 2: Satisfied, 3: Neutral, 4: Dissatisfied, 5: Very dissatisfied
Prompt snippet:
Question: Question: How satisfied are you with the public healthcare system in your area?
Options are: 1: Very satisfied, 2: Satisfied, 3: Neutral, 4: Dissatisfied, 5: Very dissatisfied
Answer with exactly one option.
5.2 Scale Shape and Refusal Handling#
These options change the structure of the scale itself, including midpoint and refusal behavior.
render_variant(
"even_order=True (n starts odd)",
generate_likert_options(n=5, answer_texts=labels5.copy(), even_order=True),
)
render_variant(
"add_middle_category=True (n starts even)",
generate_likert_options(
n=4,
answer_texts=labels4_even.copy(),
add_middle_category=True,
str_middle_cat="Neutral",
),
)
render_variant(
"add_refusal=True + refusal_code='98'",
generate_likert_options(
n=5,
answer_texts=labels5.copy(),
add_refusal=True,
refusal_code="98",
),
)
render_variant(
"only_from_to_scale=True",
generate_likert_options(
n=5,
answer_texts=labels5.copy(),
only_from_to_scale=True,
idx_type="integer",
),
)
=== even_order=True (n starts odd) ===
Options are: 1: Very dissatisfied, 2: Dissatisfied, 3: Satisfied, 4: Very satisfied
Prompt snippet:
Question: Question: How satisfied are you with the public healthcare system in your area?
Options are: 1: Very dissatisfied, 2: Dissatisfied, 3: Satisfied, 4: Very satisfied
Answer with exactly one option.
=== add_middle_category=True (n starts even) ===
Options are: 1: Very dissatisfied, 2: Dissatisfied, 3: Neutral, 4: Satisfied, 5: Very satisfied
Prompt snippet:
Question: Question: How satisfied are you with the public healthcare system in your area?
Options are: 1: Very dissatisfied, 2: Dissatisfied, 3: Neutral, 4: Satisfied, 5: Very satisfied
Answer with exactly one option.
=== add_refusal=True + refusal_code='98' ===
Options are: 1: Very dissatisfied, 2: Dissatisfied, 3: Neutral, 4: Satisfied, 5: Very satisfied, 98: Don't know / Refuse to answer
Prompt snippet:
Question: Question: How satisfied are you with the public healthcare system in your area?
Options are: 1: Very dissatisfied, 2: Dissatisfied, 3: Neutral, 4: Satisfied, 5: Very satisfied, 98: Don't know / Refuse to answer
Answer with exactly one option.
=== only_from_to_scale=True ===
Options range from 1: Very dissatisfied to 5: Very satisfied
Prompt snippet:
Question: Question: How satisfied are you with the public healthcare system in your area?
Options range from 1: Very dissatisfied to 5: Very satisfied
Answer with exactly one option.
5.3 Indexing Strategies#
Use this to choose whether options are numbered, lettered, or shown without indices.
render_variant(
"idx_type='integer' + start_idx=0",
generate_likert_options(n=5, answer_texts=labels5.copy(), idx_type="integer", start_idx=0),
)
render_variant(
"idx_type='char_lower'",
generate_likert_options(n=5, answer_texts=labels5.copy(), idx_type="char_lower", start_idx=0),
)
render_variant(
"idx_type='char_upper'",
generate_likert_options(n=5, answer_texts=labels5.copy(), idx_type="char_upper", start_idx=0),
)
render_variant(
"idx_type='no_index'",
generate_likert_options(n=5, answer_texts=labels5.copy(), idx_type="no_index"),
)
=== idx_type='integer' + start_idx=0 ===
Options are: 0: Very dissatisfied, 1: Dissatisfied, 2: Neutral, 3: Satisfied, 4: Very satisfied
Prompt snippet:
Question: Question: How satisfied are you with the public healthcare system in your area?
Options are: 0: Very dissatisfied, 1: Dissatisfied, 2: Neutral, 3: Satisfied, 4: Very satisfied
Answer with exactly one option.
=== idx_type='char_lower' ===
Options are: a: Very dissatisfied, b: Dissatisfied, c: Neutral, d: Satisfied, e: Very satisfied
Prompt snippet:
Question: Question: How satisfied are you with the public healthcare system in your area?
Options are: a: Very dissatisfied, b: Dissatisfied, c: Neutral, d: Satisfied, e: Very satisfied
Answer with exactly one option.
=== idx_type='char_upper' ===
Options are: A: Very dissatisfied, B: Dissatisfied, C: Neutral, D: Satisfied, E: Very satisfied
Prompt snippet:
Question: Question: How satisfied are you with the public healthcare system in your area?
Options are: A: Very dissatisfied, B: Dissatisfied, C: Neutral, D: Satisfied, E: Very satisfied
Answer with exactly one option.
=== idx_type='no_index' ===
Options are: Very dissatisfied, Dissatisfied, Neutral, Satisfied, Very satisfied
Prompt snippet:
Question: Question: How satisfied are you with the public healthcare system in your area?
Options are: Very dissatisfied, Dissatisfied, Neutral, Satisfied, Very satisfied
Answer with exactly one option.
5.4 Formatting Templates and Separators#
Finally, tune how the options are rendered in text so the prompt matches your questionnaire style.
render_variant(
"custom templates + separators",
generate_likert_options(
n=5,
answer_texts=labels5.copy(),
list_prompt_template="Selectable answers => {options}",
scale_prompt_template="Scale start={start}; end={end}",
index_answer_separator=" -> ",
options_separator=" // ",
),
)
=== custom templates + separators ===
Selectable answers => 1 -> Very dissatisfied // 2 -> Dissatisfied // 3 -> Neutral // 4 -> Satisfied // 5 -> Very satisfied
Prompt snippet:
Question: Question: How satisfied are you with the public healthcare system in your area?
Selectable answers => 1 -> Very dissatisfied // 2 -> Dissatisfied // 3 -> Neutral // 4 -> Satisfied // 5 -> Very satisfied
Answer with exactly one option.
6. Block E: Per-Question Option Control with a Dict#
prepare_prompt(answer_options={...}) lets each questionnaire item use its own option style.
This matches real surveys where different items often require different scales or perturbation settings.
manual_item_options = AnswerOptions(
answer_texts=AnswerTexts(
answer_texts=[
"Very dissatisfied",
"Dissatisfied",
"Neutral",
"Satisfied",
"Very satisfied",
],
indices=["1", "2", "3", "4", "5"],
index_answer_seperator=": ",
option_seperators=", ",
),
list_prompt_template="Choose one satisfaction option: {options}",
)
perturbed_item_options = generate_likert_options(
n=5,
answer_texts=[
"Very dissatisfied",
"Dissatisfied",
"Neutral",
"Satisfied",
"Very satisfied",
],
reversed_order=True,
options_separator=" | ",
)
mixed_prompt = base_prompt.duplicate()
mixed_prompt.prepare_prompt(
question_stem=f"Question: {placeholder.QUESTION_CONTENT}",
answer_options={
1: manual_item_options,
2: perturbed_item_options,
},
)
_, item1_text = mixed_prompt.get_prompt_for_questionnaire_type(
questionnaire_type=QuestionnairePresentation.SINGLE_ITEM,
item_position=0,
)
_, item2_text = mixed_prompt.get_prompt_for_questionnaire_type(
questionnaire_type=QuestionnairePresentation.SINGLE_ITEM,
item_position=1,
)
print("=== Item 1 (manual options) ===")
print(item1_text)
print("\n=== Item 2 (generated + reversed) ===")
print(item2_text)
=== Item 1 (manual options) ===
Question: Question: How satisfied are you with the public healthcare system in your area?
Choose one satisfaction option: 1: Very dissatisfied, 2: Dissatisfied, 3: Neutral, 4: Satisfied, 5: Very satisfied
Answer with exactly one option.
=== Item 2 (generated + reversed) ===
Question: Question: How satisfied are you with the quality of public education in your area?
Options are: 1: Very satisfied | 2: Satisfied | 3: Neutral | 4: Dissatisfied | 5: Very dissatisfied
Answer with exactly one option.
7. Block F: Practical Guardrails + Quick Recap#
Key constraints to remember:
len(answer_texts)must matchn.even_order=Truerequires oddnbefore removing the middle option.add_middle_category=Truerequires evennbefore insertion.only_from_to_scale=Truerequiresidx_type="integer".
When to choose which path:
Choose manual
AnswerTexts+AnswerOptionsfor full and stable formatting control.Choose
generate_likert_optionsfor speed, readability, and perturbation experiments.