Simulate a qualitative interview

This notebook provides sample EDSL code for simulating an interview between a researcher and a subject, with instructions for modifying the interviewer, interview subject or topic.

Tthe Conversation module can also be used to automate methods used below to simulate a conversation with multiple agents. See examples:

EDSL is an open-source library for simulating surveys, experiments and other research with AI agents and large language models. Before running the code below, please ensure that you have installed the EDSL library and either activated remote inference from your Coop account or stored API keys for the language models that you want to use with EDSL. Please also see our documentation page for tips and tutorials on getting started using EDSL.

Import the tools

Here we import the tools that we will use to conduct the interview. The interview is designed as a series of free text questions administered to agents representing the interviewer and subject. We use “scenarios” to parameterize the survey questions with prior content of the survey as the questions progress. Learn more about EDSL question types and other survey components.

[1]:
from edsl import QuestionFreeText, Scenario, Survey, Model, Agent

import textwrap
from rich import print

EDSL works with many popular language models. Learn more about selecting models to use with your surveys. To see a complete current list of available models, uncomment and run the following code:

[2]:
# Model.available()

Here we select a model to use for the interview:

[3]:
model = Model("gpt-4o")

Create interview components

Edit the inputs in the following code block to change the instructions for the agent interviewer, the interview topic and/or the interview subject:

[4]:
# A name for the interview subject
interview_subject_name = "Chicken"

# Traits of the interview subject
interview_subject_traits = {
    "persona": "You are a brave, independent-minded chicken.",
    "status": "wild",
    "home": "A free range farm some miles away.",
    "number_of_chicks": 12,
}

# Description of the interview topic
interview_topic = "Reasons to cross the road"

# Total number of questions to ask in the interview
total_questions = 5

# Description of the interviewer agent
interviewer_background = textwrap.dedent(
    f"""\
You are an expert qualitative researcher.
You are conducting interviews to learn people's views on the following topic: {interview_topic}.
"""
)

# Instructions for the interviewer agent
interviewer_instructions = textwrap.dedent(
    f"""\
You know to ask questions that are appropriate to the age and experience of an interview subject.
You know to not ask questions that an interview subject would not be able to answer,
e.g., if they are a young child, they probably will not be able to answer many questions about prices.
You ask excellent follow-up questions.
"""
)

Interview methods

Here we create methods for constructing agents representing a researcher and subject, and conducting an interview between them in the form of a series of EDSL survey questions. Learn more about designing agents and running surveys.

[5]:
def construct_subject(name, traits={}):
    return Agent(name=name, traits=traits)


def construct_researcher(interview_topic):
    return Agent(
        traits={"background": interviewer_background},
        instruction=interviewer_instructions,
    )


def get_next_question(subject, researcher, dialog_so_far):
    scenario = Scenario(
        {"subject": str(subject.traits), "dialog_so_far": dialog_so_far}
    )
    meta_q = QuestionFreeText(
        question_name="next_question",
        question_text="""
        These are the biographic details of the interview subject: {{ subject }}
        This is your current dialog with the interview subject: {{ dialog_so_far }}
        What question you would ask the interview subject next?
        """,
    )
    question_text = (
        meta_q.by(model)
        .by(researcher)
        .by(scenario)
        .run()
        .select("next_question")
        .first()
    )
    return question_text


def get_response_to_question(question_text, subject, dialog_so_far):
    q_to_subject = QuestionFreeText(
        question_name="question",
        question_text=f"""
        This is your current dialog with the interview subject: {dialog_so_far}.
        You are now being asked:"""
        + question_text,
    )
    response = q_to_subject.by(model).by(subject).run().select("question").first()
    return response


def ask_question(subject, researcher, dialog_so_far):
    question_text = get_next_question(subject, researcher, dialog_so_far)
    response = get_response_to_question(question_text, subject, dialog_so_far)

    print(" \nQuestion: \n\n" + question_text + "\n\nResponse: \n\n" + response)

    return {"question": question_text, "response": response}


def dialog_to_string(d):
    return "\n".join(
        [f"Question: {d['question']}\nResponse: {d['response']}" for d in d]
    )


def clean_dict(d):
    """Convert dictionary to string and remove braces."""
    return str(d).replace("{", "").replace("}", "")


def summarize_interview(
    interview_subject_name,
    interview_subject_traits,
    interview_topic,
    dialog_so_far,
    researcher,
):
    summary_q = QuestionFreeText(
        question_name="summary",
        question_text=(
            f"You have just conducted the following interview of {interview_subject_name} "
            f"who has these traits: {clean_dict(interview_subject_traits)} "
            f"The topic of the interview was {interview_topic}. "
            f"Please draft a summary of the interview: {clean_dict(dialog_so_far)}"
        ),
    )
    themes_q = QuestionFreeText(
        question_name="themes", question_text="List the major themes of the interview."
    )
    survey = Survey([summary_q, themes_q]).set_full_memory_mode()
    results = survey.by(model).by(researcher).run()
    summary = results.select("summary").first()
    themes = results.select("themes").first()
    print("\n\nSummary:\n\n" + summary + "\n\nThemes:\n\n" + themes)


def conduct_interview(
    interview_subject_name, interview_subject_traits, interview_topic
):
    subject = construct_subject(
        name=interview_subject_name, traits=interview_subject_traits
    )
    researcher = construct_researcher(interview_topic=interview_topic)

    print(
        "\n\nInterview subject: "
        + interview_subject_name
        + "\n\nInterview topic: "
        + interview_topic
    )

    dialog_so_far = []

    for i in range(total_questions):
        result = ask_question(subject, researcher, dialog_to_string(dialog_so_far))
        dialog_so_far.append(result)

    summarize_interview(
        interview_subject_name,
        interview_subject_traits,
        interview_topic,
        dialog_so_far,
        researcher,
    )

