Головна / Статті / Створення запитів LLM для промислового використання: семишарова схема

Створення запитів LLM для промислового використання: семишарова схема

Вивчіть структуровану, засновану на API схему з сімома шарами запитів — інструкції, контекст, обмеження тощо — для створення надійних систем LLM промислового рівня.

3297 слів

21 днів розширеного інжинірингу запитів

Практичний курс, який проведе вас від основ формулювання запитів до створення повноцінних систем ШІ.

Більшість людей вважають, що запит — це просто питання, яке ви передаєте ШІ.

Наприклад:

Classify this customer support ticket.

І це працює.

Доки не перестане.

Кілька вхідних даних можуть повернути саме те, чого ви очікували. Потім надходить трохи інший запит, і раптово модель:

  • обирає неправильну категорію,
  • вигадує деталі, яких не було в вхідних даних,
  • повертає формат, якого ви не просили,
  • пише абзац пояснення замість структурованих даних,
  • або поводиться непослідовно лише через зміну формулювання.

Саме тут починає мати значення різниця між запитом, який ви протестували випадково та запитом, створеним для експлуатації.

У системі експлуатації запит — це не просто запитання.

Він виконує функцію частини угоди між вашим додатком та моделлю.

Розробники вже знають, як проектувати API з чітко визначеними межами:

Request → Validation → Business Logic → Response

Системи, побудовані на запитах, також потребують такої самої дисципліни:

Instructions
     ↓
Context
     ↓
Constraints
     ↓
Task
     ↓
Examples
     ↓
Output Contract
     ↓
Validation

У решті цього матеріалу розглядається кожен з цих елементів окремо, а потім вони об’єднуються в один працюючий приклад.

1. Проблема підходу „Просто запитайте модель“

billing
technical
account
general

Мінімальний запит може виглядати так:

Classify this customer support ticket:
"I was charged twice for my subscription."

І модель може відповісти:

Billing

Зверху все виглядає нормально.

Але ваш бекенд зазвичай потребує більше, ніж просто однієї мітки. Ймовірно, він очікує щось структуроване, наприклад:

{
  "category": "billing",
  "priority": "high"
}

Саме тут справи ускладнюються.

Як насправді визначається пріоритет?

Розгляньмо заявку, в якій просто сказано:

"Я не можу увійти."

Чи належить це до категорії account, чи це справді технічна проблема?

Що станеться, якщо клієнт у одному повідомленні описуватиме як проблему з оплатою, так і проблему з входом?

А якщо категорія справді є неоднозначною?

Яка має бути поведінка за замовчуванням у такому випадку?

Жодну з цих запитань модель не може надійно відповісти, оскільки у запиті з самого початку не вказано правил.

Саме тому створення запитів для продакшну починається з визначення специфікації, а не вдосконалення формулювань.

2. Структура запиту промислового рівня

Якісний запит для продакшну можна розділити на сім окремих компонентів.

Ці розділи не обов’язково мають з’являтися буквально як позначені заголовки всередині самого тексту запиту.

Важливо те, щоб ви розуміли функцію кожного елемента.

Розглянемо їх по одному.

3. Інструкції системи — визначення ролі та поведінки

Перший елемент визначає обсяг завдань, за які несе відповідальність модель.

На прикладі класифікатора тикетів:

You are a customer support classification assistant.
Your job is to classify incoming support tickets
according to the provided category definitions.Return only information supported by the ticket.
Do not invent customer information.

Це набагато корисніше, ніж щось загальне на кшталт:

You are an intelligent AI assistant.

Чому ця різниця має значення?

Адже називати модель „розумним AI-асистентом“ не визначає жодної конкретної поведінки.

Більш конкретна версія описує:

  • яке завдання виконує модель
  • в якій сфері вона працює
  • яку інформацію їй дозволено використовувати
  • які дії є забороненими

Інструкції системи можна розглядати як контракт поведінки для взаємодії.

4. Контекст — надайте моделі те, що їй потрібно

Наступний рівень надає всю необхідну інформацію для фактичного виконання завдання.

Наприклад:

Available categories:
billing:
Questions about charges, invoices, refunds, or payments.technical:
Problems with product functionality, errors, or system behavior.account:
Login, password, profile, access, or account-management issues.general:
Questions that don't clearly belong to the above categories.

Далі йде сам зміст заявки:

Customer ticket:
"I was charged twice for my subscription this month."

Цю різницю варто чітко підкреслити:

Instructions = What to do
Context = Information needed to do it

