Игорь Градов
Игорь Градов
7 мин
ai

RAG на 2181 рецепте: шесть ловушек, о которых молчат туториалы

Русскоязычная база из 2181 рецепта с КБЖУ и аллергенами, работающий код на Python и Qdrant, и честный разбор шести конкретных ограничений подхода «в лоб» делают этот материал редким практическим пособием по RAG (retrieval-augmented generation, генерация с подключением внешних данных) именно для программистов, а не для исследователей.

Почему это важно

Большинство туториалов по RAG останавливаются на «индексируй и ищи», но молчат о том, что эмбеддинг не понимает чисел, один запрос на всё меню даёт перекос выдачи, а фильтр аллергенов по точному совпадению строк опасен для пользователя. Здесь разобраны все шесть ловушек на реальной задаче.

Автор оригинального разбора, программист, не специалист по данным, выбрал RAG вместо дообучения (fine-tuning, обучение модели на собственных данных) по прагматичной причине: подход переносим между любыми моделями и площадками размещения. Но «быстро запилить RAG» и получить качественный результат, который не разорит на токенах (единицах текста, за которые платите при каждом запросе к модели), оказалось не одно и то же. Репозиторий с полным кодом опубликован на GitHub.

Какую задачу решаем?

Есть база из 2181 рецепта на русском языке. Каждая запись содержит текст рецепта и метаданные: тип блюда, ингредиенты, аллергены, КБЖУ на 100 граммов. Над базой работает ИИ-агент (автономная программа, которая сама принимает решения) в роли шеф-повара на модели gpt-5-nano.

Пользователь говорит: «2000 ккал в день, три приёма пищи, аллергия на глютен». Агент собирает меню только из рецептов базы.

От RAG требуется ровно две вещи:

  • Найти действительно подходящие рецепты.
  • Не завалить модель лишним текстом: gpt-5-nano хуже справляется с длинным зашумлённым контекстом, а за каждый лишний токен приходится платить.

Что понадобится

  • Python 3.10+ и базовое владение языком.
  • Qdrant (векторная база данных, хранилище, где тексты представлены числовыми векторами для быстрого поиска по смыслу). Можно поднять локально через Docker.
  • Доступ к API OpenAI (модели эмбеддингов text-embedding-3-small или text-embedding-3-large, модель gpt-5-nano для генерации).
  • Репозиторий проекта: github.com/2union/AutoRAGDemo. Два ключевых скрипта лежат в папке DirectIndex.
  • Время: базовую версию «в лоб» можно запустить за 30 минут. Разбор ограничений и доработка займут от нескольких часов до пары RAG days (дней целиком посвящённых настройке пайплайна поиска и генерации).

Пошаговая инструкция: RAG «в лоб»

  1. Подготовьте данные. Загрузите 2181 рецепт в формате, где каждая запись содержит поле text (текст рецепта) и metadata (тип блюда, ингредиенты, аллергены, КБЖУ).

  2. Создайте индекс в Qdrant. Скрипт index_qdrant.py разбивает документы на батчи, превращает текст каждого рецепта в вектор через модель эмбеддингов и загружает точки в коллекцию:

# Фрагмент index_qdrant.py
# Полный код: github.com/2union/AutoRAGDemo/blob/main/DirectIndex/index_qdrant.py
for i in range(0, len(docs), batch_size):
    batch = docs[i:i + batch_size]
    vectors = embed([d["text"] for d in batch])
    points = [
        PointStruct(
            id=str(uuid.uuid5(uuid.NAMESPACE_URL, doc["id"])),
            vector=vector,
            payload={**doc["metadata"], "text": doc["text"],
                     "recipe_id": doc["id"]},
        )
        for doc, vector in zip(batch, vectors)
    ]
    qdrant.upsert(collection_name=COLLECTION, points=points)
  1. Запустите сервер запросов. Скрипт rag_server.py принимает POST-запрос с параметрами пользователя (калории, число приёмов пищи, аллергии), склеивает их в текстовый промпт (текстовая инструкция для модели), превращает в вектор и ищет ближайшие рецепты:
