Main R6 class for managing and running a focus group simulation. It orchestrates the simulation through distinct phases (Opening, Icebreaker, Engagement, Exploration, Closing) guided by a question script, manages agent interactions via a turn-taking flow, and provides methods for analysis and visualization of the conversation.
Value
An R6 generator. `FocusGroup$new()` returns a session object; most analyses run through [run_focus_group()], which builds, runs, and returns one. Construction alone makes no model call.
Public fields
topicCharacter. The main topic of the focus group discussion.
purposeCharacter. The primary purpose or research objective of conducting the focus group.
agentsNamed list. A list of `FGAgent` objects participating in the simulation, indexed by their IDs.
moderator_idCharacter. The ID of the agent designated as the moderator.
conversation_logList. A chronological log of all messages. Each message is a list that includes at least `message_id`, `round`, `speaker_id`, `is_moderator`, `text`, `timestamp`, `phase`, and `metadata`, along with call metadata: `response_id`, `finish_reason`, `sent_tokens`, `rec_tokens`, `total_tokens`, `duration_s`, `provider`, and `model`. `message_id` is unique message order; moderator and participant messages from one moderator cycle share `round`.
turn_taking_flowA `ConversationFlow` object dictating participant turn-taking.
prompt_templatesList. Holds prompt templates for agent/moderator actions.
question_scriptList. A structured list defining phases and specific questions/actions for the moderator. Each element is a list with `phase` (e.g., "opening", "icebreaker_question") and optionally `text` (for specific questions).
current_question_textCharacter. Text of the current question being discussed.
current_conversation_summaryCharacter. An LLM-generated summary of earlier parts of the conversation, used for managing context length in prompts.
final_summaryCharacter. Final LLM-generated summary from the most recent simulation run.
message_modeCharacter. The message construction used by the most recent `run_simulation()` ("roleflip" or "flat"), recorded so a saved object can be replayed (e.g. in the GUI continuation experiment) with the same construction.
admin_configAn `llm_config` object for group-level model tasks.
max_tokens_utteranceInteger. Default max tokens for participant utterances.
max_tokens_moderatorInteger. Default max tokens for moderator utterances.
max_tokens_desireInteger. Default max tokens for desire-to-talk queries.
max_participant_responsesInteger. Maximum number of participant exchanges per round before the moderator can intervene. Can also be set globally via `options(focusgroup.max_participant_responses = N)`.
total_tokens_sentNumeric. Total tokens sent across all LLM calls in the group.
total_tokens_receivedNumeric. Total tokens received across all LLM calls.
Methods
Method new()
Initialize a new FocusGroup simulation.
Usage
FocusGroup$new(
topic,
purpose,
agents,
moderator_id,
turn_taking_flow,
question_script = list(),
prompt_templates = list(),
admin_config = NULL,
max_tokens_config = list(),
max_participant_responses = NULL
)Arguments
topicCharacter. The main discussion topic.
purposeCharacter. The primary purpose of the focus group.
agentsNamed list of initialized `FGAgent` objects.
moderator_idCharacter. The ID of the agent acting as moderator.
turn_taking_flowAn initialized `ConversationFlow` object.
question_scriptList. Moderator's script defining phases and questions. If empty, a minimal default script (opening, generic discussion, closing) is used.
prompt_templatesList. Custom prompt templates. Defaults are used if not provided.
admin_configAn `llm_config` object for administrative model tasks. If `NULL`, the moderator's explicitly supplied `config` is used.
max_tokens_configList. Optional. Named list with `utterance`, `moderator`, `desire` to override default max token limits for these LLM call types.
max_participant_responsesInteger. Optional. Maximum participant exchanges per round before moderator intervention. Defaults to `getOption("focusgroup.max_participant_responses", 3)`.
Method run_simulation()
Run the full focus group simulation. Iterates through the `question_script` phases or a specified number of rounds.
Arguments
num_roundsInteger. Optional. Maximum number of moderator cycles to run. If `NULL` (default), the simulation runs until the `question_script` is exhausted or the moderator decides to end. If both `num_rounds` and `question_script` are provided, the simulation stops at whichever condition is met first.
verboseLogical. If `TRUE`, prints progress and utterances to the console.
Method summarize()
Generate a summary of the conversation using an LLM.
Usage
FocusGroup$summarize(
config,
summary_level = 1,
max_tokens = NULL,
internal_call = FALSE,
transcript_override = NULL,
.runner = NULL
)Arguments
configAn explicit `llm_config` object for the summarization model.
summary_levelInteger (1-3). 1: Prose overview, 2: Detailed bulleted, 3: Short bulleted takeaways.
max_tokensInteger. Optional. Max tokens for the summary.
internal_callLogical. If TRUE, this is an internal call (e.g. for context window management) and token counts are not added to the agent who owns `admin_config`.
transcript_overrideCharacter. Optional. If provided, this transcript is summarized instead of `self$conversation_log`.
.runnerOptional function used instead of live model calls. It receives a data frame with `config` and `messages` list-columns and returns the rows with at least `response_text`.
Method analyze()
Basic analysis of the conversation log.
Method analyze_topics()
Perform LDA topic modeling on the conversation.
Usage
FocusGroup$analyze_topics(
num_topics = 5,
min_doc_length = 20,
top_n_terms = 10,
message_ids = NULL,
speaker_ids = NULL,
seed = 110,
...
)Arguments
num_topicsInteger. Number of topics to identify.
min_doc_lengthInteger. Min words for a speaker's aggregated text to be a document.
top_n_termsInteger. Number of top terms per topic to return.
message_idsInteger vector. Optional `message_id` values to analyze.
speaker_idsCharacter vector. Optional. Specific speakers to analyze.
seedInteger or NULL. LDA seed for reproducibility (default 110, the package convention); `NULL` leaves the LDA control seed unset.
...Additional arguments to `topicmodels::LDA()`.
Method analyze_tfidf()
Calculate TF-IDF scores for terms per participant.
Method analyze_readability()
Calculate readability scores for each participant's aggregated text.
Method analyze_themes()
Perform LLM-assisted thematic analysis on the transcript.
Arguments
configAn explicit `llm_config` object for thematic analysis.
message_idsInteger vector. Optional `message_id` values to analyze.
speaker_idsCharacter vector. Optional. Specific speakers to analyze.
.runnerOptional function used instead of live model calls. It receives a data frame with `config` and `messages` list-columns and returns the rows with at least `response_text`.
Method analyze_statistics()
Perform statistical analysis on conversation patterns.
Method analyze_participation_balance()
Analyze participation balance and dominance patterns.
Method analyze_response_patterns()
Analyze response patterns and interaction behaviors.
Method analyze_question_patterns()
Analyze question asking patterns during the conversation.
Method analyze_key_phrases()
Extract and analyze key phrases using n-grams.
Method plot_participation_timeline()
Create a participation timeline of cumulative messages by participant across phases.
Method plot_word_count_distribution()
Create word count distribution plot showing message length patterns.
Method plot_participation_by_agent()
Create a participation by agent plot showing total messages per participant.
Examples
cfg <- LLMR::llm_config("groq", "openai/gpt-oss-20b",
api_key = LLMR::llm_api_key_env("GROQ_API_KEY"))
agents <- create_agents(
n_participants = 2, config = cfg,
direct_persona_descriptions = c("A retired teacher.",
"A night-shift nurse."))
flow <- create_conversation_flow("round_robin", agents, "MOD")
fg <- FocusGroup$new(topic = "Neighborhood safety",
purpose = "Pilot the moderator guide.",
agents = agents,
moderator_id = "MOD",
turn_taking_flow = flow)
#> No question_script provided. Using a minimal default script (opening, generic discussion, closing).
fg$topic
#> [1] "Neighborhood safety"
