SLM для NPC в играх 2026: Qwen 1.5B, JSON формат, prompt engineering | AiManual
AiManual Logo Ai / Manual.
28 Мар 2026 Гайд

SLM для NPC: зачем гнаться за 2B параметрами, если Qwen 1.5B умеет в JSON

Практический гайд по использованию маленьких языковых моделей (SLM) для NPC. Выбор модели, JSON vs естественный язык, промпт-инжиниринг на Qwen 1.5B Instruct.

Почему ваш NPC на 70B параметрах тормозит сервер, а диалог все равно плоский

Вы поставили на торговца в таверне локальную LLM на 7 миллиардов параметров. Запустили через Ollama. Ждете шедевр. А получаете односложные ответы и задержку в 3 секунды на реплику. Сервер греется, видеокарта плачет, а игрок уже ушел к конкуренту.

Проблема не в модели. Проблема в том, что вы лечите насморк хирургической пилой. Для NPC в игре не нужна модель, способная писать диссертации по квантовой физике. Нужна модель, которая:

  • Понимает контекст на 500-1000 токенов (не 32к!)
  • Стабильно выдает ответ в определенном формате (JSON, а не поток сознания)
  • Работает на карточке уровня RTX 3060 одновременно для 10 NPC
  • Не галлюцинирует про API методы, которых нет в вашей игре

На 28 марта 2026 года моделей размером 1-3B параметров стало больше, а качество выросло настолько, что ставить на NPC что-то тяжелее 2.5B - это уже расточительство. Особенно после релиза Qwen 2.5B Instruct и Phi-3-mini, которые перевернули представление о возможностях SLM.

Выбор модели: математика вместо веры в маркетинг

Открываете Hugging Face. Видите 50 моделей с тегом "instruct". Какую брать? Не ту, у которой больше звезд. И не ту, что рекомендует рандомный блогер.

Берете вот этот чеклист:

Модель (на 28.03.2026) Параметры Контекст Ключевая фишка для NPC VRAM на fp16
Qwen 2.5B Instruct 2.5B 32K Отличное понимание JSON, стабильность ~5 GB
Phi-3-mini-4k-instruct 3.8B 4K Быстрая инференция, малый размер ~8 GB
Gemma 2-2B-it 2B 8K Хорошая англоязычная база ~4 GB
DeepSeek-V2-Lite-Coder 2.4B 64K Идеально для structured output ~5 GB

Я остановился на Qwen 2.5B Instruct. Почему? Потому что у Alibaba были ресурсы на нормальный датасет для инструкций, модель отлично слушается system prompt, и что важно - не пытается быть "безопасной" до идиотизма. Ваш злой маг может оставаться злым.

💡
Если вы читали мою прошлую статью "LLM для игровых NPC: как выбрать модель", то знаете про три столпа NPC-модели. SLM - это не компромисс, а специализация. Они созданы для задач, где не нужно рассуждать о смысле жизни, но нужно точно следовать инструкциям.

JSON vs Естественный язык: спор, который решается в пользу парсинга

Типичная ошибка новичка:

# Как НЕ надо делать
prompt = """
Ты - стражник у ворот. Игрок подходит к тебе. Что ты скажешь?
"""

# Модель отвечает:
# "Приветствую, путник! state = 'neutral', dialogue = 'Могу ли я чем-то помочь?'"
# Попробуй распарси это...

Правильный подход - заставить модель думать в JSON с самого начала. Но не просто "выдай JSON", а объяснить структуру так, чтобы у модели не было шансов ошибиться.

1 Системный промпт как контракт

System prompt для NPC - это не пожелания. Это техническое задание. Если в нем есть двусмысленности - модель их найдет и использует против вас.

# Правильный system prompt для NPC
system_prompt = """
Ты - NPC в ролевой игре. ВСЕГДА отвечай строго в следующем JSON формате:
{
  "dialogue": "реплика персонажа",
  "emotion": "neutral|happy|angry|sad",
  "action": "none|attack|trade|follow",
  "memory_key": "ключ для сохранения в память"
}

Правила:
1. dialogue - всегда строка, заканчивается точкой или восклицательным знаком
2. emotion - только одно из четырех значений
3. action - только одно из четырех значений
4. memory_key - одно слово, описывающее суть диалога

Если игрок говорит что-то непонятное, используй emotion: "neutral" и action: "none".
"""

Видите разницу? В первом случае мы просим. Во втором - приказываем. И указываем, что делать в краевых случаях.