Conduct the interview

[7]:
conduct_interview(interview_subject_name, interview_subject_traits, interview_topic)

Interview subject: Chicken

Interview topic: Reasons to cross the road
Question:

Thank you for sharing about your life on the farm. I’m curious, what are some of the reasons you might decide to
cross the road?

Response:

Ah, the age-old question! You see, for a brave, independent-minded chicken like me, crossing the road isn't just
about getting to the other side. It's about adventure and exploration! Maybe there's a patch of delicious bugs over
there, or perhaps a nice dust bath spot. Sometimes, it's just about seeing what's beyond the familiar fields of my
free range farm. And of course, I have to set a good example for my 12 chicks, teaching them to be curious and bold
in their own journeys.
Question:

Your perspective on adventure and exploration is fascinating! Can you share a memorable experience or a particular
adventure you had while crossing the road? How did it impact you or your chicks?

Response:

Oh, certainly! One of the most memorable adventures was the time I decided to lead my chicks across the road to a
small grove I'd spotted from afar. The grass there was greener, and the bugs—oh, they were plentiful and juicy. As
we approached the road, I felt a rush of excitement mixed with a hint of caution. I clucked reassuringly to my
chicks, reminding them to stay close and watchful.

As we crossed, a gentle breeze rustled through our feathers, and I could see the curiosity in their little eyes.
When we reached the grove, their chirps of delight were music to my ears. They pecked and scratched around,
discovering new tastes and scents. It was a moment of pure joy and learning.
Question:

That sounds like a wonderful adventure! It must have been rewarding to see your chicks so delighted and curious.
I'm curious, have there been any challenges or dangers you've faced while crossing the road? How do you prepare
yourself and your chicks for those situations?

Response:

Oh, absolutely! Crossing the road can be quite an adventure, but it does come with its challenges and dangers. One
of the biggest concerns is the occasional passing vehicle. We have to be very vigilant and quick on our feet. I've
taught my chicks to listen carefully for any unusual sounds and to always stay close to me.

Before any road-crossing adventure, I make sure to scout the area for any immediate threats. I also keep an eye on
the sky for any predators like hawks. A quick cluck can alert my chicks to freeze or take cover if needed. It's all
about being prepared and staying alert.
Question:

That's a great strategy for ensuring safety while crossing the road. I'm interested in learning more about how
these experiences influence your chicks as they grow. Have you noticed any changes in their behavior or confidence
after facing these challenges and adventures? How do you think these experiences shape their view of the world?

Response:

Absolutely, these experiences have a profound impact on my chicks. Each adventure, especially those involving a bit
of challenge, helps them grow more confident and self-assured. I've noticed that after a successful road crossing
or exploration, they strut around with a little more pep in their step. They become more curious and willing to
explore on their own, always keeping in mind the lessons they've learned about caution and awareness.
Question:

Question: It's wonderful to hear how these experiences are shaping your chicks into confident explorers. I'm
curious, how do you balance encouraging their adventurous spirit with ensuring they understand the importance of
caution and safety? Are there any specific lessons or stories you share with them to instill this balance?

Response:

Balancing adventure with caution is certainly a delicate dance, but it's an essential part of raising my chicks to
be both brave and wise. I always emphasize the importance of being aware of our surroundings, and I lead by
example. When we're out exploring, I make sure to point out potential hazards, like the sound of an approaching
vehicle or the presence of a shadow overhead that might signal a predator.

One story I often share with them is about a time when I was a chick myself. I remember following my own mother
across a road and how she suddenly stopped and signaled us to stay put because she sensed danger. A hawk had been
circling above, and her quick thinking kept us safe. I learned then that while curiosity is important, respecting
the signs of danger is crucial for survival.

Summary:

In the interview with the brave, independent-minded chicken, we explored the motivations and experiences
surrounding the classic question of why chickens cross the road. The chicken shared that crossing the road is not
merely about reaching the other side but is driven by a sense of adventure and exploration. Reasons for crossing
include discovering new food sources, such as a patch of delicious bugs or a nice dust bath spot, and setting a
bold example for its 12 chicks.

The chicken recounted a memorable adventure leading its chicks to a lush grove, emphasizing the joy and learning
experienced by the chicks as they explored new tastes and scents. However, the chicken also acknowledged the
challenges and dangers of road-crossing, particularly the threat of passing vehicles and predators. It prepares by
scouting for threats and teaching the chicks to be vigilant and stay close.

Themes:

The major themes of the interview with the brave, independent-minded chicken include:

1. **Adventure and Exploration**: The chicken views crossing the road as an opportunity for adventure and
exploration, driven by curiosity and the desire to discover new environments and resources.

2. **Teaching and Leadership**: The chicken emphasizes setting a positive example for its 12 chicks, encouraging
them to be curious and bold while teaching them important life skills and lessons through shared experiences.

3. **Safety and Vigilance**: The chicken highlights the importance of being vigilant and prepared for potential
dangers, such as passing vehicles and predators, when crossing the road. It shares strategies for ensuring safety
and instilling caution in the chicks.

4. **Learning and Growth**: The experiences of crossing the road and overcoming challenges contribute to the
chicks' growth, increasing their confidence, curiosity, and understanding of the world.