Skip to content

evalpair

A command-line tool and MCP server for comparing LLM runs: a new model, prompt or quantization against the old one, question by question, with an interval on the difference and a check against how much the same model changes when asked twice.

Case study4 min read

The 4-bit and 2-bit builds of Llama 3.1 8B on the same 160 maths problems. The verdict says how big the difference is, how sure, and whether it's more than the model's own run-to-run noise.
Role
Solo project: design, code, tests and release.
When
November 2026
Stack
Python, MCP (Model Context Protocol), Ollama, OpenAI-compatible APIs, SQLite, pytest, GitHub Actions, PyPI

The problem

Most eval tools print two scores side by side, and a 2-point gap on 160 questions looks like a result when it usually isn't. Across my studies, the same mistake kept costing time: reading a difference without an interval, or without knowing how much the model moves on its own.

What I built

One engine with two ways in: an `evalpair` command and an MCP server with five tools, so an agent can run an eval and read the verdict itself. It runs a suite on Ollama or any OpenAI-compatible server, caches every reply, and compares two runs item by item with a bootstrap interval and a noise floor.

What happened

On PyPI and the official MCP registry. Its first real run reproduced the quantization study's scores exactly, and its checkers re-scored all 9,870 of that study's replies the same way. That run also caught a parsing bug two of my studies shared.

  • 5

    MCP tools, each also a CLI command

  • 470

    items in the three bundled suites

  • 9,870

    study replies re-scored, every one the same

  • 51

    offline tests against a fake model server

01

Two scores aren't a comparison

Swap a model, a prompt or a quantization and rerun the eval, and the score moves. Whether it moved because of the change is a separate question, and the usual output, two percentages, can't answer it. Two things are missing: an interval on the difference, computed over the same questions, and a sense of how much the answers change when nothing changes at all.

My studies kept needing both, so each one had its own copy of the same code. evalpair is that code as a tool: run a suite, save the run, compare two runs, get a verdict in a sentence.

02

How a comparison works

Each run is saved with the suite's hash, the model's exact build, the settings and every reply. A comparison pairs the two runs question by question, so it measures the change on the same items rather than two separate samples. It reports:

  • The score difference with a 95% bootstrap interval over items, 2,000 draws, seeded so the same runs always give the same interval.
  • How many items got worse and how many got better, with examples of each.
  • A noise floor: with `--repeat`, every question is sent a second time as a separate call, and the floor is the share of answers that changed. A change only counts as beyond noise if, item by item, it exceeds that floor with an interval above zero.
  • Warnings when the two runs used different versions of a suite or only partly overlap.

03

Suites and checkers

Three suites come with it: 160 GSM8K maths problems, 160 multiple-choice questions from four MMLU subjects, and 150 records to convert into JSON. They're the items from my Measuring LLM Systems studies with those studies' prompts word for word, so a run can be checked against published numbers. Your own suite is a folder with two files: a short TOML description and the items as JSON lines.

Scoring is plain code, never another LLM: the number after an answer line, a multiple-choice letter, an exact or contained string, a regular expression, or JSON fields one by one. Every score records how the answer was found, and the run summary lists unparsed and cut-off replies first, because those are usually the parser's or the token limit's fault, not the model's.

A run's summary puts the replies cut off at the length limit first, with how the answer was read and how the reply ended.

04

Checking it against the studies

Before trusting it, I checked it two ways. Its checkers re-scored every one of the quantization study's 9,870 saved replies and agreed with the study's own score on all of them. Then its first real run, the 4-bit and 2-bit builds of Llama 3.1 8B on GSM8K at the study's settings, took 37 minutes and gave 84.4% and 60.6%, the study's numbers exactly.

That run also found a bug the studies had missed. Among the items that got better at 2 bits was one where the 4-bit build had written “Answer: $9 500”, with a space as the thousands separator, and the parser, which evalpair had taken from the studies, read it as 9. The same misreading was in two published studies. I fixed the parser in all three places, re-scored, and published the corrections with the before and after numbers. In one study the misread answer had also been used as a planted wrong answer, so that question was re-run.

A second, independent run of the same items is the cheapest audit there is.

05

Shipping it

The tests run offline against a small fake model server that speaks both the Ollama and OpenAI-style APIs, covering the CLI, the MCP tools, caching, retries and errors. CI runs on Python 3.11 to 3.14 on Linux, and on macOS and Windows. A version tag publishes to PyPI through trusted publishing, and a second workflow lists the release in the official MCP registry, signing in as the repository through GitHub Actions. No token is stored anywhere.

Making the screenshots for this page turned up two small output problems: the examples in a comparison were labelled “b” where the summary said “B” and didn't line up, and reply excerpts kept raw tab characters. Version 0.1.1 fixes both, with a test.

Use itCommand
List the suitesuvx evalpair suites
Run with a noise floorevalpair run gsm8k-160 -m llama3.1:8b --repeat --label base
Compare two runsevalpair compare base new
Add to Claude Codeclaude mcp add evalpair -- uvx evalpair mcp

What I'd carry forward

  1. 1Compare runs item by item, with an interval on the difference. Two scores side by side invite you to read noise as a result.
  2. 2Measure the noise floor before reading answer changes. Without it, any change looks like the model's fault.
  3. 3Score with code, and record how each answer was found, so a wrong score can be traced to the parser.
  4. 4Re-run the same items independently. It found a bug that two careful studies had shared.

Get in touch

Questions about my work, a role, or a project idea. Send a message here or email me directly.

Srikarmks@gmail.com