Я собрал RAG с нуля на Python, чтобы понять, как он работает
Я использовал LangChain в продакшене в течение шести месяцев. Я не мог объяснить, как он работает. Я не знал, почему выбирал те или иные метрики и как текст превращался в векторы. Библиотека скрывала логику.
Чтобы это исправить, я удалил фреймворк. Я написал RAG-пайплайн с нуля, используя 500 строк чистого Python.
Вот чему я научился, собирая стек вручную.
Проблема «черных ящиков»
Когда вы используете высокоуровневые библиотеки, вы теряете контроль. Я видел, как модели галлюцинируют фактами или приводят неверные цитаты. Я не мог понять, где именно кроется ошибка: в чанкере, модели эмбеддингов или в промпте.
Когда вы строите систему сами, каждый слой можно проверить. Вы можете вывести в консоль именно те чанки, которые отправляются в LLM. Вы можете точно увидеть, где разрывается предложение.
Пять уровней RAG
RAG — это не один алгоритм. Это пять различных процессов, работающих в связке:
- Chunking: решение о том, как разбивать текст.
- Embedding: превращение текста в математические значения.
- Retrieval: поиск нужных фрагментов.
- Prompt Construction: инструкции модели о том, как себя вести.
- Generation: получение финального ответа.
Уроки разработки
Chunking — самый важный этап В большинстве туториалов это пропускают. Если не использовать перекрытие (overlap), вы потеряете контекст на границах фрагментов. Я использовал метод скользящего окна с посимвольным перекрытием. Это гарантирует, что модель увидит связь между двумя чанками.
Метрики расстояния имеют значение Я потратил часы на отладку плохих результатов поиска. Проблема была не в данных, а в метрике. ChromaDB по умолчанию использует L2-расстояние. Для семантического поиска вам нужна косинусная близость (Cosine similarity). Одна строка кода изменила всё.
Промптам нужны ограничения LLM — это инструмент для дополнения текста, а не оракул. Если задать расплывчатый вопрос, модель выдумает ответ. Я научился использовать строгий шаблон отказа. Я сказал модели: «Если в контексте нет ответа, скажи, что ты не знаешь». Это снизило уровень галлюцинаций с 40% до 5%.
Группируйте запросы Отправка одного HTTP-запроса на каждый чанк — это медленно. Отправка их пачками (batches) происходит гораздо быстрее. Это позволяет локальной модели выстраивать конвейер обработки.
Тестируйте снизу вверх Не пишите тесты в самом конце. Сначала протестируйте чанкер. Затем — эмбеддер. Затем — хранилище. Если тестировать в последнюю очередь, вы будете тестировать баги вместо логики.
Если вы чувствуете, что не до конца понимаете свой AI-стек, соберите его сами. Код — это не цель. Цель — это понимание.
Optional learning community: https://t.me/GyaanSetuAi
