У любого инженера, работающего с вендорскими системами, есть личная боль: сотни PDF — спецификации протоколов, мануалы, интеграционные описания. Поиск по ним — это «открой семь документов и Ctrl+F в каждом». Отдавать такие документы в облачные LLM нельзя. Решение — полностью локальный RAG: свои модели, свои документы, ничего не покидает домашнюю сеть.

Здесь — моя рабочая конфигурация: RTX 3060 12 ГБ, Ollama, OpenWebUI, ~200 увесистых PDF, и три неочевидных настройки, без которых на моделях 7B RAG просто не работает.

Архитектура

Хост — Proxmox VE (обычный Debian под капотом) с RTX 3060 12 ГБ. Схема сознательно простая:

  • Ollama — нативно на хосте. Никаких VM и контейнеров: пакет ставится прямо на Proxmox-хост, видит GPU напрямую через обычный драйвер NVIDIA. Ноль возни с passthrough, IOMMU и nvidia-container-runtime — для одной карты и одного потребителя это самый короткий путь;
  • OpenWebUI — в Docker Compose там же: интерфейс, управление коллекциями документов, RAG-конвейер. К Ollama ходит по HTTP;
  • SearXNG — в том же compose: локальный метапоиск; OpenWebUI использует его для web-поиска, когда ответа в документах нет. Наружу уходят только поисковые запросы к обычным поисковикам — сами документы никуда не уезжают.

Минимальный compose:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
services:
  openwebui:
    image: ghcr.io/open-webui/open-webui:main
    restart: unless-stopped
    ports:
      - "3000:8080"
    environment:
      OLLAMA_BASE_URL: http://host.docker.internal:11434
    extra_hosts:
      - "host.docker.internal:host-gateway"
    volumes:
      - openwebui:/app/backend/data

  searxng:
    image: searxng/searxng:latest
    restart: unless-stopped
    ports:
      - "127.0.0.1:8081:8080"
    volumes:
      - ./searxng:/etc/searxng

volumes:
  openwebui:

Один нюанс: Ollama по умолчанию слушает только localhost — чтобы контейнер до неё достучался, в systemd-юните задаётся Environment="OLLAMA_HOST=0.0.0.0:11434" (и файрволом закрывается всё, кроме docker-сети и localhost).

Модели

На 12 ГБ VRAM выбор прагматичный:

РольМодельЗачем
Генерация ответовqwen2.5:7b-instructлучший на момент сборки баланс качества/размера для инструкций и работы с контекстом; влезает в VRAM с приличным контекстом
Эмбеддингиbge-m3мультиязычные эмбеддинги (документация — смесь английского и русского), длинные чанки
Rerankerbge-reranker-v2-m3пересортировка найденных чанков по релевантности — заметно поднимает точность попадания
1
2
ollama pull qwen2.5:7b-instruct
ollama pull bge-m3

Скорость на 3060 субъективно: 7B-модель отвечает в темпе чтения — для интерактивной работы с документацией этого достаточно. Гнаться за моделями крупнее на этом железе смысла нет: они уже не помещаются в VRAM целиком, и скорость падает в разы.

Загрузка документов

В OpenWebUI документы живут в Knowledge-коллекциях: создаёте коллекцию, заливаете PDF, дальше она подключается к чату (#имя-коллекции или привязкой к модели). Я залил порядка 200 объёмных PDF — спецификации, мануалы, интеграционные описания.

На таком объёме и вылезают грабли, о которых молчат туториалы с тремя файлами.

Три граблины, без которых ничего не работает

1. Native function calling ломает RAG на маленьких моделях

Симптом: коллекция подключена, а модель отвечает «в документах ничего не найдено» или игнорирует документы вовсе. Причина: в режиме native function calling OpenWebUI ожидает, что модель сама вызовет инструмент поиска по базе — а модели уровня 7B делают это нестабильно: не вызывают, вызывают с пустым запросом, ломают формат вызова.

Лечение: в настройках модели (Admin → Models → ваша модель → Advanced Params) переключить Function Calling: Native → Default (legacy). В legacy-режиме OpenWebUI сам достаёт релевантные чанки и кладёт их в промпт — модели остаётся только читать. Для 7B-моделей это единственный надёжный режим RAG.

2. Эмбеддинги валятся с 503 на большом объёме документов

Симптом: при индексации большой коллекции — ошибки 503 от Ollama, часть документов не индексируется. Причина: OpenWebUI по умолчанию шлёт эмбеддинг-запросы крупными батчами, и Ollama на одной GPU захлёбывается.

Лечение: Admin → Settings → Documents → Embedding Batch Size уменьшить (с дефолтного до 8–16 — подобрать по своему железу), после чего переиндексировать коллекцию. Индексация 200 PDF занимает время — это разовая плата.

3. Гибридный поиск + reranker

Чисто векторный поиск по технической документации промахивается на точных вещах: номера полей, имена параметров, коды ошибок — то, что инженеру нужно чаще всего. Векторная близость находит «похожее по смыслу», а нужно «ровно это».

Лечение: Admin → Settings → Documents:

  • включить Hybrid Search — к векторному поиску добавляется полнотекстовый (BM25), и точные термины начинают находиться;
  • указать Reranking Model: bge-reranker-v2-m3 — кандидаты из обоих поисков пересортировываются кросс-энкодером, в промпт уходят действительно релевантные чанки.

Из этих трёх настроек и складывается разница между «игрушка, которая галлюцинирует» и «инструмент, которым пользуешься каждый день».

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

Хорошо работает: «в каком разделе описано X», «какие значения принимает параметр Y», «что означает ошибка Z» — вопросы, где ответ находится в одном-двух местах во всей базе документов. С указанием источника — OpenWebUI показывает, из каких документов взяты чанки, и это критично: ответу LLM по документации нельзя верить без проверки по первоисточнику.

Слабые места, о которых честно:

  • таблицы — PDF-парсер разбирает сложные многостраничные таблицы посредственно, ответы «по табличным данным» надо перепроверять всегда;
  • схемы и диаграммы — текстовый RAG их не видит в принципе;
  • обобщение по всем документам сразу («сравни, как это сделано в пяти документах») — не для 7B-модели с ограниченным контекстом; это инструмент точечного поиска, не аналитик.

Итог

Полностью локальный RAG на скромной GPU — рабочая вещь, если принять два решения: простая архитектура (Ollama нативно, обвязка в Docker) и честная настройка под маленькую модель (legacy function calling, гибридный поиск, reranker). Дальше он просто тихо экономит время: вопрос к 200 PDF стал занимать полминуты вместо получаса раскопок.