Мок-экзамен, вопрос номер сорок с чем-то. Сценарий: система должна классифицировать входящие обращения, и ни при каких условиях категория не может выходить за пределы фиксированного списка. Какой вариант обеспечивает это требование? Я уверенно тыкаю в ответ с формулировкой «добавить в системный промпт строгую инструкцию с перечнем допустимых категорий и явным запретом на другие значения».
Красным. Правильный ответ — валидация вывода на уровне приложения со схемой и retry-логикой. Я смотрю на объяснение и ловлю себя на раздражении: ну промпт же тоже работает, я сто раз так делал. А потом листаю свои прошлые мокрезультаты и вижу закономерность: я стабильно ошибаюсь в вопросах одного и того же типа. Не потому что не знаю материал — а потому что у меня в голове неправильно проведена одна граница.
Готовясь к CCA-F (Claude Certified Architect – Foundations), я ожидал, что буду зубрить параметры API и лимиты контекста. Вместо этого экзамен методично бил меня по одному и тому же месту, пока я не сформулировал, во что именно я неправильно верил.
Миф, в который я верил
Формулируется просто: хорошо написанный промпт — это и есть архитектура LLM-системы. Если модель ошибается — значит, промпт недостаточно хорош, и нужно добавить ещё правило, ещё пример, ещё «ВАЖНО:» капсом.
Это ложная эквивалентность. Промпт — это пожелание, высказанное вероятностной системе. Архитектура — это гарантии, которые выполняются независимо от того, в каком настроении сегодня температурный сэмплинг. Смешивать одно с другим — всё равно что вместо валидации на бэкенде написать над полем формы «пожалуйста, вводите только цифры» и считать вопрос закрытым.
Самое коварное — в проде эта разница маскируется. Промпт-инструкция работает в 97% случаев, и на демо, на тестовом прогоне, на первой неделе эксплуатации всё выглядит отлично. Именно поэтому миф живучий: он подтверждается почти всегда. А экзамен спрашивает про оставшиеся 3% — и про то, кто отвечает, когда они случаются.
Три типа вопросов, на которых я горел, — по сути три лица одного и того же мифа.
Грабли №1: «в промпте написано, значит, так и будет»
Наивный подход, который я применял не раз:
Классифицируй обращение строго в одну из категорий: Billing, Technical, Account, Feedback. ВСЕГДА возвращай только название категории из списка. НИКОГДА не придумывай новые категории.
Что происходит на дистанции: на подавляющем большинстве запросов всё чисто, а потом модель возвращает «Billing/Payments» со слэшем, просто «Bill», а один раз — вдохновенное «Customer Care». Код ниже по течению, который матчит категорию по точной строке, молча ломается — и хорошо ещё, если падает, а не пишет мусор в базу.
Проблема не в «непослушной» модели. Проблема в том, что работа, которую должен делать детерминированный код, поручена контролю через текст. Инструкция в промпте снижает вероятность отклонения, но не сводит её к нулю — а для ключа, по которому ветвится логика, нужен именно ноль.
Архитектурно правильный ответ скучен: enum-валидация на выходе. Ответ модели проходит через белый список; всё, что не совпало, уходит в retry с уточнением, после второй неудачи — в fallback (дефолтная категория, очередь на ручную обработку — по ситуации). Забавный побочный эффект: промпт при этом укорачивается — из него уходят все «ВСЕГДА» и «НИКОГДА», потому что их работу теперь делает код, который не умеет фантазировать.
После этого осознания я начал узнавать паттерн в самих экзаменационных вопросах: если в варианте ответа гарантия достигается формулировкой в промпте — это почти всегда дистрактор. Правильный вариант — тот, где enforcement вынесен на уровень системы.
Грабли №2: chain-of-thought там, где нужна одна метка
Вторая ошибка тоньше, потому что она выглядит как best practice. Все же знают: chain-of-thought улучшает качество рассуждений. Значит, «думай шаг за шагом» стоит добавить везде, включая классификацию? Я на мок-вопросах про выбор техники промптинга уверенно выбирал CoT — и регулярно ошибался.
Для задачи «верни одну метку из списка» CoT делает три вещи, и все плохие. Вопервых, расход токенов вырастает в разы — на потоке из тысяч запросов это прямые деньги и латентность. Во-вторых, усложняется парсинг: вместо одного слова приходит абзац рассуждений, из которого метку надо выковыривать. Втретьих — самое неприятное — модель в процессе рассуждения способна переубедить сама себя: начать с правильной категории, найти «с другой стороны, здесь упоминается…» и приехать к неправильной.
|
|
Chain-of-thought |
Few-shot + жёсткий формат |
|
Расход токенов |
Абзац рассуждений на каждый запрос — в разы дороже и медленнее |
Одно слово в ответе |
|
Парсинг ответа |
Метку нужно извлекать из текста — ещё одна точка отказа |
Ответ и есть метка, парсинг тривиален |
|
Стабильность |
Модель может «передумать» в процессе рассуждения |
Прямое сопоставление с примерами, меньше пространства для дрейфа |
|
Где уместен |
Математика, планирование, многошаговый вывод |
Классификация, извлечение, задачи с одним детерминированным ответом |
CoT — инструмент для задач с многошаговым выводом: математика, планирование, разбор противоречивых требований. Классификация по чётким категориям — задача сопоставления с образцом. Ей нужны хорошие few-shot примеры, жёсткий формат вывода и низкая температура, а не пространство для сомнений.
Урок, который экзамен вбил мне в голову: приём из чек-листа «лучших практик» — не приправа, которую сыплют во всё подряд. Это архитектурное решение с ценой и областью применимости, и вопрос «а что это стоит и где это ломается» нужно задавать каждый раз.
Грабли №3: один порог уверенности на все случаи жизни
Третий тип вопросов, на котором я горел, — про confidence thresholds. Сценарий обычно такой: система с самооценкой уверенности, ответы ниже порога уходят на ручную проверку. Вопрос: как выбрать порог?
Мой инстинктивный ответ — «подобрать одно значение, при котором точность/ полнота на валидации оптимальны» — стабильно оказывался неверным для сценариев, где среди задач были высокорисковые. И это ровно то, как я делал в реальной жизни: один порог, выбранный на глаз или по средней метрике, на все типы входов.
Почему это ломается: порог, откалиброванный по средней задаче, обслуживает тем же числом и edge-кейсы с высокой ценой ошибки. Модель может выдавать уверенность 0.85 на вопросе про обработку персональных данных — выше порога, автопропуск — и ошибаться в деталях. Для тривиального запроса цена такой ошибки — минутное неудобство пользователя. Для юридически или финансово чувствительного — инцидент.
Архитектурный ответ: порог — это не константа в конфиге, это политика, привязанная к цене ошибки. Высокорисковые категории получают свой, более консервативный порог, а часто и другой сценарий срабатывания: не «пометить плашкой», а «обязательная ручная проверка» или «отказ с эскалацией». Плюс отдельная неудобная правда, которую экзамен тоже проверяет: самооценка уверенности модели сама по себе плохо откалибрована, и опираться на неё без проверки на своих данных — ещё одна форма веры в пожелания вместо гарантий.
Что изменилось
Когда я сформулировал общий корень трёх ошибок, мок-результаты выправились почти сразу — не потому что я выучил больше фактов, а потому что появился рабочий критерий. Читая вариант ответа, я спрашиваю: это пожелание модели или гарантия системы? Это инструмент, применённый к своей задаче, или каргокульт best practices? Этот порог откалиброван под цену ошибки — или под среднюю температуру по больнице?
Смешное в том, что рабочий код я после этого тоже начал читать иначе. Открыл свой самый длинный продовый промпт — и нашёл в нём четыре «ВСЕГДА», которые на самом деле должны быть десятью строками валидации.
Вывод
Промпт-инжиниринг — это интерфейс к модели. Архитектура — это всё, что гарантирует поведение системы в те моменты, когда модель ошибается. Пока эти два слоя склеены в голове в один, каждая ошибка модели выглядит как «плохой промпт», и её лечат бесконечным докручиванием текста там, где нужен код, схема и политика обработки отказов.
Если собираетесь на CCA-F — проведите эту границу заранее, экзамен проверяет её с разных сторон настойчивее, чем что-либо ещё. А если не собираетесь — всё равно откройте свой самый длинный промпт и спросите, какие из его «ВСЕГДА» и «НИКОГДА» должны быть кодом.
ссылка на оригинал статьи https://habr.com/ru/articles/1058888/