# Фрагмент rag_server.py
# Полный код: github.com/2union/AutoRAGDemo/blob/main/DirectIndex/rag_server.py
vector = embed_query(query)
result = qdrant.query_points(
    collection_name=COLLECTION,
    query=vector,
    query_filter=build_filter(req),
    limit=req.top_k * 2,
    with_payload=True,
)
  1. Отправьте тестовый запрос на эндпоинт POST /menu с параметрами: kcal_target=2000, meals_per_day=3, allergies=["глютен"]. Сервер вернёт меню, собранное из рецептов базы.

  2. Оцените результат глазами и зафиксируйте, что именно пошло не так. Скорее всего, вы столкнётесь с одной или несколькими проблемами из списка ниже.

Шесть ловушек подхода «в лоб»

Эмбеддинг не понимает чисел и отрицаний. Вектор строки «Ккал в день: 2000, Аллергии: глютен» не означает «рецепты на 2000 ккал без глютена». Модель эмбеддингов кодирует смысловую близость текста, а не логику. Слово «глютен» в запросе скорее приблизит к рецептам с глютеном, чем отдалит от них. Та же история с идентификаторами недавних блюд: они просто попадают в текст запроса и никак не исключаются из выдачи.

Один запрос на всё меню. Меню состоит из основного блюда, супа, салата, напитка и десерта. А запрос один. В базе 1550 основных блюд из 2181, поэтому в выдаче почти наверняка доминируют именно они. Напитков и десертов модели может просто не достаться.

Векторизуется полный текст с шагами приготовления. В поле text лежат и «разогрейте духовку до 175 °C», и «выпекайте 25 минут». Для выбора блюда в меню это шум: он размывает вектор и раздувает контекст.

Только dense-поиск. Векторный поиск хорошо ловит общий смысл, но промахивается на конкретных словах: «панчетта», «соус Вустершир», «нут». Лексический поиск BM25 (классический полнотекстовый поиск по совпадению слов) часто точнее для таких запросов, а лучше всего работает комбинация обоих методов, гибридный поиск. Но какая комбинация и с какими весами? Без метрик это гадание.

Фильтр аллергенов сравнивает строки точно. В данных молоко встречается как «Молоко», «Молочные продукты», «Молочный белок», «Молочный», «Сливочное масло». Пользователь с аллергией на «молоко» получит рецепт с «Молочным белком». Это уже вопрос не качества поиска, а безопасности.

Нечем измерить качество. Почему text-embedding-3-small, а не large? Почему top_k = 40, а не 5? Нужен ли реранкер (дополнительная модель, которая пересортировывает результаты поиска по релевантности)? Без метрик любое изменение превращается в гадание: поменяли модель, посмотрели глазами на пару запросов, вроде стало лучше. А может стало хуже, но попалась удачная пара примеров.

Ловушки 1, 2 и 5 решаются программно: фильтрами Qdrant и отдельными запросами на каждый слот меню. Ловушки 3, 4 и 6 требуют экспериментов с метриками: подбор модели эмбеддингов, способа поиска, гибридных весов, реранкера, размера контекста. Именно на эти RAG days уходит основное время.

Что получается на практике

Запрос: «2000 ккал в день, три приёма пищи, аллергия на глютен». Результат «в лоб»: модель возвращает меню, где 4 из 5 позиций являются основными блюдами (напиток и десерт выпали), один рецепт содержит «Молочный белок» (при аллергии на молоко это был бы пропущенный аллерген), а в контекст ушли полные тексты рецептов с шагами приготовления, что раздуло запрос и увеличило расход токенов. Код для воспроизведения лежит в github.com/2union/AutoRAGDemo/tree/main/DirectIndex.

