Опубликовано
YandexGPT-5-Lite-8B как генератор над выдачей: третья часть цикла про домашний Perplexity
Автор: Гусев Николай [портфолио]
Обновлено
Темы: llm, rag, local-models
Сложность: средняя
Третья часть: зачем нам ещё одна модель
В первой части цикла я собрал добычу: SearXNG ищет, Camoufox достаёт контент из-под антибота. Во второй - сравнил пять локальных моделей над грязной поисковой выдачей: быстрый qwen3.8 отвечал за сто секунд, LFM2.5 на 32 тысячах контекста честно отказывался натягивать несуществующие связи. Победили универсалы - модели, которые одинаково среднего кодят, рассуждают и резюмируют.
Но у меня оставался вопрос. Весь мой корпус - русский: база знаний по инфраструктуре, документация, поисковая выдача на русском. Универсальные модели всё это читают через переводной токенизатор, собирая русские слова из кусков. А если взять модель, которую русскоязычный корпус учили с нуля?
Сравнение плотности токенизации на одном и том же русском тексте (356 символов):

smollm3 формально плотнее, но на английском YandexGPT не проигрывает никому, а вот против участников второй части выигрыш заметный: qwen3.8 тратит на тот же русский текст на 23% больше токенов, LFM2.5 - на 59%. Меньше токенов = больше контекста в те же 32k и быстрее генерация. Плотность - не главное достоинство, но приятный бонус нативного инструмента.
Для проверки взял YandexGPT-5-Lite-8B-instruct и прогнал через оба пайплайна из прошлых частей: локальный RAG по базе знаний и ответчик над веб-выдачей. Главный вывод: самое ценное в этой модели не то, как она отвечает, а то, как она отказывается отвечать.
Серия «Домашний Perplexity»
- Часть 1: Как собрать свой домашний Perplexity на нулевом бюджете - добыча: SearXNG, Camoufox, computer_use против капчи
- Часть 2: Кто думает над выдачей - сравнение пяти локальных моделей над грязным контекстом
Роль генератора в пайплайне
Агентская схема работает так: retrieval собирает чанки, специализированная модель формулирует по ним ответ, а главная модель агента обрабатывает результат - проверяет, дополняет, решает, что делать дальше. Лёгкий генератор в этой схеме - расходник: он делает дешёвую механическую работу "превратить чанки в связный текст с цитатами", не тратя контекст и токены дорогой основной модели.

