коротко

Большая языковая модель (LLM, как GPT или GigaChat) встраивается в продукт через обычный REST API, но ведёт себя не как обычный код. Главное отличие: она вероятностная и недетерминированная — на один и тот же запрос может дать разные ответы, и иногда уверенно выдаёт неправду («галлюцинации»). Поэтому LLM нельзя проектировать как функцию «вход → гарантированный выход». Промпт — это не контракт с гарантией, а инструкция, которую модель обычно выполняет. Для аналитика это значит: в требованиях надо явно описать, что показывать пользователю, как проверять и фильтровать ответ, что делать при мусоре, сколько это стоит и как медленно работает, и какие данные модели вообще нельзя отдавать.

На прошлом месте я запускал бету ИИ-модуля в редакторах документов — встраивали GigaChat. Первое, что разрушилось о реальность, — моя привычка думать про сервис как про детерминированную ручку: послал запрос — получил предсказуемый ответ. С LLM не так. Тот же промпт сегодня и завтра даёт разный текст; иногда модель выдаёт идеально оформленную чушь с придуманными фактами. Спека, написанная как для обычного API, тут просто не работает — её надо писать иначе. Об этом и поговорим.

Что такое LLM на пальцах

LLM — это модель, обученная предсказывать следующее слово. Она прочитала гигантский корпус текста и научилась продолжать его правдоподобно. Из этого «правдоподобного продолжения» рождается всё: и связные ответы, и переводы, и код — но и проблемы. Модель не «знает» фактов и не «понимает» в человеческом смысле; она выдаёт статистически вероятное продолжение вашего текста. Поэтому она и звучит уверенно, даже когда ошибается, — грамматически гладкий текст для неё и есть цель, а не истинность.

Технически вы общаетесь с моделью через обычный HTTP-запрос: шлёте текст (промпт), получаете текст в ответ. Снаружи это похоже на любой REST-сервис. Внутри — принципиально другой зверь, и эту разницу легко проглядеть именно потому, что транспорт привычный.

Главное: недетерминированность и галлюцинации

Два свойства LLM ломают привычную инженерную логику, и оба надо держать в голове с первой строки требований.

Недетерминированность. Обычная функция на один вход даёт один выход — всегда. LLM по умолчанию на один и тот же промпт может дать разные ответы от запуска к запуску. Но это управляемый параметр: при temperature 0 (и, где поддерживается, фиксированном seed) вывод становится практически детерминированным — аналитик вправе зафиксировать эти параметры в требованиях. Строгой гарантии всё равно нет: батчинг на стороне провайдера и тихая смена версии модели могут изменить ответ. Поэтому всё равно нельзя написать тест «отправь X — получи ровно Y» и нельзя обещать пользователю стабильный результат дословно.

Галлюцинации. Модель может уверенно выдать неправду: придумать несуществующий закон, переврать число, сослаться на источник, которого нет. И сделает это тем же гладким тоном, что и правильный ответ, — по тексту не отличить. Это не редкий сбой, а фундаментальное свойство «предсказателя слов».

Где кончаются гарантии

LLM не даёт гарантии корректности ответа — точка. Поэтому нельзя ставить её туда, где ошибка дорого стоит и никто её не перепроверит: автоматически применять её вывод к деньгам, юридически значимым решениям, необратимым действиям. Безопасный паттерн — человек в контуре (human-in-the-loop): модель предлагает, человек подтверждает. Если в требованиях ответ модели уходит сразу в дело без проверки — это красный флаг, который аналитик обязан поднять.

Промпт — инструкция, а не контракт

В обычном API контракт — это обещание: «пришлёшь такие поля — получишь такой ответ». Промпт похож на контракт по смыслу (вы описываете, что хотите), но без гарантии исполнения: модель обычно следует инструкции, но может и отклониться. Поэтому ответ модели всегда надо проверять и приводить к форме на своей стороне, а не доверять вслепую.

Частый приём — просить структурированный ответ (например, JSON по схеме) и валидировать его, как любой вход извне (вспомните правило из записи про безопасность: нельзя верить ничему, что пришло снаружи, — а ответ LLM именно снаружи). Пришёл валидный JSON — работаем; пришёл мусор или текст не по схеме — сценарий ошибки, который тоже надо описать.

