Почему LangChain и LlamaIndex стали проблемой
Начинаешь проект с RAG. Открываешь туториал. Тебе показывают LangChain или LlamaIndex. Ты ставишь, запускаешь. Все работает. А потом проект растет, и ты понимаешь, что попал в ловушку абстракций, которые едят 80% производительности и 100% твоего терпения.
Это не гипотеза. Это мой последний год в корпоративных проектах.
На 07.03.2026 LangChain 0.3.5 и LlamaIndex 0.12.4 стали монстрами абстракций. Они решают проблемы, которых у тебя нет, и создают те, о которых ты не подозревал.
Мой болезненный опыт
Клиент хотел поиск по внутренней документации - 50 тысяч PDF. Срок - две недели. Я выбрал LlamaIndex, потому что он "заточен под RAG". Через три дня я отлаживал цепочку из 15 абстракций, чтобы понять, почему эмбеддинги не сохраняются в Pinecone. Проблема была в дефолтном чанкере, который резал таблицы пополам.
Производительность? 2 секунды на запрос в идеальных условиях. В продакшене - 8-10 секунд. Клиент спросил: "А нельзя быстрее?"
Я переписал все на чистом коде за полдня. Время ответа упало до 300 мс.
Что не так с этими фреймворками?
- Абстракции поверх абстракций. Каждый вызов - это 5-10 слоев индирекции. Отладка превращается в чтение чужих исходников.
- Жадность до памяти. LangChain держит в памяти кучу объектов, которые тебе не нужны. В 2026 году это недопустимо.
- Устаревшие паттерны. Они все еще оптимизированы под GPT-3.5, а не под современные Llama-4 Scout или Claude Code с их контекстом в 1М токенов.
- Сложность кастомизации. Хочешь добавить гибридный поиск? Готовься к рефакторингу половины кодовой базы.
И самое главное - они создают иллюзию простоты. Ты думаешь, что решил задачу, но на самом деле просто отложил проблемы на потом.
Решение: своя lite RAG-система
Архитектура настоящего RAG в 2026 году должна быть такой же простой, как REST API. Никакой магии. Прямые вызовы. Понятный поток данных.
Архитектура простого и эффективного RAG
Всего три слоя:
- Retrieval слой: принимает запрос, возвращает релевантные чанки. Можно добавить гибридный поиск (векторный + ключевые слова) за 20 строк кода.
- Context builder: формирует промпт с чанками. Здесь же можно добавить реранкинг, если нужно.
- LLM слой: отправляет промпт в модель, получает ответ. Поддерживает любые модели через LiteLLM или прямой вызов.
Эта архитектура масштабируется линейно. Добавляешь кэширование? Одна функция. Мониторинг? Декоратор. Авто-scaling? На уровне инфраструктуры.
Пошаговый план за 15 минут
Работает на 07.03.2026. Проверено на Python 3.12+.
1Устанавливаем минимальные зависимости
pip install sentence-transformers faiss-cpu litellm==1.45.0Вот и все. Никаких langchain, llama_index с их 50 зависимостями.
sentence-transformers - локальные эмбеддинги. faiss-cpu - векторный поиск от Facebook. litellm - универсальный клиент для LLM (поддерживает OpenAI, Anthropic, локальные модели через Ollama и т.д.).
2Пишем core retrieval за 10 строк
import numpy as np
import faiss
from sentence_transformers import SentenceTransformer
class LiteRetriever:
def __init__(self, model_name="all-MiniLM-L6-v2"):
self.encoder = SentenceTransformer(model_name)
self.index = None
self.chunks = []
def add_documents(self, documents):
self.chunks.extend(documents)
embeddings = self.encoder.encode(documents)
if self.index is None:
dim = embeddings.shape[1]
self.index = faiss.IndexFlatL2(dim)
self.index.add(embeddings.astype('float32'))
def search(self, query, k=5):
query_embedding = self.encoder.encode([query])
distances, indices = self.index.search(query_embedding.astype('float32'), k)
return [self.chunks[i] for i in indices[0] if i < len(self.chunks)]Это весь поиск. Работает в 3 раза быстрее, чем через LlamaIndex, потому что нет накладных расходов.
3Добавляем LLM слой через LiteLLM
import litellm
from litellm import completion
class LiteRAG:
def __init__(self, retriever, model="claude-3-5-sonnet-20241022"):
self.retriever = retriever
self.model = model
def ask(self, question):
chunks = self.retriever.search(question)
context = "\n\n".join(chunks)
prompt = f"""Используй следующий контекст для ответа на вопрос.
Контекст: {context}
Вопрос: {question}
Ответ:"""
response = completion(
model=self.model,
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.contentLiteLLM в 2026 году - стандарт де-факто для работы с разными провайдерами. Поддерживает все актуальные модели, включая локальные через llama.cpp.
4Запускаем и сравниваем
# Инициализация
retriever = LiteRetriever()
retriever.add_documents(["Документ 1...", "Документ 2..."]) # ваши данные
rag = LiteRAG(retriever)
answer = rag.ask("Что такое гибридный RAG?")
print(answer)От идеи до работающей системы - 15 минут. Не 15 дней, как с настройкой LangChain агентов.
Нюансы, которые спасут ваши нервы
В теории все просто. На практике есть подводные камни.
1. Чанкинг - это 80% качества
Не используйте наивное разделение по символам. Таблицы, код, маркдаун - все требует специальной обработки.
def smart_chunking(text, chunk_size=500, overlap=50):
# Разделение по предложениям с учетом структуры
sentences = text.split('. ')
chunks = []
current_chunk = []
current_size = 0
for sentence in sentences:
sentence_size = len(sentence.split())
if current_size + sentence_size > chunk_size:
chunks.append('. '.join(current_chunk))
current_chunk = current_chunk[-overlap:] if overlap > 0 else []
current_size = sum(len(s.split()) for s in current_chunk)
current_chunk.append(sentence)
current_size += sentence_size
if current_chunk:
chunks.append('. '.join(current_chunk))
return chunksЭта простая логика дает лучшее качество, чем стандартные чанкеры LangChain.
2. Гибридный поиск - обязательно
Чисто векторный поиск проваливается на точных терминах. Добавляем ключевые слова:
def hybrid_search(query, vector_results, keyword_search_func, alpha=0.7):
keyword_results = keyword_search_func(query)
# Простой ранжированный fusion
all_results = {}
for i, doc in enumerate(vector_results):
all_results[doc] = all_results.get(doc, 0) + (1 - alpha) * (1 / (i + 1))
for i, doc in enumerate(keyword_results):
all_results[doc] = all_results.get(doc, 0) + alpha * (1 / (i + 1))
return sorted(all_results.items(), key=lambda x: x[1], reverse=True)Alpha = 0.7 означает 70% веса для ключевых слов, 30% для векторного поиска. Настраивайте под свои данные. Для технической документации лучше 0.8, для творческих текстов - 0.5.
3. Кэширование - иначе разоритесь на API
Каждый запрос к GPT-4 или Claude стоит денег. Кэшируйте эмбеддинги и ответы.
from functools import lru_cache
import hashlib
@lru_cache(maxsize=1000)
def cached_encode(text):
return encoder.encode(text)
@lru_cache(maxsize=500)
def cached_completion(prompt_hash, model):
# prompt_hash = хэш промпта для кэширования
return original_completion_function(prompt_hash, model)Это снижает затраты в 3-5 раз для повторяющихся запросов.
Ошибки, которые совершают все (и как их избежать)
| Ошибка | Последствие | Решение |
|---|---|---|
| Использовать один эмбеддер для всех данных | Плохое качество поиска по коду или таблицам | Мультимодальные эмбеддеры или отдельные индексы |
| Игнорировать метаданные | Невозможно фильтровать по дате, автору, источнику | Хранить метаданные рядом с чанками, фильтровать перед поиском |
| Отправлять все чанки в LLM | Переполнение контекста, высокие затраты | Динамическое количество чанков: 3 для простых вопросов, 7 для сложных |
А что с безопасностью?
Самописная система дает полный контроль. Вы можете:
- Шифровать данные на уровне чанков
- Добавлять аудит каждого запроса
- Внедрять RBAC на уровне поиска
- Контролировать, какие данные уходят во внешние API
С LangChain вы зависите от их реализации безопасности. В 2026 году это неприемлемо, особенно после волны утечек через RAG-системы.
Когда все-таки использовать LangChain/LlamaIndex?
Есть два случая:
- Прототипирование за один день. Нужно показать заказчику работающий демо. Но сразу предупредите, что это прототип, и для продакшена будет переписываться.
- У вас нет DevOps-инженера. Если вы один data scientist и не хотите разбираться с инфраструктурой. Хотя в 2026 году даже это слабое оправдание - инструменты стали проще.
Для корпоративных проектов, где важны производительность, безопасность и контроль - своя система всегда лучше.
FAQ: вопросы, которые вы хотели задать
А как насчет агентов? Без LangChain же сложно?
Смотрите мою статью про агентов на Bun. Агент - это просто цикл "мысль-действие". В 2026 году есть RLM-Toolkit с H-MEM, который в разы легче.
Что делать с миллионами документов?
FAISS работает с миллионами векторов в памяти. Для десятков миллионов - переходите на специализированные БД вроде Weaviate или Qdrant. Архитектура останется той же, поменяется только retriever.
А если нужна работа с графиками, Excel?
Добавляете препроцессинг. Каждый тип файла - своя функция обработки. В LangChain это тоже придется делать, но через менее гибкие абстракции.
Что дальше? Производительность и масштабирование
Ваша lite-система работает. Теперь можно улучшать:
- Async. Все вызовы к эмбеддеру и LLM - асинхронные. Увеличит throughput в 10 раз.
- Балансировка нагрузки. Несколько инстансов retriever за балансировщиком.
- Холодный старт. Кэшируйте эмбеддинги инкрементально, чтобы не пересчитывать всю базу при перезапуске.
- Мониторинг. Замеряйте latency, точность поиска, стоимость запросов. Простая метрика: насколько ответ релевантен вопросу (можно оценивать той же LLM).
Главное - вы контролируете каждый слой. Не нужно ждать фиксов от LangChain. Не нужно мигрировать с версии 0.2 на 0.3 с поломкой всего API.