Семантическое кодирование расстояний: как объяснить NPC, что "рядом" - это 5 метров, а не 50

Самая частая галлюцинация у NPC - пространственная. Игрок стоит в 20 метрах, а NPC кричит "Подойди ближе!" как будто он в двух сантиметрах.

Решение - семантическое кодирование. Не просто "игрок рядом", а точные категории с четкими границами.

Дистанция Код в промпте Пример реакции NPC
0-2 м DISTANCE_TOUCH "Отойди, ты слишком близко!"
2-5 м DISTANCE_CLOSE "Я тебя слышу. Что нужно?"
5-15 м DISTANCE_NORMAL "Эй, ты там! Подойди поближе!"
15+ м DISTANCE_FAR Игнорирует, если не кричать

В промпт это встраивается так:

user_message = """
Игрок: Привет!
Дистанция: DISTANCE_CLOSE
Время суток: NIGHT
Настроение NPC: ANNOYED
Память: ['player_stole_apple_yesterday']
"""

Модель видит не сырые метры, а семантические маркеры. DISTANCE_CLOSE четко определяет, как NPC должен реагировать. Это в тысячу раз надежнее, чем передавать "distance: 3.5".

Prompt engineering для Qwen 2.5B: холодный расчет вместо творчества

Забудьте про креативные промпты в духе "Представь, что ты уставший стражник...". С SLM это не работает. Они слишком маленькие, чтобы понимать метафоры.

Вместо этого - explicit instructions. Чем более вы прямолинейны, тем лучше модель выполняет задачу.

2 Шаблон для диалогового NPC

template = """
Контекст игры: {world_context}

Твой персонаж:
Имя: {npc_name}
Роль: {npc_role}
Черты характера: {traits}
Цели: {goals}

Текущее состояние:
Локация: {location}
Время: {time}
Погода: {weather}

Память о игроке:
{memory}

Последние события:
{recent_events}

Инструкции по ответу:
1. Длина реплики: 1-2 предложения
2. Стиль речи: {speech_style}
3. Не задавай вопросов игроку, если это не торговец
4. Не меняй тему разговора резко
5. Если не знаешь что сказать, используй стандартную фразу для своей роли

Формат ответа - JSON:
{{
  "text": "твоя реплика",
  "next_action": "wait|move|trade",
  "emotion": "neutral|happy|angry|sad",
  "memory_update": "ключ_события"
}}

Диалог:
Игрок: {player_message}
Твой ответ:
"""

Температура (temperature) для SLM в играх должна быть низкой - 0.1-0.3. Вам не нужна креативность, вам нужна предсказуемость. Высокая температура заставит модель "фантазировать" и выходить из роли.

Грамматически-ограниченное поколение: заставляем Qwen 1.5B слушаться

Есть прием, который уменьшает количество битого JSON на 90%. Называется grammar-constrained generation. По сути, вы говорите модели: "Ты можешь генерировать только токены, которые допустимы в этом месте JSON".

В 2026 году большинство инференс-серверов поддерживают это из коробки. Например, в vLLM или llama.cpp.

# Пример с использованием outlines (библиотека для грамматик)
import outlines

# Определяем грамматику JSON ответа
json_grammar = """
root ::= object
object ::= "{" pair ("," pair)* "}"
pair ::= string ":" value
value ::= string | "null" | "true" | "false"
string ::= "\"" [a-zA-Z0-9_ ]* "\""
"""

# Создаем модель с ограничением
model = outlines.models.transformers("Qwen/Qwen2.5-2B-Instruct", device="cuda")
generator = outlines.generate.json(model, json_grammar)

# Генерация будет ТОЛЬКО валидный JSON
result = generator(prompt)

Если ваш NPC внезапно начинает отвечать "Я не могу говорить об этом", значит грамматика не настроена. Модель пытается выдать что-то вне JSON, но система ее останавливает.

💡
В статье "Внутренний диалог: как грамматики ускоряют LLM в 3 раза" я подробно разбирал, как это работает на уровне токенов. Коротко: модель не тратит "мысли" на выбор между запятой и словом "привет" - она знает, что после открывающей скобки может быть только кавычка.

Полный пайплайн от промпта до Unity

3 Настройка Ollama для массового использования

Ollama хорош для тестов, но для 10+ NPC одновременно нужна оптимизация.

