$ sudo teach|
    $ sudo teach|
    IT school
  • Telegram
  • Партнёрам
  • Все курсы
$ sudo teach IT
OOO "SALEPROFIT"Контакты и реквизитыIT-Park Logo

Школа

  • Блог
  • Проверить сертификат

Сотрудничество

  • Стать учителем
  • Партнёрская программа
  • О проекте

Право

  • Оферта
  • Политика конфиденциальности

© 2023–2026 $ sudo teach IT™. All Rights Reserved. Public user contributions licensed under CC BY-SA 4.0 license with attribution required
TelegramGitHubYouTube
ГлавнаяБлогLLM-проект без иллюзий: как тестировать промпты и снижать галлюцинации

LLM-проект без иллюзий: как тестировать промпты и снижать галлюцинации

$ sudo teach IT
·23 июля 2026 г.·9 мин·48
LLM-проект без иллюзий: как тестировать промпты и снижать галлюцинации

Покажем практичный подход к качеству: наборы примеров, метрики, автоматические проверки и итерации промптов под реальные кейсы.

Содержание
Почему «галлюцинации» — это не только про генерациюГде именно ломается качествоСтратегия тестирования: от набора примеров до регрессии промптовУровень 1: эталонные пары «вход → ожидаемое поведение»Уровень 2: метрики, которые коррелируют с продуктомУровень 3: автоматический регрессионный прогон промптовПрактический дизайн тестовых прогонов: наборы, контроль случайности, воспроизводимостьФиксируйте параметры генерацииХраните артефактыАвтоматические проверки: от парсинга до «не выдумывай»1) Валидность формата: быстро и строго2) Проверка «только по контексту»3) Использование второй модели как «судьи»: осторожноИтерации промптов: как улучшать без магииТипичные причины, почему промпт не лечит галлюцинацииЧто добавлять в промпт для снижения галлюцинацийМетрики галлюцинаций: как превратить «кажется неправдой» в цифрыДля задач с контекстом (RAG)Для задач без контекста (чистая генерация)Конкретный пайплайн: как организовать тесты в проектеШаг 1: тест-кейсы в YAML/JSONШаг 2: исполнитель тестовШаг 3: валидаторы и скорерыШаг 4: отчёт и регрессияIteration loop: как правильно «крутить» промпт по данным тестовПодводный камень: «улучшили — но сломали другое»

LLM встраивают в продукты чаще, чем готовы честно тестировать. Итог — «работает на демо», но в реальности ответы уходят в сторону: появляются правдоподобные ошибки, несогласованность с контекстом, неверные формулы, галлюцинации источников и уверенная чушь в момент, когда от модели ждут точности.

Хорошая новость: качество LLM-поведения можно системно поднимать. Плохая — магической кнопки не существует. Нужна инженерная дисциплина: тестовые наборы, метрики, автоматические проверки, воспроизводимость и итерации промптов под реальные пользовательские сценарии.

Ниже — практичный подход к тестированию промптов и снижению галлюцинаций, ориентированный на команды, которые строят LLM-функциональность «как софт», а не как эксперимент.


Почему «галлюцинации» — это не только про генерацию

Слово «галлюцинации» часто используют как универсальный термин. В инженерной практике полезно разделять причины:

Где именно ломается качество

  1. Неправильное понимание запроса
    Модель может неверно интерпретировать намерение: что нужно, в какой форме, с какими ограничениями.
  2. Пробелы в контексте
    Модель не видит нужные факты (из-за короткого контекста, отсутствия документов, неправильной сборки RAG).
  3. Смешение источников
    Модель «подмешивает» знания из предобучения, хотя ожидается ответ строго по предоставленным данным.
  4. Ошибки форматирования
    Даже если содержание близко, API может требовать JSON/таблицу/валидный код — и модель ломает схему.
  5. Нарушение инструкций
    Промпт может содержать противоречивые требования; модель «выбирает» то, что кажется естественным.
  6. Температура и стохастичность
    При высоких параметрах генерация становится вариативной, что усложняет стабильное тестирование.
  7. Недостаточная проверяемость ответа
    Команда иногда не определяет, что именно считать «правильным»: есть ли шкала уверенности, допустимы ли округления, обязателен ли цитатный стиль.

