Локальный RAG для 60 ГБ писем на 8 ГБ ОЗУ: пошаговый гайд | AiManual
AiManual Logo Ai / Manual.
28 Дек 2025 Гайд

Как сделать локальный RAG для 60 ГБ писем на слабом железе (8 ГБ ОЗУ): реально ли?

Практическое руководство по созданию приватной системы поиска по 60 ГБ писем на слабом компьютере. Векторная база, оптимизация памяти, локальные LLM.

Проблема: 60 ГБ писем и слабое железо

Представьте: у вас накопилось 60 ГБ электронных писем за 10+ лет работы. Это десятки тысяч писем — контракты, переговоры, технические обсуждения, личная переписка. Вам нужно найти конкретное письмо, но стандартный поиск в почтовом клиенте не справляется. Вы хотите задать вопрос на естественном языке: "Какие условия были в договоре с компанией X в 2020 году?" или "Кто предлагал решение проблемы Y в прошлом месяце?".

Идеальное решение — RAG (Retrieval-Augmented Generation). Но есть проблема: у вас обычный компьютер с 8 ГБ оперативной памяти. Все облачные решения отпадают — ваши письма содержат конфиденциальную информацию. Можно ли развернуть полноценную локальную систему поиска на таком железе? Да, можно, но с правильным подходом.

Главный миф: многие думают, что для работы с 60 ГБ данных нужно минимум 32 ГБ ОЗУ. Это неверно. Ключ — в потоковой обработке и правильном выборе инструментов.

Архитектура решения: как обойти ограничения памяти

Вместо того чтобы загружать все 60 ГБ в память одновременно, мы будем использовать стратегию "обрабатывай по частям, храни эффективно". Вот ключевые компоненты нашей системы:

  • Потоковый парсинг писем — обрабатываем письма батчами по 100-200 МБ
  • Локальная векторная база на диске — ChromaDB в persistent-режиме
  • Крошечная, но эффективная модель эмбеддингов — all-MiniLM-L6-v2 (80 МБ)
  • Оптимизированная локальная LLM — Mistral 7B или Phi-3 через Ollama
  • Умное чанкирование — разбиваем письма на смысловые блоки
Компонент Потребление ОЗУ Альтернатива
Модель эмбеддингов ~300 МБ SentenceTransformers
Векторная база (Chroma) ~500 МБ Qdrant, FAISS
Локальная LLM (7B параметров) ~4.5 ГБ (4-бит квант.) Phi-3 mini (3.8B)
Обработка данных ~1 ГБ Потоковая загрузка
Итого (пиковое) ~6.3 ГБ Вписывается в 8 ГБ!

Пошаговый план реализации

1 Подготовка данных: экспорт и парсинг писем

Первым делом нужно получить письма в машиночитаемом формате. Большинство почтовых клиентов позволяют экспортировать письма в формате MBOX или EML.

# Пример структуры проекта
email-rag-project/
├── data/
│   ├── raw_emails.mbox          # 60 ГБ исходных данных
│   └── processed/               # Обработанные письма
├── scripts/
│   ├── parse_emails.py          # Парсинг MBOX
│   ├── create_embeddings.py     # Создание эмбеддингов
│   └── query_system.py          # Система запросов
└── chroma_db/                   # Векторная база

Для парсинга используем Python с потоковой обработкой:

import mailbox
import json
from tqdm import tqdm

def process_mbox_in_chunks(mbox_path, chunk_size=1000):
    """Обрабатывает MBOX файл частями для экономии памяти"""
    mbox = mailbox.mbox(mbox_path)
    emails_processed = 0
    
    for i in tqdm(range(0, len(mbox), chunk_size)):
        chunk = []
        for msg in mbox[i:i+chunk_size]:
            email_data = {
                'id': emails_processed,
                'from': msg['from'],
                'to': msg['to'],
                'subject': msg['subject'],
                'date': msg['date'],
                'body': extract_email_body(msg),
                'size': len(msg.as_string())
            }
            chunk.append(email_data)
            emails_processed += 1
        
        # Сохраняем чанк на диск
        save_chunk(chunk, i)
        del chunk  # Освобождаем память