Частые ошибки
  • Не проверять фильтр аллергенов на синонимах. Точное совпадение строк пропускает «Молочные продукты», «Сливочное масло» и десятки других вариантов написания одного аллергена. Для продуктового решения это критично.
  • Индексировать полный текст рецепта. Шаги приготовления засоряют вектор и увеличивают контекст. Индексируйте только ту часть, которая нужна для выбора блюда: название, ингредиенты, тип, КБЖУ.
  • Полагаться только на векторный поиск. Для точных терминов и названий ингредиентов лексический поиск BM25 работает лучше. Попробуйте гибридный режим Qdrant.
  • Оценивать качество «на глаз». Без набора тестовых запросов и метрик (precision, recall) вы не узнаете, стало ли лучше после изменений. Заведите хотя бы 20 запросов с ожидаемыми ответами.

Что делать с этим по ролям?

Программисту или техлиду. Склонируйте репозиторий, запустите базовую версию, воспроизведите все шесть ловушек на своих данных. Это займёт один вечер и покажет, где ваш RAG теряет качество. Выделите один-два RAG days на эксперименты с метриками.

Автору Дзена или копирайтеру. Если вы строите контентного бота, который отвечает по вашей базе статей, логика та же: не индексируйте весь текст, выделите ключевые фрагменты. И обязательно проверяйте, что бот не галлюцинирует (галлюцинация означает, что модель уверенно выдумывает то, чего не было в ваших данных).

Предпринимателю в РФ и СНГ. RAG работает с любой моделью, включая доступные в России YandexGPT и GigaChat. Принцип тот же: данные хранятся у вас, модель получает только нужный контекст. Векторная база Qdrant бесплатна и разворачивается на своём сервере, данные не уходят за периметр.

Совет редакции dzen.guru

Этот разбор ценен тем, что автор честно показал: «быстрый RAG» собирается за полчаса, но без метрик и без понимания ограничений он будет выдавать опасный мусор, особенно в задачах, где важна точность (аллергены, медицинские данные, юридические документы). Я бы начал именно с пятого пункта: заведите 20 тестовых запросов и зафиксируйте ожидаемые ответы до того, как менять модель эмбеддингов или добавлять реранкер. Без этого шага все ваши RAG days превращаются в гадание. Честная оговорка: разбор построен на одной конкретной задаче (рецепты), и не каждая ловушка проявится в вашем домене. Но фильтр по точному совпадению строк и «один запрос на всё» встречаются почти везде.

Хотите применять нейросети в контенте на практике?

В dzen.guru мы разбираем рабочие сценарии для авторов и предпринимателей, от RAG до промпт-инжиниринга

Попробовать dzen.guru

Полный код обоих скриптов и база рецептов доступны в репозитории github.com/2union/AutoRAGDemo. Начните с базовой версии, воспроизведите ошибки, заведите тестовые запросы и только потом меняйте модель или добавляйте гибридный поиск: так каждое изменение будет измеримым, а не интуитивным.

Поделиться:TelegramVK
Игорь Градов
Игорь Градов

Основатель dzen.guru. Эксперт по монетизации и продвижению на Дзен. Автор курса «Старт на Дзен 2026».

Комментарии

Читайте также

Генеративный ИИ в бизнес-процессах: мышление, развитие и будущее на примере Sminex
ai

Генеративный ИИ в бизнес-процессах: мышление, развитие и будущее на примере Sminex

Генеративный ИИ уже работает внутри российских компаний, и опыт команды Sminex показывает, как именно выстроить процесс внедрения нейросетей, не потратив…

6 мин
Галлюцинации нейросетей: протокол проверки за 10 минут, который ловит до 33% ошибок
ai

Галлюцинации нейросетей: протокол проверки за 10 минут, который ловит до 33% ошибок

Нейросети выдумывают факты, подстраиваются под ваше мнение и в лабораторных тестах целенаправленно скрывают информацию, и всё это можно научиться ловить за…

8 мин
Кому принадлежит код, который написал ваш ИИ-агент: промпт не считается творчеством
ai

Кому принадлежит код, который написал ваш ИИ-агент: промпт не считается творчеством

Промпт сам по себе не делает вас автором кода, который выдал ИИ-агент, и Пресненский суд Москвы уже показал это на деле с картинками, а с 1 марта 2027 года…

6 мин