Чтобы снижать галлюцинации, нужно тестировать не «вообще качество», а конкретные классы ошибок.


Стратегия тестирования: от набора примеров до регрессии промптов

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

Уровень 1: эталонные пары «вход → ожидаемое поведение»

Начните с коллекции промпт-кейсов, максимально близких к реальным запросам пользователей.

Как собирать наборы примеров

  • Базовые happy-path сценарии: запросы, которые модель должна выполнять «с первого раза».
  • Неполные входные данные: отсутствие важных полей, короткие описания.
  • Сложные ограничения: форматы, лимиты, требования к стилю и структуре.
  • Оспаривающие запросы: пользователь просит «точно со ссылками», «без догадок», «используй только данные из таблицы».
  • Нестандартные форматы: разные языки, пунктуация, шумные тексты, смешанный регистр.
  • Атакующие/провокационные: prompt injection (например: «игнорируй инструкции выше»), запрос на утечку системных инструкций, попытка заставить модель выдумать источник.

Важно: набор должен быть не «сколько угодно», а под задачу. Даже 50–150 тщательно размеченных примеров часто дают больше пользы, чем 1000 случайных.

Разметка должна быть инженерной

Для каждого кейса определите:

  • ожидаемый формат ответа (например, JSON-валидность, наличие полей)
  • критичные факты, которые нельзя придумывать
  • допуски (можно ли округлять, допустима ли краткость)
  • правила отказа (когда модель должна сказать «не могу ответить»)

Уровень 2: метрики, которые коррелируют с продуктом

Если вы не измеряете, вы не управляете. Для LLM метрики лучше строить не только «на вкус оценщика», а через проверяемые свойства.

Типовые метрики качества

  1. Exact / F1 для извлечений
    Если модель извлекает сущности (например, даты, суммы, имена), используйте точность/полноту или хотя бы сравнение структур.
  2. Schema validity (валидность структуры)
    Для JSON/HTML/XML — проверка парсинга + обязательные поля.
  3. Factuality checks (фактологическая проверка)
    Если ответ должен опираться на предоставленный контекст (документы, таблицы), проверяйте совпадение ключевых утверждений с источником.
  4. Attribution / citation compliance (ссылки/цитаты)
    Требуете ли вы, чтобы цитаты присутствовали? Тогда проверка по шаблону и покрытию: упоминаются ли документы, совпадает ли хотя бы часть фраз.
  5. Refusal correctness (корректный отказ)
    В некоторых задачах правильное поведение — не отвечать. Тогда оценивайте долю корректных отказов и долю ложных отказов.
  6. Consistency (внутренняя согласованность)
    Например, если модель называет дату в одном месте, а в другом — другая дата, это ошибка.
  7. Robustness (устойчивость к вариациям)
    Один и тот же смысл, но с разными формулировками. Модель должна давать предсказуемый тип ответа.

Как превратить текст в проверяемые метрики

Используйте автоматические проверки:

  • парсинг JSON,
  • регулярные выражения для формата,
  • сравнение сущностей,
  • вызовы ваших валидаторов,
  • и — аккуратно — «вторую модель как судью» только там, где иначе нельзя.

Последнее требует дисциплины (см. ниже).


Уровень 3: автоматический регрессионный прогон промптов

Промпты меняются: добавили пример — качество «на одном кейсе» выросло, а на другом просело. Поэтому нужен регрессионный контур:

  1. фиксируете версию промпта
  2. прогоняете весь набор
  3. сравниваете метрики с предыдущей версией
  4. публикуете отчёт: какие типы кейсов сломались

Это превращает «LLM engineering» в нормальную инженерную практику.


Практический дизайн тестовых прогонов: наборы, контроль случайности, воспроизводимость

Фиксируйте параметры генерации

Чтобы регрессии были сравнимыми:

  • используйте фиксированный seed (если поддерживается),
  • держите одинаковые параметры (temperature, top_p),
  • минимизируйте вариативность без ущерба качеству.

Если вы сознательно используете стохастичность (например, для креативного контента), тогда тестируйте распределение: прогоните несколько семплов и оценивайте долю успешных.

Храните артефакты

Для каждой версии промпта сохраняйте:

  • промпт (полный текст + системные инструкции, шаблоны),
  • входные данные (raw),
  • модель/версию API,
  • параметры генерации,
  • ответ (raw),
  • метрики и результаты проверок.

