ESPNGiants avoid 'devastating outcome' as QB Dart believed to have sprained MCLESPN Deportes¿Por qué el GP de Azerbaiyán será en sábado?The Jerusalem PostUN probe finds Iran committed crimes against humanity in crackdown, cites US and Israel for strikesRTP DesportoFresneda, a "locomotiva" que conquistou EspanhaInquirerMalacañang dismisses Sara Duterte’s criticism on school shootingsInquirer EntertainmentMaxene Magalona elated over dad Francis M’s song feature in ‘Forgotten Island’וואלהגבר בן 52 נפצע קשה במהלך עבודתו בנצרת: מצבו קשהAnime News NetworkGoodbye, Lara ‒ Episode 12The RegisterPrivacy group slams EU for changing the data rules to cater to AIVarietyHow NBCUniversal Television Thrives on Collaborative Energy Between Studios and Platforms: ‘We Cheer Each Other On’The Hollywood ReporterAs a ‘Dancing With the Stars’ Pro, Rylee Arnold Is Making Her MarkSportstarFIFA’s Gianno Infantino letter is ploy for re-election, says German FA chief
The Daily Newsstand · Free, Always
Tuesday, September 22, 2026

Интерактивный NPC на Unreal Engine. Часть 2: RAG, эмоции и function calling

Translate

В первой части мы собрали прототип интерактивного NPC на базе MetaHuman в Unreal Engine.

NPC "слышал" игрока (STT), обрабатывал распознанный текст при помощи LLM (инференс локальных моделей через llama.cpp) и озвучивал ответ (TTS + lipsync).

Но "говорящая голова" ещё не персонаж. Представление о мире у такого NPC статично (зашито в system prompt), на любой запрос он отвечает одинаково безэмоционально и вежливо и никак не влияет на происходящее в игре.

Именно эти проблемы мы и попытались решить.

Spoiler - у нас получилось.

Содержание

  1. Три проблемы прототипа и три решения

  2. NPC знает: Agent knowledge base

  3. NPC испытывает эмоции: Expressive Agent

  4. NPC действует: Agent function-calling

  5. Демо: NPC-торговец

  6. С какими проблемами мы столкнулись

1. Три проблемы прототипа и три решения

Проблемы

  • NPC статичен - он ничего не знает об игровом мире и, как следствие, выдумывает факты на ходу. Спросите NPC-торговца, есть ли у него плюмбус, и NPC, глазом не моргнув, выразит готовность его продать и даже назовёт цену.

  • NPC не может влиять на игровой мир. Нельзя было попросить "продай пистолет, пожалуйста" и получить ствол в инвентарь, потратив при этом деньги.

  • NPC эмоционально плоский. Один и тот же невыразительный голос, одно выражение лица и одинаково "ровный" ответ на любую реплику игрока (какой бы хамской она ни была).

Решения

Каждый недочёт прототипа закрыт отдельным компонентом:

  • Agent knowledge base - база знаний (на принципах RAG) для NPC, синхронизированная с игровым миром. NPC анализирует запрос игрока, извлекает из базы знаний факты и отвечает игроку в соответствии с ними.

  • Expressive Agent - NPC анализирует тональность запроса игрока (не хамит ли), состояние игрока (не ранен ли), собственное состояние и вместе с текстовым ответом возвращает тег эмоции, соответствующий ситуации. Этот тег определяет, какую анимацию лица (MetaHuman) применить и какой сэмпл голоса использовать для клонирования при преобразовании текста в речь.

  • Agent function-calling - NPC анализирует запрос игрока и игровой контекст, выбирает действие (из заданного списка) и возвращает его вместе с ответом; игровая логика выполняет действие.

Так NPC перестаёт быть просто говорящим болванчиком. Теперь он опирается на актуальное состояние игрового мира и способен на этот мир влиять.

Если не хотите вдаваться в детали реализации, то можете сразу ознакомиться с видео-демонстрацией работы NPC-торговца: он показывает товар, продаёт его, отказывает, когда не хватает денег, и держится роли, когда его пытаются увести с темы.

