Evidence-First LLM Systems
A working note on the question this site keeps returning to: what evidence should technical work leave behind, and how to design pipelines so the evidence is a byproduct, not a chore.
Most LLM systems fail quietly. A retrieval change ships, answers get a little worse, and nobody can say when or why — because the run that would have shown it left nothing behind. The habit this site is organized around is the opposite: treat every run as something that should produce evidence, not just output.
The question underneath all of it: what should technical work leave behind so that a later version of you — or a model — can inspect, debug, and improve it?
Output Is Not Evidence
An answer is output. Evidence is the surrounding record that lets you decide whether the answer was any good and reproduce the path to it. A useful run of a retrieval-augmented system leaves at least four things:
- The input and the source area it should have drawn from.
- The retrieved context — chunks, scores, and metadata, not just the final prompt.
- The answer with its cited spans.
- A label: recall, grounding, usefulness, refusal quality.
Miss any one of these and a regression becomes a guessing game. Keep all four and most debugging collapses into a query.
Design bias: prefer boring, queryable artifacts over dashboards. A Parquet file of traces you can
SELECTagainst outlives every chart.
Make Evidence a Byproduct
The failure mode is asking people to also log things. Instrumentation that lives beside the work gets skipped the first busy week. The fix is structural — the pipeline emits the record because it cannot run without it:
def answer(question: str, index: Index) -> Trace:
hits = index.search(question, k=8) # scores + metadata retained
ctx = select(hits, budget=2_000)
out = generate(question, ctx)
return Trace(
question=question,
hits=hits, # the whole ranked set, not top-1
context=ctx,
answer=out.text,
citations=out.spans,
# label is attached later by review, but the slot exists from run one
)
The Trace is the unit of evidence. Everything downstream — eval sets,
regression gates, training data — is a view over a pile of traces.
Scoring Without Fooling Yourself
Retrieval quality has a compact definition worth writing down. For a query and document with unit-normalized embeddings, the score is just cosine similarity, , and a retrieval is . The metric is easy; the trap is optimizing it against a question set that quietly tracks your current index.
A fixed question set is only useful if it survives index changes. If it was built by inspecting what today’s retriever returns, it measures agreement with today’s retriever — not correctness. Freeze the questions and their expected source areas before tuning, and let the traces show you where they diverge.
What Regressions Should Block
Not every dip should stop a release; some are noise. The ones worth a hard gate are the cheap-to-check, expensive-to-miss kind:
- A grounded answer becoming ungrounded (cited span no longer supports it).
- A previously-correct refusal turning into a confident wrong answer.
- Recall dropping on the frozen question set beyond a set threshold.
Everything else is a review queue, not a blocker.
The Through-Line
Corpus audits, retrieval evals, agent traces — recurring threads here, and the same idea pointed at different layers. Each asks the pipeline to leave behind enough of a record that what changed, why, and which examples prove it has an answer you can query rather than argue about.
If a run can’t answer that, it isn’t done — it just stopped.
Last revised .