Якщо змінити контекст, залишивши інструкції недоторканими, модель все одно має змогу правильно обробити новий вхідний даний.

Розділення їх також значно полегшує підтримку шаблонів запитів з часом.

5. Обмеження — покажіть моделі, де зупинитися

Обмеження — це рівень, де усувається неоднозначність.

Продовжуючи приклад:

Rules:
1. Select exactly one category.
2. Use only the four categories provided.
3. Do not create new categories.
4. Do not infer facts that are not present.
5. If the issue is unclear, use "general".
6. Priority must be one of: low, medium, high.
7. Return only the requested JSON.

Ці правила значно скорочують кількість можливих відповідей.

Без них запит на кшталт:

Classify the ticket.

може дати щось на кшталт:

This appears to be a billing-related issue because
the customer mentions being charged twice.

Це абсолютно розумна відповідь для людини-читача.

Але ваш API, ймовірно, очікує чистий JSON, а не прозу.

Додайте обмеження явно:

Return only the requested JSON.

і очікувана поведінка стане однозначною.

6. Завдання — Визначте точну операцію

Як тільки будуть встановлені інструкції та обмеження, потрібно детально описати саме ту операцію, яку ви очікуєте виконання.

Task:
1. Identify the most appropriate category.
2. Determine the priority based on the rules.
3. Provide a short reason.
4. Return the result using the specified JSON structure.

Порівняйте це з передачею нечітко сформульованої директиви на кшталт:

Understand this customer issue.

Коли завдання сформульоване настільки точно, ви можете пізніше перевірити, чи модель справді виконала те, що просили.

Input
  ↓
Classify
  ↓
Determine priority
  ↓
Generate reason
  ↓
Return JSON

У цей момент завдання перестає бути припущенням та стає чимось вимірюваним та перевірюваним.

7. Приклади — Покажіть бажану поведінку

Інструкції пояснюють моделі, що робити, словами.

Приклади демонструють це безпосередньо.

Візьмемо цей приклад:

Example 1
Example 1Input:
"I was charged twice for the same subscription."Output:
{
  "category": "billing",
  "priority": "medium",
  "reason": "The customer reports a duplicate subscription charge."
}

Ще один приклад може ілюструвати зовсім іншу ситуацію, наприклад звіт про технічну помилку:

Example 2Input:
"The application crashes every time I upload an image."Output:
{
  "category": "technical",
  "priority": "high",
  "reason": "The customer reports a repeatable application failure."
}

Приклади найбільш корисні тоді, коли поведінку, якої ви прагнете, важко визначити лише за допомогою правил, адже вони дозволяють моделі зафіксуватися на конкретному шаблоні.

Проте є один нюанс, про який варто пам’ятати.

Більше прикладів ≠ автоматично кращі запити

Накопичення прикладів один за одним має реальні наслідки. Це призводить до:

  • Збільшення розміру запиту
  • Підвищеного споживання токенів
  • Затримки в обробці
  • Потенційних суперечностей

Розумнішою стратегією є ретельний вибір невеликої кількості прикладів.

Надавайте пріоритет прикладам, які представляють суттєво різні ситуації, особливо ті неоднозначні та крайні випадки.

Наприклад, така трійка:

Clear billing issue
Clear technical issue
Ambiguous issue

зазвичай краща за десять майже ідентичних прикладів билінгу, накопичених один на одного.

8. Формат виводу — ставіться до нього як до контракту API

У запитах промптів виробничого рівня мало що має таке ж важливе значення, як цей шар.

Якщо інша система має аналізувати те, що повертає модель, ви не можете залишати форму цієї відповіді на волю випадку у вигляді вільного тексту.

Натомість необхідно чітко визначити схему.

Наприклад:

{
  "category": "billing | technical | account | general",
  "priority": "low | medium | high",
  "reason": "string"
}

Як тільки це впроваджено, у моделі з’являється фіксована мета, до якої вона прагне, а код на наступних етапах може спиратися на такий потік даних:

LLM
 ↓
JSON
 ↓
Parser
 ↓
Schema validation
 ↓
Business logic

замість чогось набагато більш хаотичного, наприклад:

LLM
 ↓
Some paragraph
 ↓
Regex
 ↓
Hope it works

Ця друга конфігурація — справжній кошмар для підтримки її працездатності з часом.

Добре специфікований структурований вихід створює чіткий розділ між тим, що генерує ШІ, та тим, що насправді потребує ваше застосунок.