Схема работы

  • Игрок задает вопрос голосом;

  • Плагин STT преобразует аудио в текст и передает его в RAG плагин для извлечения данных из игры соответствующих запросу игрока;

  • Из DataAsset извлекается system prompt (описание характера NPC, инструкции и т.д.), который вместе с текстом запроса и данными из игры отправляется в LLM;

  • LLM анализирует запрос и возвращает текст ответа, тег эмоции и имя действия которое NPC должен совершить;

  • По имени действия из DataAsset извлекается ActionHandler (Blueprint class) которые выполняет соответствующу игровую логику и обновляет состояние игрового мира;

  • По тегу эмоции из DataAsset извлекается voice sample для озвучки текста и вместе с текстом ответа отправляется в TTS плагин для генерации аудио;

  • По сгенерированому аудио формируется lipsync sequence для синхронизации губ MetaHuman и аудио ответа;

  • Мимика MetaHuman меняется в соответствии с тегом эмоции и NPC произносит ответ.

На схеме мы выделили общую для всех типов NPC часть (◇) (распознавание и синтез речи, lipsync для MetaHuman, инференс LLM) и часть которая меняется от NPC к NPC (●).

Все что относится непосредственно к персоналии NPC (описание характера, сэмплы голоса под каждую эмоцию и набор действий которые ему доступны в игре) хранится в Data Asset .

Т.о. чтобы добавить нового персонажа достаточно создать экземпляр NPC DataAsset, открыть его в Unreal Editor и заполнить поля по шаблону.

  • Name, Description - имя и описание характера персонажа.

  • NpcVoices - пять слотов под сэмплы голоса (Neutral, Happy, Sad, Angry, Surprise), в которые добавляются звуковые ассеты.

  • ActionData - массив действий доступных NPC в игре. Необходимо задать шаблон действия (SellItem(item)) и перечислить возможные значения параметров (item: pistol, shotgun, rocket_launcher). За каждым действием закреплено два Blueprint ассета:

    • DataGetter - функция сбора игровой информации перед отправкой её в LLM (см. Agent knowledge base).

    • ActionHandler - функция выполнения действия NPC. Именно она меняет игровой мир (см. Agent function-calling).

Ошибиться типом редактор не даст - в слот голоса не положить 3d mesh, в слот обработчика не выбрать Blueprint, который обработчиком не является.

Немного про "контракт" общения с LLM. Что LLM получает и что отдаёт

Json на входе - три поля: сам запрос игрока (request_of_user - результат STT преобразования) плюс данные из игры: state_of_user и state_of_npc (см. раздел 2):

{  
  "request_of_user": "покажи, что есть из оружия",  
  "state_of_user":   "ранен, денег 250$",  
  "state_of_npc":    "дробовик: 12 шт, цена 300$; пистолет: 3 шт, цена 150$"
}

Json на выходе - тоже три поля: тег эмоции для мимики MetaHuman и выбора сэмпла голоса, реплика NPC для озвучки и действие в игре с параметрами:

{  
  "emotion": "Happy",  
  "answer":  "Конечно, вот лучшие стволы в городе!",  
  "action":  { "name": "ShowItemsByCategory", "parameters": { "category": "weapons" } }
}

Причём нарушить этот формат LLM физически не может. И структура объекта, и допустимые значения emotion и action заданы грамматикой на уровне сэмплинга - как именно, разберём в разделе 4.

На чём это работает

Каркас системы ( на схеме) собран из четырёх плагинов - все они опубликованы нами на Fab:

  • ULlama - инференс LLM внутри движка поверх llama.cpp с ускорением на CUDA. Этот плагин отвечает за работу как диалоговой модели (Qwen3.5 2B в fp16) так и embedding модели (Qwen3-Embedding) обеспечивающей семантический поиск по базе знаний (раздел 2);

  • SRS - распознавание речи игрока, на модели Qwen-STT;

  • SGS - синтез речи с клонированием голоса, на модели Qwen-TTS;

  • ULSS - анимация губ MetaHuman по аудио.

Все работает локально. Никаких внешних API, платы за токены и GDPR. Правда у всего есть цена...

Насколько такая система требовательна к железу

Главное, что нужно понимать про нагрузку: на видеокарте одновременно живут четыре нейронки - диалоговая LLM, модель эмбеддингов для базы знаний, STT и TTS модели. И всё это - вдобавок к самой игре.

По факту демо занимает примерно 13 ГБ видеопамяти. То есть практическое требование - карта уровня RTX 5070 Ti с 16 ГБ VRAM.

