Skip to main content
Logfire for evals

Evals and datasets for teams who write the tests themselves

Build datasets from production traces, define scorers in Python, compare a candidate against a baseline case by case, and review by hand where it matters. Evaluation results are queryable telemetry records covered by the normal plan allowance, not a separate per-score meter.

Python
from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import LLMJudge

dataset = Dataset(
    cases=[Case(inputs='Return after 40 days?')],
    evaluators=[LLMJudge(rubric='States the 30-day window.')],
)

report = dataset.evaluate_sync(answer_question)
Definition

What is an eval?

An eval is a test for behavior that has no single correct answer. Rather than asserting one exact string, you collect a dataset of cases, define scorers that judge each result, and watch how the scores move as you change the prompt, the model or the retrieval.

The number itself is not the point. The point is being able to say this change made things better, and to say it before your users find out otherwise.

Offline and online

Unit tests, and the traffic they miss

Offline evaluation runs against a curated dataset while you are developing, so it catches regressions before you deploy. It is the closest thing an LLM application has to unit tests, and it belongs in CI for the same reason.

Online evaluation scores real production traffic after the fact. It is the only way to see the inputs your dataset does not contain yet, which is most of them. Offline tells you whether to ship; online tells you what to add to the dataset next.

The loop

Dataset, scorer, experiment, comparison

Build a dataset

Cases come from four places: production traces you found going wrong, user feedback, hand-written examples of behavior you care about, and synthetic variations of all three. The first is the one that compounds: a run you saw fail in Live view can be saved directly as a case, so the bug report and the regression test are the same object rather than two things you keep in sync.

Define scorers

A scorer decides whether one result was good. Deterministic checks handle structure, schema and forbidden content, and cost nothing. LLM judges handle the qualitative questions a regex cannot express, with a rubric you write. Human review covers what you do not yet trust either to decide. Real suites use all three, because each is wrong in a different way.

Run experiments from your code

You run the evaluation, not us. Call it from CI, from a script, or from your machine, with your model credentials and your data; Logfire records the experiment and stores every case-level result. That boundary is deliberate: your evals stay in your pipeline, and what you get here is the history, the comparison and the query surface over it.

Compare against a baseline

Pick a baseline experiment and a candidate, and read the delta per scorer and per case. The summary tells you what moved; the case view tells you which inputs moved it, which is the part that decides whether to ship. Direction is something you set per scorer, because a higher number is not automatically an improvement: latency and cost go the other way.

In code

Evals are Python, and they live in your repo

Python
from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import IsInstance, LLMJudge

dataset = Dataset(
    cases=[
        Case(
            name='refund_policy',
            inputs='Can I return this after 40 days?',
            expected_output='No, the return window is 30 days.',
        ),
    ],
    evaluators=[
        IsInstance(type_name='str'),
        LLMJudge(rubric='States the 30-day window, invents no exception.'),
    ],
)

report = dataset.evaluate_sync(answer_question)

pydantic-evals works with any Python you can call, not only Pydantic AI, and a custom scorer is an ordinary function. Because the suite is code, it is reviewed in pull requests and runs in CI beside your other tests.

Note what this means: Logfire does not run your evals. You run them, with your credentials and your data, and Logfire records the experiment, stores every case-level result, and gives you the history and the comparison over it.

What you get

From one failing run to a suite that guards it

Scorers in Python, not in a form

pydantic-evals gives you Case, Dataset and a library of evaluators, and a custom scorer is an ordinary function. That means your evals live in your repository, get reviewed in pull requests, and run in CI beside your other tests. On most eval products the scorer is configured in the vendor's UI instead, which is a different thing wearing the same word: it cannot be code-reviewed, and it does not travel with the branch that changed the prompt.

The per-case diff, not just the average

An aggregate score that moved two points does not tell you whether you fixed three cases and broke one. The comparison view lists every case with its baseline result, its candidate result and the outcome, so you can read the cases that changed rather than trusting the mean, which is the difference between knowing a change is safe and hoping it is.

Human review with a queue

Reviewers work a queue and record a verdict, a category, an expected output and tags against a run, with the whole trace visible beside it. Verdicts are stored with the run and are queryable, so they drive triage and dataset curation. They are deliberately kept out of experiment and live-eval aggregates, so one reviewer's opinion never quietly moves a number your CI gates on.

Scoring production, from your side of the line

Your application emits evaluation results against real traffic and Logfire reads them, so a scorer can watch live requests rather than only the dataset you thought to write. Note where the work happens: we do not reach into your traffic and score it for you, which some products do. You decide what gets scored and how often, in your code, and that is also why scoring production never becomes a bill you did not choose.

Prompts you can change without a deploy

Keep prompts out of your code as versioned, labeled templates the SDK fetches at runtime, roll one out to a percentage of traffic, and roll it back without shipping. Every resolution is recorded, so an experiment result can be traced back to the exact prompt version that produced it.

Scores are telemetry, so they are just data

Evaluation results are stored as ordinary queryable telemetry records rather than behind a separate score API. They use the same plan allowance instead of a per-score meter, and the same SQL can answer 'which scorer regressed most this week, by customer tier' without waiting for a purpose-built dashboard.

Aggregate comparison of two experiment runs, baseline prompt-v1 against candidate prompt-v2. Summary cards read 17 of 20 to 20 of 20 completed cases, assertions steady at 85 percent, task errors 3 down to 0, and average task duration 765 up to 810 milliseconds. An evaluator analysis table lists safe as an assertion at 75 up to 90 percent, route as a label breakdown that is marked not comparable, and groundedness and quality as scores rising from 0.63 to 0.79 and 0.68 to 0.84, each with a distribution histogram. Below, an operational metrics table marks task duration, input tokens and output tokens all worse, input tokens by 334 percent.
Two runs, side by side: prompt-v2 cleared the three task errors and lifted groundedness and quality, and cost 334% more input tokens to do it. A comparison that only showed the wins would be the less useful one. Where the evaluators do not line up across runs, it says 'not comparable' instead of inventing a delta.
Production