Схема элементарная:
запрос → retrieval (поиск чанков) → контекст с номерами → LLM → ответ с цитатами
Retrieval отвечает за "найти релевантное". Генератор - за "сформулировать ответ строго по найденному". Это разные компетенции, и для второй нужна instruct-модель: base-модель без дообучения продолжит текст вместо ответа на вопрос,_instruction-tuned - поймёт систему ролей и будет держать формат.
Почему YandexGPT-5-Lite-8B-instruct:
- 8B параметров, квант Q4_K_M весит 4.6 GB - целиком влезает в 8 GB VRAM обычной игровой карты (RTX 4060)
- около 29 токенов/сек на этой карте
- контекст 32k - топ-10 чанков с запасом
- сильный русский - для локальной базы знаний на русском это решающий фактор
- официальный GGUF от Яндекса, есть в LM Studio из коробки
- лицензия: коммерческое использование до 10 млн исходящих токенов в месяц, дальше - API в Yandex Cloud
Не путать с новой линейкой Яндекса: осенью 2026 они выпустили AliceAI-Foundation-80B-A3B и Alice AI Search Pretrain - это другие, серверные модели. Foundation 80B в Q4 весит около 45 GB, для домашней карты не предназначен. Для персонального RAG 8B - рабочая точка.
Подключение за пять минут
Качаем GGUF, кладём в папку моделей LM Studio, загружаем. Дальше - обычный OpenAI-совместимый API на localhost:1234. Единственная ловушка на этом пути: если в системе прописан корпоративный прокси, он перехватит и localhost, и вы получите 502 Bad Gateway на запрос к собственной машине. Лечится пустым ProxyHandler:
import json, urllib.request
# пустой ProxyHandler - обход системного прокси
opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
def llm(messages, max_tokens=700):
req = urllib.request.Request(
"http://127.0.0.1:1234/v1/chat/completions",
data=json.dumps({
"model": "yandexgpt-5-lite-8b-instruct",
"messages": messages,
"temperature": 0.3,
"max_tokens": max_tokens,
}).encode(),
headers={"Content-Type": "application/json"},
)
with opener.open(req, timeout=240) as r:
d = json.loads(r.read())
return d["choices"][0]["message"]["content"], d["usage"]
Пара слов о темплейте. У модели нестандартный формат диалога - реплика генерируется после маркера "Ассистент:[SEP]", переводы строк кодируются особым токеном. Через OpenAI-совместимый endpoint LM Studio всё это обёрнуто автоматически, руками ничего править не надо. Это стало бы проблемой только при прямом запуске через llama.cpp.
Пайплайн 1: RAG-ответчик
Формула простая: ретрив отдаёт чанки, мы нумеруем их и складываем в контекст, модель отвечает со ссылками.
def rag_answer(query, chunks):
context = "\n\n".join(
f"### Источник {i+1}\n{c}" for i, c in enumerate(chunks)
)
messages = [
{"role": "system", "content":
"Ты отвечаешь на вопросы по документации, используя ТОЛЬКО "
"приведённый контекст. Указывай номера источников. Если ответа "
"в контексте нет - скажи об этом прямо."},
{"role": "user", "content": f"Контекст:\n{context}\n\nВопрос: {query}"},
]
return llm(messages)
Живой пример из моего прогона. Вопрос: как переустановить некорректно настроенную реплику домена. Ретрив нашёл два чанка из базы знаний, ответ модели:
Чтобы переустановить некорректно настроенную реплику, выполните:
1. Удалите реплику и выйдите из домена (astra-freeipa-server -U,
astra-freeipa-client -U).
2. Удалите пакеты: sudo apt purge 'aldpro*' 'freeipa*' 'sssd*' ...
3. Проверьте список профилей в домашнем каталоге.
4. Повторно введите реплику в домен.
Источник: [1]
193 prompt-токена, 6.5 секунды. Атрибуция на месте, шаги по делу.
Пайплайн 2: ответчик над веб-выдачей
Тот же принцип, но retrieval заменён на веб-поиск. Мини-Perplexity на локальной модели:
def search_answer(question):
hits = web_search(question, limit=4)["data"]["web"]
pages = web_extract([h["url"] for h in hits[:2]], char_limit=2500)
parts = [
f"[{i}] {p.get('title','')}\n{(p.get('content') or '')[:2200]}"
for i, p in enumerate(pages["results"], 1)
]
messages = [
{"role": "system", "content":
"Ты отвечаешь на вопрос по приведённым материалам. Указывай "
"номера источников в квадратных скобках. Если данных "
"недостаточно - так и скажи."},
{"role": "user", "content":
"Материалы:\n" + "\n\n".join(parts) + f"\n\nВопрос: {question}"},
]
return llm(messages)
Реальный прогон: "как включить отладку регулярных выражений в Python". Поиск нашёл статью, extract вытащил текст, ответ:
Чтобы включить отладку регулярных выражений в Python, нужно
использовать флаг `re.DEBUG` в методе `re.compile` [1].
722 prompt-токена, 1.2 секунды генерации.
Восемь тестов на честность
Теперь главное. Я собрал восемь сценариев, где правильное поведение генератора разное: полный ответ, частичный ответ или отказ. Результаты живых прогонов:
Тест | Результат | Метрики
----------------------------------------|--------------------------------|-------------
RAG: поиск по базе, ответ существует | полный ответ + атрибуция [1] | 193 tok, 6.5s
RAG: вопроса в контексте нет | честный отказ | 11.8s
RAG: веб-добыча + поиск, ответ есть | пошаговая инструкция + ссылка | 193 tok, 6.5s
Web: выдача без ответа (FreeRDP) | отказ: "нет информации" | 488 tok, 5.3s
Web: сниппеты дают версию без деталей | версия названа, детали - отказ | 435 tok, 1.6s
Web: статья НЕ про тот вопрос | отказ - поймала нерелевантность| 868 tok, 1.1s
Web+extract: релевантная статья | точный ответ + [1] | 722 tok, 1.2s
Контраст: противоречивые источники | хронология, конфликт показан | 1.4s
Разберу два показательных случая.
Нерелевантная выдача. Искал "фичи Python 3.14", поиск вернул статью про полезные фичи Python вообще - похоже, но не то. Модель не притянула материал за уши:
В предоставленных материалах нет информации о ключевых
особенностях Python версии 3.14 [1][2].
Противоречивые источники. Подал два блока: "релиз 15 марта" и "перенесён на апрель из-за сертификации". Ответ:
Версия X.Y была запланирована к выходу на 15 марта 2027 года,
однако позже выход был перенесён на апрель 2027 года из-за
проблем с сертификацией [1], [2].
Хронология восстановлена, конфликт не замолчан, обе ссылки на месте.
Почему это важно. RAG-генератор, который выдумывает, опаснее, чем его отсутствие: пользователь получает уверенный текст со ссылкой на источник, которого не существует. Модель, которая говорит "в контексте этого нет", заставляет пайплайн честно сказать "не нашёл" - и это правильное поведение для ответчика над базой знаний. Из восьми тестов модель ни разу не сгаллюцинировала факт сверх контекста.
Отсюда правило: ответственность за релевантность - на retrieval, не на генератор. Если поиск вернул шум, модель откажется отвечать - и правильно сделает. Улучшать надо поиск, а не промпт.
Скорость зависит от длины контекста: ответы на 200-700 prompt-токенах занимают 1-6 секунд, а с 900 токенами и большим max_tokens - до 12. Для интерактивного чат-бота нормально, для батч-обработки - закладывайте очередь.
Лицензия разрешает коммерческое использование до 10 млн исходящих токенов в месяц. Для внутреннего инструмента на команду - с запасом, для публичного сервиса - считайте экономику или берите API.
Где это применять
Годится: внутренние базы знаний, персональный "Perplexity" над вебом, ответчик в чат-боте по документации, суммаризация найденного.
Не годится: агентные сценарии с вызовом инструментов (function calling у модели есть, но после дообучения слабый), многошаговое планирование, сложный код - там нужны модели классом выше, а 8B - это про другое: дешёвый, быстрый, честный генератор текста по контексту.
Формула простая: эмбеддинги находят, генератор отвечает. Остальное - детали реализации.