💡
Не пытайтесь обработать все 60 ГБ за один раз. Разбейте на чанки по 1000 писем. Это позволит работать даже на слабом железе и сохранять прогресс при сбоях.

2 Умное чанкирование: как разбивать письма

Простого разбиения по символам недостаточно. Длинные письма с перепиской нужно разбивать по смыслу:

from langchain.text_splitter import RecursiveCharacterTextSplitter

def chunk_email(email_text, max_chunk_size=500):
    """Разбивает письмо на смысловые блоки"""
    # Определяем естественные разделители в письмах
    separators = ["\n\n--- Forwarded message ---",
                 "\n\nOn ",  # Начало цитаты
                 "\n\n> ",   # Уровень цитаты
                 "\n\n",     # Двойной перенос
                 "\n",       # Одиночный перенос
                 " ",        # Пробел
                 ""]         # Любой символ
    
    text_splitter = RecursiveCharacterTextSplitter(
        separators=separators,
        chunk_size=max_chunk_size,
        chunk_overlap=50,
        length_function=len
    )
    
    return text_splitter.split_text(email_text)

3 Создание векторной базы с ChromaDB

ChromaDB — идеальный выбор для локального развертывания. Она работает в persistent-режиме, хранит данные на диске и загружает в память только индексы.

import chromadb
from sentence_transformers import SentenceTransformer
import torch

# Используем легковесную модель эмбеддингов
model = SentenceTransformer('all-MiniLM-L6-v2')

# Инициализируем Chroma с сохранением на диск
chroma_client = chromadb.PersistentClient(path="./chroma_db")
collection = chroma_client.create_collection(
    name="emails",
    metadata={"hnsw:space": "cosine"}  # Используем HNSW для скорости
)

# Пакетная обработка для экономии памяти
def add_emails_to_chroma_in_batches(email_chunks, batch_size=100):
    """Добавляет письма в векторную базу батчами"""
    for i in range(0, len(email_chunks), batch_size):
        batch = email_chunks[i:i+batch_size]
        
        # Генерируем эмбеддинги для батча
        embeddings = model.encode(batch)
        
        # Создаем ID для каждого чанка
        ids = [f"email_{i+j}" for j in range(len(batch))]
        
        # Добавляем в коллекцию
        collection.add(
            embeddings=embeddings.tolist(),
            documents=batch,
            ids=ids
        )
        
        # Очищаем память
        del embeddings
        torch.cuda.empty_cache() if torch.cuda.is_available() else None

Важно: Используйте `all-MiniLM-L6-v2` вместо больших моделей типа `all-mpnet-base-v2`. Она в 5 раз меньше (80 МБ vs 420 МБ), но сохраняет 90% качества для поиска по письмам.

4 Настройка локальной LLM через Ollama

Для генерации ответов используем Ollama — самый простой способ запускать локальные LLM. В нашем полном гиде по Ollama мы подробно разбирали все тонкости установки и настройки.

# Устанавливаем Ollama (Linux/Mac)
curl -fsSL https://ollama.com/install.sh | sh

# Загружаем оптимизированную модель
ollama pull mistral:7b-instruct-q4_K_M  # 4.5 ГБ, 4-битное квантование
# Или еще более легкую
ollama pull phi3:mini-128k-instruct-q4_K_M  # 2.3 ГБ

Для 8 ГБ ОЗУ лучше всего подходят:

  • Phi-3 mini (3.8B) — 2.3 ГБ в 4-битном формате, отличное качество
  • Mistral 7B (4-бит) — 4.5 ГБ, проверенная производительность
  • Llama 3.2 3B — новейшая маленькая модель от Meta

В статье "Лучшие локальные LLM 2025 года" сообщество Reddit рекомендует именно эти модели для слабого железа.

5 Сборка системы запросов

Теперь объединяем все компоненты в единую систему:

import requests
import json

