
Designed conversations: debates, focus groups, interviews, deliberations
Source:vignettes/designed-conversations.Rmd
designed-conversations.RmdSocial research has spent a century refining conversation formats:
the debate elicits the strongest case each side can muster; the focus
group surfaces how opinions move in company; the interview goes deep on
one person; the deliberation ends in a decision. LLMRagent ships each as
a one-call preset. All four share a structure: agents built with
agent(), a shared attributed transcript, and tidy returns.
What you learn from one transfers to the others.
Throughout, one model and one cast:
library(LLMRagent)
cfg <- LLMR::llm_config("groq", "openai/gpt-oss-20b", temperature = 0.8)Debate: the strongest case for each side
debate() runs opening statements, rounds of
rebuttals, and closings, in fixed alternation; an optional judge returns
a structured verdict. Because every statement is labeled with its phase,
you can study how arguments develop: openings state values, rebuttals
engage evidence, closings synthesize.
d <- debate(
pro = agent("Pro", cfg, persona = "You argue FOR the motion. Rigorous, concrete."),
con = agent("Con", cfg, persona = "You argue AGAINST the motion. Rigorous, concrete."),
topic = "Algorithmic screening should be banned from hiring decisions.",
rounds = 1,
judge = agent("Judge", cfg, persona = "A strict, impartial debate judge.")
)
d # compact print: motion, statements, verdict
d$transcript # tidy: turn, phase, speaker, text
d$verdict$reasoningFocus group: opinions in company
focus_group() puts each question to every participant;
speaking order rotates across questions so no one speaks first every
round, and participants see the discussion so far, as in a real group.
The moderator closes with a synthesis.
fg <- focus_group(
moderator = agent("Mod", cfg, persona = "A neutral, probing focus-group moderator."),
participants = list(
agent("Maya", cfg, persona = "A 34-year-old nurse; prudent, skeptical of tech."),
agent("Tom", cfg, persona = "A 22-year-old gig worker; tech-optimistic."),
agent("Ines", cfg, persona = "A 58-year-old teacher; worries about fairness.")
),
topic = "Using AI screening in hiring",
questions = c(
"How would you feel if an algorithm screened your next job application?",
"What, if anything, would make that acceptable to you?"
)
)
fg$summary # the moderator's synthesis
fg$transcript # utterance-level frame for content analysisLeave questions = NULL and the moderator drafts its own.
This helps when piloting an instrument before you spend human-subjects
time on it.
Interview: depth on one respondent
interview() works through a question list with one
adaptive probe after each answer (the interviewer decides whether a
probe is warranted; NONE suppresses it). The return is
already the frame interview studies analyze: one row per question or
probe.
iv <- interview(
interviewer = agent("Interviewer", cfg,
persona = "A careful qualitative researcher."),
respondent = agent("Respondent", cfg,
persona = "A warehouse worker whose shift assignments are set by software."),
topic = "Working under algorithmic management",
n_questions = 3
)
iv[, c("type", "question")]Deliberation: talk, then decide
deliberate() is the format with a dependent variable.
Everyone speaks each round, seeing the discussion so far; then each
agent votes privately through structured output, with a one-sentence
reason. Public positions and private votes can disagree, and that gap is
itself measurable.
panel <- list(
agent("Aila", cfg, persona = "Data-driven; cautious about unintended effects."),
agent("Bo", cfg, persona = "Mission-driven; impatient with delay."),
agent("Cyn", cfg, persona = "A budget hawk. Blunt.")
)
dl <- deliberate(panel,
proposal = "Adopt AI resume screening for all entry-level hiring.",
rounds = 2)
dl$votes # voter, vote, reason
dl$decisionFrom transcripts to analysis
Every preset returns its transcript as a tidy tibble, so the path to analysis is short. Two common moves: score utterances with a model (via LLMR’s tidy verbs), and compare across runs with [agent_experiment()].
# Who spoke most, and how much?
tr <- fg$transcript
aggregate(nchar(text) ~ speaker, data = tr, FUN = sum)
# Score each utterance for stance with a one-line LLMR call:
scored <- LLMR::llm_mutate(
tr, stance,
prompt = "One word, support/oppose/neutral. Stance on AI hiring in: {text}",
.config = cfg
)
table(scored$speaker, scored$stance)To turn any of these into an experiment, vary the personas, the
framing, or the group composition, then wrap the call in a function and
hand it to agent_experiment(). The deliberation vignette
walks through a complete factorial study. And for a permanent record of
every call these formats make, switch on
LLMR::llm_log_enable("study.jsonl") first.