Temper Bench v2

10 октября 2026

Первая версия собственного бенчмарка, на которой оценивали Temper 0.5B, со временем перестала отвечать тому, что мы от неё ждали. В ней было 100 задач трёх уровней: 50 тривиальных, 30 простых и 20 средних. Пересечений с обучающим датасетом и утечек в ней не было, но тексты задач готовили те же модели DeepSeek, что генерировали обучающие данные, поэтому бенчмарк совпадал с датасетом по стилю. К тому же большую часть составляли короткие алгоритмические упражнения вроде проверки числа на простоту, факториала, НОД или FizzBuzz. Они хорошо проверяют знание синтаксиса, но мало говорят о том, как модель справляется с реальным кодом, а именно это для нас было главным.

Вторую версию мы писали так, чтобы она стала показателем прикладной работы. Вместо алгоритмических упражнений в ней небольшие самостоятельные задачи, похожие на типичные части реальных проектов, например расчёт стоимости доставки, разбор конфига, разбор HTTP-запроса или кэш, с которым одновременно работают несколько потоков. На каждую задачу приходится от пяти тестов, в них обязательно есть пустой ввод и крайние случаи. Бенчмарк получился непростым даже для крупных моделей: Qwen2.5-Coder-7B с первой попытки решает 46% базовых задач, 22% средних и только 10% продвинутых.

0 10 20 30 40 50 Qwen2.5Coder7B 46 Temper 11.5B 42 Qwen2.5Coder1.5B 14 Qwen2.5Coder3B 26 Temper 10.5B 22
0 10 20 30 40 50 Qwen7B 46 Temper1.5B 42 Qwen1.5B 14 Qwen3B 26 Temper0.5B 22
Рис. 1. Процент решённых с первой попытки задач Temper Bench v2 (greedy pass@1, %) по уровням сложности.

Примеры задач

Задание (как его получает модель)

Write normalize_phone(raw: &str) -> Option<String> for US phone numbers. Ignore everything that isn't a digit. If there are 10 digits, or 11 digits starting with 1, format the last 10 as (555) 123-4567. Anything else gives None. Reply with a single file containing the solution.

Скрытые тесты
#[cfg(test)]
mod tests {
    use super::*;

    fn some(s: &str) -> Option<String> {
        Some(s.to_string())
    }

    #[test]
    fn plain_ten_digits() {
        assert_eq!(normalize_phone("5551234567"), some("(555) 123-4567"));
    }

    #[test]
    fn various_separators() {
        assert_eq!(normalize_phone("555-123-4567"), some("(555) 123-4567"));
        assert_eq!(normalize_phone("(555) 123 4567"), some("(555) 123-4567"));
        assert_eq!(normalize_phone("555.123.4567"), some("(555) 123-4567"));
    }

    #[test]
    fn country_code() {
        assert_eq!(normalize_phone("+1 555 123 4567"), some("(555) 123-4567"));
        assert_eq!(normalize_phone("15551234567"), some("(555) 123-4567"));
    }

    #[test]
    fn eleven_digits_without_one() {
        assert_eq!(normalize_phone("25551234567"), None);
    }

    #[test]
    fn wrong_length() {
        assert_eq!(normalize_phone("555123456"), None);
        assert_eq!(normalize_phone("115551234567"), None);
        assert_eq!(normalize_phone(""), None);
        assert_eq!(normalize_phone("call me"), None);
    }

    #[test]
    fn letters_ignored() {
        assert_eq!(normalize_phone("tel: 555 123 4567 (home)"), some("(555) 123-4567"));
    }
}
Эталонное решение
pub fn normalize_phone(raw: &str) -> Option<String> {
    let digits: String = raw.chars().filter(|c| c.is_ascii_digit()).collect();
    let digits = match digits.len() {
        10 => digits.as_str(),
        11 if digits.starts_with('1') => &digits[1..],
        _ => return None,
    };
    Some(format!("({}) {}-{}", &digits[..3], &digits[3..6], &digits[6..]))
}

Ограничения

Мы старались сделать Temper Bench v2 честным и качественным. Задачи написаны с нуля, решения проверяют компилятор и скрытые тесты, бенчмарк проверен на утечки. Но мы не исследовательская лаборатория и раньше бенчмарки не создавали, поэтому ставить его в один ряд с HumanEval или MBPP, которые годами проверяло сообщество, пока рано. Мы сделали его, потому что для Rust почти нет готовых альтернатив, кроме переводов этих же наборов. Окончательный вывод о модели всё равно стоит делать на своих задачах, попробовав её в работе.