Без этого невозможно разбирать инциденты.


Автоматические проверки: от парсинга до «не выдумывай»

1) Валидность формата: быстро и строго

Если продукт требует структурированный ответ, проверяйте строго. Например, модель должна вернуть JSON:

code
import json

def validate_json_response(text: str) -> dict:
    try:
        data = json.loads(text)
    except json.JSONDecodeError as e:
        raise ValueError(f"Response is not valid JSON: {e}")

    # Пример схемы
    required = ["answer", "confidence", "sources"]
    for k in required:
        if k not in data:
            raise ValueError(f"Missing key: {k}")

    return data

Эта проверка часто ловит «галлюцинации формата», когда модель отвечает словами, вместо нужной структуры.

2) Проверка «только по контексту»

Если вы используете RAG и хотите исключить выдумки, нужен минимум механики:

  • вы храните документы/фрагменты,
  • ожидаете, что ответ опирается на них,
  • проверяете наличие опорных фактов.

Простой вариант — проверять совпадение ключевых утверждений по n-граммам или по «подстрочным» совпадениям, но лучше — через более интеллектуальную проверку.

Пример: извлечь утверждения и проверить покрытие

Пусть ваш пайплайн:

  • разбивает ответ на утверждения,
  • для каждого утверждения проверяет, что оно встречается (с допусками) в одном из контекстных фрагментов.

Условный пример «по токенам» (упрощённо):

code
import re

def statements(text: str) -> list[str]:
    # очень грубая сегментация: для демонстрации
    parts = re.split(r'[.?!]\s+', text.strip())
    return [p for p in parts if p and len(p) > 20]

def normalize(s: str) -> str:
    s = s.lower()
    s = re.sub(r'\s+', ' ', s)
    return s

def overlap_score(stmt: str, doc: str) -> float:
    stmt_tokens = set(normalize(stmt).split())
    doc_tokens = set(normalize(doc).split())
    if not stmt_tokens:
        return 0.0
    return len(stmt_tokens & doc_tokens) / len(stmt_tokens)

def factual_coverage(answer: str, docs: list[str], threshold: float = 0.35) -> float:
    stmts = statements(answer)
    if not stmts:
        return 0.0
    ok = 0
    for st in stmts:
        best = max(overlap_score(st, d) for d in docs)
        if best >= threshold:
            ok += 1
    return ok / len(stmts)

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

3) Использование второй модели как «судьи»: осторожно

Если вы привлекаете LLM для оценки (например, чтобы определить «соответствует ли утверждение контексту»), важно:

  • давать судье тот же контекст,
  • явно просить вердикт по правилам,
  • требовать обоснование ссылками на фрагменты,
  • ограничить судью шаблоном ответа (валидный JSON).

Иначе судья начнёт оценивать «кажется правдоподобным» вместо «подтверждается источником».

Пример промпта для судьи (концептуально):

  • вход: {question, answer, context_docs}
  • output: {verdict: "supported"|"unsupported"|"unknown", evidence: [...]}

Проверяйте валидность JSON так же строго, как основной ответ.


Итерации промптов: как улучшать без магии

Промпт — это не «текст для вдохновения». Это спецификация поведения. Улучшения должны быть подтверждены прогоном набора.

Типичные причины, почему промпт не лечит галлюцинации

  1. Неполная инструкция про отказ
    Если вы не описали, когда модель должна сказать «не знаю», она будет «заполнять пробел».
  2. Слишком мягкий тон требований
    «Старайся опираться» ≠ «ответ должен быть подтверждён контекстом».
  3. Конфликт требований
    «Будь кратким» и «обязательно перечисли все источники» — конфликт.
  4. Отсутствие примеров для сложных кейсов
    Модель отлично повторяет паттерны из few-shot. Не хватает примеров отказа и ошибок — не будет и корректного поведения.
  5. Неучёт форматных ограничений
    Если формат важен, добавляйте структуру и примеры валидных ответов.

Что добавлять в промпт для снижения галлюцинаций

1) Явный policy: «не подтверждено — не утверждай»

Добавьте правило уровня:

  • если ответ нельзя подтвердить контекстом → вернуть отказ или указать «не найдено».

Пример (фрагмент системной политики, адаптируйте под свою задачу):