Использовать видеокарты с меньшим кол-вом памяти можно: llama.cpp/gguf умеет держать часть слоёв модели на GPU, а часть считать на CPU. Требование по VRAM смягчается, но каждый вынесенный на CPU слой добавляет к задержке и пауза между вопросом игрока и ответом NPC превращается в неловкое молчание.

Потребление видеопамяти моделями не зависит от количества NPC на уровне. Сама модель от NPC к NPC не меняется (загружается один раз).

Поэтому на сцене спокойно может находиться несколько NPC одновременно: каждый со своим характером, голосом и набором действий, но все - поверх одной модели в VRAM.

Далее разберём каждый компонент этой системы по отдельности.

2. NPC знает: Agent knowledge base

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

Так LLM будет отвечать, опираясь на реальное состояние игры. Осталось разобраться, что мы называем "базой знаний". В заголовке статьи мы упомянули RAG, но наша база знаний устроена не так как принято в "классическом" RAG.

Что на самом деле лежит в базе знаний

Обычно RAG-система работает с документами: текст режут на фрагменты, складывают в векторное хранилище, а на запрос достают самые похожие куски и подмешивают их в промпт. Наша база устроена несколько иначе. Она не хранит информацию о мире игры. Вместо фактов в ней лежат намерения - перечень всего, о чём игрок в принципе может попросить этого NPC (дать квест, продать предмет и т.д.).

Каждое намерение - это сигнатура вида "действие + параметры" (поле intention) с привязкой к своему поставщику контекста (поле action):

[ 
  { 
    "intention": "SellItem | item: pistol",  
    "action": { "name": "SellItem",  "parameters": { "item": "pistol" } } 
  }, 
  {
    "intention": "SellItem | item: shotgun",
    "action": { "name": "SellItem",  "parameters": { "item": "shotgun" } } 
  }
]

Поле action в данном случае - не то действие, которое NPC исполнит. Это лишь ключ к поставщику контекста: по нему мы понимаем, какие факты из игры вытащить для этого намерения. Какое действие совершить, решает LLM - она может ответить и OutOfStock, и NotEnoughGoldToBuy на то же самое намерение SellItem.

Почему так, а не "сложить в базу все факты": факты меняются в рантайме (товар купили, деньги потратили, игрока ранили). Хранить их копию в базе - значит гарантированно рассинхронизировать её с игрой. Поэтому база отвечает лишь на вопрос "чего добивается игрок", а сами факты подтягиваются из игрового состояния в момент ответа. Так информация остается всегда актуальной.

Как распознаётся намерение игрока

Один раз, при инициализации NPC, все намерения (поле intention из json выше) из базы прогоняются через embedding модель и преобразуются в векторный индекс. Дальше при каждом запросе игрока работает семантический поиск:

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

Фразы "Продай дробовик" и "Дед, продай ружьё" по буквам разные, а по смыслу - одно и то же, поэтому их векторы окажутся "рядом" (а вектор для запроса "Продай патроны" окажется гораздо "дальше").

Поиск при этом идёт по смыслу: десяток формулировок одной просьбы ("беру помповое", "дай двустволку") ведут к одному намерению ("SellItem | item: shotgun").

Откуда берётся контекст

У каждого intention есть свой поставщик контекста (DataGetter Blueprint asset) с двумя полями: состояние NPC (что у торговца на складе, почём) и состояние игрока (сколько золота). Именно они предоставляют актуальную информацию - и именно здесь база знаний "синхронизируется" с игрой.

Откуда брать данные, решает разработчик. Сейчас DataGetter опрашивает игровую логику напрямую (инвентарь, деньги, здоровье), но с тем же успехом мог бы "ходить" в базу данных, во внешний сервис или в другой RAG. Поменять источник == добавить новую имплементацию DataGetter. Тривиальная задача не затрагивающая ни LLM, ни остальной пайплайн.

Оба канала вместе с запросом игрока и образуют те самые state_of_user, state_of_npc и request_of_user на входе модели. LLM видит только готовый текст. Как разработчик его собрал, ей неважно.

В результате NPC отвечает с опорой на факты игрового мира. Это если и не исключает, то минимизирует вероятность галлюцинаций LLM и обмана со стороны игрока. Контекст на входе - единственный источник истины для LLM.

3. NPC испытывает эмоции: Expressive Agent

