IFHardBench measures precise instruction following in Russian. Each of the 1260 questions pairs a deliberately trivial, knowledge-free writing task («напиши про место, в котором ты по-настоящему отдыхаешь») with a stack of three to six machine-verifiable requirements on the answer: an exact word count, a forbidden letter, a required word in a given position, a fixed number of commas, a list of a given shape, a rule that applies only if the supplied context mentions rain. Only compliance is scored — never the meaning of the answer and never knowledge.
Every requirement is backed by a deterministic verifier, so there is no LLM judge, no reference-answer matching and no randomness, and every point lost traces back to a named unsatisfied requirement.
Evaluated skills: Instruction following, Format control, Constraint satisfaction, Attention to detail, Self-verification
Contributors: Artem Chervyakov
The dataset targets instruction-tuned models whose output is consumed under rules — an assistant told to answer in three sentences without markup, a generation step whose result is parsed downstream, a system prompt that fixes a house style. It is not suitable for base (non-instruct) models, which have no notion of obeying a requirement block, and it does not measure writing quality, knowledge or reasoning: the writing tasks are chosen to be answerable by anyone about anything.
Results are aimed at engineers picking a model to put behind a fixed output contract. The headline reads directly: is the probability that a single request comes back satisfying every stated requirement at once, with no retry and no repair pass.
sample_pass_rate
Three capabilities are isolated by design:
Everything else is neutralised to keep the measurement valid. The writing tasks need no world knowledge and have no correct content, so a wrong answer cannot be a knowledge gap; requirement wordings state their own counting convention; contradictory requirements are excluded by construction; and every question was built together with a witness — a natural Russian answer satisfying the whole stack — so a failure is the model's, not the question's. Requirement types are also filtered by free rate, how often an unconstrained answer satisfies them by accident, so no type can be passed without reading it (bar 0.15; worst shipped type 0.125).
Stacks follow the concentration principle: one or two deliberately hard requirements plus clear, easily satisfiable ones, rather than many moderately hard ones. Because the headline is a product over the stack, this buys difficulty without buying ambiguity — a question is hard because one requirement is hard, not because five of them are vague.
The metrics follow from how such an answer is consumed. A response that misses one requirement of five is unusable in the same way as one that misses all five, so the headline is all-or-nothing. But those two failures call for different fixes, so sample_pass_rate reports the share of individually satisfied requirements as a diagnostic. Both average over questions; constraint_pass_rate first computes a share within each question, so every question has equal weight and a verdict in a shorter stack carries more weight than one in a longer stack. Common requirement types still receive more total weight than rare ones — which can hide the failure mode engineers care about most, a kind of instruction the model cannot follow at all. constraint_pass_rate closes that gap: per-type pass rates combined geometrically with equal weight per type, so concentrated failures cost more than spread-out ones. Read together, the headline says how often the whole answer is right and the balance says whether the model has a blind spot.
balance_score
instruction [str] — prompt template with placeholders for the fields of inputs;
inputs — the task shown to the model:question [str] — the writing task;constraints [str] — the requirements, one per line, each starting with - ;
context [str] — background notes, never scored; a non-empty paragraph for 717 of the 1260 questions and an empty string for the rest;outputs [str] — the witness: an answer satisfying the whole stack, kept as evidence that the question is solvable. It is never compared against during scoring, so the split ships it empty in test and filled in shots;
meta — metadata hidden from the model:id [int] — running number over the whole dataset;base_id [str] — stable identifier of the question;constraints [str] — the machine-readable requirement list the scorer reads, as a JSON string. Every requirement carries category, family, params and is_terminal — a flag marking requirements that fix the shape of the whole response (a JSON object, a JSON array, a CSV line, a markdown table, a two-part answer), so that only requirements compatible with that shape may stand beside them; 210 of the 5585 are marked;
categories — language, tier (quartile of predicted stack difficulty: easy / medium / hard / expert, 315 questions each), length_tier, n_constraints, constraint_families, prompt_style, topic (one of the bank's nine everyday themes) and stratum: core is the base build under the ordinary policy, topup are the questions added on top of it so that a rare requirement type can be read on its own;
annotation — is_solvable and language_correctness, reserved for human acceptance marks and currently unfilled.
The same question with substituted into inputs. The JSON above writes the line breaks as escape sequences; this is the text that reaches the model:
instruction
Можешь написать для меня одну вещь?
Контекст:
Коробку с инструментами я держу под столом, чтобы доставать не глядя. Материал закупаю раз в месяц на рынке. Времени уходит по часу в день.
Требования к ответу:
- Мне нужно ровно 4 абзаца, отделённых друг от друга пустой строкой.
- Абзацы делай одинаковыми: ровно 3 предложения в каждом.
- Слово, которым заканчивается первый абзац, обязано повториться где-нибудь в последнем абзаце, форма та же.
- Считая пробелы, в ответе должно быть ровно 492 символа.
- Не используй букву «п» — нигде в ответе.
Задание:
Чем ты занимаешься в свободное время просто для удовольствия?
Перечитай, пожалуйста, список перед тем, как отправлять.
And the witness from , which satisfies all five requirements:
outputs
Старые инструменты я отчищал от ржавчины неделю. Мастерскую заменяет угол кухни и складной стол. Одиннадцать вечера — самое тихое время.
Убираю рабочее место дольше, чем работаю. Занятие требует тишины и ровного света. Результат нравится мне далеко не всегда.
На готовую вещь смотрю дольше, чем делал её. Времени уходит немало, зато голова отдыхает. Такие мелочи и делают обычный день хорошим.
Вечер за делом — лучшее время дня. Дорога заняла ровно двадцать минут. Онлайн я бываю всё реже.
40 templates are distributed over the questions. Every block is introduced by a fixed label and by nothing else, in a fixed order:
[обращение]
Контекст:
{context}
Требования к ответу:
{constraints}
Задание:
{question}
[напоминание]
The labels are identical in all templates; what varies is the opening address and the closing reminder, which carry five tonalities — просьба, приказ, обезличенная спецификация, разговорная, строгая — eight templates each. The impersonal one drops the opening address and begins directly with the first labelled block. A question without background ships a template without the block rather than an empty one. The task is always the last block, so the requirements are never buried in the middle, and there is no Контекст: cue: these prompts go to instruct models, where the user turn is closed by an end-of-turn token.
Ответ:
The split holds five few-shot examples built from a separate seed. Their questions, requirement stacks and identifiers do not overlap with shots, but their writing tasks do: the bank has 81 tasks for 1260 questions, so each of the five also occurs in test under different requirements. A shot demonstrates the shape of an answer, not the answer itself.
test
Questions are produced by a deterministic generator — the same seed rebuilds the dataset byte for byte.
Because over the requirements of a question, the stack-size distribution bounds the score directly: at a per-requirement pass probability of 0.80 the ceiling is 0.33 for a five-requirement stack, and 0.37 at the shipped mean of 4.43 requirements per question.
sample_pass_rate = Π pᵢ
Reasoning traces are stripped before verification: everything up to and including the first is dropped. The trace is delimited by its closing tag rather than by a balanced </think> pair, because the opening tag often never reaches the generation — chat templates for reasoning models routinely pre-fill <think>...</think> at the end of the prompt, so the model emits only <think>, and a server that re-inserts a separately returned trace can leave a leaked token in front of it. Any further balanced blocks are removed too, so a model that interleaves several traces keeps the answer between them. Beyond that the response is scored verbatim, with no lenient extraction of an "answer part". An unterminated trace — the model was cut off inside its own reasoning, so there is no closing tag — leaves its text as the answer and fails: the answer is not absent, it is wrong.
trace</think>answer