Week 3
Build this week's exercise, then run the gate.
make check WEEK=3
| Session | Session 3: Observation, 60 min |
| Build | POST /ask with its two gates and citations; your own hundred questions; a judge you build and check against thirty labels; twenty trajectory tasks. |
| Files | app/api/ask.py, app/eval/judge.py, eval/my-test-set.jsonl, eval/trajectories.jsonl |
| Measure | make evidence WEEK=3 on a real model, into evidence/week-3.json |
| Record | decisions/week-3.md from the evidence, ai-use/week-3.yml, reflections/week-3.md |
| Guides | Worked examples B to E: evaluation · Glossary |
checks fail 5 of 18 checks pass
Red is the normal state of a week you have not built yet. Each name is a test in the file beside it.
Written last: make evidence WEEK=3, then decisions/week-3.md and ai-use/week-3.yml.
| metric | value | threshold | verdict |
|---|---|---|---|
| abstain_rate_unanswerable | 0.00 | 0.70 | FAIL |
| answer_rate_answerable | 0.00 | 0.50 | FAIL |
| hit_rate | 0.00 | 0.60 | FAIL |
| citation_validity | 0.00 | 0.80 | FAIL |
| total_cost_usd | 0.0000 |
The exercise is in your repository, where you can run it:
code weeks/3/README.md make next
Hybrid search, keyword and meaning together, restricted to the customer's version and called by the agent as a tool, with every choice in it made by a number.
Answers that cite their source and decline when the evidence is thin, your own test set, a judge you checked against labels, and the agent's path measured as well as its answer.
Week 4 lets the agent act. You cannot let it act until you can tell whether a change made it worse.
A system that always answers is worse than one that sometimes declines. And nothing ships without a number that says how often it does each.
Read this once (about 20 minutes). Write the sentence in your own words in reflections/week-3.md,
Q1, before you touch code.
A model will answer any question you put to it. That is the problem. Grounding is the set of disciplines that make it answer only from what you gave it:
grounded: false) and treat that as a first-class, expected outcome, not an error.flowchart TD
Q[question] --> S[search top k]
S --> G1{"top score ≥
RELEVANCE_THRESHOLD?"}
G1 -- no --> A1["abstain
reason: threshold
cost: $0, no model call"]
G1 -- yes --> M["model reads numbered passages
returns answer, citations, grounded"]
M --> G2{"grounded and
a valid citation?"}
G2 -- no --> A2["abstain
reason: passages do not contain the answer
cost: one call"]
G2 -- yes --> O["answer + citations
each #91;n#93; mapped to a real chunk"]
style A1 fill:#eef7ef,stroke:#2e7d4f
style A2 fill:#eef7ef,stroke:#2e7d4f
Interviewers ask two questions: how do you know it works, and what did it do for the business. Evaluation is the answer to the first.
eval/golden.jsonl) is what CI runs on the fake model; yours is what your evidence runs on.Four numbers, one grid. Each cell is a different thing going wrong.
| answered | declined | |
|---|---|---|
| answerable | hit rate: did it contain the fact? citation validity: did it cite the right file? | answer rate drops → look at retrieval first |
| unanswerable | abstention rate drops → look at the prompt and gate 2 | correct |
And the gate in CI, which is what "we have evals" means:
flowchart LR PR[pull request] --> CK[make check WEEK=3] CK --> EV["scripts/eval.py
golden.jsonl through /ask"] EV --> TH{"every metric ≥
thresholds-ci.json?"} TH -- yes --> OK[merge allowed] TH -- no --> NO["merge blocked
the table says which metric"] style NO fill:#fbeeed,stroke:#b3261e
Your own test set has a weakness you cannot remove: you wrote it, so your system and your questions share your blind spots. From this week the programme runs about 150 questions and 20 agent tasks you never see, written for your product, on every pull request, on one reference model. You see the totals. If your own score is well above the hidden one, your questions are easier than real users' (worked example E). The gap is the finding; chasing the hidden questions is not the fix, rewriting your own is.
From next week the system acts: it looks a customer up, searches, drafts, escalates. An answer can be right for the wrong reasons: it guessed the version instead of looking it up, it searched four times when once would do, or it reached for a refund nobody asked for. A trajectory is the path, and four numbers describe it: the right tool (first, and in order), the steps it took, what it cost, and whether anything that needs a person's approval ran without one. You write twenty tasks this week; the agent you build next week is measured on them, and on twenty hidden ones.
| Idea | Where it lives | What you do this week |
|---|---|---|
| Threshold and decline | app/api/ask.py, RELEVANCE_THRESHOLD |
Build the two gates |
| Citations | AskOut.citations |
Map the model's [n] to real chunk ids and files |
| Your test set | eval/my-test-set.jsonl |
Write 100 questions from what users ask |
| Metrics and gate | scripts/eval.py, eval/thresholds.json |
Run it, read it, make CI block on it |
| Judge | app/eval/judge.py, scripts/judge_agreement.py |
Build it, label 30 answers, measure agreement |
| Threshold | scripts/threshold_curve.py |
Read the threshold off a measured curve |
| Trajectories | eval/trajectories.jsonl, app/eval/trajectory.py |
Write 20 tasks; the agent meets them in week 4 |
Checked September 2026.
judge_v1.md names them because of this paper./ask numbers its passages.Answers are kept in this browser only, under hub.quiz in local storage. They are never sent anywhere: not to the repository, not to the progress export, not to your mentor, and they have no effect on your route. Clearing your browser data clears them. The explanations are the point, not the score.