Takes a design frame (one row per condition), runs run_fn once per
condition and replication, and returns the design with rep, result
(list-column), error, and duration columns. A failing cell records its
error message and does not stop the others; re-run failures by filtering
on !is.na(error).
Arguments
- design
A data frame; each row is a condition, each column a factor of the design (personas, prompts, treatments, model names, ...).
- run_fn
function(cond, rep)wherecondis one row ofdesignas a named list. Whatever it returns is stored in theresultlist-column.- reps
Replications per condition (default 1).
- parallel
If TRUE, cells run concurrently via the
futureframework (set a plan first, e.g.future::plan(future::multisession)).- quiet
FALSE prints one progress line per completed cell.
Details
run_fn receives the condition as a named list plus the replication
number, and should build its agents inside the function so every cell
starts fresh:
run_fn <- function(cond, rep) {
cfg <- LLMR::llm_config("groq", "openai/gpt-oss-20b", temperature = 0.9)
panel <- list(
agent("A", cfg, persona = cond$persona_a),
agent("B", cfg, persona = cond$persona_b)
)
deliberate(panel, cond$proposal, quiet = TRUE)
}A note on seeds: model replies are sampled server-side, so a local seed
never affects them. Set one only when your code draws random numbers
itself (a run_fn that randomizes stimuli, or the "random" turn policy
inside it); under parallel = TRUE the workers then use statistically
sound parallel streams (future.seed). Combine with
LLMR::llm_log_enable() to keep a per-call audit file of the whole
experiment.
Examples
if (FALSE) { # \dontrun{
design <- expand.grid(
temperature = c(0.2, 1.0),
framing = c("gains", "losses"),
stringsAsFactors = FALSE
)
res <- agent_experiment(design, reps = 3, run_fn = function(cond, rep) {
cfg <- LLMR::llm_config("groq", "openai/gpt-oss-20b",
temperature = cond$temperature)
a <- agent("Subject", cfg, quiet = TRUE)
a$reply(paste("Decide under", cond$framing, "framing: ..."))
})
} # }