03 Improve continuously
Turn flagged feedback into new golden questions and feed them back into the Arena.
Starting point
Module 02 complete: you have a winning config and a detailed quality picture for it โ
per-tier accuracy, the llm_judge signal, and trace-level detail on where it succeeds
and fails.
Why
Selecting and measuring a model once is not the end of the story โ it's a loop, not a pipeline. No golden dataset anticipates every way real users will phrase a question, so production feedback should make the next Arena run sharper. This module builds that mechanism: real questions, once a human has approved them, become new golden questions that Module 01 measures against. The moment you release in Module 04, every ๐/๐ a real user clicks starts feeding straight into this same loop.
Concepts โ under the hood
The human-in-the-loop flywheel. Every module up to now has run against a fixed 20-question golden set. That set is a snapshot of what you anticipated users would ask โ it will never anticipate everything. The fix isn't a bigger golden set written up front; it's a mechanism that grows the golden set from what actually happens in production, with a human checkpoint in the middle so bad SQL never becomes "ground truth" by accident:
The improvement flywheel: production feedback becomes a LangFuse score, a human approves a corrected answer into the golden dataset, and re-running the Arena crowns a steadier winner โ today's production failure becomes tomorrow's regression test.
Two pieces are doing work here:
- Annotation queue โ a review inbox for traces. LangFuse can route flagged traces
(e.g. tagged
servingwith a low or negativeuser_feedbackscore) into a queue a human works through one at a time, without hand-hunting through the full trace list. It exists because some judgments automation can't make: whether a question was even answerable, whether the SQL a model produced is acceptable even if not what a human would have written, what the correct SQL actually is. The queue is where a human makes that call and records it. - Compounding golden data โ every item you promote is permanent: it stays in
arena-goldenand gets re-measured on every future Arena run, for every model and prompt in the grid, not just the one that surfaced it. Today's production failure becomes tomorrow's regression test. Run this loop for a few weeks and the golden set stops being 20 questions someone guessed at design time and starts being a record of what your users actually ask โ which is exactly the distribution you want your benchmark measuring.
Goal
At least one real question, reviewed and approved by a human, promoted into the
arena-golden LangFuse dataset โ and a benchmark re-run that includes it.
Step 1 โ Know where feedback comes from
Once you release in Module 04 and the Chat tab is live,
every ๐/๐ a user clicks is written back automatically as a user_feedback score on
the corresponding LangFuse trace โ nothing to build here, it's wired into the serving
API from the start.
Step 2 โ Triage with a LangFuse Annotation Queue
In the LangFuse UI, set up an Annotation Queue to route interesting traces to a human reviewer:
- Annotation Queues โ New Queue โ name it something like
production-review. - Configure it to pull in traces tagged
serving, prioritizing ones with a low or negativeuser_feedbackscore (these are the turns most likely to reveal a real gap โ a phrasing the prompt didn't handle, a join the model got wrong, a question outside the schema's coverage). Set this up now so it's ready the moment real traffic starts arriving. - A reviewer opens each queued trace, reads the question, the generated SQL, and the
result, and โ for the ones where the right SQL is clear even if the model didn't
produce it โ writes the correct
golden_sqland approves the item.
What you should see. A configured queue with a filter (tag serving, sorted by
user_feedback ascending) and, once traffic exists, a growing worklist of individual
traces rather than a raw trace table โ each one waiting on a human decision, not a
score.
๐ธ Screenshot: the Annotation Queue view โ the queue configuration and its worklist of flagged traces โ capture from the LangFuse UI.
If you skip configuring the queue, negative feedback still lands as a score on the trace โ it's just invisible until someone remembers to go filter the Traces list by hand. The queue is a convenience for triage, not a requirement for feedback to exist, but without it real gaps sit unreviewed indefinitely.
Step 3 โ Export approved items
Export the reviewer's approved items as a JSON array shaped like this:
reviewed.json
[
{
"id": "prod-001",
"question": "How many customers placed an order in the last 30 days?",
"golden_sql": "SELECT count(DISTINCT customer_id) FROM v_orders WHERE order_ts >= now() - INTERVAL 30 DAY",
"tier": 2,
"ordered": false
}
]idโ a unique id for the new golden question.questionโ the natural-language question, verbatim.golden_sqlโ the reviewer-approved correct SQL against thev_*views.tierโ difficulty tier (matches the tiers used elsewhere inarena-golden).orderedโ whether row order matters when grading this question's result set.
The golden_sql you write here becomes ground truth: every future Arena run scores
every model against it. If it's subtly wrong โ a missing filter, an off-by-one date
boundary โ every config gets graded against the wrong answer until someone notices and
fixes the dataset item. Run it against ClickHouse yourself and eyeball the result set
before you promote it.
Step 4 โ Promote to the golden dataset
source .env && python -m scripts.promote_to_golden reviewed.jsonFor each reviewed item, this snapshots the golden result set by running golden_sql
against ClickHouse right now, and adds it to LangFuse's arena-golden dataset with the
same shape the benchmark harness already uses โ no separate migration step.
What you should see. One prepared <id>: <N> golden row(s) line per item, then a
summary line: promoted <N> question(s) into the 'arena-golden' dataset โ re-run python -m eval.harness to measure them. In LangFuse, open Datasets โ arena-golden
and confirm the new item is listed with its question, expected_output, and
metadata.golden_sql.
๐ธ Screenshot: the new item in the arena-golden dataset in LangFuse โ its
question, expected output, and metadata โ capture from the LangFuse UI.
promote_to_golden.py calls LangFuse's dataset-item API with the id you supplied in
reviewed.json. Whether promoting the same id a second time updates that item in
place or quietly creates a second one is not something this workshop has pinned down โ
treat re-running promote_to_golden on an id you've already promoted as an open
question, and check the dataset in the UI afterward rather than assuming either
behavior.
Step 5 โ Re-run the Arena
python -m eval.harnessThe leaderboard now measures the promoted, real-world questions alongside the original golden set. If a config that looked strong in Module 01 stumbles on these new questions, that's the loop working: production surfaced a gap, review confirmed it, and the Arena now catches it โ before it ships again.
How to verify you are done
reviewed.jsoncontains at least one approved item with agolden_sql.python -m scripts.promote_to_golden reviewed.jsonran without error and the item appears in thearena-goldendataset in LangFuse.- Re-running
python -m eval.harnessproduces leaderboard results that include the promoted question.
Exercise โ close the loop on one of your own failures
Module 02 asked you to write down 2โ3 questions your winning config got wrong, along with why. Now put one of them through the whole flywheel and predict the outcome before you look:
-
Pick one question from your Module 02 table where the outcome was
sql_exec_error,empty_but_expected, orwrong_resultโ notsql_policy_rejected, since that's a prompt-behavior problem, not a missing golden question. -
Write its correct SQL by hand against the
v_*views, and add it toreviewed.jsonwith a freshid(e.g.prod-fix-01) and an appropriatetier. -
Promote it:
source .env && python -m scripts.promote_to_golden reviewed.json -
Before re-running, predict: will your winning
config_idnow pass this question? Same model, same prompt, same failure mode it had before โ is there any reason promoting the question changes the model's behavior on it? -
Re-run just your winner against it to check:
python -m eval.harness --models <winner-model> --prompts <winner-prompt> -
Verify against the leaderboard: did your prediction hold? And separately โ is the
arena-goldendataset now one item bigger than it was at the start of Module 02?
The likely answer in step 4 is "no, it still fails" โ promoting a question doesn't change the model, it only changes what gets measured. That's the point: the gap is now permanently visible on every future leaderboard, for every config, instead of being a one-off observation in a trace you happened to read.
Wrap-up
This is the whole point of routing everything through LangFuse: the same traces that Module 04 will watch in production are the traces that feed back into Module 01's Arena, with a human decision in the middle where it matters.
End state
A feedback mechanism ready to close the loop โ annotation queue, promotion script, and a re-run that already includes your first promoted question. Continue to 04 Release to production to ship the winner live and start feeding this loop with real traffic.