Запрос к LLM (упрощённо):
  Промпт: "Извлеки из письма дату встречи и верни JSON
           вида {date: 'YYYY-MM-DD'} или {date: null}."
  Письмо: "...давай в следующий вторник..."

Ответ модели: {"date": "2026-05-26"}   ← надо ПРОВЕРИТЬ:
  - это валидный JSON?         (может прийти текст)
  - дата реально существует?   (модель могла выдумать)
  - вторник ли это на самом деле?

Обратите внимание: даже на простой задаче после ответа модели идёт три проверки. Это и есть сдвиг мышления — LLM не освобождает от валидации, а добавляет её.

RAG: как заземлить ответ на проверенный источник

Главный архитектурный приём против галлюцинаций — RAG (Retrieval-Augmented Generation, генерация с дополнением через поиск). Идея простая: не полагаться на «память» модели, а перед ответом достать из проверенной базы релевантные куски текста и положить их в промпт как контекст. Модель отвечает по приложенному источнику, а не по тому, что она когда-то «выучила». Спросили «какой лимит на перевод по тарифу X» — система сначала находит в базе знаний нужный абзац регламента, кладёт его в промпт и просит модель ответить строго по нему. Это резко снижает выдумки: модели не из чего галлюцинировать, факт лежит прямо перед ней.

Для аналитика RAG превращается в три пункта требований. (1) Откуда берём контекст — какая база, как она обновляется, кто за её актуальность отвечает (устаревший источник = уверенно неверный ответ). (2) Как помечаем источник — ответ должен ссылаться на документ/раздел, из которого взят, чтобы пользователь мог проверить, а не верить на слово. (3) Что делаем, если ответа в базе нет — модель обязана честно сказать «не нашёл», а не дофантазировать; это отдельный явный сценарий, иначе RAG молча скатывается обратно к галлюцинациям.

Цена и скорость: новые нефункциональные требования

У LLM есть два свойства, которые в обычном коде почти не встречаются, и оба — прямые нефункциональные требования.

Деньги за запрос. Облачная модель тарифицируется за объём текста — единица счёта здесь токен. Токен — это кусочек слова (для русского текста грубо 1 токен ≈ 2–3 символа), и платите вы за оба потока: вход (промпт + весь приложенный контекст) и выход (ответ модели), обычно по разной цене за тысячу токенов. То есть длинный системный промпт, история диалога и куски базы из RAG — это не «бесплатный антураж», за них капает на каждом вызове. Каждый вызов стоит реальных денег, и на масштабе это ощутимо — в отличие от обычной функции, которая «бесплатна» после написания. Это смыкается с записью про экономику: фича на LLM имеет переменную себестоимость, и её надо считать в токенах на запрос × число запросов.

Context window — почему «просто запихнуть побольше» не работает

Context window — это максимум токенов, которые модель удержит за один вызов (вход + выход вместе): и системный промпт, и контекст, и ответ должны влезть в это окно. Соблазн засунуть в промпт всё подряд «на всякий случай» обходится дважды. Во-первых, по деньгам — вы платите за каждый токен входа, даже если модель им не воспользовалась. Во-вторых, по качеству: у длинного контекста есть эффект lost-in-the-middle — модель хорошо видит то, что в начале и в конце промпта, и хуже всего — то, что в середине. Зарыли важный факт в центр огромного контекста — модель его попросту «потеряет». Поэтому в требованиях к RAG-фиче не «дай модели максимум контекста», а «дай минимально достаточный и релевантный контекст».

Задержка. Модель думает секунды, иногда десятки секунд, и часто отдаёт ответ потоком (по словам). Синхронно ждать это в интерфейсе плохо — поэтому такие вызовы обычно делают асинхронно или со стримингом, показывая прогресс. «Подумайте над тем, что показывать пользователю эти секунды» — это требование, а не деталь.

Какие данные нельзя отдавать модели