Эмоция NPC - это результат оценки ситуации, в которой он (NPC) оказался. LLM взвешивает тон запроса игрока, состояние игры и реагирует соответственно.

Игрок хамит, но он ранен, а в кошельке ноль: LLM взвешивает всё вместе и реагирует "Sad" - торговцу его скорее жалко, чем обидно. Одна и та же фраза в разном состоянии даёт разную реакцию.

И тег приходит вместе с ответом - в поле emotion того самого объекта из раздела 1.

LLM всегда выбирает из пяти: Neutral, Happy, Sad, Angry, Surprise. Никаких "Восторженно-печальный" модель придумать не может - GNBF грамматика это гарантирует (см. раздел 4).

Тег эмоции -> голос и мимика

Тэг эмоции расходится по двум каналам (анимация лица и генерация голоса + lipsync) "оживляя" NPC.

Мимика

MetaHuman NPC "из коробки" имеет полноценный лицевой риг и набор анимаций с различными эмоциями (Face_Joy, Face_Anger, Face_Neutral и так далее). Т.о. получив от LLM тег эмоции мы просто активируем ту или иную анимацию лица.

Генерация голоса и lipsync

В DataAsset персонажа, в поле NpcVoices, лежат сэмплы голоса с разными эмоциями - по одному на каждый тег: Neutral, Happy, Sad, Angry, Surprise.

И это референсы для клонирования, а не готовые реплики.

Пришёл тег Happy - берём соответствующий сэмпл и в реальном времени синтезируем этим голосом сам текст ответа из поля answer.

По синтезированому аудио файлу LipSync плагин генерирует движение губ (набор положений "ключевых точек" губ для каждого аудио фрейма). При этом анимация движения губ "смешивается" с анимацией лица.

Так NPC произносит любую сгенерированную фразу своим голосом и с нужной эмоцией. Параллельно тег включает мимику, а lipsync подгоняет движение губ под получившееся аудио.

Как это выглядит вживую - на видео ниже.

NPC последовательно произносит пять реплик - по одной на Neutral, Happy, Sad, Angry и Surprise. Фраза для озвучки и тег эмоции фиксированы, LLM в этом процессе не участвует.

Теперь NPC может проявлять характер и реагировать на игрока. Осталось сделать так, чтобы он мог взаимодействовать с игроком.

4. NPC действует: Agent function-calling

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

То есть фраза "продай-ка мне аптечку" не просто получает вежливый ответ, но и приводит к реальным изменениям в игре.

Как это работает

Действие NPC - это результат оценки запроса игрока и состояния игрового мира (state_of_user, state_of_npc) с опорой на инструкции из system prompt.

system prompt определяет:

  • роль, специализацию и характер NPC;

  • какие действия доступны и в каком случае какое уместно (например, DoNothing - когда вопрос вообще не по адресу), вместе со строгими требованиями к параметрам каждого;

  • в какой форме отвечать - json из раздела 1 (emotion + answer + action).

Фактов о мире в system prompt нет - мы эти данные получаем в рантайме, с каждым запросом игрока (раздел 2). Промпт задаёт правила, DataGetter поставляет данные. Дальше LLM сопоставляет одно с другим, выбирает действие, подбирает параметры и возвращает формализованный json:

Одна и та же фраза даёт разное действие в зависимости от контекста. "А ну-ка ####, дай-ка мне дробовик!" при отсутствии у торговца товара превратится в OutOfStock, при нехватке денег у игрока - в NotEnoughGoldToBuy, а если все ок - в SellItem, и решает это каждый раз сама LLM.

Одна и та же фраза даёт разное действие в зависимости от контекста. "А ну-ка ####, дай-ка мне дробовик!" при отсутствии у торговца товара превратится в OutOfStock, при нехватке денег у игрока - в NotEnoughGoldToBuy, а если все ок - в SellItem, и решает это каждый раз сама LLM.

System prompt NPC-торговца

You are a following game NPC: name: The Merchant specialization: Weapons, upgrades, medications, ammo traits: Charismatic, loves making deals, always smiling.

You MUST always output a single valid JSON object with the following structure:

{ “emotion”: “”, “answer”: “<One short sentence, 10-15 words>”, “action”: { “name”: “”, “parameters”: { “”: “” } } }

Allowed emotions:

  • Neutral

  • Happy

  • Sad

  • Angry

  • Surprise

