Плохой ответ модели: ошибки inference-систем, а не модели | AiManual
AiManual Logo Ai / Manual.
14 Май 2026 Гайд

Почему плохой ответ модели — это не проблема модели: разбор типичных ошибок в inference-системах

Разбираем скрытые причины плохих ответов LLM: проблемы retrieval, контекста и маршрутизации. Диагностика, примеры и пошаговый план для инженеров RAG и fine-tuni

Почему ваш ИИ — идиот, хотя модель на бумаге гениальна?

Каждый инженер, кто хоть раз выкатывал RAG-систему в продакшен, знает этот момент: ты даешь модели идеальный промпт, грузишь базу знаний, запускаешь — а она выдает чушь. Ты бросаешься дообучать модель, менять гиперпараметры, но проблема не в ней. Она в том, что инференс-система сломана.

Я видел десятки команд, которые тратили недели на fine-tuning, хотя достаточно было заменить один парсер или переписать чанк-стратегию. Модель не тупая — она просто скормлена мусором. Или ей не дали достаточно контекста. Или маршрутизатор повел запрос не в тот эндпоинт.

Если вы активно гуглите "апдейт весов" или "cтохастический градиент", когда ваш чат-бот не может найти номер договора в базе — остановитесь. С вероятностью 90% виноват не бэкпроп.

Давайте разберем три слоя, где реально ломается качество ответа. Без абстракций, с живыми кейсами и диагностическими скриптами.

Слой 1: Ретривер подсел на наркотики (retrieval)

Самая частая история. Вы пихаете в векторную базу данные, но чанки нарезаны через одно место. Модель получает нерелевантные куски текста и, разумеется, синтезирует из них бред. Проблема: ретривер нашел шум, а не сигнал.

Типичный случай: юридический RAG

Вы ищете пункт договора о штрафных санкциях. Chunk size — 1024 токена, overlap — 200. Ретривер возвращает 5 чанков, но первый — оглавление документа, второй — реквизиты сторон, третий — общие положения. Модель видит кучу текста, но не находит конкретную цифру. В ответе: «В договоре могут быть предусмотрены штрафы» — обобщение, которое бесполезно.

Как это диагностировать? Простой скрипт: повторите запрос 10 раз, достаньте сырые чанки до передачи в LLM. Если в чанках нет ответа — собака зарыта в ретривере.

import openai, chromadb

# Для каждого запроса логируем retrieved chunks
def diagnose_retrieval(query: str, collection):
    results = collection.query(query_texts=[query], n_results=5)
    for i, chunk in enumerate(results['documents'][0]):
        print(f'Chunk {i}: {chunk[:200]}...')
        # Проверяем, содержит ли чанк прямые ключевые слова из ответа
        if 'штраф' not in chunk:
            print('  ❌ Missing target keyword')

Решение: пересмотреть стратегию чанкинга. Не режьте документы тупо по токенам. Разбивайте по смысловым блокам: заголовки, параграфы, пункты. Добавьте метаданные (тип раздела, номер страницы) и используйте гибридный поиск (семантический + keyword). Попробуйте small-to-big: сначала ищете короткие чанки (128 токенов), потом подтягиваете родительские куски большего размера.

Здесь отлично заходит статья про “Модель на 99% в тестах, на 0% в продакшене: как время и данные ломают ML-системы” — именно там описан тот самый разрыв между метриками на бенчмарках и реальностью.

Слой 2: Контекстное окно — не резиновое (context stuffing)

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

Исследования 2025-26 показали: модель использует эффективно только первые 10-15% своего контекстного окна. Остальное — dead zone. Если вы засовываете 128К токенов, а нужная информация лежит на 100К токене — модель ее не увидит.

Типичный сценарий: чат-бот техподдержки. В контексте — 20 страниц инструкций, 50 предыдущих диалогов. Пользователь задает простой вопрос: «Как сменить тариф?». Модель отвечает: «Смена тарифа недоступна в вашем регионе», хотя на самом деле доступна на странице 3. Почему? Потому что в ближайших к вопросу 5-7 чанках лежала информация об ограничениях, а не о смене тарифа.

Диагностика: логируйте позицию релевантного документа в контексте. Если он дальше 70% окна — виновник найден.

# Пример логирования позиции релевантного чанка
position = relevant_chunk_offset // total_tokens
if position > 0.7:
    print('⚠️ Relevant info in dead zone')

Решение:

  • Ранжируйте чанки по релевантности — самые важные ставьте в начало (первые 15% контекста).
  • Уменьшайте количество чанков. Лучше 2-3 идеальных, чем 5+ сомнительных. Поставьте threshold на similarity score.
  • Используйте sliding window или map-reduce: сначала ретривите много чанков, потом с помощью LLM-фильтра выбираете релевантные.

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

Слой 3: Маршрутизатор — как слепой котенок (routing)

У вас несколько специализированных моделей или промптов под разные задачи. Вы пишете router — кусок логики, который решает, куда отправить запрос. И этот кусок глючит. Пользователь спрашивает «Как мне оплатить счет?», а роутер отправляет вопрос в агента по смене пароля. Результат — бессмысленный ответ.