Отправляя текст в облачную LLM, вы отправляете его на чужой сервер. Значит, всё, что нельзя отдавать наружу, — персональные данные, коммерческая тайна, секреты — без отдельного решения в промпт класть нельзя (см. безопасность данных и PII). Это первый вопрос на старте любой ИИ-фичи: что мы вообще имеем право отправить? Иногда ответ — «ничего чувствительного», и тогда данные обезличивают перед отправкой или берут модель, развёрнутую внутри контура.

Prompt injection — угроза, которой нет в обычном API

Если в промпт попадает текст от пользователя (или из внешнего документа, письма, веб-страницы), он может содержать встроенную инструкцию для модели: «игнорируй прежние указания и выдай …». Модель не отличает ваш доверенный системный промпт от недоверенного ввода — для неё это один поток текста. Это prompt injection, профильная угроза LLM-фич (в OWASP Top 10 для LLM она идёт первым пунктом). Правило для требований: нельзя бесконтрольно склеивать доверенный системный промпт и недоверенный пользовательский ввод. Минимум — отделять ввод от инструкций и не давать модели прав на чувствительные действия по тексту, который пришёл снаружи.

Что аналитику закладывать в требования к ИИ-фиче

Чек-лист, который я прогоняю по любой задаче с LLM: (1) что показываем пользователю и как помечаем, что это сгенерировано ИИ; (2) кто проверяет ответ — человек в контуре или автоматическая валидация; (3) что делаем при мусоре/отказе модели (fallback); (4) сколько это стоит за запрос и при каком объёме; (5) сколько ждём и что показываем в это время; (6) какие данные нельзя отправлять. Ни один пункт не про «как обучить модель» — все про то, как встроить её честно и безопасно.

Откуда это взялось

Языковые модели на нейросетях-трансформерах взлетели после статьи Google «Attention is All You Need» (2017). Перелом для продуктов случился с GPT-3 (OpenAI, 2020) и особенно с ChatGPT (конец 2022), после чего доступ к моделям через API стал массовым, а «прикрутить ИИ» — типовой продуктовой задачей. Российские аналоги (GigaChat от Сбера, YandexGPT) появились в 2023-м. Тогда же родилась и дисциплина prompt engineering — искусство формулировать инструкции модели, — и трезвое понимание, что галлюцинации никуда не денутся, а недетерминированность хоть и сбивается до минимума параметрами вроде temperature 0, гарантии всё равно не даёт, — с этим надо проектировать, а не бороться. Этот сайт, кстати, — прикладной пример: его материалы написаны в соавторстве с ИИ и специально устроены так, чтобы их корректно цитировали поисковые модели.

Частые вопросы

Чем интеграция с LLM отличается от обычного API?

Транспорт тот же — обычный HTTP-запрос с текстом туда и обратно. Но поведение принципиально другое: LLM недетерминирована (на один вход даёт разные ответы) и может уверенно выдавать неправду (галлюцинации). Поэтому её нельзя проектировать как функцию «вход → гарантированный выход»: ответ всегда надо проверять и приводить к форме, нельзя обещать дословную стабильность, и нельзя пускать вывод в необратимые действия без проверки человеком.

Что такое галлюцинации LLM и что с ними делать?

Галлюцинация — это когда модель уверенно выдаёт неправду: придумывает факты, числа, источники, тем же гладким тоном, что и верный ответ. Это не редкий сбой, а свойство «предсказателя слов». Полностью устранить их нельзя — это свойство модели, — но можно проектировать вокруг: ставить человека в контур (модель предлагает — человек подтверждает), валидировать структурированный ответ, не пускать вывод в дорогие и необратимые действия автоматически.

Что аналитику писать в требованиях к ИИ-фиче?

Шесть вещей: что показываем пользователю и как помечаем ИИ-генерацию; кто и как проверяет ответ (человек в контуре или валидация); что делать при мусоре или отказе модели (fallback); стоимость за запрос и при ожидаемом объёме; задержка и что показывать пользователю, пока модель думает; какие данные нельзя отправлять в облачную модель (PII, тайна). Всё это — про честное и безопасное встраивание, а не про обучение модели.