В середине 2026 года счёт за GPT-4o и Claude 4 Opus в Flowwow перевалил за $12 000 в месяц. Маржа таяла, юристы разводили руками из-за GDPR, а latency бесила даже ботов. Мы решили засунуть всё в свой дата-центр. Итог: RAG-ассистент на n8n с self-hosted LLM, который обходится в 5,5 раз дешевле и работает быстрее облачных аналогов. Да, без фанатизма и без API.
Дисклеймер: статья — не реклама вендоров. Мы используем то, что реально работает в проде. Все цифры — из нашего счета.
Почему n8n, а не LangChain или ручное API?
Сначала хотели написать логику на Python. Но поддержка 50+ интеграций, визуальные workflow, мониторинг прямо в UI — это перевесило. n8n (версия 1.82 на момент написания) позволяет инженерам не из AI-команды менять pipeline без деплоя кода. Плюс community-ноды для Ollama и Qdrant уже есть в маркетплейсе. Не пришлось даже писать кастомную ноду для ретривера.
Кстати, если вы ещё не в курсе, как n8n работает с агентами, советую глянуть наш гайд про голосового ассистента на n8n — там разобраны основы работы с памятью и инструментами.
Архитектура: что у нас под капотом
Мы не изобретали велосипед. Стандартный RAG chain: query → embed → retrieve → rerank → generate. Но с тремя особенностями:
- Self-hosted LLM — Qwen2.5-14B-Instruct через Ollama (1× RTX 6000 Ada в нашей стойке). Не Mistral, не Llama — именно Qwen дал лучший balance quality/speed для русскоязычной базы знаний.
- Векторная БД — Qdrant (не Chroma, не FAISS). Причина: sharding и гибридный поиск из коробки, без костылей.
- Chunking — не фиксированный size, а семантическое разделение по заголовкам Markdown + hierarchical parent-child (родительский чанк 512 токенов, дочерние 128).
| Компонент | Что используем | Почему |
|---|---|---|
| Оркестратор | n8n 1.82 (self-hosted) | Low-code, Community nodes, webhooks |
| LLM | Qwen2.5-14B-Instruct (Ollama) | Лучшее качество для русского языка в 14B |
| Embeddings | mxbai-embed-large-v1 (Ollama) | 768 dim, отлично работает с Qdrant |
| Vector DB | Qdrant 1.12 | Sharding, гибридный поиск, быстрый deploy |
| Reranker | BAAI/bge-reranker-v2-m3 | Поднимает precision на +15% |
Шаг 1. Поднимаем железо и софт
Железо: одна машина с RTX 6000 Ada (48GB VRAM) хватает на Qwen2.5-14B (int4 quant) + embeddings + reranker + Qdrant. Для прода докупили вторую карту — на раздельные инференс и векторный поиск, но для старта хватит и одной.
Ставим Docker Compose на Ubuntu 24.04 LTS. Вот рабочий конфиг:
version: '3.8'
services:
n8n:
image: n8nio/n8n:1.82.0
ports:
- "5678:5678"
volumes:
- ./n8n_data:/home/node/.n8n
environment:
- N8N_METRICS=true
- EXECUTIONS_DATA_PRUNE=true
restart: unless-stopped
qdrant:
image: qdrant/qdrant:v1.12.0
ports:
- "6333:6333"
volumes:
- ./qdrant_storage:/qdrant/storage
ollama:
image: ollama/ollama:0.5.7
ports:
- "11434:11434"
volumes:
- ./ollama_data:/root/.ollama
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
reranker:
image: ghcr.io/huggingface/text-generation-inference:3.0.0
command: --model-id BAAI/bge-reranker-v2-m3
ports:
- "8080:80"
volumes:
- ./reranker_cache:/data
environment:
- HF_TOKEN=${HF_TOKEN}
Внимание: без NVIDIA Container Toolkit docker не увидит GPU. Не забудьте установить nvidia-container-toolkit перед стартом.
Шаг 2. Заливаем базу знаний и индексируем
У нас база знаний — 2000+ markdown-файлов с документацией, FAQ и инструкциями. Пишем скрипт на Python (запускаем как n8n Execute Command), который:
- Читает все .md файлы.
- Сплитит по заголовкам второго уровня (##) — каждый блок становится родительским чанком.
- Внутри блока режет по 128 токенов с overlap 20 — дочерние чанки.
- Для каждого дочернего чанка генерирует эмбеддинг через Ollama.
- Записывает в Qdrant коллекцию с payload (текст, метаданные, id родителя).
Код для эмбеддинга (используем Ollama REST API):
import requests
def get_embedding(text: str) -> list[float]:
resp = requests.post(
"http://ollama:11434/api/embeddings",
json={"model": "mxbai-embed-large-v1", "prompt": text}
)
return resp.json()["embedding"]
После индексации проверяем в n8n: триггер на Webhook → HTTP Request к Qdrant на поиск → возвращаем топ-5 chunks.
Шаг 3. Строим workflow в n8n
Самый ответственный момент. Workflow состоит из пяти нод:
- Webhook — принимает вопрос от пользователя (например, из Telegram бота).
- HTTP Request — отправляет запрос к Ollama embeddings.
- HTTP Request — поиск в Qdrant (hybrid query: dense + sparse).
- HTTP Request — reranker на TGI (переранжирует 20 candidates до 5).
- Ollama Chat Model node (из n8n Community) — генерирует ответ с контекстом.
Вот пример конфигурации поискового запроса в Qdrant (n8n HTTP Request):
{
"method": "POST",
"url": "http://qdrant:6333/collections/knowledge_base/points/search",
"headers": {
"Content-Type": "application/json"
},
"body": {
"vector": {{$json.embedding}},
"limit": 20,
"with_payload": true,
"params": {
"hybrid": true
}
}
}
После reranker формируем prompt для LLM:
// n8n Code node
const context = $items("Reranker").map(item => item.json.payload.text).join("\n---\n");
const question = $json.question;
return {
messages: [
{
role: "system",
content: "Ты поддержка Flowwow. Отвечай на русском, используя только контекст. Если не знаешь — скажи честно."
},
{
role: "user",
content: `Контекст:\n${context}\n\nВопрос: ${question}`
}
]
};
Типичные ошибки (наши грабли)
Ошибка №1. Chunking без учёта структуры.
Сначала резали по 256 токенов тупо. Ответы LLM были неполными, потому что обрывались на середине инструкции. Решение — семантический сплит по Markdown-заголовкам с parent-child.
Ошибка №2. Забыли про rate limit Ollama.
Когда запустили prod, Qdrant и reranker начали слать запросы параллельно — Ollama на одном GPU упал в OOM. Пришлось добавить очередь: n8n нода "Wait" перед каждой генерацией (только на первом запросе — дальше кэшируем embeddings).
Ошибка №3. Не поставили reranker.
Первые версии без reranker выдавали мусор: LLM могла ответить на основе нерелевантного чанка. BGE-reranker поднял accuracy с 72% до 89% на нашем тестовом сете.
Если у вас мало GPU — reranker можно заменить простым косинусным расстоянием, но точность упадёт. Лучше всё-таки выделить отдельный CPU-инференс для reranker (через ONNX).
Во сколько это обошлось?
Считаем ежемесячные затраты (дата-центр + железо + амортизация):
- Аренда 2× RTX 6000 Ada + 64GB RAM — $1 200 (hetzner auction).
- Обслуживание, сетевое, электричество — $400.
- Амортизация сервера (за 3 года) — $300.
- Итого: ~$1 900 / месяц.
До этого мы платили за GPT-4o и Claude 4 Opus ~$12 000 (токены + фиксированные планы). Экономия в 5,5 раз. Да, мы потеряли в скорости (6-8 секунд против 1-2 у облачных), но для внутреннего ассистента это некритично.
Для полного погружения в локальный RAG без API советую почитать этот гайд про Agentic RAG — он идеально дополняет наш кейс (у нас chain, а там самостоятельные агенты).
Кстати, мы также используем эту инфраструктуру для голосовых ассистентов. Если интересно — вот отдельный кейс с n8n и RAG в голосе.
Неочевидный совет (зачем я это писал)
Большинство материалов про RAG научат вас ставить Ollama, Qdrant и писать простой pipeline. Но реальная боль начинается, когда модель отвечает "я не знаю" на вопрос, на который есть ответ в базе. Главная причина — перекос токенов в системном промпте. Мы потратили неделю, чтобы выставить правильный temperature (0.15) и добавить few-shot примеры из нашей базы прямо в system prompt. Без этого ни reranker, ни правильный chunking не спасают.
Второй момент: не гонитесь за самой большой моделью. Qwen2.5-7B дал нам 85% качества, 14B — 92%, а Llama-3.1-70B (quantized) — 94%, но прирост скорости падал в 3 раза. Для внутреннего ассистента 92% — золотая середина.
И напоследок: если вы думаете, что self-hosted — это про анархию и "никаких облаков", то ошибаетесь. Через месяц после запуска мы поняли, что для обновления эмбеддингов при изменении базы знаний нужна CI/CD. Сейчас у нас GitHub Actions → n8n webhook → переиндексация одной коллекции. Без автоматизации локальный RAG превращается в ад ручного управления.
Технологии не стоят на месте. В 2026 году локальные LLM уже на равных конкурируют с облачными. Наш кейс — лучшее тому доказательство.