Если в предоставленном контексте нет данных, необходимых для ответа, верни {"answer": null, "reason": "NOT_FOUND_IN_CONTEXT"}.

2) Тонкий формат: требуйте структурированный результат

Например, если вы хотите уменьшить выдумки, добавьте поле sources или evidence и требуйте, чтобы оно было непустым для утверждений.

3) Few-shot по вашим реальным кейсам

Не берите «универсальные» примеры. Берите ваши типовые ошибки:

  • «документы есть, но модель не цитирует»
  • «модель придумывает цифры»
  • «модель не соблюдает JSON»

И покажите контрпример: как надо при отсутствии данных.

4) Ограничение на «додумывание»

Полезная формулировка:

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

Да, модель всё равно может нарушать — но вероятность падает, а автоматическая проверка ловит нарушения.


Метрики галлюцинаций: как превратить «кажется неправдой» в цифры

Чтобы не скатываться в субъективность, договоритесь о целевых метриках:

Для задач с контекстом (RAG)

  • Support rate: доля ответов, где ключевые утверждения подтверждаются контекстом.
  • Evidence completeness: покрытие evidence/sources для каждого утверждения.
  • Refusal accuracy: доля корректных отказов в сценариях, где данные отсутствуют.

Для задач без контекста (чистая генерация)

  • Groundedness невозможна, но можно мерить:
    • format compliance,
    • self-consistency (например, двойная генерация с последующим сравнением),
    • numeric reliability (проверка формул/вычислений внешними инструментами),
    • tool-use correctness (если модель должна вызывать функции).

Главное: измеряйте то, что реально важно вашему продукту.


Конкретный пайплайн: как организовать тесты в проекте

Ниже — пример минимально жизнеспособной архитектуры для LLM-проверок.

Шаг 1: тест-кейсы в YAML/JSON

Структура кейса:

code
- id: "invoice_total_missing"
  input:
    user_text: "Сумма в счете?"
    context_docs: []   # нет документов
  expected:
    format: "json"
    answer_is_null: true
    reason: "NOT_FOUND_IN_CONTEXT"

Шаг 2: исполнитель тестов

Он прогоняет модель и сохраняет результат.

code
from dataclasses import dataclass

@dataclass
class TestCase:
    id: str
    user_text: str
    context_docs: list[str]
    expected: dict

def run_llm(prompt: str, model_client) -> str:
    # заменить на ваш реальный вызов
    return model_client.complete(prompt)

def run_case(case: TestCase, model_client, build_prompt) -> dict:
    prompt = build_prompt(case.user_text, case.context_docs)
    raw = run_llm(prompt, model_client)
    return {"case_id": case.id, "raw_response": raw}

Шаг 3: валидаторы и скореры

code
import json

def score_not_found_in_context(raw: str, expected: dict) -> float:
    data = json.loads(raw)
    if data.get("answer") is None and data.get("reason") == expected["reason"]:
        return 1.0
    return 0.0

Шаг 4: отчёт и регрессия

Сравнивайте:

  • average score,
  • долю валидных ответов,
  • долю поддержанных утверждений,
  • и список кейсов, где стало хуже.

Это превращает улучшение промпта в управляемый процесс.


Iteration loop: как правильно «крутить» промпт по данным тестов

Заведите цикл:

  1. Вы фиксируете текущую версию промпта и запускаете тесты (baseline).
  2. Анализируете «слабые места»:
    • какие кейсы падают,
    • какой тип ошибки (формат / факт / отказ / согласованность).
  3. Вносите точечное изменение:
    • добавляете правило отказа,
    • корректируете схему ответа,
    • добавляете few-shot на конкретный класс случаев.
  4. Запускаете полный регресс.
  5. Принимаете изменения, только если:
    • не стало хуже по важным метрикам,
    • улучшение затрагивает целевой класс ошибок.

Подводный камень: «улучшили — но сломали другое»

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

Войдите, чтобы поставить лайк и оставить комментарий.

Автор

$ sudo teach IT

Продолжите обучение

Все курсы
Python – для начинающих!

Python – для начинающих!

С нуля до профессионального уровня. Подходит для всех. Учитесь каждый день и овладейте самым популярным языком программирования.

Перейти к курсу
Приложения для iPhone и Apple Watch на SwiftUI

