Score task types¶
Choose assertions that match the business claim. Exact equality is a good default, but it is wrong for unordered labels and too brittle for extractions that permit several source-faithful boundaries.
Choose a comparison¶
| Comparison | Use for | Behavior |
|---|---|---|
exact |
Scalar labels, booleans, structured objects, ordered arrays | Type-sensitive JSON equality; array order matters |
set |
Unordered label arrays | Ignores top-level order and duplicates; JSON types remain distinct |
source_span |
Verbatim extraction | Candidate must be a source occurrence containing the required range and contained by the allowed range |
custom |
A deterministic domain rule unavailable in JSON configuration | Python-only async scorer registered by name and revision |
Exact and set examples¶
These are expectation fragments:
{
"name": "category",
"path": "/flows/triage/steps/classify/result/answer/optionId",
"expected": "billing_dispute",
"comparison": "exact"
}
{
"name": "labels",
"path": "/flows/triage/steps/tag/result/answer/optionIds",
"expected": ["billing", "urgent"],
"comparison": "set"
}
set is only for a top-level JSON array. It does not recursively normalize
nested arrays or coerce true, 1, and "1".
Source-span example¶
Suppose the case input message is Cancel renewal for account C-1.. Code-point
offsets [0, 14] identify the required words Cancel renewal, while [0, 29]
allows the returned extraction to include the account phrase:
{
"name": "requested_action",
"path": "/payload/requested_action",
"expected": {
"input_path": "/payload/message",
"required": [0, 14],
"allowed": [0, 29]
},
"comparison": "source_span"
}
Ranges use Unicode code-point offsets and an exclusive end. required must be
nonempty, inside allowed, and both must fit the source string. The candidate
must occur verbatim; evaluation performs no case folding, normalization, or
semantic judging.
Custom Python scorer¶
JSON datasets cannot import code or register scorers. Build or extend an in-memory suite explicitly:
from foliqant.core.json import FrozenJson
from foliqant.evaluation import Expectation, RegisteredScorer
async def same_casefolded(actual: FrozenJson, expected: FrozenJson) -> bool:
return (
isinstance(actual, str)
and isinstance(expected, str)
and actual.casefold() == expected.casefold()
)
expectation = Expectation(
"normalized_category",
"/payload/category",
"billing",
"custom",
"casefolded",
)
scorers = (RegisteredScorer("casefolded", "1", same_casefolded),)
Pass scorers=scorers to evaluate. Missing registration fails before the
pipeline runs. A scorer exception becomes a check error rather than an execution
failure. Change the scorer revision when its meaning changes.
Match metrics to the output¶
Metrics are optional summaries over a declared path and label catalog. Gold still comes from the matching expectation in each case.
Choice, ordinal, and predicate¶
Use classification for one catalog value:
{
"name": "queue_quality",
"path": "/flows/triage/steps/classify/result/answer/optionId",
"kind": "classification",
"labels": ["billing_dispute", "service_change", "cancellation"]
}
Ordinal levels and native decision predicate values use the same metric kind
with their complete string catalog. Predicate answers are the strings "true"
and "false" at /answer/value, not JSON booleans. For a generic boolean
output, use an exact expectation or project it to a string label before applying
a classification metric. A classification catalog may explicitly include null. If it
does, null gold and an actual null are a valid observed label. Otherwise an
actual null is an abstention, not a category.
Multiselect¶
Use multilabel with string labels:
{
"name": "issue_quality",
"path": "/flows/triage/steps/classify/result/answerability/issues",
"kind": "multilabel",
"labels": [
"no_supported_answer",
"conflicting_information",
"multiple_valid_options"
]
}
Multilabel reports include exact-set accuracy and per-label TP, FP, FN, and TN, plus micro and macro precision, recall, and F1. The label catalog is string-only.
Score structured and operational behavior explicitly¶
For extraction and request units, make separate assertions for fields that have separate business meanings. Use source spans for verbatim text and exact or set checks for IDs, statuses, relationships, and counts. Do not reduce a request-unit assessment to one label if unit identity or relations matter.
For decision fallbacks, native model correctness and application policy are different paths:
/flows/triage/steps/classify/result/answerability/status
/flows/triage/steps/classify/result/answer/optionId
/flows/triage/steps/classify/selection/category/id
/flows/triage/steps/classify/selection/origin
For handlers and MCP steps, assert the validated business result and expected failure status separately. For routing, assert both the selected transition or terminal status and the final projected payload. For a flow collection, assert ordered item identity, each relevant child result, and any partial ledger after failure. Evaluation traverses only records marked as flow collections; an ordinary business object that resembles a ledger is not treated as one.
Evaluate text and tool-assisted answers¶
A good reply can have several correct phrasings. Do not use exact sentence equality as a general measure of writing quality. Separate the claims you can check deterministically from those a person must review:
| Output or behavior | Useful check | What it does not establish |
|---|---|---|
| JSON answer fields | Exact values for references, statuses, and amounts | Correctness of unrelated prose |
| Free-form reply | A registered deterministic scorer for a precise requirement, plus human review | General factuality or helpfulness |
| Read-only lookup | Expected final facts and step completion | Which tool arguments the model selected |
| Required tool use | /flows/{flow}/steps/{step}/usage/tool_calls |
Whether the call was useful or correct |
| Agent-loop limit | A scripted model that keeps asking for tools, expecting budget_exhausted |
Quality of a live model's stopping decision |
For example, this expectation checks the attempt count in a controlled fixture that requires exactly one lookup:
Choose counts from the business requirement, not from one observed model run. Tool retries also consume attempts. If several valid strategies exist, an exact count is too restrictive; use a registered scorer for an allowed bound, and inspect the final answer independently. A standard result does not expose a full tool-call transcript. Assert tool names and arguments in isolated integration tests with a recording fake; do not infer them from the final reply or count.
For live text, review grounding in the provided input and tool facts, omissions, unsupported promises, language, and requested tone. The built-in comparisons do not call an LLM judge. The support-email evaluation tutorial shows separate checks for status, classification, extraction, and a scripted reply; its exact draft expectation verifies wiring, not writing quality.
Review reasons and evidence strength¶
Decision reasons and evidence strength are model assessments of the input. They are not probabilities or evidence that the answer is correct. Gold can assert a reviewed strength enum and answerability issues at their public paths, but avoid exact prose matching for reasons: review whether the reason names the actual supporting signal, conflict, or missing information.
Evaluate these assessments separately from category accuracy. A wrong category with strong reported evidence is a particularly useful case to inspect. A strong assessment that an input is not answerable can be correct when the input clearly contains incompatible active requests. See reason and evidence strength for the precise meaning of each field.
Understand metric limits¶
Classification confusion rows are expected labels and columns are predicted
labels, both in catalog order. Accuracy is correct / support; coverage is
observed / support. Missing, skipped, error, invalid, and abstained values stay
visible and do not become predictions. Multilabel exact-set accuracy also keeps
these unavailable cases in support.
Metrics describe the authored cases. They do not infer acceptance thresholds, statistical significance, population accuracy, or the business cost of one kind of error. Review confusion and per-label counts together with coverage before changing a prompt or policy.