Scoring the traffic you did not predict

Live evals read the evaluation results your application emits against real traffic, so a scorer can watch production rather than only the dataset you thought to write. Because those results are OpenTelemetry events in the same records table as your traces, watching them is a query:

Worth being exact about the boundary, because it is the same one as the Run button. Some products score your production traffic for you, server-side, and that is genuinely less work. Here the scoring happens in your application, which means the prompt, the response and the judge's reasoning never have to leave your side of the line for a score to exist, and the volume you score is a decision you make rather than one that arrives on an invoice.

Worst-performing scorer in the last day
select
  attributes->>'gen_ai.evaluation.name' as scorer,
  avg(cast(attributes->>'gen_ai.evaluation.score.value' as double))
    as mean_score,
  count(*) as scored_runs
from records
where attributes->>'gen_ai.evaluation.score.value' is not null
  and start_timestamp > now() - interval '24 hours'
group by scorer
order by mean_score asc;

Sampling is configured in your code, so how much of production gets scored stays your decision. And because it is SQL, the same question broken down by customer, by model or by prompt version is the same query with one more group by.

Human review

The judgments a scorer cannot make

Some failures no scorer catches: the answer was right but the tone was wrong, or it guessed where it should have looked something up. Those get marked by a person, on the run itself, and the verdict is stored as structured data rather than as a note in someone's head.

The annotate panel on an agent run. Verdict is set to Fail, with Pass, Neutral and Fail bound to keys 1, 2 and 3. Category reads 'wrong tool'. An expected-output field, labeled optional and described as the corrected response for export or later dataset use, contains 'Look up the account before answering.' A comment reads 'The response guessed instead of using the account tool.' Tags read tool-use and needs-review, and a footer notes no other reviewers yet.
A verdict, a failure category, tags, and, the part that closes the loop, the corrected output, captured for export into a dataset. The run you just failed becomes the case that guards against it. Reviews are per-run, so two people can disagree and you can see that they did.
In production

What the loop is worth

In a month, we were already 10 times better, just because we could really control the state of each of the steps that we have.
Jorge Torres, Co-founder & CEO, MindsDB Read the case study
Decision guide

Is Logfire right for you?

Choose Logfire if

  • You want your evals to live in your repository and run in your CI
  • You want the production trace and the test case to be the same object
  • You need code scorers, LLM judges and human review in one workflow
  • You want evaluation history queryable with SQL rather than only charted
  • You are running enough scores that per-score pricing would hurt
  • Your team already writes Python and would rather not learn a new DSL

Choose a hosted evals product if

  • You want a hosted Run button and would rather not run evals from your own code
  • You need a specific proprietary scorer library only one vendor ships
  • You want to compare more than two experiment runs side by side in one view
FAQ

Common questions

What is an eval?

An eval is a test for behavior that has no single correct answer. Instead of asserting one exact output, you collect a dataset of cases, define scorers that judge each result, and track how the scores move as you change the prompt, the model or the retrieval. The point is not a pass or fail number, it is being able to tell whether a change made things better or worse before it reaches users.

What is the difference between offline and online evaluation?

Offline evaluation runs against a curated dataset while you are developing, so it catches regressions before you deploy. It is the closest thing an LLM application has to unit tests. Online evaluation scores real production traffic after the fact, which is the only way to see the inputs your dataset does not contain yet. Most teams need both: offline to gate a change, online to find the cases the dataset is missing.

Does Logfire run my evals for me?

No, and this is worth being precise about. You run an evaluation from your own code, with pydantic-evals or anything else that emits the OpenTelemetry GenAI evaluation events. Logfire records the experiment, stores it, and lets you compare it against a baseline case by case. There is no Run button in the UI, which means your evals run in your CI, on your machine, with your data and your model credentials.

How do production traces become test cases?

When you find a run that went wrong in the Live view, you can save that span directly as a dataset case rather than retyping the input somewhere else. That is the loop the whole category talks about and it is the reason evals and tracing belong in one product: the bug report and the regression test are the same object.

Can I combine code scorers, LLM judges and human review?

Yes, and most useful eval suites do all three. Deterministic checks are cheap and exact, so use them for structure, schema and forbidden content. LLM judges handle the qualitative questions a regex cannot. Human review is for the cases where you do not yet trust either, and for building the labeled set that tells you whether your judge agrees with you.

What does human annotation record?

Reviewers work through a queue and record a verdict, a category, an expected output and tags against a run. Those verdicts are stored with the run and are queryable like anything else, so they are useful for triage and for curating a dataset. They are deliberately not folded into experiment or live-eval aggregates, so a reviewer's opinion never silently moves a score your CI is gating on.

How much do evals cost?

Evaluation results use Logfire's normal telemetry allowance rather than a separate per-score meter. Personal includes 10 million records per month and pauses ingestion at the limit. Team and Growth include the same allowance, then charge $2 per million additional records. That distinction matters once a nightly suite runs thousands of scores a night.

Do I have to use Pydantic AI?

No. pydantic-evals works with any Python code you can call, including applications built on other frameworks or on raw provider SDKs, and Logfire ingests evaluation results from anything emitting the OpenTelemetry GenAI evaluation conventions. Using Pydantic AI means the traces underneath are richer, but it is not a requirement for the evals workflow.

Turn your worst production run into a test

Get started with 10 million free spans, logs, and metrics per month. No credit card required.