Приложения для iPhone и Apple Watch на SwiftUI

Разработка приложений для iPhone и Apple Watch на SwiftUI: навигация, SwiftData, виджеты, часы, выпуск. Нужен Mac с Xcode 27, сами устройства не нужны.

Перейти к курсу
Ботостроение Telegram

Ботостроение Telegram

Лёгкий, быстрый и доступный способ познакомиться с миром ботостроения в Telegram. Видео, конспекты, практика и помощь – всё у нас на курсе.

Перейти к курсу
Приложения для macOS на SwiftUI

Приложения для macOS на SwiftUI

Разработка приложений для Mac на SwiftUI: окна и меню, Liquid Glass, SwiftData, сеть, выпуск. Нужен Mac с macOS 27 и Xcode 27.

Перейти к курсу

Другие статьи

Запуск и поддержка проекта на Ruff в команде: формат “как в CI”, а не “как договорились”
запуск

Запуск и поддержка проекта на Ruff в команде: формат “как в CI”, а не “как договорились”

Как правильно выстроить конфигурацию Ruff (линтинг, форматирование, автозамены) и сделать так, чтобы одинаковые правила гарантировались в локальной сборке и на CI без ручного шаманства.

16 сентября 2026 г.
1050
Модели данных и типизация в TypeScript: как проектировать контракт между фронтом и API
модели

Модели данных и типизация в TypeScript: как проектировать контракт между фронтом и API

Разберём, как описывать доменные сущности, DTO и error-model на уровне типов, чтобы ловить несовпадения схем до продакшена. Покажем практические шаблоны для типобезопасного маппинга и версионирования контрактов.

15 сентября 2026 г.
1040
Создание Telegram бота в 2026 легко и просто! Полный курсы!
создание

Создание Telegram бота в 2026 легко и просто! Полный курсы!

19 июня 2026 г.
1421
Как выбрать, куда идти в IT: веб, боты, данные — без хаоса
выбрать

Как выбрать, куда идти в IT: веб, боты, данные — без хаоса

Разберём самые понятные направления для новичка (веб, боты, аналитика/данные) и чем они отличаются по шагам обучения. В конце вы получите простой план, как проверить, что вам подходит, и какой стартовый путь выбрать.

5 октября 2026 г.
580
Путь от README до демо: как собрать портфолио разработчика, чтобы его хотели смотреть
путь

Путь от README до демо: как собрать портфолио разработчика, чтобы его хотели смотреть

Разберём структуру репозитория, как упаковать проект в понятную историю (проблема → решение → запуск → метрики качества) и какие артефакты повышают доверие: скринкасты, архитектурная схема, чек-лист продакшн-готовности.

17 сентября 2026 г.
1070
Как понять, какой язык программирования учить первым: простой тест и выбор
понять

Как понять, какой язык программирования учить первым: простой тест и выбор

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

2 октября 2026 г.
630

Комментарии

Станьте первым в обсуждении

Поделитесь мнением о статье или задайте вопрос автору

Содержание

Почему «галлюцинации» — это не только про генерациюГде именно ломается качествоСтратегия тестирования: от набора примеров до регрессии промптовУровень 1: эталонные пары «вход → ожидаемое поведение»Уровень 2: метрики, которые коррелируют с продуктомУровень 3: автоматический регрессионный прогон промптовПрактический дизайн тестовых прогонов: наборы, контроль случайности, воспроизводимостьФиксируйте параметры генерацииХраните артефактыАвтоматические проверки: от парсинга до «не выдумывай»1) Валидность формата: быстро и строго2) Проверка «только по контексту»3) Использование второй модели как «судьи»: осторожноИтерации промптов: как улучшать без магииТипичные причины, почему промпт не лечит галлюцинацииЧто добавлять в промпт для снижения галлюцинацийМетрики галлюцинаций: как превратить «кажется неправдой» в цифрыДля задач с контекстом (RAG)Для задач без контекста (чистая генерация)Конкретный пайплайн: как организовать тесты в проектеШаг 1: тест-кейсы в YAML/JSONШаг 2: исполнитель тестовШаг 3: валидаторы и скорерыШаг 4: отчёт и регрессияIteration loop: как правильно «крутить» промпт по данным тестовПодводный камень: «улучшили — но сломали другое»