9. Перевірка — модель не є вашим верифікатором

Ось помилка, яка постійно зустрічається в системах на основі ШІ.

Припустимо, модель повертає:

{
  "category": "billing",
  "priority": "urgent",
  "reason": "The customer has a billing issue."
}

З синтаксичної точки зору цей JSON є коректним.

Проблема криється ось тут:

"urgent"

„urgent“ ніколи не було одним із допустимих значень.

Виявлення такої неузгодженості — це завдання вашого додатку, а не моделі.

Ось як це виглядає з використанням Pydantic:

from pydantic import BaseModel
from typing import Literal

class TicketClassification(BaseModel):
    category: Literal[
        "billing",
        "technical",
        "account",
        "general"
    ]
    priority: Literal[
        "low",
        "medium",
        "high"
    ]
    reason: str

Потім ви викличете:

result = TicketClassification.model_validate(llm_response)

А якщо модель поверне щось на кшталт:

{
  "category": "billing",
  "priority": "urgent",
  "reason": "Duplicate charge."
}

експертиза має відразу відхилити його.

Саме це і є метою.

Ніколи не дозволяйте результатам роботи моделі проходити без перевірки.

Це нагадує про правило, яке варто дотримуватися під час проектування систем на основі LLM:

LLM генерує. Ваш додаток перевіряє.

Модель може допомогти з прийняттям рішень, але будь-які справді непереговорні вимоги мають бути реалізовані у детерміністичному коді програми, який безпосередньо їх застосовує.

10. Повний запит, орієнтований на виробництво

Якщо об’єднати всі елементи, вийде щось на кшталт цього:

SYSTEM
You are a customer support classification assistant.
Your job is to classify incoming support tickets
according to the provided category definitions.
Return only information supported by the ticket.
Do not invent customer information.

CONTEXT
Available categories:
billing:
Charges, invoices, refunds, or payment-related issues.
technical:
Product functionality, errors, crashes, or system behavior.
account:
Login, password, profile, access, or account-management issues.
general:
Issues that do not clearly belong to another category.

Customer ticket:
{{ticket_text}}

CONSTRAINTS
1. Select exactly one category.
2. Use only the categories provided above.
3. Do not create new categories.
4. Do not infer unsupported facts.
5. If the issue is unclear, use "general".
6. Priority must be "low", "medium", or "high".
7. Return only the requested JSON.

TASK
1. Classify the ticket.
2. Determine the priority.
3. Provide a short reason.
4. Return the result in the required JSON format.

EXAMPLE
Input:
"I was charged twice for the same subscription."
Output:
{
  "category": "billing",
  "priority": "medium",
  "reason": "The customer reports a duplicate subscription charge."
}

OUTPUT FORMAT
{
  "category": "billing | technical | account | general",
  "priority": "low | medium | high",
  "reason": "string"
}

Тепер розмістіть це поруч із місцем, де почалося все це завдання:

Classify this customer support ticket.

Різниця між цими двома запитами полягає не стільки у кількості слів.

Змінилася та частка, наскільки все стало чітким.

