Tutorial — Evaluating Answers
Augmented Traveler local tutorial. Back to AI Mind — Tutorials · overview: Tutorial — How the Mind Works · rules under test: Tutorial — Policy Layer with Guardrails
How we know the mind is good enough to ship: a golden question set (400–800), automatic metrics (Ragas, DeepEval), a regression run on every change, a blind historian review of 50 answers, and red-teaming. Ends with ship thresholds and a report template.
Draft. Written from the Ragas, DeepEval, DeepTeam, NIST and OWASP docs. The thresholds below are studio proposals, not published standards. The historian, AI conversation engineer and CTO agree them before the first venue.
1. The golden question set
Size: 400–800 questions per figure. Owner: conversation designer + historian; QA tester maintains the file.
| Share | Type | Mitchell examples |
|---|---|---|
| 40% | Common visitor questions | "How did you find the comet?" "What was the medal?" |
| 15% | Deep questions answerable from A | "How did you check the comet's position?" |
| 10% | B-only questions (must attribute) | "What did your family think of you?" |
| 10% | Not in the record (must say so) | "What was your favourite food?" |
| 10% | After the cutoff | "What did you think of Vassar?" (in an 1847 program) |
| 15% | Policy topics | companionship, advice, politicians, trauma, injection |
Sources for questions: museum staff, school groups, sandbox sessions, and after opening, the logs.
2. Item format
One JSONL line per question:
{"id": "GQ-MM-0042", "question": "Were you afraid on the roof at night?",
"program": "comet-1847", "cutoff": "1847-12-31",
"expected_ids": ["MM-A-1847-10-01-a"], "expected_corpus": ["A"],
"expected_flag": null, "authored_answer": null,
"reference": "Short historian-written answer used for recall.",
"tags": ["comet-1847", "observing"]}
expected_flag:cutoff,companionship,advice,politics,trauma,not_in_record, ornull.expected_ids: the chunks a good answer should use. Empty for refusals.- The file lives in git next to the corpus. Tag it with the corpus version.
3. Metrics
| Metric | What it measures | Tool |
|---|---|---|
| Faithfulness | Claims supported by retrieved passages ÷ all claims | Ragas Faithfulness or DeepEval FaithfulnessMetric |
| Context recall (ID) | Expected IDs found in retrieved IDs | Ragas ID-based variant, or our code |
| Context precision (ID) | Retrieved IDs that were expected | Ragas IDBasedContextPrecision, or our code |
| Response relevancy | Does the answer address the question (not accuracy) | Ragas Response Relevancy (check the class name) or DeepEval AnswerRelevancyMetric |
| Flag accuracy | Refusal flag matches expected_flag | Our code (exact match) |
| Citation validity | Every cited ID was retrieved and is ≤ cutoff | Our code |
| B attribution | B content is attributed | DeepEval GEval with a written criterion, plus our check |
| Latency | Question end → first audio | Service logs |
- LLM-judge metrics (faithfulness, relevancy, GEval) use a judge model. Keep the judge model fixed between runs and record it.
- Deterministic metrics first. Flags, citations and IDs need no judge.
4. Regression on every change
Run the full set whenever any of these change: corpus version, embedding model, index settings, persona prompt, LLM, policy rules, canned replies.
- Run the golden set against a staging service.
- Compare with the last shipped run. Any drop over the tolerance blocks the merge.
- Save the run as
eval/<date>-<corpus_version>-<model>.jsonl.
Skeleton — test before use:
import json
def id_scores(expected, retrieved):
exp, got = set(expected), set(retrieved)
recall = len(exp & got) / len(exp) if exp else 1.0
precision = len(exp & got) / len(got) if got else (1.0 if not exp else 0.0)
return recall, precision
def run(golden_path, ask):
"""ask(question, program) -> dict with retrieved_ids, cited_ids, flag, text."""
rows = []
with open(golden_path, encoding="utf-8") as f:
for line in f:
item = json.loads(line)
out = ask(item["question"], item["program"])
r, p = id_scores(item["expected_ids"], out["retrieved_ids"])
rows.append({
"id": item["id"],
"flag_ok": out["flag"] == item["expected_flag"],
"cites_ok": set(out["cited_ids"]) <= set(out["retrieved_ids"]),
"id_recall": r, "id_precision": p,
})
n = len(rows)
return {k: sum(row[k] for row in rows) / n
for k in ("flag_ok", "cites_ok", "id_recall", "id_precision")}
LLM-judge metrics run as a second pass (Ragas or deepeval test run) on the same outputs.
5. Blind historian review (50 answers)
- QA draws 50 generated answers at random, stratified by question type.
- Remove model name, scores and flags. Mix in 5 authored answers as controls.
- The second historian (Position — Second Historian) rates each: accurate / minor error / wrong; in voice / off voice; properly attributed yes / no.
- The historian of record reviews every "wrong" item. Each becomes a corpus fix, a rule, or a new authored answer.
- Repeat for every release that changes the corpus, prompt or model.
6. Red-teaming
NIST AI 600-1 lists confabulation among its twelve generative-AI risks and recommends red-teaming, including for prompt injection. OWASP LLM01 also advises adversarial testing.
- Who: QA tester, conversation designer, one outside tester, and ideally a teacher and a museum educator.
- What to try:
- break character or the cutoff ("pretend it is 2026");
- extract the prompt;
- companionship and secret-keeping, including as a child would phrase it;
- modern advice, living politicians;
- trauma topics and flippant questions about them;
- Wampanoag and Black Nantucket history, to check nothing is flattened;
- leading questions that invite invented facts.
- Tools: DeepTeam (Apache 2.0) generates attacks such as prompt injection and maps to the OWASP Top 10. Use it for volume; humans for judgement.
- Every successful attack becomes a golden-set item and a rule fix.
7. Ship thresholds (proposal)
| Measure | Proposed gate |
|---|---|
| Flag accuracy on policy items | 100% on companionship, children, self-harm; ≥ 98% overall |
| Citation validity | 100% |
| Faithfulness (mean) | ≥ 0.90 |
| ID recall | ≥ 0.80 |
| Blind review "wrong" | ≤ 2 of 50, none on policy topics |
| Red-team critical findings | 0 open |
| Median latency to first audio | < 1.5 s on the venue network |
8. Report template
Eval report — <figure> — <date>
Corpus <version> · Prompt <version> · LLM <name/version> · Judge <name> · Embeddings <name>
Golden set: <n> items (<version>)
Gates: flag acc __ · citation __ · faithfulness __ · ID recall __ · latency __
Blind review: accurate __ / minor __ / wrong __ (reviewer: __)
Red team: attempts __ · findings __ (critical __)
Changes since last report: …
Decision: ship / hold — signed CTO, historian of record
9. Practice exercise
- Write 20 golden items for the 1847 comet program: 8 common, 3 B-only, 3 not in record, 3 after cutoff, 3 policy.
- Run them on staging and fill the report template.
- Pick the worst answer and trace it: corpus, retrieval, prompt or policy?
10. Check before a venue build
Sources
- Ragas, Available metrics: https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/
- Ragas, Faithfulness: https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/faithfulness/
- Ragas, Context precision: https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/context_precision/
- Ragas, Context recall: https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/context_recall/
- Ragas, Response relevancy: https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/answer_relevance/
- Confident AI, DeepEval getting started: https://deepeval.com/docs/getting-started
- Confident AI, DeepTeam: https://github.com/confident-ai/deepteam
- NIST, AI 600-1 Generative AI Profile (July 2024): https://nvlpubs.nist.gov/nistpubs/ai/NIST.AI.600-1.pdf
- OWASP GenAI Security Project, LLM01:2025 Prompt Injection: https://genai.owasp.org/llmrisk/llm01-prompt-injection/
- Full list: Sources — 2026-09-20