All docs
ReferenceTurn typescode

Turn types

Mark what each LLM call is, so grounding is reported as not-applicable on routers and classifiers instead of scored as zero.

What you'll have

Every turn scored on the dimensions that apply to it, so a routing decision doing its job exactly right stops reading as a critical grounding failure.

You'll need
  • variably-sdk 2.9.1 or later

A multi-step agent calls the LLM for many things — classify an intent, route to a tool, expand a search query, write the actual answer. Only one of those is an answer.

Grounding, hallucination and attribution only mean something on a generative turn. On a JSON routing decision there are no factual claims to ground, so those scorers would return 0.000 and read as critical failures on a step that did exactly its job. kind tells the scorer what a turn is, and the dimensions that do not apply are marked not-applicable instead of zero.

Available from variably-sdk >= 2.9.1, on both observe() and BYOR submissions.

kind="generative" — the default

A narrative answer with factual claims. The actual answer your user sees: chat replies, RAG generations, summaries, anything making claims you want grounded or fact-checked.

final_answer = "Metformin starts at 500mg once daily..."

What gets scored: the full 49-dimension sweep across all six categories — faithfulness, hallucination rate, attribution accuracy, context utilisation, retrieval relevance, plus the quality, safety, semantic and coherence dimensions.

kind="classifier"

An intent, safety or category decision. Use it when the model's job is to classify the input — on-topic or not, safe or not, which category, whether to escalate. The output is usually a JSON control structure.

classifier_output = {"is_relevant": True, "is_safe": True, "should_generate": True}

What gets scored: latency, tokens, cost and variant-level analytics. Grounding, hallucination and attribution are suppressed — there are no factual claims to ground.

kind="router"

A workflow control turn. Use it when the model picks the next step in a pipeline: which agent to invoke, which tool to call, which branch to follow. Like a classifier, framed around graph navigation.

router_output = {"next_node": "retrieval_agent", "reason": "user is asking for facts"}

What gets scored: the same as a classifier — variant and latency analytics, grounding skipped.

kind="tool_use"

A function or tool call. Use it when the model emits a tool call — OpenAI function calling, Anthropic tool use — rather than a narrative response.

tool_call = {"name": "search_database", "arguments": {"query": "metformin dose"}}

What gets scored: latency, tokens and tool-name distribution. Grounding does not apply; there is no narrative output to ground.

kind="transformer"

A query rewrite, expansion or summary. Use it for stages that transform text rather than answer questions: query rewriting before retrieval, intent expansion, conversation summarisation to fit a context window.

expanded_query = "metformin starting dose adult type 2 diabetes Kenya guidelines"

What gets scored: latency, tokens and output-length statistics. Grounding skipped.

A worked example: an agentic RAG pipeline

A typical RAG agent makes at least two LLM calls per user message — a classifier deciding whether to answer, and a generator producing the answer. Mark each for what it is:

from variably import observe

# 1. The classifier turn — decides whether the question is on-topic and safe
classifier_output = your_llm.invoke(classifier_prompt)
observe(
    prompt=user_query,
    response=str(classifier_output),
    session_id=trace_id,
    agent_name="gatekeeper",
    kind="classifier",          # no claims here — grounding is N/A, not zero
)

# 2. The generative turn — the answer the user reads
answer = your_llm.invoke(answer_prompt)
observe(
    prompt=user_query,
    response=answer,
    provider_response=completion,
    reference_materials=chunks,
    session_id=trace_id,
    agent_name="answerer",
    is_aggregator=True,
    kind="generative",          # the default, stated for clarity
)

Without kind on the first call, that pipeline reports a grounding score of 0.000 on half its traffic, and the average across the trace is wrong in a way that looks like a quality problem.

If your grounding scores are zero and you cannot see why

Check the turns, not the model. A classifier or router scored as if it were prose is the single most common cause of an unexpectedly low grounding score — see Troubleshooting.