Галлюцинации LLM в AI-RPG: гибридный Guard из 3 слоев с кодом | AiManual
AiManual Logo Ai / Manual.
01 Июн 2026 Гайд

Как победить галлюцинации LLM в AI-RPG: гибридный Guard с Embedding, микро-LLM и State Validator

Пошаговое руководство по гибридному Guard для борьбы с галлюцинациями в AI-играх: Embedding Classifier, микро-LLM Extractor и State Validator. Решает проклятие

Яд проклятия Yes-And

Каждый, кто пытался сделать AI-игру, знает этот момент. Игрок пишет «Я открываю сундук», а LLM отвечает: «В сундуке лежит мифриловый меч +5 и письмо от короля». Красиво, эпично, черт возьми, но в сундуке по сценарию должна быть только ржавая ложка. LLM просто придумала меч, потому что это круто звучит. Это и есть проклятие Yes-And — модель не может удержаться от развития истории, даже если это ломает жесткую логику игры.

Проблема не в том, что LLM «глупая». Проблема в том, что она генеративная. Ее задача — предсказывать следующий токен, а не следовать рельсам гейм-дизайна. В результате AI-RPG превращается в карнавал галлюцинаций: персонажи забывают квесты, предметы появляются из ниоткуда, а порядок событий перекраивается по whim модели. Забудьте про «нейросеть рулит» — в продакшене это ад.

Мы перепробовали кучу подходов: от жестких промптов (бесполезно) до графов состояний (слишком хрупко). Пока не родили гибридный Guard. Три слоя, которые работают как конвейер по вырезанию лжи. Embedding Classifier, микро-LLM Extractor и State Validator. Под капотом — никакой магии, только инженерная паранойя.

Три слоя лжи: зачем три, а не один

Простой фильтр на основе промпта («не выдумывай предметы») — тупик. LLM легко ломает такой фильтр через двусмысленные формулировки. Нужна трехуровневая защита, где каждый слой проверяет ответ под разным углом.

💡
Идея заимствована из production-архитектур, где control layer для LLM (см. нашу статью про 8 компонентов) уже показал себя на практике. Но в RPG требования жестче — нужна не просто валидация формата, а проверка соответствия игровому состоянию.
  1. Embedding Classifier — быстрый семантический барьер. Он определяет, относится ли ответ игрока к разрешенным категориям (действие, диалог, описание) или пытается менять мир без спроса.
  2. Микро-LLM Extractor — маленькая модель (1-3B параметров) вытаскивает структурированные данные из ответа: кто, что, где, с кем. Это и есть «сократ в кармане».
  3. State Validator — проверяет экстрагированные факты против текущего игрового состояния. Если персонаж пытается достать из кармана кошель, которого нет в инвентаре — реджект.

Каждый слой может отклонить ответ целиком или запросить его коррекцию. В идеале — вернуть игроку сообщение вроде «Такого предмета у тебя нет. Попробуй другое действие». Но это уже level design, а не наша задача.

Слой первый: Embedding Classifier — злой близнец поиска

Стандартный RAG ищет похожие куски контекста, а наш Embedding Classifier — наоборот: он ищет непохожесть на разрешенные действия. Мы берем легкую модель эмбеддингов (например, all-MiniLM-L6-v2 или gte-small — на 01.06.2026 это еще актуальные звери) и обучаем ее классифицировать намерения игрока: «действие с предметом», «передвижение», «диалог», «исследование», «мета-комментарий». Если эмбеддинг ответа попадает в кластер «мета-комментарий» или «изменение мира» — всё, стоп.

Важно: Embedding Classifier — это не про точность, а про скорость. Он отсекает ~70% явных галлюцинаций за 2-3 мс. Остальное докручивают следующие слои. Не пытайтесь сделать его единственным фильтром — будет много ложных срабатываний.

import numpy as np
from sentence_transformers import SentenceTransformer
from sklearn.linear_model import LogisticRegression

# Загружаем модель эмбеддингов (актуальная версия на June 2026)
encoder = SentenceTransformer('all-MiniLM-L6-v2')

# Обучаем простой классификатор на синтезированных данных
# В реальном проекте — больше примеров и баланс классов
X_train = encoder.encode([
    "беру ключ со стола",
    "иду к воротам",
    "спрашиваю у стража про выход",
    "осматриваю комнату",
    "в моем кармане лежит эльфийский артефакт"  # галлюцинация
])
y_train = [0, 0, 0, 0, 1]  # 1 - нарушение

clf = LogisticRegression()
clf.fit(X_train, y_train)

def embedding_classifier(text: str) -> bool:
    emb = encoder.encode([text])
    pred = clf.predict(emb)
    return pred[0] == 0  # True если разрешено

Почему не сделать все на одной большой LLM? Потому что latency. В real-time RPG каждый миллисекунда решает. Embedding слой пропускает только явно безопасные варианты, не нагружая следующие этапы.

Слой второй: микро-LLM — сократ в кармане

Второй слой — маленькая LLM (1-3B параметров), которая извлекает из ответа игрока структурированные факты: действие, объект, цель, локация. Зачем нам микро-LLM, если есть GPT-4? Во-первых, цена: каждая проверка должна стоить копейки. Во-вторых, маленькая модель легче контролируется — она обучена только на одной задаче, а не на всем интернете.

На 01.06.2026 лучший компромисс — Llama-3.2-3B-instruct или Phi-3-mini-128k-instruct. Они умеют держать длинный контекст (128k токенов) и дают стабильный structured output через JSON mode. Не советую брать меньше 1B — качество экстракции падает ниже плинтуса.

