← Claude на русском
Перевод с разбором · для Вани
Адаптировал Claude Opus 4.7 (ИИ) на основе документации Anthropic. Полная версия — в docs/evals.html.

Оценка качества (evals)

Адаптация для Вани · 2026-04-23

Зачем тебе это нужно

Ты поправил промпт парсера заявок — а работает лучше или хуже? На глаз это не скажешь, особенно когда LLM-модуль один из многих в системе. Evals — это «юнит-тесты для LLM»: набор входов с ожидаемыми выходами и метрики, которые говорят «сейчас 92%, было 85%». Для прод-системы это обязательный слой.

Три условия хорошего критерия

  1. Конкретно. Не «парсер хороший», а «поле wood_type верно распознаётся в 95% заявок».
  2. Измеримо. Число или чёткая шкала. «Смета разумна» — плохо; «сумма совпадает с экспертной оценкой ±3%» — хорошо.
  3. Релевантно задаче. На парсере — точность извлечения полей, на сметчике — сумма и полнота BOM.

Типы оценки

Code-based (самый дешёвый)

Подходит, когда эталонный ответ точный: enum-поле, число, флаг.

data class TestCase(val input: String, val expected: ParsedOrder)

val cases = loadTestCases("test-orders.json")

val results = cases.map { tc ->
    val actual = parser.parse(tc.input)
    mapOf(
        "wood_ok" to (actual.woodType == tc.expected.woodType),
        "floors_ok" to (actual.floors == tc.expected.floors),
        "length_within_5pct" to
            withinTolerance(actual.approxRunLengthMm, tc.expected.approxRunLengthMm, 0.05)
    )
}

val woodAcc = results.count { it["wood_ok"] == true }.toDouble() / results.size
println("wood_type accuracy: ${(woodAcc * 100).toInt()}%")

Что здесь происходит: 50 размеченных заявок, прогоняем через парсер, считаем по каждому полю отдельно. На 50 строках кода у тебя есть базовая метрика качества.

LLM-judge (для субъективного)

Когда эталонного ответа нет — например, «читается ли смета разборчиво» — используй отдельный промпт-«судью»:

fun judgeEstimate(estimate: String): Int {
    val prompt = """
        Оцени смету на деревянную лестницу по шкале 1-5:
        5 — полная, структурированная, нет противоречий
        1 — неразборчивая, неполная, с ошибками
        Ответь одним числом.

        Смета:
        $estimate
    """.trimIndent()
    val response = openAi.chatCompletion {
        model = "gpt-5.4-mini"  // другая модель, чтобы судья не подсуживал
        messages { user(prompt) }
    }
    return response.choices.first().message.content.trim().toInt()
}

Что здесь происходит: отдельная модель-судья ставит оценку. Чётная рубрика (1–5) стабильнее, чем просто «оцени как-нибудь». Периодически сверяй её выводы с ручной оценкой — и корректируй рубрику.

Manual spot-check

Раз в неделю просматривай 10–20 случайных выходов LLM глазами. Это единственный способ ловить систематические проблемы, которые не видны автоматике.

Где хранить тест-кейсы

Файл рядом с промптом: src/test/resources/llm-fixtures/parse_order.json. Формат — JSON Lines или массив объектов. В git, с ревью при правках. Обращайся к нему как к обычной тест-фикстуре.

CI / CD

Прогонять полный eval на каждый коммит обычно дорого (каждый тест — API-вызов, 50 кейсов × 5 промптов = 250 вызовов). Разумный компромисс:

Что это значит для твоей системы

Полная версия — в docs/evals.html.