От Zero-Shot к самоконсистентности: практическая иерархия техник формулировки подсказок.
Инжиниринг промптов как лестница: zero-shot, few-shot, декомпозиция, циклы критики и промпты для рассуждений, с примером бота-помощника и указанием, когда использовать обычный код.
Источники: Prompt Engineering for LLMs: Zero-Shot, Few-Shot, Reasoning, Decomposition & ReAct
Как только приложение определило, что должен видеть модель, какие документы загружаются, что сокращается и что остаётся за пределами обработки, возникает другая проблема: сформулировка самого запроса. Нет универсального метода формулировки запросов. Разумной практикой является начало с самого простого возможного указания, а примеры или пошаговая структура добавляются только тогда, когда задача требует этого.
Приведённый ниже пример — ассистент поддержки клиентов.
Начало без каких-либо примеров
Запрос без примеров описывает задачу, не показывая, каким должен быть хороший результат. Для ассистента поддержки это может быть однострочный запрос на краткое изложение жалобы, при этом сама жалоба приводится ниже:
Summarize the following customer complaint in one sentence.
<complaint>
The package arrived three days late and the box was damaged.
</complaint>
Модель получает задание и ничего больше. Именно эта простота является причиной того, что стоит попробовать её в первую очередь. Любое дополнение к запросу имеет свою цену: примеры занимают место в контексте, дополнительные инструкции увеличивают сложность, а каждый новый элемент создаёт ещё одну возможность для столкновения двух правил. Если прямая инструкция уже обеспечивает надёжный результат, сложная структура запроса — это лекарство от болезни, которой у никого нет.
Что должна ответить хорошая инструкция
Хорошо сформулированное описание задания определяет четыре аспекта: действие, которое необходимо выполнить, источник информации, ограничения и форму, в которой должен быть представлен ответ. Более полная версия запроса выглядит так:
Task:
Summarize the customer complaint.
Source:
<complaint>
The package arrived three days late and the box was damaged.
</complaint>
Constraints:
- Use only the information in the complaint.
- Do not infer the cause of the damage.
- Use one sentence.
Output:
Return only the summary.
Это более подробно, чем простое требование «Кратко подытожьте», но каждая дополнительная строка устраняет неопределенность. Модели больше не нужно гадать, стоит ли предполагать причины, сколько времени продолжать работу или допускаются ли комментарии. Рекомендации, выпускаемые поставщиками крупных моделей, указывают на одно и то же: укажите, в чем заключается задача, каким должен быть результат, какие существуют ограничения и какой контекст имеет значение, вместо того чтобы рассчитывать на то, что модель это сама поймет. Проще говоря, любые требования, которые важны для приложения, должны быть зафиксированы письменно.
Конкретные цели лучше дружелюбных
Приказ «Будьте полезны» практически не задаёт модели цели. Приказ «Укажите проблему, описанную клиентом, и кратко её обозначьте одним предложением», — даёт ей чёткую цель. Сравните два указания для инструмента поддержки: в слабом из них просто просится помочь клиенту, тогда как в более строгом требуется указать проблему аутентификации, описанную клиентом, а также не более трёх шагов устранения неполадок, основанных на предоставленной документации.
Это важно, поскольку модель может развивать поставленную задачу во многих возможных направлениях. Размытая цель позволяет ей выбрать интерпретацию, отличную от намерения разработчика, тогда как чёткая цель сужает такие возможности.
Ограничения должны быть указаны в самой задаче
Пусть в инструкции будет четко указано, что ассистент ни в коем случае не должен придумывать решение, отсутствующее в документации. Это правило продукта, поэтому в запросе необходимо прямо указать: использовать только предоставленную документацию, а если она не охватывает вопрос, заявить, что имеющейся информации недостаточно. Сравните это с требованием «Быть точным», которое описывает желаемый результат, но не дает указаний по поводу действий в случае неопределенности.
Эта схема можно обобщить. Необходимо не только описывать идеальный результат, но и указывать, что должен делать модель, когда этот результат невозможно получить безопасным способом. Это особенно важно позже, при вызове инструментов и форматировании вывода.
Когда достаточно подхода zero-shot
Простое указание обычно срабатывает, когда задача проста, её описание легко сформулировать, модель уже знает тип задачи, правила вывода просты, а входные данные не содержат необычных крайних случаев. Запрос версии продукта в сообщении, указав только номер версии, — это одна из таких задач. Перевод предложения на французский — ещё одна. Суммирование пяти примеров в одном запросе почти не приносит пользы.
Поэтому первое правило оптимизации промптов кажется неожиданно консервативным: начинайте с подхода без примеров, добавляя дополнительные механизмы только после того, как оценка покажет необходимость.
Способы сбоев подхода без примеров
Повторяются несколько типов сбоев.
Неоднозначность. Фраза «Сделайте это лучше» не содержит ответа на вопрос: короче, убедительнее, точнее, формальнее? Модели приходится догадываться. Сравните это с запросом переписать ответ службы поддержки так, чтобы он был кратким, профессиональным и понятным нетехническому клиенту, при этом все фактические утверждения оставались нетронутыми. Во втором случае условия гораздо более четко определены.
Отсутствие контекста. Модель не может опираться на факты, которые ей никогда не предоставлялись и которые она не может получить. Если спросить ее, почему у клиента не прошла аутентификация, не указав сообщения об ошибке или деталей системы, никакая перефразировка не сможет восполнить отсутствующие данные. Здесь решением является наличие более полного контекста, а не улучшение формулировки запроса. Это очевидный момент, а также реальное ограничение инжиниринга промптов.
Противоречивые требования. Задание, требующее полного объяснения ровно из десяти слов, предполагает выполнение двух условий, которые невозможно выполнить одновременно качественно. Хорошее проектирование запросов предотвращает такие конфликты, вместо того чтобы полагаться на модель в решении каждого из них.
Неуказанные предположения. Разработчик, пишущий «Подготовьте краткое резюме жалобы», может иметь в виду «указать только те факты, которые указал клиент». Вместо этого модель может предоставить связное резюме с разумными выводами. Если разница имеет значение, в инструкции необходимо это четко указать. Предположения, важные для системы, должны становиться явными, машинно читаемыми требованиями.
Добавление примеров
Когда обычные инструкции оказываются недостаточными, используется метод небольшого набора примеров: в контекст включаются готовые примеры выполнения задачи. Классификатор, отсортирующий сообщения по категориям счетоводства, технических вопросов и учета, может показать несколько маркированных сообщений перед реальным сообщением:
Task:
Classify the customer message as BILLING, TECHNICAL, or ACCOUNT.
Example 1:
"My invoice contains an unexpected charge."
→ BILLING
Example 2:
"The application crashes when I upload a file."
→ TECHNICAL
Example 3:
"I cannot reset my password."
→ ACCOUNT
Now classify:
"I was charged twice for my subscription."
На основе этих примеров модель выводит соответствующую связь. Это называется обучением в контексте, поскольку модель изменяет своё поведение в ответ на текущий запрос, используя примеры из своего окна, при этом её базовые параметры остаются неизменными. По сути, примеры говорят: «Вот как выглядит задача на практике», и они могут передать детали, которые трудно изложить в прозе.
Примеры учат выполнять задачу, а не только форматирование
Многие разработчики рассматривают примеры с небольшим количеством случаев как средство форматирования. На самом деле они выполняют более важную функцию. Возьмём пример пары «приложение работает медленно» с меткой TECHNICAL. Это показывает формат вывода, но также демонстрирует, как это приложение понимает категорию TECHNICAL. Примеры формируют понимание самой задачи у модели.
Именно поэтому плохие примеры представляют риск. Несогласованные метки, случайная предвзятость или крайние случаи, противоречащие правилам, могут быть воспроизведены моделью.
Выбор примеров для включения
При наличии 10 000 исторических заявок включение их всех в запрос нереалистично, поэтому необходимо выбрать подмножество. Случайный выбор — это наивный подход. Лучший вариант — целенаправленно выбирать репрезентативные случаи:
Example 1: straightforward billing issue
Example 2: straightforward technical issue
Example 3: ambiguous issue
Example 4: boundary case
Example 5: short, poorly written request
То, что считается хорошим набором примеров, зависит от задачи. Для классификации необходимо представить основные классы и границы между ними. Для задач форматирования требуются примеры, демонстрирующие нужную структуру. Задачи, требующие сложного обоснования, выигрывают от примеров, показывающих, как разбить проблему на части или проверить её. В любом случае пример заслуживает своего места благодаря информации, которую он содержит, а не из-за того, что он доступен.
Сколько
Большее количество не всегда означает лучшее качество. Если один тщательно подобранный пример делает поведение очевидным, ещё тридцать примеров могут добавить тысячи токенов без какой-либо пользы и даже привести к противоречивым закономерностям. Поэтому метод использования небольшого количества примеров сталкивается с той же проблемой ограниченности ресурсов, что и все остальные подходы, и оптимальное количество примеров необходимо определять путём тестирования.
Здесь инжиниринг промптов начинает напоминать экспериментальную работу. Вопрос уже не в том, звучит ли промпт лучше, а в том, работает ли эта версия лучше на репрезентативных тестовых случаях. Это изменение становится ключевым, как только в игру вступает оценка.
Разнообразие примеров
Если каждая демонстрация представляет собой короткий, четкий и хорошо написанный запрос, модель может выучить узкий шаблон, которого не придерживаются реальные пользователи. Настоящие сообщения выглядят так:
"can't login"
"I've been locked out since yesterday"
"Password reset isn't sending me anything"
"login broken after changing phone"
Все это описывает схожие проблемы совершенно разными словами. Хороший набор примеров с несколькими вариантами отражает те вариации, которые ожидает приложение. Это не означает добавление примеров случайным образом; это означает целенаправленное покрытие значимых различий.
Примеры того, чего следует избегать
Для некоторых задач полезно сочетать хороший результат с плохим, чтобы модель могла увидеть границы. В случае с поддержкой клиентов сообщение о том, что посылка прибыла с опозданием, может сопровождаться как приемлемым кратким описанием, так и некорректным:
Input:
"The customer says the package arrived late."
Good output:
"The customer reports that the package arrived late."
Bad output:
"The courier lost the package."
Reason:
The customer did not state that the courier lost it.
Плохой пример указывает на предел: не следует выводить факты, которые не подтверждаются входными данными. Однако с отрицательными примерами нужно обращаться осторожно. Пример, помеченный как неверный, может случайно научить именно тому поведению, которое предполагалось предотвратить, особенно когда разница незначительна. Приложение должно проверять, улучшают ли отрицательные примеры результаты, а не просто предполагать, что они это сделают.
Примеры структуры
Когда сложно описать формат вывода, хорошо подходят примеры. Предположим, что ассистент должен ответить тремя строками с метками «Проблема», «Доказательства» и «Действие»:
Issue: ...
Evidence: ...
Action: ...
Одна рабочая демонстрация сразу же делает этот макет конкретным:
Input:
"The login token expired after 30 days."
Output:
Issue: Expired authentication token
Evidence: Token expiration is reported after 30 days
Action: Regenerate the authentication token
Это исправляет как шаблон содержимого, так и его форматирование. Однако примеры не всегда являются правильным решением. Если программному обеспечению требуется JSON, читаемый машиной с гарантированной корректностью, функция вывода с ограничениями по схеме может оказаться надежнее, чем показ модели примера.
Сохранение последовательности примеров
Задания с небольшим количеством примеров плохо реагируют на противоречия. Рассмотрим два примера с одинаковым входным данным «Не удается войти», помеченные как ACCOUNT в одном случае и TECHNICAL — в другом:
Example 1:
"Can't log in" → ACCOUNT
Example 2:
"Can't log in" → TECHNICAL
У модели нет четкого способа определения того, какое соответствие предполагается использовать. Даже более тонкие несоответствия влияют на качество результата. Если в некоторых примерах используется метка BILLING, а в других — фраза «Проблема с оплатой», пространство возможных выходов становится неоднозначным. Метки, форматирование, терминология, уровень детализации и способ обработки крайних случаев должны быть одинаковыми во всех примерах. Демонстрации являются частью спецификации и заслуживают такого же внимания.
Обучение в рамках текущего контекста
Обучение в контексте — это основная идея метода нескольких примеров: примеры находятся в текущем контексте, и поведение модели меняется в ходе этого взаимодействия. Это не означает, что модель навсегда усваивает их. Если приложение отправляет один пример с меткой, а затем сообщение от пользователя о невозможности доступа к аккаунту:
Example:
"Password reset email never arrived." → ACCOUNT
User:
"I can't access my account."
Модель может воспользоваться демонстрацией для выполнения этого запроса. Её параметры не обновляются, и пример не добавляется в её постоянные знания. Это соответствует ранее описанному разделению на контекст и память. Методы, которые действительно изменяют параметры, такие как тонкая настройка на дополнительных данных для обучения, представляют собой отдельный механизм, и их сравнение с использованием подсказок происходит гораздо позже. Пока что подсказки на основе нескольких примеров лучше рассматривать как временную условную обработку задачи через контекст.
Выбор между двумя подходами
Эффективный процесс принятия решений начинается с определения причин ошибки в результатах. Неоднозначная задача требует более чёткой формулировки; отсутствие информации — более полного контекста. Если модель упускает какую-то тонкую конвенцию, примеры могут помочь. Если задача требует точных арифметических вычислений или доступа к базе данных, дополнительные описания в подсказке могут оказаться совершенно неподходящим решением.
Последний пример ведет к более сложным ситуациям. Некоторые задачи слишком запутанны, чтобы их можно было надежно решить с помощью одной инструкции и одного ответа. Их необходимо разделить на более мелкие операции, проверить или связать с внешними вычислениями. После достижения определенной точки задача уже не заключается в поиске идеального предложения, а в разработке небольшой процедуры, которую должен следовать модель.
Возьмем, к примеру, случай, когда клиент теряет доступ сразу после смены пароля, и от ассистента требуется просмотреть историю, определить вероятную причину и написать ответ. На первый взгляд это кажется одной задачей. На самом деле она скрывает несколько этапов: выяснение того, что произошло, поиск соответствующих доказательств, разделение фактов от предположений, анализ возможных причин, определение того, что можно безопасно порекомендовать, проверка этой рекомендации и написание окончательного ответа. Понимание этого — ключ к декомпозиции задачи.
Разделение одной задачи на несколько
Метод разложения заключается в замене одной сложной задачи на несколько более мелких с четко определенными границами. Вместо того чтобы просить выполнить все сразу, приложение может разбить запрос на этапы:
Task:
Analyze this customer support case.
Step 1:
Extract the relevant facts.
Step 2:
Identify the customer's actual problem.
Step 3:
List plausible causes supported by the evidence.
Step 4:
Select the safest supported resolution.
Step 5:
Draft the customer-facing response.
Модели буквально не нужно слово «Шаг». Важно то, что расплывчатая цель превращается в последовательность управляемых операций. Это особенно полезно, когда элементы имеют разную природу: извлечение фактов — это информационная задача, выбор диагноза — рассуждение, а написание сообщения — коммуникация. Объединение их в одно неразличенное указание затрудняет управление результатом и его оценку.
Разложенная на составные части система также предоставляет больше возможностей для тестирования. Если окончательный ответ неверен, можно выяснить, сорвалось ли извлечение фактов, постановка диагноза или написание ответа. Изменение подхода заключается в том, что подсказку можно построить как конвейер, а не просто написать в виде вопроса.
Сначала спланируйте, затем действуйте
Связанная техника предполагает, что модель должна сначала составить план перед выполнением сложной задачи. Это помогает, когда модели необходимо определить, какие действия требуются, прежде чем она сможет дать ответ. Помощник может начать с поставленной цели и последовательного плана:
Goal:
Resolve the customer's login problem using only supported evidence.
Plan:
1. Read the latest customer message.
2. Check the relevant conversation history.
3. Identify authentication-related evidence.
4. Compare the evidence with the known troubleshooting guidance.
5. Produce a response that does not claim unsupported facts.
Планирование отделяет процесс принятия решения о том, как что-то решить, от самого процесса решения. Это разделение становится особенно важным при использовании инструментов, поскольку модели может потребоваться сначала определить, следует ли искать информацию в базе знаний, проверять записи в учетных данных или выполнить расчеты. Предварительная настройка позволяет сформировать структуру работы еще до ее начала.
Это имеет свою цену. Разбиение задачи увеличивает длину команд, а когда каждый этап выполняется как отдельный вызов, возникает дополнительное количество вызовов модели и задержки. Пятиэтапная процедура может стоить гораздо дороже, чем один вызов. Она оправдана тогда, когда дополнительный контроль и надежность компенсируют такую стоимость, а не потому, что большее количество этапов автоматически делает процесс лучше.
Генерация, анализ, корректировка
Ещё один эффективный механизм позволяет модели снова просмотреть свой собственный черновик. Первый ответ не считается окончательным; второй этап предусматривает его проверку по чётким критериям. Для системы поддержки это означает составление ответа, а затем его проверку по списку определённых проблем:
First:
Draft the response.
Then:
Check the draft for:
- unsupported claims,
- missing evidence,
- incorrect troubleshooting steps,
- promises the support team cannot make,
- failure to answer the customer's actual question.
Finally:
Revise the response to correct any problems.
Это работает потому, что процессы генерации и оценки являются разными задачами. Генерация означает создание чего-то правдоподобного. Проверка существующего ответа позволяет модели искать нарушения установленного списка критериев. Определение критериев обычно эффективнее, чем просто просьба подумать лучше.
Эта схема может применяться на разных уровнях сложности: черновик, проверка, окончательный ответ, или, в случае более структурированной работы, извлечение информации, проверка результата извлечения, классификация, проверка результатов классификации, генерация. Второй вариант удобнее для отладки, поскольку у каждого этапа есть четко определенные требования.
Один важный ограничение заключается в следующем: модель, к которой обращаются с просьбой самокритики, не становится тем самым независимым источником истины. Если первоначальный ответ основан на ложном предположении, критикующая модель может унаследовать это предположение. Самокритика может способствовать повышению последовательности выводов, но не гарантирует их корректности; при принятии важных решений целесообразно сверять результаты с чем-то внешним по отношению к модели — базой данных, известным правилом, расчётом, полученным документом или детерминистическим кодом.
Подсказки, раскрывающие структуру рассуждений
Исследования, посвящённые использованию подсказок, стимулирующих последовательное мышление, показали, что предоставление модели примеров с промежуточными шагами рассуждений может улучшить результаты при решении некоторых многоэтапных задач; такие улучшения были зафиксированы как в арифметических, так и в задачах здравого смысла и символического анализа. Урок, вытекающий из этих исследований, шире, чем просто эта формулировка: сложные задачи могут получить пользу, если подсказка делает их промежуточную структуру явной.
Identify the relevant facts.
Determine which facts support each possible conclusion.
Eliminate conclusions that require unsupported assumptions.
Select the conclusion supported by the evidence.
Это запросы, ориентированные на логическое мышление: задача формулируется таким образом, чтобы промежуточные решения были достаточно ясными и позволяли направить модель к желаемому результату. Это не обязывает прикладное приложение показывать пользователю внутренние рассуждения модели. Часто более удобный интерфейс требует указания результата проверки, а не длинного объяснения. Приложение может использовать краткий внутренний отчет, подобный приведенному ниже, а затем отдельно сгенерировать краткий ответ для клиента:
classification: password_reset_issue
evidence_supported: true
recommended_action: resend_reset_link
confidence: high
Структура обоснования и объяснение для клиента — это разные требования. Разделение их также помогает избежать распространённой ошибки, заключающейся в предположении, что более заметное обоснование делает ответ более надёжным. На промежуточных этапах также могут возникать ошибки. Исследования метода подсказок типа «цепочка мыслей» показали, что такие свойства демонстраций, как их релевантность и порядок, имеют огромное значение, поэтому простое добавление длинных обоснований не является универсальным решением.
Несколько попыток, затем голосование
Генерация происходит стохастически, поэтому одна и та же задача может давать разные ответы при разных запусках. Метод самосогласованности использует это, выбирая из нескольких возможных путей обоснования тот ответ, который поддерживается большинством из них, вместо того чтобы полагаться на один конкретный путь.
Предположим, что система поддержки не уверена, является ли проблема восстановлением пароля или блокировкой аккаунта. Вместо того чтобы выбрать одну из категорий, она может сгенерировать несколько анализов и сравнить полученные результаты.
Согласие между анализами всё равно не является доказательством истины. Если модель постоянно неправильно интерпретирует ситуацию, множество независимых расчётов могут привести к одному и тому же неверному ответу. Согласованность — это признак стабильности, а не доказательство правильности. Кроме того, это требует больше вычислительных ресурсов и увеличивает задержку, поэтому такой подход применим только для задач, настолько сложных, что повышение надёжности оправдывает затраты.
Когда запросы должны передаваться коду
Здесь проходит граница между всё более сложными запросами и хорошим проектированием системы. Представьте, что вам нужно найти сумму счёта, содержащего 17 позиций по цене 1500 рупий каждая. Модель, вероятно, сможет вычислить эту сумму, но когда важна точная финансовая цифра, нет причин заставлять её имитировать арифметические операции с помощью всё более длинных инструкций. Калькулятор или обычная программа выполняют умножение детерминированно.
Та же логика применима и к другим задачам:
- Данные счёта должны поступать из запроса к базе данных, а не из памяти модели.
- Текущие факты следует получать из внешних источников, а не вспоминать из процесса обучения.
- Арифметические операции — это задача калькулятора или программы.
- Проверка схемы — задача парсера и валидатора.
- Авторизация регулируется кодом приложения и правилами контроля доступа.
- Критически важные бизнес-правила следует кодировать детерминированно, когда это возможно.
Существует полезное правило: используйте инструкции для управления поведением языковой модели, а также применяйте детерминистское программное обеспечение там, где задача действительно требует детерминистских вычислений или строгого соблюдения правил.
Внешние действия усиливают этот эффект. ReAct — известный исследовательский подход, сочетающий рассуждения с действиями, позволяющий модели обращаться к внешним источникам или средам вместо того, чтобы полагаться только на внутренние знания; он показал, что такое взаимодействие может снизить проблемы, такие как галлюцинации и распространение ошибок, в некоторых задачах.
Это мост к тому, что будет дальше. Система поддержки начиналась как простой запрос, а теперь напоминает небольшую программу. Как только для верификации требуется база данных, база знаний, API или другие внешние ресурсы, модель больше не работает только с текстом, и вопрос переходит к инжинирингу контекста: необходимо решить не только, какие инструкции написать, но и какую информацию должна получить модель.