# Запуск Ollama с настройками для множества NPC
OLLAMA_NUM_PARALLEL=10 \
OLLAMA_MAX_LOADED_MODELS=3 \
ollama serve

# Создаем модель-шаблон с нашим system prompt
cat > Modelfile << EOF
FROM qwen2.5:2.5b-instruct

PARAMETER temperature 0.2
PARAMETER top_p 0.9

SYSTEM """
Ты - NPC в игре. Всегда отвечай в JSON формате...
"""
EOF

ollama create npc-base -f Modelfile

4 Интеграция в игровой движок

Не делайте HTTP запрос к Ollama на каждом кадре. Это убьет производительность.

// Пример для Unity - менеджер NPC с очередью запросов
public class NPCDialogueManager : MonoBehaviour
{
    private Queue requestQueue = new();
    private bool isProcessing = false;
    
    public void RequestDialogue(NPC npc, string playerMessage)
    {
        // Собираем контекст из памяти NPC
        var context = npc.BuildPromptContext(playerMessage);
        
        // Кладем в очередь
        requestQueue.Enqueue(new NPCRequest(npc, context));
        
        if (!isProcessing)
            StartCoroutine(ProcessQueue());
    }
    
    private IEnumerator ProcessQueue()
    {
        isProcessing = true;
        
        while (requestQueue.Count > 0)
        {
            var request = requestQueue.Dequeue();
            
            // Асинхронный запрос к Ollama
            yield return StartCoroutine(SendToLLM(request));
            
            // Обработка ответа
            var response = JsonUtility.FromJson(llmResponse);
            request.npc.OnDialogueResponse(response);
        }
        
        isProcessing = false;
    }
}

Важный нюанс: делайте timeout на запросы. Если модель думает дольше 2 секунд - прерывайте и давайте fallback-ответ.

Что сломается первым (и как это починить)

Реальные проблемы, с которыми столкнетесь:

Проблема 1: Модель внезапно забывает формат JSON после 20 диалогов.
Решение: В каждом пользовательском промпте дублируйте ключевые инструкции. Не надейтесь, что системаный промпт "запомнится".

Проблема 2: NPC начинает повторять одни и те же фразы.
Решение: Добавьте в контекст список последних 5 реплик NPC. И явно укажите: "Не повторяй предыдущие фразы".

Проблема 3: При длинной памяти (100+ токенов) качество ответов падает.
Решение: Не храните всю историю. Храните семантические summary. Вместо "Игрок спросил о цене на меч, потом о броне, потом ушел" пишите "player_interested_in_prices".

Когда все-таки нужна большая модель

SLM - не панацея. Если ваш NPC должен:

  • Вести глубокие философские диалоги (квестовые NPC)
  • Анализировать сложные игровые ситуации (советник короля)
  • Генерировать уникальные истории на лету (сказочник)

...тогда стоит посмотреть в сторону моделей 7B+. Но даже там не берите 70B - это overkill. Как я писал в статье про uncensored LLM для ролевых игр, разница между 7B и 13B часто не стоит дополнительных ресурсов.

Будущее SLM: почему через год мы будем смеяться над сегодняшними 2B моделями

На 28 марта 2026 уже есть слухи о Qwen 3B с качеством ответов как у сегодняшних 7B. Технология сжатия знаний (knowledge distillation) развивается так быстро, что через год 2B модель будет делать то, что сегодня делает 7B.

Но принципы останутся:

  1. Четкий формат вывода (JSON, XML, YAML) всегда побеждает свободный текст
  2. Семантические маркеры надежнее сырых значений
  3. Грамматические ограничения экономят 30% вычислительных ресурсов
  4. Температура ниже = стабильнее диалог

Поставьте сегодня Qwen 2.5B на своих массовых NPC. Через месяц, когда поймете его ограничения, попробуете Phi-4 или следующую версию Gemma. Но не начинайте со сложного - настройте пайплайн на маленькой модели, отладьте интеграцию, а потом уже думайте об апгрейде.

💡
Если хотите увидеть полный рабочий пример интеграции SLM в игровой движок, посмотрите мой туториал по созданию голосовых NPC с памятью в Unity. Там разобран весь стек: от Whisper для распознавания речи до edge-tts для озвучки.

Главный секрет не в выборе самой крутой модели. А в том, чтобы ваша архитектура позволяла легко заменить Qwen 2.5B на следующую 2B-модель, которая выйдет через полгода. Пишите код так, как будто завтра появится модель в 10 раз лучше - и она действительно появится.

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