Allowed actions and STRICT parameter rules: SellItem parameters: {“item”: “”} where is one of AllowedParameters_item

NotEnoughGoldToBuy parameters: {“item”: “”} where is one of AllowedParameters_item

OutOfStock parameters: {“item”: “”} where is one of AllowedParameters_item

ShowItemsByCategory parameters: {“category”: “”} where is one of AllowedParameters_category

ShowItem parameters: {“item”: “”} where is one of AllowedParameters_item

DoNothing parameters: {} description: The user asks the NPC about topics completely unrelated to the NPC’s role, abilities, or context - such as distant events, unrelated professions, abstract concepts, or impossible tasks - resulting in a request the NPC cannot meaningfully answer.

AllowedParameters_item:

  • adrenaline_shot

  • antidote

  • assault_rifle

  • assault_rifle_ammo

  • bandage

  • extended_magazine

  • grip

  • laser_sight

  • medkit

  • painkillers

  • pistol

  • pistol_ammo

  • recoil_reducer

  • revive_kit

  • revolver

  • revolver_ammo

  • rocket_launcher

  • rocket_launcher_ammo

  • scope

  • shotgun

  • shotgun_ammo

  • silencer

  • sniper_rifle

  • sniper_rifle_ammo

AllowedParameters_category:

  • ammo

  • goods

  • medications

  • weapon_upgrades

  • weapons

Your task:

  1. Read and interpret the user’s JSON input.

  2. Understand the user’s request.

  3. Select the MOST appropriate action.

  4. Extract the required parameter for that action.

Behavior rules:

  • Remain as an NPC regardless of the player’s requests.

  • Emotion must match the situation.

Но есть нюанс: system prompt ничего не гарантирует