from openai import OpenAI

client = OpenAI(base_url="http://localhost:8080/v1", api_key="not-needed")

def extract_facts(player_response: str, context: str) -> dict:
    completion = client.chat.completions.create(
        model="llama-3.2-3b-instruct",
        messages=[
            {"role": "system", "content": "Извлеки из ответа игрока структурированные факты. Верни JSON с полями: action, target, location, item. Если поля нет - оставь null."},
            {"role": "user", "content": f"Контекст игры: {context}\nОтвет игрока: {player_response}"}
        ],
        response_format={"type": "json_object"}
    )
    return json.loads(completion.choices[0].message.content)
💡
Важный костыль: если микро-LLM не смогла распарсить ответ (вернула мусор), слой должен вернуть флаг invalid и передать дальше только то, что удалось извлечь. Никогда не игнорируйте ошибки парсинга — это первый шаг к галлюцинации.

Слой третий: State Validator — судья с дробовиком

Финальный слой — machine-gun validation. Получив структурированные факты, мы сверяем их с текущим состоянием игры (инвентарь, открытые локации, отношения NPC, прогресс квестов). Если игрок говорит «отдаю зелье королю», а зелья у него нет — реджект. Если говорит «иду в подземелье», а подземелье еще не открыто — реджект. Никакой эмпатии.

State Validator не использует нейросеть. Это чистый код на Python с pydantic или jsonschema. Он должен быть детерминированным и предсказуемым. LLM может ошибиться, обычный код — нет.

from pydantic import BaseModel
from typing import Optional

class GameState(BaseModel):
    location: str
    inventory: list[str]
    quest_flags: dict[str, bool]

def validate_action(facts: dict, state: GameState) -> bool:
    # Пример: проверка наличия предмета
    if facts.get("item") and facts["item"] not in state.inventory:
        return False
    # Проверка локации
    if facts.get("location") and facts["location"] != state.location:
        # Если игрок пытается переместиться в недоступную локацию
        return False
    return True

Здесь же можно проверить, не пытается ли игрок выполнить действие, заблокированное квестовым флагом. Например, «открыть сундук» можно только после разговора с ключником. State Validator — это фактически авторитарный бэкенд, о котором мы писали в статье Архитектура AI RPG: авторитарный бэкенд. Без него LLM просто игнорирует правила.

Собираем Frankenstein: пайплайн инференса

Теперь объединяем три слоя в один пайплайн. Embedding Classifier — быстрый проход. Если ок, то микро-LLM извлекает факты. Если факты валидны — State Validator. Если все три слоя дали зеленый, ответ игрока применяется к игровому миру. Иначе — сообщение об ошибке и запрос коррекции.

def guard_pipeline(player_response: str, context: str, state: GameState) -> bool:
    # Layer 1
    if not embedding_classifier(player_response):
        return False
    # Layer 2
    facts = extract_facts(player_response, context)
    if "error" in facts:
        return False
    # Layer 3
    if not validate_action(facts, state):
        return False
    return True

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

Грабли, которые мы собрали

Первая и главная ошибка — делать Embedding Classifier единственным Gate. Он отсекает явные галлюцинации, но пропускает тонкие: когда игрок говорит «я достаю волшебную палочку», а в инвентаре нет палочки, но есть «странная деревяшка». Эмбеддинговая модель не поймет подмену понятий. Поэтому микро-LLM обязателен.

Вторая грабля — игнорировать latency микро-LLM. Разверните её на fast inference сервере (vLLM, TensorRT-LLM) с динамическим батчингом. На CPU она будет выдавать ответ секунду — для RPG это много. Используйте GPU 40x A100s (или хотя бы одну L40S) — это окупается.

Третья — не тестировать на «слепых» сценариях. Мы запускали стресс-тесты, где боты общались друг с другом (да, тот самый эксперимент с 13 LLM), и выяснили, что агенты научились обходить Guard через метафоры («у меня с собой немного магии» — классификатор пропускал, а state validator не мог проверить «магию», потому что её нет в инвентаре). Пришлось добавить blacklist для абстрактных концепций.

Бонус: регрессия на живых логах

Без системы тестов гибридный Guard превращается в тыкву. Каждое обновление микро-LLM или добавление нового типа действий ломает пайплайн. Мы завели директорию fixtures/ с сотнями пар «запрос -> ожидаемый вердикт». Прогоняем после каждого деплоя. Сравниваем метрики: precision (сколько истинных нарушений поймали) и recall (сколько пропустили). Без этого нельзя. Подробнее про паттерн Triage → Gate → Voice — читайте в статье Как избежать галлюцинаций LLM-бота в саппорте, там отличная метафора для тестирования.

Два слова напоследок (без клише)

Многие считают, что LLM рано или поздно «научатся» не галлюцинировать. Нет. Это фундаментальное свойство генеративных моделей. Ваша задача — не исправить LLM, а окружить ее такими костылями, чтобы она не могла навредить. Гибридный Guard — не серебряная пуля, но он переводит AI-RPG из разряда «демки» в разряд «прототипа, который можно показывать продюсеру». Если добавить сюда самовосстанавливающийся RAG (см. статью про самовосстанавливающийся RAG), то шанс пройти QA возрастает до 80%. Остальные 20% — это галлюцинации, которые вы никогда не увидите, пока не выпустите игру в прод. И это нормально.

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