class EmailRAGSystem:
    def __init__(self, chroma_path="./chroma_db"):
        self.embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
        self.chroma_client = chromadb.PersistentClient(path=chroma_path)
        self.collection = self.chroma_client.get_collection("emails")
        
    def search_emails(self, query, n_results=5):
        """Ищет релевантные письма"""
        query_embedding = self.embedding_model.encode([query]).tolist()[0]
        
        results = self.collection.query(
            query_embeddings=[query_embedding],
            n_results=n_results
        )
        
        return results['documents'][0]
    
    def generate_answer(self, query, context):
        """Генерирует ответ на основе найденных писем"""
        prompt = f"""Ты — помощник для поиска в электронных письмах.
        Пользователь спрашивает: {query}
        
        Вот релевантные письма из архива:
        {context}
        
        Ответь на вопрос, используя информацию из писем.
        Если информации недостаточно, скажи об этом.
        Не выдумывай информацию."""
        
        # Отправляем запрос в Ollama
        response = requests.post(
            'http://localhost:11434/api/generate',
            json={
                'model': 'phi3:mini',
                'prompt': prompt,
                'stream': False
            }
        )
        
        return response.json()['response']

Оптимизация производительности

Даже с правильно выбранными инструментами нужно дополнительно оптимизировать систему:

1. Используйте HNSW индекс в ChromaDB

collection = chroma_client.create_collection(
    name="emails_optimized",
    metadata={
        "hnsw:space": "cosine",
        "hnsw:construction_ef": 200,  # Для лучшего качества
        "hnsw:M": 16  # Баланс скорость/память
    }
)

2. Настройка подкачки (swap) в Linux

# Создаем файл подкачки 8 ГБ
sudo fallocate -l 8G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# Делаем постоянным
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

3. Приоритизация процессов

# Запускаем Ollama с низким приоритетом ввода-вывода
ionice -c 3 -p $(pgrep ollama)

# Ограничиваем использование CPU для фоновых процессов
cpulimit -l 50 -p $(pgrep chroma)

Возможные ошибки и их решение

Проблема Причина Решение
Out of Memory при создании эмбеддингов Слишком большой батч Уменьшите batch_size до 50 или 25
Медленный поиск в ChromaDB Слишком много векторов в памяти Используйте persistent режим и HNSW
LLM генерирует "галлюцинации" Слишком мало контекста или слабая модель Увеличьте n_results до 7-10, используйте Phi-3
Обработка занимает дни Последовательная обработка 60 ГБ Обрабатывайте только последние N лет или по отправителям

Альтернативные подходы

Если описанный подход все еще требует слишком много ресурсов, рассмотрите эти альтернативы:

1. Гибридный подход: облачные эмбеддинги + локальная LLM

Используйте бесплатные API для создания эмбеддингов (например, Google AI Studio предлагает бесплатные квоты), а LLM запускайте локально. Это снизит нагрузку на CPU при индексации.

2. Предварительная фильтрация по метаданным

Перед семантическим поиском фильтруйте письма по дате, отправителю или теме. Это уменьшит размер векторного поиска в 10-100 раз.

# Сначала фильтруем по дате
filtered_emails = collection.query(
    query_embeddings=[query_embedding],
    n_results=1000,
    where={"date": {"$gte": "2023-01-01"}}  # Только письма за 2 года
)

3. Поэтапное развертывание

Как в кейсе с мультиагентными системами, начинайте с малого: обработайте сначала 1 ГБ писем, проверьте качество, затем масштабируйтесь.

Заключение: это реально!

Создание локального RAG для 60 ГБ писем на компьютере с 8 ГБ ОЗУ — абсолютно реальная задача. Ключевые моменты:

  1. Используйте потоковую обработку данных — не загружайте все сразу
  2. Выбирайте легковесные, но эффективные модели (all-MiniLM-L6-v2, Phi-3 mini)
  3. Храните векторную базу на диске с persistent-режимом ChromaDB
  4. Оптимизируйте чанкирование под структуру писем
  5. Настройте систему подкачки и приоритеты процессов

Первые результаты вы получите уже после обработки 5-10% данных. Полная индексация 60 ГБ займет от нескольких дней до недели на слабом железе, но система будет работать в фоне. Главное — вы получите полностью приватную, локальную систему поиска по вашему архиву писем, которая не отправляет ваши данные в облако.

🚀
Как и в истории про создание своего ngrok, сложные задачи становятся простыми, когда разбиваешь их на маленькие шаги и выбираешь правильные инструменты.