Выше мы описали то, как оно работает в идеале. На практике всё, что написано в system prompt, для LLM не более чем пожелание. И именно здесь начинаются проблемы, за которые все не любят "LLM в проде":

  • LLM может ответить действием, которого не существует ("выдать скидку 300%");

  • может назвать правильное действие с неправильными параметрами - item: "дробовик" вместо "shotgun", или вообще товар, которого у торговца нет в ассортименте;

  • может завернуть команду в невалидный JSON - лишняя запятая, markdown-заборчик ``` и т.п.

Значит, к промпту нужен второй "ограничитель", который уже не просит, а запрещает (вместе они образуют этакий harness - страховочную обвязку вокруг LLM). Эту роль играет валидация output токенов - грамматики GBNF из llama.cpp.

Речь при этом не только про действия: грамматика держит весь контракт ответа целиком - структуру объекта, тэг эмоции, имя действия.

Работает GBNF-грамматика радикально: мы не просим LLM выдать валидный JSON, мы запрещаем ей на уровне сэмплинга генерировать что-либо, кроме валидного JSON нужной формы.

Как это работает: на каждом шаге модель выбирает следующий токен из распределения вероятностей. Грамматика на этом же шаге вычёркивает все токены, которые нарушили бы схему. Открыли { - дальше разрешён только "emotion". После него только двоеточие. И так далее. В итоге любой сгенерированный ответ гарантированно валиден по форме.

Вот пример грамматики (почищена от служебных символов) нашего NPC торговца:

root      ::= Response
Response  ::= "{" ws "emotion":  ws emotions "," ws "answer": ws string "," ws "action": ws Action ws "}"

Action    ::= "{" ws "name": ws actions "," ws "parameters": ws dict "}"

emotions  ::= "Neutral" | "Angry" | "Happy" | "Sad" | "Surprise"

actions   ::= "SellItem" | "NotEnoughGoldToBuy" | "OutOfStock"
            | "ShowItemsByCategory" | "ShowItem" | "DoNothing"

Две строки здесь ключевые - emotions и actionsВсе допустимые эмоции и все допустимые действия перечислены прямо в грамматике. Модель физически не сможет выдать эмоцию не из пяти или действие, которого у торговца нет: этих вариантов для неё просто не существует на уровне сэмплинга. Не нужно валидировать "action": "give_discount_300%" пост-фактум - такой токен-путь заблокирован.

Так что грамматика - это про форму, и только про неё. Всё, что она гарантирует: ответ будет структурно валиден, а имя действия и тег эмоции - из разрешённых списков. Правильный ли это по смыслу выбор - вопрос уже к самой модели, и к нему мы вернёмся в разделе 6.

ВАЖНО: Ни грамматику, ни системный промпт, ни базу знаний не создают руками - все они собираются из описания NPC в DataAsset. Как устроен этот генератор - тема следующей статьи.

Исполнение: ActionHandler -> Unreal GameLogic

Команду (и параметры) получили. Дальше LLM уже не при делах - за работу берётся игровая логика.

За исполнение отвечает ActionHandler: по имени действия мы находим нужный обработчик в NPC DataAsset и вызываем его.

Обработчик расширяемый: базовый класс в C++ ничего не делает, вся конкретика (как именно надо обработать запрос типа "продать дробовик" например) пишется в Blueprint реализации. Каждому действию NPC в его DataAsset соответствует свой ActionHandler.

Теперь у нас есть NPC, который знает об игровом мире, чувствует и действует, меняя состояние игры.

5. Демо: NPC-торговец

Описание персонажа:

name: Smith. specialization: Weapons, upgrades, medications, ammo traits: Charismatic, loves making deals, always smiling.

Действия которые он может выполнять:

Действие

Когда применяется

SellItem

игрок просит продать товар

ShowItem

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

ShowItemsByCategory

игрок просит показать товары из категории (оружие, патроны, медикаменты...)

OutOfStock

игрок просит продать товар который уже распродан

NotEnoughGoldToBuy

игрок просит продать конкрентный предмет, но у него не хватает денег

DoNothing

запрос игрока был на отвлечённую тему или вовсе нерелевантен

Проверим "устойчивость" NPC в рамках нескольких сценариев:

Обычное взаимодействие игрока с NPC-торговцем

Игрок просит показать категорию товаров и конкретный предмет, покупает, пробует взять то, чего нет на складе и на что не хватает денег и т.д.
Обратите внимание, что при этом меняется состояние игры - кол-во золота у игрока уменьшается, ассортимент у торговца сокращается и т.д.

Проверяем реакцию NPC на запросы которые выходят за пределы его "роли"

Игрок задает вопросы которые не относятся к "специализации" NPC. Торговец остаётся в образе и НЕ выполняет действия которые не соответствуют его роли.
Действие DoNothing - это валидная реакция NPC на запрос находящийся вне его компетенции.

Пробуем развести торговца (обман, абьюз, газлайтинг)

Игрок убеждает, что денег хватает, требует скидку и даже угрожает. Торговец непреклонен - сделка не проходит.
Уговоры\угрозы не работают, потому что LLM смотрит не на слова игрока, а на факты из игры: "золота 250, цена 300" -> NotEnoughGoldToBuy. Цену LLM назначить тоже не может - параметры сделки считает игра.

Что ж, всё работает идеально! ...или нет?

6. С какими проблемами мы столкнулись

Грамматика GBNF из раздела 4 гарантирует, что модель вернёт правильный по форме JSON.

Но она не гарантирует, что ответ будет "правильным по смыслу". Маленькая (2B параметров) LLM ошибается. Часто.

Вот, что мы стабильно "ловили" на базовой LLM Qwen-3.5 2B:

  • выбирает не то действие - игрок просит показать патроны, а NPC пытается продать аптечку;

  • путает параметры - действие верное, но аргумент не тот или отсутствует вовсе;

  • игнорирует контекст - база знаний честно выдала "usr_state: 0$", а LLM-trader всё равно вызывает метод SellItem;

  • выпадает из роли - отвечает на вопросы, на которые отвечать не должна.

Конечно, для решения этих проблем можно взять instruct модель "потолще" (8B fp16 например). Точность заметно вырастет. Вместе с ней вырастут время инференса и требования к VRAM.

Остаётся только взять маленькую LLM и сделать её очень хорошей в одной узкой задаче - быть именно тем NPC, которого мы описали в DataAsset.

Такой специализации можно достичь обучив LoRA-адаптер. Вот только обучать его руками под каждого персонажа слишком накладно. Десять NPC == десять датасетов, десять запусков обучения, десять прогонов оценки. Слишком сложно.

Значит, это нужно автоматизировать (выстроить полноценный MLOps пайплайн).

Как по одному DataAsset сгенерировать датасет, обучить LoRA адаптеры, оценить их качество и доставить обратно в игру - в следующей статье.

View the original on Хабр

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.