Skip to content
Chapter 8 of 8

Tracing and observability

An eval score is a single number, and a single number cannot tell you why. When a gate goes red, you need to see exactly which cases failed and what happened inside them, and when the system is live you need the same visibility on real traffic. That is tracing: for every case, record the full path, the input, the prompt actually sent, the raw output, the grader used, and the verdict. Tracing is what turns a red eval into a fix instead of a mystery, and what lets you watch quality in production instead of waiting for complaints. In this lab you run the whole harness you built across this course, a golden dataset scored by a code grader, and emit one trace record per case, then prove observability: every result links back to its case and its grader, coverage is complete, and the aggregate score is fully explained by the passing traces. Nothing is a black box.

The lab: read it, then run it

labs/evals/ev8-tracing.py
#!/usr/bin/env python3
"""
LAB EV8: Tracing and observability.

An eval score is a single number, and a single number cannot tell you WHY. Tracing
records, for every case, the full path: the input, the prompt sent, the raw output,
the grader used, and the verdict. That is what turns a red eval into a fix, and
what lets you watch quality in production. In this lab you run the whole harness
you built across this course (golden dataset, code grader, LLM-as-judge) and emit
one trace record per case, then prove observability: every result links back to
its case and the grader that produced it, and the aggregate score equals the number
of passing traces. Nothing is a black box.

Run: python3 modules/academy-content/labs/evals/ev8-tracing.py
"""
import sys, os
_cands = [os.path.join(os.path.dirname(__file__), "..") if "__file__" in globals() else None,
          os.path.join(os.getcwd(), "..", "labs"), os.path.join(os.getcwd(), "labs")]
for _c in _cands:
    if _c and os.path.exists(os.path.join(_c, "academy_llm.py")):
        sys.path.insert(0, os.path.abspath(_c)); break
from academy_llm import complete, chat, embed, cosine

GOLDEN = [
    ("case-1", "I love this",           "POSITIVE"),
    ("case-2", "this is terrible",       "NEGATIVE"),
    ("case-3", "great and nice work",    "POSITIVE"),
    ("case-4", "the worst awful product","NEGATIVE"),
]

def system_under_test(review):
    prompt = ("Classify the sentiment of this review.\n"
              "love -> POSITIVE\nhate -> NEGATIVE\n"
              f"Input: {review}\nOutput:")
    return prompt, complete(prompt)

def exact_match_grader(output, gold):
    return output == gold

# Run the harness and collect a TRACE record per case: everything needed to
# explain the verdict later, in production or in a failing CI run.
traces = []
for case_id, review, gold in GOLDEN:
    prompt, output = system_under_test(review)
    verdict = exact_match_grader(output, gold)
    traces.append({
        "case_id": case_id,
        "input": review,
        "prompt": prompt,
        "output": output,
        "grader": "exact_match",
        "gold": gold,
        "verdict": "PASS" if verdict else "FAIL",
    })

print(f"STEP 1: run the harness and emit one trace per case ({len(traces)} traces)")
for t in traces:
    print(f"  {t['case_id']}: {t['output']:8} vs {t['gold']:8} [{t['grader']}] -> {t['verdict']}")

# OBSERVABILITY CHECKS.
case_ids = {c[0] for c in GOLDEN}
# 1. Every result traces back to a real case id.
all_linked = all(t["case_id"] in case_ids for t in traces)
# 2. Every trace names the grader that produced its verdict.
all_have_grader = all(t.get("grader") for t in traces)
# 3. One trace per case, none lost, none duplicated.
complete_coverage = (len({t["case_id"] for t in traces}) == len(GOLDEN))
# 4. The aggregate score equals the number of PASS traces (the number is explained
#    by the traces, not asserted on faith).
passes = sum(1 for t in traces if t["verdict"] == "PASS")
aggregate = passes / len(traces)
score_matches_traces = (round(aggregate * len(traces)) == passes)

print("")
print("STEP 2: verify the run is fully observable")
print(f"  every result links to a case : {all_linked}")
print(f"  every trace names its grader : {all_have_grader}")
print(f"  one trace per case           : {complete_coverage}")
print(f"  aggregate score {aggregate:.2f} == {passes}/{len(traces)} passing traces : {score_matches_traces}")

ok = all_linked and all_have_grader and complete_coverage and score_matches_traces
print("")
print(f"EVERY EVAL RESULT TRACES TO ITS CASE AND GRADER: {'YES' if ok else 'NO'}")
if not ok:
    sys.exit(1)
print("Traces turn a score into a diagnosis. You have finished Evaluation and Testing.")
Runnable lab
ev8-tracing.py

Run the harness with per-case traces and prove every result links to its case and grader.

Proves: EVERY EVAL RESULT TRACES TO ITS CASE AND GRADER

Open the notebook

Runs in your browser via Pyodide. First run loads the runtime once; no install, no server.

Every result traced back to a real case, every trace named the grader that produced its verdict, coverage was complete, and the aggregate score was fully explained by the passing traces. That is observability: not one opaque number, but a run you can open up and read. When a gate fails, the traces tell you which cases and why in seconds. When the system is live, the same records let you monitor quality on real traffic and catch drift before users report it. You have finished Evaluation and Testing. You can now build the eval harness first, grade with code and with a calibrated judge, score groundedness, reason about variance with pass@k, gate releases on regressions, and trace every result end to end. That is the discipline that separates an LLM demo from an LLM system.
Check your understanding
  1. 1. Why is an eval score alone not enough?

  2. 2. What does a trace record capture for each case?

  3. 3. How does tracing help in production, not just CI?