Ты поправил промпт парсера заявок — а работает лучше или хуже? На глаз это не скажешь, особенно когда LLM-модуль один из многих в системе. Evals — это «юнит-тесты для LLM»: набор входов с ожидаемыми выходами и метрики, которые говорят «сейчас 92%, было 85%». Для прод-системы это обязательный слой.
wood_type верно распознаётся в 95% заявок».Подходит, когда эталонный ответ точный: 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 строках кода у тебя есть базовая метрика качества.
Когда эталонного ответа нет — например, «читается ли смета разборчиво» — используй отдельный промпт-«судью»:
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) стабильнее, чем просто «оцени как-нибудь». Периодически сверяй её выводы с ручной оценкой — и корректируй рубрику.
Раз в неделю просматривай 10–20 случайных выходов LLM глазами. Это единственный способ ловить систематические проблемы, которые не видны автоматике.
Файл рядом с промптом: src/test/resources/llm-fixtures/parse_order.json.
Формат — JSON Lines или массив объектов. В git, с ревью при правках.
Обращайся к нему как к обычной тест-фикстуре.
Прогонять полный eval на каждый коммит обычно дорого (каждый тест — API-вызов, 50 кейсов × 5 промптов = 250 вызовов). Разумный компромисс:
./gradlew llmEval. Отчёт — в
build/reports/llm/. Проверяй перед релизом.