Auto-BenchmarkCard writes structured documentation for AI benchmarks. Given a benchmark name and its evaluation records, the pipeline finds the benchmark's paper, dataset repository and web pages, extracts verbatim evidence from them, composes a card with a fixed schema, and checks the card's claims against the collected sources. Every filled field carries provenance back to the quote it was built from, and a field the pipeline cannot support is recorded as "Not specified" rather than guessed.
The pipeline reads evaluation records in the format of Every Eval Ever and can also start from a Unitxt catalog entry. Cards produced with it are published in the evaleval/auto-benchmarkcards dataset.
A card is one JSON document with five sections and a few top-level keys.
| Section | Fields |
|---|---|
benchmark_details |
name, overview, data type, domains, languages, similar benchmarks, resources, authors, organisation, benchmark type, and the suites the benchmark appears in |
purpose_and_intended_users |
goal, audience, tasks, limitations, out-of-scope uses |
data |
source, size, size breakdown, format, annotation, collection date, contamination controls |
methodology |
methods, metrics, calculation, interpretation, baseline results, validation, human baseline, judge model details, validity justification |
ethical_and_legal_considerations |
privacy and anonymity, data licensing, consent procedures, compliance with regulations |
possible_risks lists candidate risks from the IBM AI Risk Atlas with a
justification each. flagged_fields holds the fields whose support in the
sources was weak, and missing_fields the fields left as "Not specified".
card_info records the model, schema version and run metadata. A provenance
sidecar maps every field to the evidence items behind it, and the evidence
items carry the exact quote, its character offsets and the source document.
The pipeline is a LangGraph graph. An orchestrator node decides which worker runs next from the state produced so far, and each worker writes its raw output to the run directory before the next one starts.
Identification. Evaluation records are grouped by dataset name. Subject variants of one benchmark, such as the subjects of MMLU, are collapsed into a single benchmark, and a folder that holds several benchmarks is treated as a composite suite whose members are documented first. Entries that reach the pipeline only through an aggregator, without a repository or paper of their own, pass an identity gate before any source is fetched.
Source binding. The benchmark is bound to a Hugging Face dataset, taken from the record when it names one and otherwise searched on the Hub with a strict name match. The binding is verified in three tiers, deterministic reject, deterministic accept, and one model call for the ambiguous cases. The paper is found through the record, the dataset card, or an open search over OpenAlex, Semantic Scholar and arXiv, and every candidate passes a verification step that checks that the paper introduces this benchmark and not a sibling or a derivative. The paper is converted to text with Docling, web pages with trafilatura, the GitHub README through the GitHub API, and structural facts such as row counts and licence come from the dataset's Croissant metadata.
Composition. The composer works in two stages. Stage A reads each source in isolation and extracts candidate evidence as verbatim quotes assigned to card fields. A quote survives only if it is an exact substring of its source after whitespace normalisation. A referent frame, built from the document structure and the benchmark's metadata, records which names in the paper denote the target benchmark and which denote siblings, subsets, versions or splits, so that evidence about a neighbouring benchmark can be demoted before it reaches the card. An entity-attribution pass re-checks the surviving quotes against that frame. Stage B then produces the card sections in three grouped structured-output calls from the evidence digest alone, validates each group against the schema and the evidence links, and repairs violations for up to three rounds. Values that can be derived deterministically from metadata, such as languages, size and licence, override the model's output.
Risks. A retriever proposes candidate risks from the IBM AI Risk Atlas, and one model call selects at most five with a justification each.
Factuality check. The card's prose fields are split into atomic claims.
Each claim is scored by FactReasoner
against passages retrieved from the bound sources, with the probabilistic
inference done by the Merlin engine. Fields whose claims fall below the
support threshold are listed in flagged_fields, with an exception for
identity fields whose cited evidence passed the attribution check.
Finalisation. For runs that started from evaluation records, the final step adds the recorded scores as baseline results, maps the benchmark type, records the suites the benchmark appears in, and recomputes the list of missing fields.
A more detailed walk through the stages, with the module that implements
each one, is in docs/architecture.md.
Python 3.11 or newer is required.
git clone https://github.com/evaleval/auto-benchmarkcard.git
cd auto-benchmarkcard
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e .The install pulls FactReasoner from a pinned commit of its repository. The
factuality check additionally needs the Merlin binary. Build it from the
Merlin repository and point
MERLIN_BIN at the executable, or place it at external/merlin/bin/merlin
inside the checkout. Without Merlin, generate still writes a card but skips
the factuality check, while generate-unitxt reports the run as failed.
Copy .env.example to .env and fill in what you use. The file documents
every variable; the ones that matter most are listed here.
LLM_ENGINE_TYPEselects the model backend for all calls. Supported values arehf(Hugging Face inference, the default),ollama,vllm,wml,ritsandopenrouter. The composer model is read from<ENGINE>_COMPOSER_MODEL, for exampleHF_COMPOSER_MODEL.COMPOSER_ENGINE_TYPEreroutes only the composer calls to another backend, which is useful when the factuality model and the composer model are served in different places.FACTREASONER_MODELandFACTREASONER_ENGINE_TYPEconfigure the factuality model. It must be served by an endpoint that returns token log probabilities; any OpenAI-compatible endpoint can be given withFACTREASONER_API_BASEandFACTREASONER_API_KEY.OPENALEX_API_KEY,S2_API_KEYandGITHUB_TOKENare optional and raise the rate limits of the source search.
benchmarkcard validate checks that the variables required by the selected
engine are set, that the core imports work and that Merlin is executable.
With --live it also makes one model call.
The repository ships a few evaluation records under external/eee_samples
so a first run needs no other input:
benchmarkcard generate ./external/eee_samples -b "TruthfulQA,GPQA" -o runs-b restricts the run to the named benchmarks; without it every benchmark
found in the records is documented. A run from a Unitxt catalog entry looks
like this:
benchmarkcard generate-unitxt glue -o runsEach run creates one session directory per benchmark. With -o runs the
layout is
runs/output/<slug>_<YYYY-MM-DD_HH-MM>/
benchmarkcard/benchmark_card_<slug>.json the card
tool_output/
composer/provenance_<slug>.json field to evidence map
composer/frame_<slug>.json referent frame
paper_resolver/, hf/, docling/, html/, github/, rag/, factreasoner/, ...
The output/ level is always inserted under the directory given with -o.
Sessions can be listed and inspected:
benchmarkcard list -o runs/output
benchmarkcard show runs/output/<session> --detailedFor larger jobs, scripts/batch_generate.py runs a backlog of benchmark
names, scripts/run_sharded.sh runs such a backlog in parallel shards with
a per-card timeout, and scripts/generate_manual.py documents benchmarks
that have neither evaluation records nor a Unitxt entry from a small JSON
input file. scripts/field_status.py summarises, for a finished batch, how
many fields were filled from quotes, from metadata, or left empty.
The composer's building blocks are independent of the benchmark schema.
field_spec.CardSchema describes a schema's sections, fields and allowed
values, evidence.build_extraction_prompt and evidence.verify_items run
Stage A against it, docstructure.build_index indexes a source document,
and validator.run_group_with_repair runs Stage B with the repair loop.
The pipeline's own entry point, compose_benchmark_card, is bound to the
benchmark schema. The model-cards
project uses the same building blocks with a model-card schema.
- Sources are fetched live, so two runs of the same benchmark can differ, and a card is only as good as the sources that were reachable when it ran.
- Only the dataset binding and the paper binding are verified for subject agreement. Web pages and GitHub READMEs enter the composer unverified.
- The factuality check measures support in the collected sources, not truth. Flags are cues for a reviewer, and an unflagged field is not a verified one.
- The risk list is a set of candidates for human review.
- The pipeline follows URLs found in its inputs. Run it in an isolated
environment when the inputs are untrusted; see
SECURITY.md.
src/auto_benchmarkcard/: the package, with the CLI incli.py, the graph inworkflow.pyandworkers.py, and the record-driven entry point ineee_workflow.pysrc/auto_benchmarkcard/tools/: one package per stage, with the composer undertools/composer/scripts/: batch runners and the field-status reporttests/: offline tests, run withpython -m pytestexternal/eee_samples/: sample evaluation recordsspaces/benchmarkcard-webhook/: a Hugging Face Space that regenerates cards when new evaluation records arrivedocs/architecture.md: the stages in order, with their modules
If you use this software, please cite the paper that introduced it:
@article{hofmann2025autobenchmarkcard,
title = {Auto-BenchmarkCard: Automated Synthesis of Benchmark Documentation},
author = {Hofmann, Aris and Vejsbjerg, Inge and Salwala, Dhaval and Daly, Elizabeth M.},
journal = {arXiv preprint arXiv:2512.09577},
year = {2025}
}The code is released under the MIT License. Papers, dataset
cards, web pages and benchmark content fetched at run time remain under their
own terms; see THIRD_PARTY_NOTICES.md.