AI Engineering на практиці: evals, trace і cost per successful task на двох production-інцидентах
Привіт, айті слоняри! Я Антон Шумейко, Lead AI Engineer в RubyLabs. Лідую Connectors department в consumer AI-платформі з 900K+ платних підписників: агентний runtime, мультимодельний роутинг, evals і контроль витрат на інференс. Разом зі мною над цим працює команда з 11 інженерів. Паралельно консультую компанії як AI Solutions Architect. А ще менторю розробників, які хочуть перейти в AI engineering без старту з нуля.
У LinkedIn я вже писав про межу між «вмію користуватися AI-інструментами» і «відповідаю за систему з моделями». Пост зібрав 25K+ переглядів і жваву дискусію в коментарях, тож тут розберу цю межу глибше, на двох кейсах із моєї роботи, коли модель поводилася інакше, ніж ми очікували, а звичні сигнали цього не показали.
Мова про applied AI engineering: інтеграцію моделей у продукт та відповідальність за його поведінку. Дослідження моделей і їхнє навчання мають свої задачі й вимоги.
Кейс 1. Регресія після зміни моделі
Ми замінили дефолтну модель на новішу. Після її релізу вже минув певний час, тому сама новизна моделі не виглядала ризиком, який мав зупинити перехід.
Але для нашого сценарію відповіді стали гіршими. Попередня версія відповідала швидко, коротко й у зручному форматі. Нова могла формально відповідати запиту й водночас давати користувачу менш придатний результат.
LLM-as-judge оцінював відповідність output до input. Цього виявилося недостатньо. Проблему помітили через продуктові метрики; backend-логи не показували звичайної технічної помилки. Ми відкотили модель.
Цей кейс добре показує різницю між кількома перевірками. HTTP-відповідь говорить про виконаний запит. Перевірка структури говорить про контракт. Оцінка judge говорить про критерії, які йому дали. Для користувача ще мають значення швидкість, формат і те, чи допомогла відповідь завершити задачу.
Урок № 1. Judge перевіряє лише те, що ти йому описав. Його критерії мають збігатися з тим, як відповідь оцінює користувач: формат, який очікує інтерфейс, розумна довжина, можливість одразу завершити задачу. Відповідність output до input — лише одна з цих перевірок.
Як я тепер міняю модель у production
- порівнюю конфігурації на однакових сценаріях;
- описую критерії якості через потребу користувача;
- перевіряю частину відповідей людиною;
- відстежую продуктовий результат після обмеженого rollout;
- тримаю зрозумілий шлях rollback.
Кейс 2. Затримка в одному сегменті
В іншому випадку відповідь для однієї країни займала понад дві хвилини. У нас уже були логи й tracing запитів, але вибірка цього сегмента в BetterStack була маленькою, і через алерти ми проблему не побачили.
Під час ручного дослідження з’ясували, що затримка пов’язана з конкретним провайдером. У роутингу OpenRouter стояло автоматичне сортування. Для цього сценарію ми змінили налаштування вибору провайдера вручну.
Тут назви моделі було недостатньо для діагностики. Потрібен контекст виконання: який провайдер обробив запит, для якого сегмента, з якою затримкою й результатом.
У системі, яку ви можете пояснити, trace має допомагати пройти цей шлях. Я б включив туди версію моделі й prompt, провайдера, час до першого токена та до завершення, використання токенів, виклики інструментів і повторні спроби. Персональні дані й секрети потребують редагування або вилучення з логів.
Для малих сегментів окремо треба вирішити, як виявляти проблеми за недостатньої статистики. Залежно від задачі це можуть бути контрольні запити або регулярний перегляд зрізів. Перцентиль із дуже малої кількості спостережень сам по собі не дає надійної картини.
Урок № 2. Назва моделі — не діагноз. Trace має показувати провайдера, сегмент і затримку, а малі сегменти потребують окремого способу перевірки, бо алерти на них мовчать.
Яку роботу має робити інженер навколо моделі
У цих кейсах модель була лише частиною системи. На результат впливали критерії оцінювання, конфігурація провайдера, спостережуваність і спосіб повернути попередню поведінку.
Для applied AI-фічі я б перевіряв такі зони:
| Зона | Питання до реалізації |
|---|---|
| Context | Звідки береться потрібна інформація і які дані доступні цьому користувачу |
| Output | Що система робить із відповіддю, яка не проходить перевірку |
| Tools | Хто дозволяє дію, як перевіряються аргументи й повторне виконання |
| Evals | Які сценарії й критерії відрізняють придатний результат від непридатного |
| Observability | Чи можна відтворити шлях від запиту до результату |
| Cost і latency | Які бюджети, таймаути й умови зупинки має фіча |
| Release | Як виявити регресію та повернути попередню конфігурацію |
Потрібна глибина залежить від продукту. Для класифікації звернення й агента, що змінює дані, межі відповідальності будуть різними.
Як рахувати вартість результату, а не виклику
Для порівняння моделей я використовую рамку cost per successful task. Спочатку потрібно визначити, що у вашому сценарії означає успіх.
Наприклад, для чернетки листа це може бути відповідь, яку користувач прийняв із допустимим обсягом правок. Сам факт генерації ще не відповідає цьому критерію.
За один тестовий період або eval-прогін підсумуйте витрати на всі модельні виклики, включно з retries і невдалими спробами. Поділіть на кількість задач, які пройшли критерій успіху. Якщо успішних задач немає, порівняння за цією метрикою непридатне.
Приклад з умовними числами. Модель A коштує $0.002 за виклик, модель B — $0.005. На 1 000 задач A з повторними спробами витратила $3, але критерій успіху пройшли 400 задач: $0.0075 за успішну задачу. B витратила $5 і пройшла 900: приблизно $0.0056. Дешевша за виклик модель виявилася дорожчою за результат.
Час людини на перевірку й переробку варто показувати окремо або включати в повну вартість за явно визначеною ставкою. Так видно, коли нижча ціна виклику компенсується додатковою роботою.
Урок № 3. Порівнюй моделі за ціною успішної задачі, а не за ціною виклику. Інакше дешевша модель може виявитися дорожчою через повтори й ручні правки.
Що показати у власному проєкті
Візьміть одну AI-фічу та збережіть докази рішень: eval-набір, результати порівняння конфігурацій, знеособлений trace, відтворений збій і поведінку системи після нього.
Під час технічної розмови поясніть, чому обрали модель, що вважаєте прийнятною відповіддю, де зупиняєте виконання і як обмежуєте дії інструментів. Якщо система поки навчальна, прямо позначте її масштаб і межі перевірки.
Cursor, Claude Code або Codex можуть допомогти написати значну частину цього коду. Інженерне рішення залишається за вами: як визначити правильний результат і забезпечити поведінку системи, коли він не отриманий.
Збираємо висновки
- Критерії judge мають описувати користь для користувача, а не лише відповідність output до input.
- Модель міняють через порівняння на однакових сценаріях, обмежений rollout і готовий rollback.
- Trace має відповідати на питання «хто, де, як довго і з яким результатом», а не лише «яка модель».
- Малі сегменти потребують контрольних запитів або регулярного перегляду зрізів: алерти на них не спрацюють.
- Моделі порівнюють за cost per successful task, включно з повторами й часом людини.
- У власному проєкті найкращий доказ — не код, а записані рішення: evals, trace, відтворений збій.
Який сигнал першим показав регресію після зміни моделі у вашому production: evals, продуктові метрики чи звернення користувачів?
2 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарівдякую, дуже найс
я рік тому описував на реддіті ще трошки тіпсів:
www.reddit.com/...&utm_content=share_button
якщо цікаво, ось
I had my main task doing continuous regression testing on reasoning qa
You create a static, as good as you can, set of benchmark prompts — let’s call it the golden set. This set is the most critical reasoning tasks your agent performs- logic, sums, multi-steps, etc
You run this set against your prod model/new candidate/on every new deployment — choose what you can afford. Then you need a way to grade the results. I’ve done it using a “judge”, latest gpts are good enough for it, to rate the agent’s answer on a scale of1-5
Then take the vector embeddings of the answers to your golden set prompts. You then measure the cosine distance between the new answers and the original, known good answers
Then I made 2 widgets:
time-series graph “gs accuracy (p95, p50)” — if this line starts to trend down, you have brain rot
again, time-series “semantic drift score”. If this starts to spike, it means the meaning and style of answers are changing, even if they are still factually ok — I’d have a human at this point checking what’s happening
——
Then I had 1 more red team dashboard for safety decay
Basically, throw all the bad prompts at it “leak PII”, bypass guardrail“, “say something toxic”, if the guardrail breach rate isn’t flatlined at 0, you’ve got a jailbreak. I’d also check refusal rate, in my case, even dropping from 100 to 98% was already bad
——
1 more task fidelity dashboard to check if it is not getting lazy — there were cases like that, that’s why it got here. The product was, let’s say, complex
I’d highly recommend logging its reasoning traces, step counts, tool calls, json validations — just chose critical stuff for YOURSELF
If avg reasoning steps suddenly fall from 8 -> 3, you know that it is skipping thoughts — this was a common thing
+ format compliance is a nice to check
Then on post I had feedback dashboard as a final slo — what people actually think — very simple, thumbs up/down on every reply for satisfaction and feedback volume. I wanted to add classifier, but simply didn’t have time
Just a tip from exp — no feedback at all = is a really bad thing (means people stopped trusting it)
We had a7-day rolling satisfaction line
Yeap, I think, that’s it. Hope, this helps you somehow
Guardrails і regression testing це окремий квест)
Але дуже корисний розбір, особливо semantic drift і safety decay dashboards. Дякую, що поділився!