Scores every (course objective, session objective) pair with a pluggable scoring system and returns the long form plus everything needed to reproduce the run.
Usage
map_course_to_sessions(
course_objectives,
session_objectives,
course_id,
llmconfig,
scoring_system = "ioc",
block = c("pairwise", "joint"),
repeats = 1L,
rationale = TRUE,
verbosity = 1L,
outdir = NULL,
...
)Arguments
- course_objectives
Character: Course objectives.
- session_objectives
List of character vectors: Session objectives, named by session.
- course_id
Character: Course identifier, used to build stable ids.
- llmconfig
rtemis.llm::LLMConfig: Model configuration, fromconfig_Ollama(),config_OpenAI()orconfig_Anthropic(). One object carries the model, temperature, endpoint and key handling, so an OpenAI-compatible gateway needs no special support here – see the OpenRouter example. Only the model name, backend, base URL and temperature are recorded inrun; an API key held on the config is never persisted.- scoring_system
Character or
ScoringSystem: Scoring system. Seelist_scoring_systems().- block
Character {"pairwise", "joint"}: Prompt block structure.
- repeats
Integer
[1, Inf): Independent raters per pair. IOC and CVI are panel instruments and are defined over several raters; withrepeats = 1they still work but their thresholds do not mean what they were calibrated to mean.- rationale
Logical: Whether to ask for a one-sentence justification per cell. Rationales are what make a mapping reviewable, so this defaults to
TRUE– but they dominate generation time under constrained decoding, since the free-text field is far longer than the label it explains. SetFALSEfor a fast pass when only the matrix is wanted.- verbosity
Integer
[0, Inf): Verbosity level.- outdir
Character: Directory to write resumable parts to, or
NULLfor a run that keeps nothing. Each criterion's scores are written as they are produced, and a later run over the same objectives with the same settings reads them back instead of scoring them again.- ...
Additional per-call arguments forwarded to
rtemis.llm::generate(), e.g.temperature,seed.
Block structure
block decides how many criteria the model sees at once, and it is not a
free choice.
"pairwise"(default) asks one isolated question per pair. Each call is short – one label, one rationale – so despite issuingn_criteriatimes as many calls it is competitive on wall time, and it buys three things joint scoring cannot. Judgements are independent, which makesrepeatsa genuine panel rather than correlated draws within a row – the thing IOC and CVI are actually defined over. The per-pair cache invalidates one cell at a time. And because the criterion sits in the system prompt while the run is criterion-major, the cached prefix is reused across every candidate."joint"shows all course objectives in one call and rates the session objective against each. The comparative context can improve discrimination: an isolated judgement has nothing to be relatively stingy against, so pairwise runs are worth checking for a dense, undifferentiated matrix.
The distinction matters most across courses. Joint scoring induces an
implicit normalization within a call: shown a list, a model spreads its
ratings across it, so a course with 20 objectives will produce systematically
different marginals from one with 6. That is harmless within a course and
misleading between them, which is the other reason "pairwise" is the
default. Never pool joint-mode matrices with different n_criteria; block
and n_criteria are both recorded in run so the comparison can be checked
rather than assumed.
Duplicate candidates
A candidate text that appears more than once – a module taught over several
sessions, a column copied in the source sheet, an objective two sessions
share – is scored once and the answer written to every id that carries it.
The map keeps its full width, and the calls made are
n_criterion_texts x n_candidate_texts in pairwise mode and
n_candidate_texts in joint; run records the distinct-text counts
alongside n_criteria and n_candidates, so what was asked and what was
returned stay separable. Criteria are deduplicated in pairwise mode only:
joint mode shows every criterion in one call, where a copy costs no extra
call and removing it would change the list the model normalizes across.
With repeats > 1 the copies share the panel drawn for their text: it is
the same question about the same words, so the raters are not asked twice.
Examples
# Requires a running Ollama server and the named model
if (FALSE) { # \dontrun{
obj <- read_objectives("objectives.yaml")
m <- map_course_to_sessions(
obj[["objectives"]],
obj[["sessions"]],
course_id = "datasci213",
llmconfig = config_Ollama("granite4.2:8b", temperature = 0, think = FALSE),
scoring_system = "ioc"
)
as.matrix(m)
# Via OpenRouter with zero data retention
m <- map_course_to_sessions(
obj[["objectives"]],
obj[["sessions"]],
course_id = "datasci213",
llmconfig = config_OpenAI(
"openai/gpt-4o-mini",
temperature = 0,
base_url = "https://openrouter.ai/api/v1",
api_key_env = "OPENROUTER_API_KEY",
zero_data_retention = TRUE
),
scoring_system = "ioc"
)
} # }