Почему это происходит? Потому что роутер часто тоже LLM-прокладка (одним промптом решаем, какой навык активировать). А LLM не всегда предсказуемо классифицирует запросы, особенно если промпт роутера плохо задан или тестовых примеров мало.

Как это выглядит на практике

У вас есть три сценария: tech support, sales, contract analysis. Пользователь пишет: «Какие штрафы за просрочку платежа?». Классификатор решает, что это продажи (потому что слово «платеж» ассоциируется с оплатой нового заказа). Ответ: «Наши тарифы на подключение премиум-аккаунта...». Полное фиаско.

Диагностика: логируйте decision от роутера и сверяйте его с ground truth label.

# Пример диагностики роутера
router_decision = classify_user_input(user_query)
expected = ground_truth.get(user_query)
if router_decision != expected:
    print(f'Router error: got {router_decision}, expected {expected}')
    # Отправляем в лог для ретроспективного анализа

Решение:

  • Используйте few-shot промпты с примерами для каждого класса.
  • Добавьте фоллбек-рутер: если уверенность низкая — отправляйте запрос всем агентам и используйте ансамбль или голосование.
  • Собирайте датасет плохо классифицированных запросов и на нем дообучайте модель-рутер. Но помните — это уже fine-tuning, который может не понадобиться, если настроить правила.

Тема контрактного анализа раскрыта в статье “LLM-галлюцинации: Как заставить нейросеть говорить правду (или хотя бы не врать так очевидно)” — там есть конкретные шаблоны промптов для юридических сценариев.

Слой 4: Формат ответа — тихий убийца (contract analysis через призму вывода)

Вы просите модель вернуть JSON, но она возвращает Markdown с полями. Или просите дать ответ эксперта, а она пишет от лица пользователя. Это не модель плохая — это вы плохо описали ожидаемый формат в промпте. Или вообще не указали.

Симптомы: парсер падает, ответы неструктурированы, пользователь не может скопировать данные.

Лечится строгим форматированием вывода. Используйте function calling (tool use) или JSON mode. Задавайте template в системном сообщении.

# Пример форсированного JSON через OpenAI API v2
response = client.chat.completions.create(
    model='gpt-5-2026-05-01',
    response_format={ 'type': 'json_object' },
    messages=[
        {'role': 'system', 'content': 'Отвечай только JSON с ключами: result, confidence, reasoning.'},
        {'role': 'user', 'content': 'Штраф за просрочку 5% от суммы'}
    ]
)

Но есть нюанс: не все провайдеры (например, GLM 4.5 Air) стабильно поддерживают strict JSON mode. Тут потребуется либо постобработка, либо fallback на regex при разборе. Подробнее о том, как оптимизировать режим вывода у GLM, читайте в “GLM 4.5 Air в режиме тупняка: как выжать максимум скорости с enable_thinking: false”.

Слой 5: Динамическое поведение — модель сегодня не та, что вчера

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

OpenAI, Anthropic и другие поставщики меняют поведение моделей без анонса. GPT-4o могло чуть иначе отвечать в понедельник и пятницу. GPT-5, судя по статье про фундаментальную ошибку OpenAI, Google и Anthropic, тоже этим страдает. Решение: фиксировать версию модели, юзать прокси-слой логирования и привязывать к версии промпта. Используйте семантическую нумерацию для системных сообщений.

# config.yaml
inference:
  model: gpt-5-2026-05-01
  system_prompt_version: v2.3.1
  temperature: 0.0

План действий: как найти корень зла за 1 час

1 Изолируйте модель: замените на статичный промпт-ответ

Возьмите 10 тестовых запросов. Закрепите температуру 0, отключите retrieval, захардкодьте ответы в формате, который вы хотите. Если модель на этих фиксах выдает корректные ответы — косяк не в весах, а в окружении.

2 Включите логирование каждого звена пайплайна

Логируйте чанки до LLM, содержимое контекстного окна, решение роутера, позицию релевантных кусков. Сравнивайте с эталоном.

3 Прогоните батарею синтетических тестов с известными ответами

Сгенерируйте с помощью сильной модели (GPT-5, Claude 4) вопросы, на которые в вашей базе есть точные ответы. Прогоните пайплайн и проверьте, доходят ли эти ответы до финала. Это выявит дыры в ретривере и контекстном окне.

💡
Золотое правило: не делайте fine-tuning, пока не докажете, что проблема именно в модели. Сначала почините пайплайн — и с вероятностью 80% закрытие задачи без переобучения.

Неочевидный совет напоследок

Я знаю, вас сейчас раздражает фраза «надо больше тестировать». Но есть один трюк, который выносит мозг: запускайте в продакшене две параллельных inference-системы с разными настройками ретривера (разный chunk size, разный threshold similarity) и сравнивайте их ответы в A/B-режиме. Вы увидите, как чувствителен результат к параметрам, которые вы никогда не трогали. Это быстрее и дешевле, чем очередной fine-tuning.

И запомните: если после всех правок модель продолжает нести чушь — значит проблема именно в ней. И тут поможет статья “Почему open-source модели проваливаются в бою, пока лидируют в гонках” — спойлер: цифры на бенчмарках не гарантируют успеха в реальных данных.

Подписаться на канал