Session 7, second half · Week 4 · Tue 27 Oct 2026

Build the eyes and ears.

Tuesday you learned what to measure. Tonight you build the machine that measures it: tracing, dashboards, prompt versioning, human-in-the-loop. You leave with a harness plan for Lab 5, and your runbook finished.

Ehsan Gazar
Production-Ready Systems with LLMs and Agents · session 7 of 8
Tonight sets up Lab 5

Traces in, decisions out.

0–8The harness, end to endwhat Lab 5 is measuring
8–20Tracing: what to recordthe span you can replay
20–30The trap: what your average hidesand why p95s never average
30–40Versioning + human-in-the-loopthe two fields people fudge
40–65Build your harness plan+ finish the runbook
65–75Wrap, then Thursdaythe capstone defence
By the end of tonight

You'll walk out with…

1A trace spec: exactly what each request records, enough to replay it.
2Three dashboards named: cost, latency, quality, with the alert lines.
3A prompt-versioning and human-in-the-loop plan.
4A harness plan ready for Lab 5, and your runbook completed.
The whole machine

Traces feed everything downstream.

One artifact powers the rest. Instrument the request once into a trace, and evals, dashboards, alerts, and the flywheel all draw from it. No traces, no observability, that's the S1 "flying blind" failure, fixed.

flowchart LR
  REQ["Request"] --> TR[("Trace")]
  TR --> EV["Evals"]
  TR --> DASH["Dashboards"]
  TR --> AL["Alerts"]
  EV --> FLY["Flywheel
fails → cases"] classDef ok fill:#D6F5E3,stroke:#1F2937,color:#0E1726; classDef m fill:#DCEBFE,stroke:#1F2937,color:#0E1726; class TR m; class FLY ok;
Tracing · the foundation

Record enough to replay the whole run.

Borrow OpenTelemetry thinking: one request is a trace, each step a span. When something goes wrong, you should be able to open the trace and see exactly what the model saw and did, no guessing.

Per requestinput · retrieved context · final output · total cost + latency · outcome
Per model callprompt version · tokens in/out · latency · the raw completion
Per tool calltool name · args · result · success/failure
The testcould a teammate replay and debug this run from the trace alone?
What a trace actually looks like

One request, replayable.

# one request trace · a span tree
{ "request_id": "req_8f2a", "outcome": "ok",
  "cost_usd": 0.014, "latency_ms": 1830,
  "spans": [
    { "type": "retrieve", "ms": 210, "chunks": 5 },
    { "type": "model", "prompt_ver": "answer@e7aa2e", "in": 3120, "out": 240 },
    { "type": "tool", "name": "lookup_policy", "ok": true } ] }

Cost, latency, prompt version, token counts, every step. When a user reports nonsense, you open their trace and see exactly what happened. That's the replay test, passed.

Three dashboards · and the trap in the middle one

1432 ms says fine. Twelve users in a hundred waited nine seconds.

Cost per request, latency, and the sampled eval pass-rate. Each needs a line you can defend, because a dashboard without an alert line is wallpaper. But look at what the latency one does depending on which number you put on it. This is one real window from the companion repo: 88 requests at 400 ms, 12 at 9000.

mean
1432 ms  "about a second and a half, we're fine" p50
400 ms  the median agrees, and both are lying p95
9000 ms  this is the number that pages someone
And you cannot average two shard p95s: the test in the repo gets 4550 where the truth is 200. Keep the samples, not the summaries.
run it → s07/dashboards/percentiles.test.ts
Treat prompts like code

A hand-typed "v13" is wrong the first time somebody forgets.

Prompts change weekly and every change moves quality. But the moment a trace records a version number that a human typed, the trace is only as honest as the least careful edit anyone made that month. Derive the id from the content and it cannot disagree with what was sent.

// The id is derived, never typed by a human.
export function definePrompt(name, template): Prompt {
  const id = `${name}@${createHash("sha256")
    .update(template).digest("hex").slice(0, 6)}`;
  return { name, template, id };   // → "answer@e7aa2e"
}

// Six months later, that id still returns the exact bytes
// the model was given. That is the whole feature.
it("cannot be edited without the id moving", () => {
  expect(definePrompt("answer", "Answer from the policy only.").id)
    .not.toBe(
  definePrompt("answer", "Answer from the policy only. Cite it.").id);
});
And a new version has to win quality without losing cost or the tail. An average going up is a release note, not a decision.
run it → s07/versioning/registry.ts
Where a person stays

Automate the many. Keep humans on the few.

Human-in-the-loop isn't failure, it's design. Put a person where the stakes or the uncertainty are highest: approving irreversible actions, labeling ambiguous cases for the eval set, reviewing low-confidence outputs.

flowchart TB
  R["Agent result"] --> C{"High stakes
or low confidence?"} C -- no --> AUTO["Ship automatically"] C -- yes --> H["Human reviews
approve / correct / label"] H --> LBL["Correction → eval set"] classDef ok fill:#D6F5E3,stroke:#1F2937,color:#0E1726; classDef h fill:#FEF3C7,stroke:#1F2937,color:#0E1726; class AUTO ok; class H,LBL h;

Bonus: every human correction is a labeled example. The loop feeds the flywheel.

Build it · in order

The harness drill.

1Write your trace spec.List the fields per request, per model call, per tool call. Pass the replay test.
2Name three dashboards + alert lines.Cost, latency, quality. What number pages someone, at what threshold?
3Decide prompt versioning + HITL.How prompts are versioned, and the one place a human stays in the loop.
4Finish the runbook.Add entries for the top failure modes from Project 2, now that you can observe them.
Capture it here · fill this in

Eval Harness Plan.

Trace spec
Each request records: · each model call:
Eval sets
Offline golden set of cases · online sampling at …%
Graders
Rules for · LLM-judge for · human for
Dashboards + alerts
Cost > · p95 > · pass-rate <
Prompt versioning + HITL
Versioned by · human stays at
Runbook
Now covers failure modes: (from Project 2)
What this feeds · before Thursday

Lab 5, then the capstone integration.

Nothing to hand in tonight. What you wrote goes straight into Lab 5, the eval harness you actually build, and the observability section of the capstone design document you defend on Thursday.

Into Lab 5
trace spec, graders, the pass-rate report, and the regression you plant yourself.
build it
Into the capstone doc
alert lines, versioning, human-in-the-loop, and the finished runbook.
Lab 6
The bar
could someone else replay a bad run and fix it using only your plan?
Thursday asks this
Recap · then Thursday

Trace once. Watch the tail. Version everything. Keep a person on the few.

A trace you can replay, three lines with numbers you can source, prompt versions that cannot lie because nobody types them, and a human on the irreversible and the uncertain. Plus the runbook, finished. That's the operating layer, the difference between a demo and a system.

Thursday · S8 capstone defence
Everything on one page. Labs 1 to 5 behind one entry point, the seven-section design document, and the five-minute walkthrough, stressed live in the room.
Thu 29 Oct
Before then
Lab 5, the eval harness, and Lab 6, the capstone integration.
bring both