Skip to contents

Sends each text to an LLM with a structured-output schema built from the codebook, and adds one logical column per code to data. Each text is coded in an independent conversation, so earlier texts never leak into the context of later ones.

Usage

llm_auto_code(
  data,
  id_col,
  text_col,
  codebook,
  chat = NULL,
  coder = NULL,
  max_active = 10
)

Arguments

data

A data frame with at least an ID column and a text column. Rows may be participants or individual transcript segments.

id_col

Column holding the participant/unit identifier (unquoted or a string).

text_col

Column holding the qualitative text (unquoted or a string).

codebook

Character vector of codes. Either unnamed labels (c("UI_Confusion", "Feature_Request")) or named definitions (c(UI_Confusion = "The user reports being lost or confused")). Explicit definitions give far more reliable coding.

chat

An ellmer::Chat object, e.g. ellmer::chat_openai() or ellmer::chat_anthropic(). Required unless coder is supplied.

coder

Optional function function(texts, codebook) returning a data frame of logical columns named after the codes, one row per text. Use this to plug in another backend, or mock_coder() for offline demos.

max_active

Maximum number of concurrent LLM requests.

Value

data with one logical column per code. Missing or empty texts get NA. The codebook is stored in the "saydo_codebook" attribute so cross_tab_themes() can find the code columns automatically.

Examples

# Offline, with the keyword-based demo coder:
df <- mock_mixed_data(30, seed = 1)
coded <- llm_auto_code(df, user_id, transcript_feedback,
                       codebook = c("UI_Confusion", "Positive_Experience"),
                       coder = mock_coder())
head(coded)
#> # A tibble: 6 × 7
#>   user_id  profile   session_duration_sec errors_encountered transcript_feedback
#>   <chr>    <chr>                    <dbl>              <int> <chr>              
#> 1 USR-1001 frustrat…                  470                 11 Why is the checkou…
#> 2 USR-1002 casual                     215                  2 The application lo…
#> 3 USR-1003 frustrat…                  536                  4 The layout changed…
#> 4 USR-1004 efficient                  190                  0 Really clean UI. N…
#> 5 USR-1005 frustrat…                  428                 10 I got completely l…
#> 6 USR-1006 casual                     218                  1 I opened the setti…
#> # ℹ 2 more variables: UI_Confusion <lgl>, Positive_Experience <lgl>

if (FALSE) { # \dontrun{
# With a real LLM (needs an API key, e.g. OPENAI_API_KEY):
coded <- llm_auto_code(
  df, user_id, transcript_feedback,
  codebook = c(
    UI_Confusion = "The user reports being lost, confused or unable to find something",
    Feature_Request = "The user asks for a feature or capability that does not exist"
  ),
  chat = ellmer::chat_openai(model = "gpt-4.1-mini")
)
} # }