Содержание
- Надёжность распадается на две величины, а мониторинг видит одну
- Арифметика длинной цепочки
- Собственные ошибки в истории тянут точность вниз
- Петля самоисправления работает ровно от одного условия
- Внешним арбитром служит код, а не вторая модель
- Четыре бюджета и распознавание зацикливания
- Лестница деградации: что отдавать, когда основной путь закрыт
- Схема держит форму ответа и не держит содержание
- Как читать чужие цифры про надёжность
- Итог
- FAQ
- Источники
Панель мониторинга зелёная. Доступность за сутки 99,95%, ошибок 5xx нет, время ответа держится в норме. При этом в поддержку идут жалобы. Бот банка «перестал понимать вопросы», на «где мой перевод» предлагает оформить карту, а на просьбу вернуть комиссию отвечает выдержкой из тарифов трёхлетней давности. Формально сервис работает.
Мониторинг видит одну половину дела: пришёл ответ или не пришёл. Вторую половину, годится ли ответ, чтобы на него опереться, коды HTTP не показывают вовсе. Двухсотый с валидным JSON, внутри которого неверная сумма, устроен ровно так же, как двухсотый с правильной.
Разрыв между этими половинами приходится закрывать руками. Доступность вытягивают повторы, таймауты и запасной канал к другому провайдеру. За качество отвечают проверка ответа кодом, границы, дальше которых агенту ходить нельзя, и заранее приготовленный ответ попроще на случай, когда хороший не получился. Модель сама ни того ни другого не гарантирует. Всё это живёт в коде вокруг неё.
Дневник курса, урок 16. Здесь понадобится разобранное раньше: инженерия контекста (урок 14) — сборка окна, у которой тут обнаружится вторая функция; контракты инструмента (урок 15) — валидатор, который здесь получает новую роль; предохранители графа (урок 13) — восстановление после сбоя, которое от этой поломки не спасает. Пост читается отдельно: все термины вводятся заново.
Сквозным примером идёт всё тот же чат-бот банка. В сутки 8000 обращений, около пятнадцати ходов на диалог, доступ к данным счетов и к оформлению возвратов.
1. Надёжность распадается на две величины, а мониторинг видит одну
Противоречия между зелёными графиками и жалобами нет, они просто меряют разное. Надёжность сервиса на языковой модели складывается из двух независимых величин: доступности (сервис ответил) и качества (ответом можно пользоваться). Доступность видят health-check и статус-страница провайдера. Качество не видит никто, кроме проверок, которые вы написали сами, и потому проседает оно молча.
Эти две величины разъезжаются из-за того, как устроен сам вызов. Обычная функция на один вход отдаёт один выход всегда. Вызов модели на один вход отдаёт распределение выходов, из которого берётся один экземпляр; температура и top-p управляют только тем, насколько строго мы из этого распределения выбираем. Отсюда следствие, ломающее привычный порядок отладки: «один раз заработало» ничего не доказывает. Это единичная выборка, и судить по ней о качестве нельзя, нужен набор кейсов.
Код вокруг вызова и превращает вероятностную функцию в предсказуемый сервис. Этот слой называют обвязкой (harness), и укладывается он в четыре части. Сама модель гарантий не даёт и дать не может, так что весь разговор о надёжности агента сводится к обвязке.
обращение
│
▼
┌──────────────────────── обвязка ────────────────────────┐
│ 1 транспорт класс ошибки, повтор с паузой, таймауты │
│ 2 границы шаги · деньги · время · повторы │
│ 3 исправление валидатор → конкретный фидбек → ещё ход │
│ 4 деградация другой провайдер · дешёвая модель · код │
│ ▲ │
│ вызов модели ─────────┘ выборка из распределения │
└─────────────────────────────────────────────────────────┘
│
├──► health-check, статус-страница: ответил или нет
└──► валидация, метрики качества: годится или нет▲ показывает, что сам вызов модели живёт внутри обвязки, а не рядом с ней. Снаружи видна только доступность, качество остаётся внутри и меряется своими проверками.Первые два слоя в серии уже разбирались. Классификация ошибок провайдера, повторы и идемпотентность лежат в узлах графа (урок 6), а лимиты шагов и денег разобраны в бюджетах агента (урок 2). Здесь мы идём по нижней стрелке, по качеству ответа, и смотрим, что с ним происходит.
2. Арифметика длинной цепочки
Прототип на десятке примеров отвечает отлично, а в проде тот же агент начинает сыпаться на диалогах в пятнадцать ходов, хотя прод ничем не сложнее. Надёжность цепочки равна произведению надёжностей шагов, поэтому даже отличная пошаговая точность разваливается на длине. Подставим наш диалог. При 99% на ход пятнадцать ходов доходят до конца целыми в 86% случаев (0,99¹⁵), то есть каждый седьмой клиент видит сбой где-то посередине.
Точную форму этой зависимости дала работа «The Illusion of Diminishing Returns» на синтетическом наборе dict_sum, где моделям выдают готовый план и все нужные знания, так что планирование и память из результата уходят, остаётся одно исполнение. Если шаги независимы, пошаговая точность p постоянна и самоисправления нет, длина цепочки, на которой успех падает до половины, считается так:
пошаговая точность p длина, на которой успех падает до 50 %
0,95 13 шагов
0,99 69 шагов
0,999 693 шага
0,9999 6931 шаг
H(p) = ln 0,5 / ln pЭта лестница читается в обе стороны, и вторая сторона обычно теряется. Пессимистическое прочтение известно всем. Точность 0,99 в шестой степени даёт 94%. Длинные цепочки обречены. Оптимистическое следует из той же формулы. Вблизи единицы выигрыш горизонта на фиксированный прирост точности растёт квадратично. Прибавка к пошаговой точности, на бенчмарке выглядящая косметической (99,0 против 99,9), в длине решаемой задачи даёт десятикратную разницу. Отсюда и название работы. Убывающая отдача от прироста точности — иллюзия, если смотреть на длину горизонта, а не на процент правильных ответов.
Порядки величин на dict_sum показывают, насколько тут всё зависит от режима работы модели. DeepSeek-V3 без режима рассуждений не выполняет и четырёх шагов подряд, а его рассуждающая версия R1 — больше сотни. Из передовых рассуждающих моделей GPT-5 проходит более 2100 шагов за один ход, а следующим идёт Claude-4 Sonnet с 432.
У той же арифметики есть вторая проекция. Считать можно не шаги внутри одной задачи, а повторы задачи целиком. В бенчмарке τ-bench, где агент обслуживает клиента через инструменты интернет-магазина и авиакомпании, ввели метрику pass^k. Она показывает вероятность того, что все k независимых прогонов одной задачи закончатся успехом. Её постоянно путают с pass@k («хотя бы один из k»), а меряют они противоположное. Первая величина описывает способность повторить решение, вторая — способность его найти.
GPT-4o через вызов инструментов даёт pass^1 около 61% на рознице (115 задач) и около 35% на авиакомпании, а pass^8 на рознице падает примерно до 25%. Задача и база данных при этом одни и те же; разброс берётся только из семплирования реплик агента и симулированного пользователя. То есть падение меряет невоспроизводимость поведения на одном и том же входе, а сложность задачи тут ни при чём.
Для нашего бота это переводится в простую арифметику потока. Восемь тысяч обращений по пятнадцать ходов дают сто двадцать тысяч ходов в сутки. Если сбоит один процент, это больше тысячи испорченных ходов ежедневно, и ни один из них не попадёт в графики доступности.
3. Собственные ошибки в истории тянут точность вниз
Формула горизонта держится на допущении, что пошаговая точность постоянна. Эмпирически это неверно, и неверно систематически. Точность падает по ходу траектории, причём не только из-за роста контекста. Авторы того же исследования называют механизм самообуславливанием (self-conditioning). Модель, которая видит в истории собственные прошлые ошибки, дальше ошибается чаще. Проверили это в контролируемом эксперименте: долю ошибок в подставленной истории повышали искусственно, и точность следующих шагов резко проседала. Размер модели эффект не снимает. Зато снимает его управление контекстом.
история без ошибок история с ошибками модели
ход 1 ✓ ход 1 ✗ ← ошиблась и видит это
ход 2 ✓ ход 2 ✓
ход 3 ✓ ход 3 ✗
ход 4 ✓ ход 4 ✗
ход 5 ✓ ход 5 ✗
точность держится точность падает к концу
скользящее окно: в контекст едут только N последних ходов,
прошлые ошибки уходят из виду, пошаговая точность восстанавливаетсяВ уроке 14 обрезка истории подавалась как способ уместиться в окно и не платить за одно и то же на каждом ходу, то есть как экономия. Тот же самый приём, оказывается, работает лечением, потому что выносит из ленты собственные ошибки модели. Уборка мусора из контекста и восстановление пошаговой точности — одно действие, хотя мотивы у них разные.
И вторая связка, уже с восстановлением после сбоев. В уроке 13 разбиралась поломка инфраструктурная: упал узел, подняли граф с последнего чекпоинта, продолжили. Самообуславливание описывает отказ другого рода. Процесс жив, чекпоинты на месте, база отвечает, а траектория портится изнутри, и повтор с последнего чекпоинта её не чинит, потому что ошибки уже лежат в сохранённой истории и приедут в контекст снова.
Насколько часто механизм срабатывает в живых агентных задачах, известно хуже. Авторы вручную пересмотрели чужой набор размеченных отказов и оценили, что примерно 20% отказов на GAIA, 48% на ALFWorld и 33% на WebShop похожи на самообуславливание, но сами оговорили, что правильность отдельного шага на таких задачах определяется субъективно. Контролируемым замером это не назовёшь, оценка сделана по чужой разметке, и относиться к ней надо соответственно.
4. Петля самоисправления работает ровно от одного условия
Ответ модели не понравился — попросим её проверить себя. Ход очевидный, реализуется за час, стоит один дополнительный вызов. Ровно так и выглядит первая версия «самовосстановления» почти в любом проекте, и работает она хуже, чем её отсутствие. Разница между полезной петлёй и вредной проходит по одному признаку. Приходит ли в цикл проверяемый сигнал извне?
Обе стороны этой границы измерены в одной работе (Huang et al., ICLR 2024) на GSM8K (1319 задач), CommonSenseQA (1221 вопрос) и HotpotQA (100 вопросов). Когда в цикл подают внешний признак правильности и останавливают его на верном ответе, петля улучшает результат. Когда модель сама решает, менять ли ответ, результат падает — на всех бенчмарках и всех проверенных моделях.
| Режим | Вызовов | GSM8K | CommonSenseQA | HotpotQA |
|---|---|---|---|---|
| GPT-4, обычный промпт | 1 | 95,5 | 82,0 | 49,0 |
| GPT-4, самопроверка, раунд 1 | 3 | 91,5 | 79,5 | 49,0 |
| GPT-4, самопроверка, раунд 2 | 5 | 89,0 | 80,0 | 43,0 |
| GPT-4, петля с внешним признаком | — | 97,5 | 85,5 | 59,0 |
| GPT-3.5, обычный промпт | 1 | 75,9 | 75,8 | 26,0 |
| GPT-3.5, самопроверка, раунд 1 | 3 | 75,1 | 38,1 | 25,0 |
Ни одна из двух итераций не окупается: они либо проигрывают одиночному вызову, либо равны ему, а стоят три и пять запросов вместо одного. В уроке 15 вопрос «сколько попыток самопроверки давать» остался без замера. Теперь замер есть, и для петли без внешнего арбитра он даёт ноль попыток.
Внутренняя механика падения объясняет, почему так. Разбор судьбы ответов после двух раундов на GSM8K у GPT-3.5 раскладывается на четыре доли.
не изменились ██████████████████████████████████ 74,7 %
верный → неверный ████ 8,8 %
неверный → верный ███ 7,6 %
неверный → неверный ████ 8,9 %Дело не в том, что модель не умеет исправлять. Она не умеет судить о правильности собственного рассуждения, ведь проверяет она себя тем же знанием, которое только что её подвело. Работа «Self-Correction Bench» подводит под это механистическое основание. Слепая зона там измерена: на 14 открытых нерассуждающих моделях в 64,5% случаев модель проходит мимо собственной ошибки, хотя такую же чужую исправляет. Внутри модели при этом нашёлся устойчивый признак «чей это ответ», от которого включение исправления зависит напрямую. Умение есть, оно просто не запускается на своей ошибке. Вставка одного слова «Wait» сокращает эту слепую зону на 89,3%, а дообучение на 5306 траекториях с исправлениями — на 76,0%.
Замер с внешним признаком, кстати, не доказывает способности моделей к самоисправлению, и авторы честно это оговаривают: если правильный ответ у нас уже есть, модель для решения задачи не нужна вовсе. Ценность верхней строки в другом. Она показывает потолок, до которого петлю можно дотянуть, если научиться получать сигнал со стороны.
Есть и вторая опора, полученная на совсем другой задаче. В работе про длинный горизонт пробовали чинить самообуславливание самопроверкой на каждом ходу. У Gemma3 с режимом рассуждений это даёт начальный прирост, но раздувает число токенов на ход, контекст кончается раньше, и обрыв получается резче. У рассуждающих моделей Qwen3 прироста нет вовсе. Они переусердствуют и ошибаются в самой проверке, включая арифметику при пересчёте. Авторы формулируют это прямо: проверка сама по себе — сложная исполнительская задача, в которой легко ошибиться. Два разных механизма отказа, и они складываются.
В уроке 15 мы записали короче: самопроверка ухудшает ответ. Это половина правды. Граница проходит не по самопроверке, а по наличию внешнего сигнала, и вредит именно ревизия без арбитра.
5. Внешним арбитром служит код, а не вторая модель
Хорошо, сигнал нужен внешний. Откуда его брать в задаче, где готового правильного ответа нет по определению? Ответ скучный и рабочий: арбитром служит детерминированный код, написанный заранее. Вопроса «хорошо ли получилось» он не задаёт вовсе: он идёт по конечному списку утверждений, каждое из которых либо верно, либо нет.
| Что проверяем | Чем | Пример для бота поддержки |
|---|---|---|
| Формат | схема, Pydantic | поля на месте, типы сошлись |
| Код | компилятор, линтер, тесты | сгенерированный запрос парсится и выполняется |
| Данные | бизнес-правила | сумма возврата не больше удержанной комиссии |
| Действия | исполнение в песочнице, сухой прогон | счёт принадлежит обратившемуся клиенту |
Валидатор пишется до запуска и от модели не зависит — на этом всё и держится. Проверка, которую та же модель сгенерировала в том же цикле, валидацией не считается, ведь она наследует ту же ошибку. По той же причине модель-судья (LLM-as-a-judge) годится в офлайновой оценке качества (урок 7) и как подсказка при неоднозначности, но не годится единственным гейтом перед критичным действием, ведь судья ошибается там же, где ошибается генератор.
from pydantic import ValidationError
MAX_ATTEMPTS = 3
def refund_with_validation(dialog, tools):
feedback = None
for attempt in range(MAX_ATTEMPTS):
draft = model.call(dialog, feedback=feedback)
try:
refund = RefundRequest.model_validate_json(draft)
check_business_rules(refund, tools) # сумма, счёт, срок, тариф
except (ValidationError, RuleViolation) as err:
feedback = str(err) # что не так и почему
continue
return refund
return escalate_to_human(dialog, reason="validation_failed")
Содержание сообщения об ошибке решает не меньше, чем сам факт повтора. «Сумма возврата 1450,00 больше удержанной комиссии 900,00; допустимо от 0 до 900,00» модель исправляет со второй попытки. «Ты уверен? Проверь ещё раз» запускает ровно ту петлю без арбитра, которая портит верные ответы.
Теперь неприятная часть. Проверяющий слой сам оказывается источником отказов, и довольно крупным. В исследовании MAST разобраны трассы отказов многоагентных систем и построена таксономия из четырнадцати мод. На проверку результата приходится 23,5% всех отказов; из них 9,1 п.п. — ошибочная проверка, когда проверяющий одобрил сломанный результат, 8,2 п.п. — проверка отсутствующая или неполная, 6,2 п.п. — преждевременное завершение. Ошибочная проверка встречается чаще, чем её отсутствие. Авторы прямо описывают типичного проверяющего: несмотря на просьбу проверить тщательно, он смотрит, компилируется ли код и не осталось ли комментариев TODO.
Условия у этих долей такие. Таксономию строили по 150+ трассам методом Grounded Theory силами шести разметчиков, согласие κ = 0,88 (κ — мера согласия разметчиков, 1,0 значит полное совпадение) измеряли между тремя разметчиками на пятнадцати трассах. Полный набор состоит из 1642 трасс, из которых человеком размечены 210, остальное размечено моделью-судьёй на o1, откалиброванной до κ = 0,77 с человеком. То есть это не «полторы тысячи трасс размечены экспертами», а «судья, настроенный на экспертах».
Точечные вмешательства там же и измерены: улучшение ролевых спецификаций в ChatDev дало у GPT-4o при том же промпте +9,4 п.п. успешности, а добавление шага проверки задачи на верхнем уровне прибавило там же +15,6 п.п. на задачах ProgramDev. Авторы тут же оговариваются, что изолированными правками надёжность многоагентной системы не чинится.
Для нашего возврата комиссии проверяемое перечисляется за пять минут: счёт принадлежит обратившемуся, сумма не превышает удержанного, операция не старше срока обжалования, тариф действовал на дату списания. Непроверяемого тоже хватает — тон ответа, уместность формулировки, выбор между «вернём» и «объясним, почему удержали». Для этой части работает другое правило. Вкладываться надо в генерацию, в промпт, примеры и структуру ответа, потому что чинить постфактум тут нечем. Валидатор держат узким и точным, иначе он начинает браковать нормальные ответы, а это уже потеря качества с обратным знаком.
6. Четыре бюджета и распознавание зацикливания
Петля с валидатором сходится не всегда. Задача бывает нерешаемой при текущих данных, инструмент отвечает ошибкой на любые аргументы, модель упорно предлагает одно и то же. Кто в этот момент остановит цикл? По умолчанию его останавливает сама модель, когда сочтёт задачу выполненной, и это решение остаётся такой же выборкой из распределения, как любой другой ответ. Агентный цикл — это while True с привязанной кредитной картой, и границу ему ставит код.
На границы приходится заметная доля отказов. В той же таксономии MAST дублирование уже сделанного шага стоит 15,7%, а непонимание условия остановки, когда агент продолжает вызовы после достижения цели, даёт ещё 12,4%. Вместе эти 28,1% отказов лечатся тем, что в библиотеках выглядит технической настройкой: жёстким лимитом и явно записанным условием завершения.
Бюджетов нужно четыре, и подменять один другим не выходит.
class TaskBudget:
max_steps = 12 # шаги цикла на одно обращение
max_cost_usd = 0.05 # входные + выходные токены за задачу
deadline = 90 # секунд на всю задачу, не на вызов
max_retries = 6 # повторы вызовов суммарно по задаче
def agent_loop(task, budget):
state = init(task)
while not state.done:
if budget.exhausted(state):
return {
"status": "budget_exceeded",
"partial_result": state.best_effort(),
"steps_used": state.steps,
"cost_usd": state.cost,
}
state = step(state)
return state.result
Для нашего бота это двенадцать шагов цикла на обращение, пять центов, девяносто секунд и шесть повторов. Время считают по задаче целиком. Десять шагов, каждый в пределах своего таймаута, складываются для клиента в минуты ожидания. Повторы считаются тоже по задаче, иначе десять шагов с тремя повторами на каждом превращаются в тридцать запросов к провайдеру.
Выход по бюджету делают управляемым: статус, частичный результат, потраченные шаги и деньги. Исключение в логах на эту роль не годится, потому что показать клиенту всё равно что-то нужно, а половина ответа обычно лучше пустоты. Доля упёршихся в бюджет задач при этом сама становится метрикой настройки: растёт — либо лимит занижен, либо агент начал ходить кругами.
Счётчиков, впрочем, мало. Цикл распознают по трём признакам: повтор (тот же инструмент с теми же аргументами N раз), отсутствие прогресса (состояние не меняется K шагов), осцилляция (A → B → A → B). Сложность в том, что зацикливание и законная настойчивость выглядят одинаково, поэтому реагируют в два приёма. Сначала идёт подсказка «этот путь не работает, попробуй другой», и только если она не помогла, цикл останавливают принудительно. Для бота поддержки это разница между «переспросил у базы знаний по-другому и нашёл» и «двенадцать раз запросил один и тот же документ».
Одного в источниках нет вовсе, и честнее сказать об этом прямо. Какое значение лимита правильное, никто не измерил. Есть основание для самого лимита (те самые 28,1% отказов), есть механизм отказа при его отсутствии, есть дефолты в библиотеках вроде recursion_limit = 25 из урока 13 и калибровочные ориентиры из урока 2. Работы, которая мерила бы оптимум и цену слишком жёсткой границы, найти не удалось. Лимит ставят по опыту и подкручивают по доле упёршихся задач — так это и следует называть, без ссылок на несуществующий замер.
7. Лестница деградации: что отдавать, когда основной путь закрыт
Бюджет исчерпан, валидатор третий раз забраковал ответ, провайдер вернул код перегрузки. Клиент всё это время ждёт, и что он увидит через десять секунд? Спрашивать «упадёт ли» поздно. Важнее решить другое: что показать клиенту в минуту падения. Ответ готовят заранее в виде лестницы, где каждая ступень даёт меньше ценности, чем предыдущая, но больше нуля.
1 основная модель полная ценность
2 повтор с паузой и разбросом то же, но позже
3 та же модель у другого провайдера то же, другой канал
4 дешёвая модель со своими промптами ответ проще, тема та же
5 детерминированный код: правила узкий набор частых тем
6 ответ из кэша по близкому вопросу про соседний случай, но по делу
7 честный отказ и очередь к оператору клиент знает, что будет дальшеСпуск по этой лестнице запускают пять разных триггеров: превышен бюджет времени, ответ N раз подряд не прошёл валидацию, исчерпан бюджет задачи, модель показала низкую уверенность, провайдер вернул отказ по лимиту при пустом бюджете повторов. Обсуждают обычно последний, падение провайдера. Остальные четыре срабатывают при живом провайдере и зелёных графиках.
У каждой ступени поэтому должен быть свой счётчик, иначе случается худший сценарий этой конструкции: трафик тихо переезжает на ступень «детерминированный код» и живёт там неделями. Качество упало, доступность идеальная, алерта нет.
Повтор и предохранитель решают разные задачи, хотя часто ставятся рядом. Повтор работает с одним запросом. Сеть моргнула, попробуем ещё раз. Предохранитель (circuit breaker) работает с потоком: после серии сбоев мы вообще перестаём ходить к провайдеру на минуту, отдавая ступень ниже мгновенно, и только потом пробуем одним запросом, ожил ли он. Без него повторы нагружают уже упавшую сторону и держат воркеры занятыми ожиданием (подробнее в уроке 11).
Ступень с дешёвой моделью выглядит бесплатной, а обходится дороже, чем кажется: это другой продукт. Промпт, написанный под флагман, на младшей модели даёт мусор, поэтому ей нужна своя ветка промптов и свой набор тестов, иначе запасной путь окажется хуже отказа.
Раз речь зашла о младших моделях, разведём два приёма, которые постоянно склеивают. Каскад — маршрутизация по умолчанию, по сложности запроса. Запасной путь — реакция на сбой. Экономику каскада меряли на данных предпочтений Chatbot Arena (сильная модель gpt-4-1106-preview, слабая Mixtral 8x7B), и результат ломает привычку говорить про «экономию N процентов»:
| Класс задач | Качество, которое держим | Доля запросов в сильную модель |
|---|---|---|
| Открытый диалог (MT Bench) | 95% от уровня GPT-4 | 13,4% |
| Знания (MMLU, 5-shot) | 92% от уровня GPT-4 | 35,4% |
| Математика (GSM8K, 8-shot) | 87% от уровня GPT-4 | 33,6% |
На открытом диалоге наверх уходит 13,4% запросов, то есть каждый седьмой, а на знаниевых 35,4% и на математических 33,6% — уже каждый третий. Единой цифры экономии не существует, она зависит от класса задач, а авторская формулировка «до 75% экономии» сравнивается со случайным маршрутизатором при том же бюджете, а не с отправкой всего трафика во флагман. Практически полезная часть в другом. Те же маршрутизаторы без переобучения работают на других парах моделей, потому что гейт ловит сложность запроса, а не особенности конкретной модели. Как это встраивается в счёт целиком, разбиралось в уроке 19.
8. Схема держит форму ответа и не держит содержание
Есть соблазн считать, что часть проблемы уже решена схемой. Строгий режим гарантирует, что придёт валидный JSON нужной формы. Значит ли это, что ответ стабильнее? Стабильнее он ровно в одном смысле: парсеры перестают ломаться. Содержание внутри правильной формы от этого лучше не становится, и это измерено напрямую.
Работа «The Format Tax» сравнивала десять моделей на четырёх задачах и четырёх форматах, разводя два фактора. Формат либо просто просят в промпте, либо подкрепляют ту же просьбу грамматикой на декодере (ограниченным декодированием, при котором на каждом шаге маскируются токены, ломающие схему).
| Режим | Соблюдение формата | Точность |
|---|---|---|
| Свободная генерация | — | 61,5% |
| Формат просят в промпте | 55,7% | 57,3% |
| Промпт + грамматика на декодере | 92,2% | 55,7% |
Число 55,7 стоит в таблице дважды, и это совпадение: в верхней строке так соблюдают формат при одной просьбе в промпте, в нижней — такова точность под грамматикой на декодере. Величины разные.
Грамматика поднимает соблюдение формата с 55,7 до 92,2%, а точность при этом не двигается. Модели, которые формат соблюли, теряют в качестве ровно столько же, сколько те, которые его провалили. Стабильность формы и стабильность содержания — независимые величины, и первая за вторую не платит.
Дальше выясняется, где именно берётся потеря, и место оказывается неожиданным. Считают здесь по ячейкам, а ячейка — это одна комбинация модели, задачи и формата. Из 39 значимых эффектов на 72 ячейках 36 (92%) присутствуют уже при одной просьбе в промпте; декодер вообще затрагивает 15 (38%), а ячеек, где просадку даёт только он, всего три. Просьба в промпте роняет точность в среднем на 3,9 п.п., а грамматика на декодере — на 1,6 п.п.
Виноват не механизм ограничения генерации, а сама формулировка «ответь в JSON», сдвигающая распределение ответов ещё до всякого декодера. Хуже всего это бьёт по задачам с рассуждением: на MATH-500 значима 41 ячейка из 48, и в 40 из них сдвиг идёт в сторону налога, а на вопросах со знаниями (GPQA) эффекта почти нет.
Помогает разведение решения и форматирования: сначала свободный ответ, потом отдельный ход на переформатирование. Ниже — замер на WritingBench в формате LaTeX, без расширенного режима рассуждений; во второй и третьей колонках стоит изменение относительно свободной генерации, а не общая величина налога.
| Модель | Свободно | В один ход | В два хода |
|---|---|---|---|
nemotron3-nano |
62,3 | −15,7 | −7,0 |
qwen3-8b |
51,5 | −13,7 | −6,1 |
qwen3-32b |
56,7 | −6,8 | −0,5 |
Две оговорки, без которых число переносить нельзя. Первая: разброс по моделям огромный. В среднем по значимым ячейкам всех задач и форматов, а не по WritingBench: у qwen3-8b −9,9 п.п. на 17 ячейках из 24, у nemotron3-nano −4,3 п.п. Вторая оговорка важнее. Авторы отмечают, что у свежих закрытых моделей налога на формат почти нет. Для команды, работающей через API фронтирных моделей, вывод скорее успокаивающий, а цифры на открытых восьмимиллиардных на такой прод не переносятся.
В уроке 15 цена строгого режима считалась организационной: лимиты провайдеров, потолки сложности схемы, различия реализаций. Теперь видно, что есть и содержательная цена, но платится она на промпте, а декодер тут почти ни при чём. Валидатор на своей стороне нужен, соответственно, не только ради бизнес-правил. Качество ответа под схемой ниже, и ловить эту потерю больше нечем.
Заодно измеренную цену получает правило из урока 4 — про порядок полей. Когда GPT-3.5-Turbo просили отвечать в JSON-режиме, ключ answer оказывался перед reason в 100% случаев, то есть модель выдавала ответ раньше рассуждения, и цепочка рассуждений теряла смысл. Поле для рассуждения стоит в схеме первым по простой причине: порядок полей задаёт порядок генерации.
9. Как читать чужие цифры про надёжность
Допустим, обвязка собрана. По каким числам понять, что стало лучше, и каким числам из чужих таблиц можно верить? Начать придётся с того, что за «галлюцинацией действия» скрывается не один отказ, а несколько, и меряют их по-разному. Галлюцинация выбора распадается надвое. Галлюцинация типа — это вызов нерелевантного или вовсе выдуманного инструмента, галлюцинация момента — повторный вызов того же инструмента с теми же входами и теми же выходами. Отдельной веткой идёт галлюцинация использования, когда инструмент выбран правильно, а параметры неверные. Измеримой величиной это сделали так: считают долю галлюцинированных вызовов от всех вызовов на задачу и усредняют по задачам. Это первое определение такого рода, которое удалось найти.
На лидербордах это часто сворачивают в одно число, и число получается лукавое. В Berkeley Function Calling Leaderboard способность агента не выдумывать действия меряется двумя колонками, которые тянут в разные стороны. Колонка Relevance считает случаи, где запрос решаем имеющимися инструментами и вызвать их надо, а колонка Irrelevance считает случаи, где не подходит ни один и вызова быть не должно. Строки ниже взяты из версии V4, снимок от 30 августа 2026 года; лидерборд перезаписывается, и через месяц числа будут другими.
| Модель | Общий балл | Relevance | Irrelevance |
|---|---|---|---|
| Claude-Opus-4-5 | 77,47% | 62,50% | 84,72% |
| o3 | 63,05% | 93,75% | 83,98% |
| Gemini-2.5-Flash | 56,24% | 75,00% | 93,67% |
| Ministral-8B-Instruct | 11,10% | 0,00% | 100,00% |
| Llama-3.1-Nemotron-Ultra | 10,00% | 0,00% | 100,00% |
Две последние строки — лучший аргумент против однобокого чтения. Стопроцентного «воздержания от лишних вызовов» здесь добилась модель, которая не вызывает инструмент никогда. Отдельно взятая метрика воздержания вознаграждает бесполезность.
И вторая находка, которую видно только в исходных данных. Все значения Relevance по всем 109 моделям лидерборда кратны 6,25%, то есть одной шестнадцатой. В подкатегории всего шестнадцать тестовых случаев, а печатаются доли с точностью до сотых процента. Один перевернувшийся случай двигает «метрику» на 6,25 п.п. Соседняя колонка Irrelevance устроена иначе: шкала там дробная, выборка заметно больше. Две колонки стоят рядом, выглядят одинаково и имеют разный порядок выборки — сравнивать их как равные нельзя.
Ловушка того же рода прячется в заявленных приростах самокоррекции. Всё решает база сравнения. Приём Self-Refine, где модель несколько раундов подряд критикует и переписывает собственный ответ, поднимает результат на генеративном наборе CommonGen-Hard с 44,0 до 67,0. Прирост выглядит внушительно ровно потому, что базовый промпт авторы взяли намеренно слабый. На той же шкале нормальный базовый промпт даёт 81,8, а после семи вызовов самокоррекции результат опускается до 75,1. Тот же класс подмены встречается в разговорах про многоагентные дебаты: у gpt-3.5-turbo-0301 три агента в двух раундах дают на полном тесте GSM8K 83,0 при бюджете в девять ответов, а простое голосование большинством при том же бюджете даёт 88,2. Дебаты полезны как способ прийти к согласованности, но критикой они не работают.
Три места в этой теме честнее назвать пробелами, чем закрыть правдоподобной цифрой. Первый мы уже назвали, оптимальное значение жёсткого лимита не измерил никто. Второй — ложный отчёт об успехе, когда агент сообщает о выполненном действии, которого не совершал; как отдельная измеряемая величина он не выделен ни в одной работе, и ближе всего к нему стоят те самые 9,1% ошибочной проверки в MAST. Третий пробел говорит сам за себя. По надёжности агентов не нашлось ни одного материала от Anthropic, OpenAI или DeepMind, тема пока держится на академических работах и на практике команд.
Мерить у себя нам при этом никто не мешает, и мерить надо на своём наборе. Полсотни реальных обращений, прогнанных по восемь раз, дают pass^k, то есть долю задач, которые решаются каждый раз, без скидки на удачный бросок. Дальше к ним добавляются счётчики: доля ответов, забракованных валидатором, доля задач, упёршихся в бюджет, срабатывания на каждой ступени лестницы. Эти четыре ряда чисел и есть та вторая половина надёжности, которой нет в мониторинге.
Итог
Надёжность = доступность × качество, и обе величины проектируются явно, в коде вокруг модели. Доступность приходит из транспорта, повторов и запасных каналов, качество — из валидации, границ и лестницы деградации; ни одну из них модель не обеспечивает сама.
- Длина цепочки бьёт по надёжности степенью, а пошаговая точность вдобавок падает по ходу траектории: собственные ошибки в истории повышают вероятность следующих. Скользящее окно контекста лечит это лучше, чем самопроверка на каждом ходу, поставленная промптом.
- Петля самоисправления работает от внешнего проверяемого сигнала и вредит без него: на GSM8K у GPT-3.5 без арбитра она портит 8,8% верных ответов, чиня 7,6% неверных. Обе итерации при этом либо проигрывают одиночному вызову, либо равны ему, хотя стоят три и пять запросов к модели вместо одного.
- Арбитром служит заранее написанный детерминированный код, а на слой проверки приходится 23,5% отказов, из них 17,3% — ошибочная проверка и её отсутствие. Ошибочная проверка при этом встречается чаще.
- Жёсткий лимит шагов и явное условие остановки закрывают 28,1% отказов; рядом с ними стоят бюджеты денег и времени. Упёршись в границу, агент выходит управляемо, с частичным результатом. Значение самой границы не измерено никем — ставится по опыту и настраивается по доле упёршихся задач.
- Строгая схема покупает стабильность формы и не покупает стабильность содержания. Соблюдение формата растёт с 55,7 до 92,2%, а точность не восстанавливается: 61,5 на свободной генерации против 55,7 под схемой. Налог уплачен на промпте, а не на декодере.
Вернёмся к той зелёной панели. После этого урока рядом с доступностью на ней стоят четыре ряда чисел: pass^k на полусотне своих кейсов, доля ответов, забракованных валидатором, доля задач, упёршихся в бюджет, и счётчик срабатываний на каждой ступени деградации. Тот же сбой, когда бот отвечает выдержкой из старых тарифов, теперь виден счётчиком за минуты, а не жалобой через неделю. Наш агент поддержки умеет останавливаться, объяснять, почему остановился, и отдавать меньшую ценность вместо пустоты. Чего он всё ещё не умеет — пережить выкатку новой версии на живом трафике.
FAQ
Помогает ли просьба «проверь свой ответ» снизить число ошибок?
Без внешнего сигнала — нет, она ухудшает результат. В замерах Huang et al. GPT-4 на GSM8K падает с 95,5 до 89,0 после двух раундов такой самопроверки, а разбор изменений на GPT-3.5 показывает, что 8,8% верных ответов портятся против 7,6% починенных. Та же петля с внешним признаком правильности, наоборот, поднимает результат до 97,5. Второй взгляд самой модели ничего не добавляет; добавляет проверяемый сигнал со стороны: валидатор схемы, тесты, бизнес-правила, интерпретатор.
Чем pass^k отличается от pass@k и что из них считать в проде?
Метрика pass@k считает вероятность того, что успешен хотя бы один из k прогонов задачи. Метрика pass^k считает вероятность того, что успешны все k. Первая описывает способность найти решение, вторая описывает способность его воспроизвести, и для сервиса важна именно вторая. Разрыв между ними большой: у GPT-4o в τ-bench на рознице pass^1 около 61%, а pass^8 около 25%, при том что задача и база данных во всех прогонах одинаковы.
Снижает ли структурированный вывод качество ответа?
Он снижает качество на открытых моделях, но не по той причине, о которой обычно думают. В замерах «The Format Tax» 92% значимых просадок присутствуют уже тогда, когда формат просто попросили в промпте, без всякой грамматики на декодере; ограниченное декодирование добавляет мало, зато поднимает соблюдение формата с 55,7 до 92,2%. У свежих закрытых моделей налога на формат авторы почти не находят. Практический вывод такой: строгий режим включать стоит, а просьбу о формате лучше увести из промпта с рассуждением, оставив на форматирование отдельный ход.
Какой лимит шагов ставить агенту?
Измеренного оптимума не существует: работы, которая меряла бы лучшее значение и цену слишком жёсткой границы, найти не удалось. Библиотечные дефолты порядка 25 шагов и практические ориентиры в 10–15 итераций опираются на опыт, замера за ними нет. На практике лимит ставят по самой длинной разумной траектории своей задачи, при его достижении отдают статус превышения бюджета с частичным результатом, а само значение настраивают по доле упёршихся задач.
Можно ли поставить модель-судью проверкой перед критичным действием?
Единственным гейтом ставить её нельзя. Судья ошибается там же, где ошибается генератор, а в разборе отказов многоагентных систем ошибочная проверка (9,1%) встречается чаще, чем отсутствие проверки (8,2%). Модель-судья хорошо работает в офлайновой оценке качества и как подсказка при неоднозначности. Перед необратимым действием ставят детерминированную проверку: схему, бизнес-правила, сухой прогон, подтверждение человеком.
Что показывать пользователю, когда качественный ответ получить не удалось?
Ступень ниже, спроектированную заранее. Это тот же вопрос к другому провайдеру, дешёвая модель со своей веткой промптов, ответ по правилам или шаблонам, ответ из кэша по близкому вопросу и, последней ступенью, честный отказ с постановкой в очередь к человеку. Каждая ступень даёт меньше ценности, чем предыдущая, но больше нуля. Обязательное условие: счётчик срабатываний на каждой ступени, иначе трафик тихо переезжает на нижнюю и остаётся там.
Источники
- Huang et al., «Large Language Models Cannot Self-Correct Reasoning Yet» (arXiv:2310.01798) — обе стороны границы самоисправления: замеры с внешним признаком правильности и без него, разбор судьбы ответов после двух раундов.
- Sinha et al., «The Illusion of Diminishing Returns: Measuring Long Horizon Execution in LLMs» (arXiv:2509.09677) — формула длины горизонта, самообуславливание и проверка трёх способов его чинить.
- Lee et al., «The Format Tax» (arXiv:2604.03616) — разведение налога промпта и налога декодера, соблюдение формата против точности, лечение вторым ходом.
- Cemri et al., «Why Do Multi-Agent LLM Systems Fail?» (arXiv:2503.13657) — таксономия из четырнадцати мод отказа и их доли, включая отказы проверяющего слоя.
- Yao et al., «τ-bench» (arXiv:2406.12045) — метрика
pass^kи разрыв между способностью решить задачу и способностью повторить решение. - Berkeley Function Calling Leaderboard — парные колонки
RelevanceиIrrelevanceи то, почему их нельзя читать поодиночке. - Ong et al., «RouteLLM» (arXiv:2406.18665) — экономика каскада по классам задач и корректная база сравнения.
- Tsui, «Self-Correction Bench» (arXiv:2507.02778) — механистическое объяснение слепой зоны: умение исправлять есть, оно не включается на собственной ошибке.
- Xu et al., «Reducing Tool Hallucination via Reliability Alignment» (arXiv:2412.04141) — таксономия галлюцинаций при вызове инструментов и метрика их доли.
- Tam et al., «Let Me Speak Freely?» (arXiv:2408.02442) — порядок полей в JSON-режиме и его влияние на цепочку рассуждений.
Числовые ориентиры из текста сняты на конкретных наборах задач, моделях и датах замера. Доли отказов зависят от состава систем в выборке, налог на формат — от семейства модели, а экономика каскада — от класса запросов. Переносить их на свой профиль нагрузки без собственного замера не стоит.