Кожен блок довшої версії виконує певну функцію:

  • Частина, що описує систему, визначає, як повинна діяти модель
  • Частина, що описує контекст, надає інформацію, з якою працює модель
  • Частина, що описує обмеження, чітко вказує правила, яких не можна порушити
  • Частина, що описує завдання, точно визначає, що потрібно досягти
  • Частина з прикладами ілюструє, як виглядає правильна відповідь.
  • Частина з результатом визначає, у якій формі має бути відповідь.
  • Частина з перевіркою вирішує, чи можна довіряти цій відповіді.
  • 11. Дизайн запиту схожий на дизайн API

    Для інженерів програмного забезпечення це порівняння робить процес створення запитів майже миттєвим.

    POST /tickets/classify
    

    Тіло запиту:

    {
      "ticket": "I was charged twice."
    }
    

    І тіло відповіді:

    {
      "category": "billing",
      "priority": "medium",
      "reason": "Duplicate charge reported."
    }
    

    Тепер застосуйте ту саму логіку до запиту до LLM.

    Запит фактично є логікою реалізації, яка знаходиться за цією кінцевою точкою.

    API
     ↓
    Input
     ↓
    Prompt Template
     ↓
    LLM
     ↓
    Structured Output
     ↓
    Validation
     ↓
    API Response
    

    Ось чому інженерія запитів поступово перетворюється на галузь інженерії програмного забезпечення, а не на вправу у мистецтві формулювання слів.

    Ви не шукаєте ідеальної формулювання.

    Ви створюєте надійний інтерфейс на основі системи, яка поводиться ймовірнісно.

    12. Поширені помилки у запитах до системи

    1. Нечіткість формулювань

    Analyze the ticket carefully.
    

    Що означає тут „ретельно“?

    Опишіть точну поведінку, якої ви хочете, замість того щоб залишати все на розсуд інтерпретації.

    2. Запит на те, що вам не потрібно

    Зазначте, що ваше застосунок використовує лише:

    {
      "category": "billing"
    }
    

    Тоді не просіть:

    category
    reason
    summary
    sentiment
    customer mood
    recommended response
    next action
    

    якщо тільки ваш застосунок справді не використовує ці поля далі у своїй роботі.

    Кожне додаткове поле, про яке ви просите, — це ще одна причина, чому результат може відрізнятися або стати неконсистентним.

    3. Переміщення всієї бізнес-логіки усередину запиту

    Приклад цієї помилки:

    If the customer has been waiting more than 48 hours,
    has contacted support three times, and is a premium customer,
    set priority to high...
    

    Подібні правила зазвичай мають бути в детерміністичному коді вашого застосунку, а не в запиті.

    Більш зрозумілий розділ виглядає так:

    LLM → classify issue
    
    Application → calculate priority
    

    Такий спосіб розділення елементів зазвичай значно полегшує тестування всієї системи.

    4. Зміна запитів без оцінки

    Припустимо, версія 1 працює добре в умовах продакшену.

    Потім хтось вносить зміни:

    Classify the ticket.
    

    на:

    Analyze and intelligently classify the ticket.
    

    Це виглядає як тривіальна перефразування.

    Проте поведінка в продакшені може суттєво змінитися.

    Саме тому запити потребують контролю версій та оцінки — такої ж дисципліни, яку ви застосовуєте до коду застосунку.

    5. Припущення про те, що модель завжди буде слідувати інструкціям

    Пам’ятайте, що ШІ на основі ланцюгових моделей за своєю природою є ймовірнісними.

    Навіть ретельно складений запит може все одно повернути щось, чого ви не просили.

    Ось чому справжня система для виробництва потребує більшого, ніж просто хорошого запиту — їй потрібно:

    Prompt
    +
    Structured output
    +
    Validation
    +
    Monitoring
    +
    Fallback handling
    

    Опора лише на формулювання запиту не є безпечною стратегією.

    13. Для ефективних запитів у виробництві необхідна оцінка

    Основна різниця між випадковими експериментами з ШІ та запуском справжньої функції на основі ШІ полягає у оцінці.

    Ви можете створити з них набір для тестування у такій структурі:

    200 billing
    200 technical
    200 account
    200 general
    100 ambiguous
    100 edge cases
    

    Потім ви працюєте з різними версіями запиту на цьому ж наборі та порівнюєте результати.

    Наприклад:

    Prompt V1    Prompt V2
    ----------------------------------------
    Category accuracy    88%         93%
    Schema failures       4%          1%
    Invalid values        3%          0.5%
    Average latency      1.8s        2.1s
    Token usage          650         820
    

    На цьому етапі робота з запитами вже не є суб’єктивною.

    Ви більше не запитуєте:

    "Чи цей запит здається кращим?"

    Натомість ви запитуєте:

    «Чи досягає ця версія кращих результатів у тих випадках, які справді мають значення для нашого додатку?»

    Це набагато корисніше запитання, яке варто поставити.

    14. Компроміс: більше інструкцій проти більшої складності

    Не існує жодного загальноприйнятого правила, яке б стверджувало:

    «Чим довша інструкція, тим кращий результат.»

    Інструкції можуть стати надмірно складними для виконання.

    Щось на кшталт:

    30 rules
    +
    20 examples
    +
    multiple exceptions
    +
    long explanations
    +
    repeated instructions
    

    може перетворитися на тягар для підтримки замість допомоги.

    Корисною звичкою є постійне запитування себе:

    Чи дійсно ця інструкція вирішує реальну проблему, яку я спостерігав?

    Якщо відповідь «ні», ця інструкція, ймовірно, не повинна бути присутньою.

    Найкраща інструкція для роботи — це не та, що містить найбільше інформації.

    Це той підхід, який забезпечує правильний контекст, правильні обмеження та правильну очікувану поведінку, водночас мінімізуючи зайвий обсяг інформації.

    15. Практична ментальна модель

    Коли ви починаєте створювати запит, корисно розглянути сім керівних запитань.

    1. Хто є моделлю у цьому процесі?

    Інструкції системи

    2. Що мусить знати модель?

    Контекст

    3. Що вона ніколи не повинна робити?

    Обмеження

    4. Що саме вона мусить виконати?

    Завдання

    5. Чи можу я показати очікувану поведінку?

    Приклади

    6. Яким має бути вигляд відповіді?

    Формат виводу

    7. Як моя програма зрозуміє, що відповідь прийнятна?

    Перевірка

    Разом ці сім запитань утворюють єдину структуру.

    Ця ментальна модель коштує набагато більше, ніж запам’ятовування будь-якої окремої шаблонної формули запиту.

    16. Інженерія запитів рухається у напрямку проектування систем

    Це, можливо, найважливіша ідея в усьому цьому обговоренні.

    На початкових етапах робота з ШІ-моделлю зазвичай зводиться до такого запитання:

    How can I phrase this question better?
    

    Коли системи стають складнішими, це запитання змінюється на щось зовсім інше:

    What does the model need to know?
    What should it be allowed to do?
    What should it return?
    How do I validate it?
    What happens when it fails?
    How do I evaluate changes?
    

    Це вже не запитання щодо формулювання — це запитання інженерії програмного забезпечення.

    Саме тому інженерія запитів промислового рівня мало пов’язана з пошуком „чарівного“ речення та багато більше — з проектуванням надійної взаємодії між вашим додатком та моделлю, яка поводиться ймовірнісно.

    Ключові висновки

    Є кілька основних ідей, які варто запам’ятати з усього цього:

    1. Готовий до використання запит — це більше, ніж просто одне запитання.

    Він визначає поведінку, надає контекст, встановлює межі, описує завдання, пропонує приклади та визначає, яким має бути результат.

    2. Тримайте інструкції окремо від даних.

    Модель має мати змогу одразу зрозуміти, що від неї вимагають зробити, та яку інформацію потрібно використовувати.

    3. Чіткі обмеження зменшують кількість припущень.

    Не залишайте на розсуд моделі вирішення питань, що виникають, коли дані відсутні, неоднозначні або мають неправильну структуру.

    4. Приклади існують для ілюстрації поведінки.

    Використовуйте їх свідомо, особливо у складних крайніх випадках.

    5. Структурований результат має оброблятися як контракт API.

    Кожного разу, коли сервіс нижчого рівня читає відповідь моделі, необхідно чітко визначити, у якій саме формі має бути ця відповідь.

    6. Не дозволяйте запиту бути єдиним засобом перевірки безпеки.

    Справжня верифікація має відбуватися у вашому додатку через перевірки схеми та детерміністичні бізнес-правила.

    7. Ставіться до запитів як до коду, який потребує оцінки.

    Ведіть версіонування запитів, тестуйте їх на репрезентативних прикладах та відстежуйте їхню ефективність.

    8. Довший запит не означає автоматично кращого.

    Кожен доданий рядок має мати свою причину для наявності.

    Висновок

    Створення запитів промислового рівня — це не спроба змусити ШІ звучати розумніше.

    Це спосіб зробити взаємодію більш прогнозованою, більш прозорою та легко інтегруваною у реальну систему.

    Загалом цей перехід виглядає так:

    Simple question
          ↓
    Structured instructions
          ↓
    Clear context
          ↓
    Explicit constraints
          ↓
    Defined task
          ↓
    Useful examples
          ↓
    Structured output
          ↓
    Application validation
          ↓
    Evaluation + monitoring
    

    Як тільки цей підхід стає зрозумілим, інженерія запитів більше не здається процесом проб і помилок у формуванні тексту, а починає нагадувати створення контракту API для компонента, який діє ймовірнісно.

    Цей перехід має величезне значення, коли ви переходите від демонстрацій до створення систем, призначених для роботи в продакшені.

    Пов’язана література

    • Створення AI-агента з нуля: шаблони, ReAct та LangGraph — Дізнайтеся про основні концепції AI-агентів — планування, використання інструментів, рефлексія та шаблон ReAct — а також про те, як LangChain та LangGraph допомагають у ручному створенні такого агента.
  • LangChain проти LlamaIndex: вибір правильної платформи LLM — порівняння LangChain та LlamaIndex щодо архітектури, технології RAG, агентів та продуктивності, яке допоможе вам обрати оптимальну платформу для вашого проекту з ШІ.