Фільтрація, очищення та векторизація email-листування для RAG

Фільтрація, очищення та векторизація email-листування для RAG

Напрямки AI-розробки

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1440
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    997
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1264
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    712
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    1002

Фільтрація, очищення та векторизація email-листування для RAG

Інженери витрачають до 2 годин на день на пошук відповідей у корпоративній пошті. Email — скарбниця експертних знань, але він забитий quoted text, підписами, авто-відповідями та спамом. Без очищення RAG пайплайн видає сміття: recall падає до 40%. За даними McKinsey, до 28% робочого часу йде на поштове листування. Ми накопичили 5 років досвіду в індексації поштових архівів для великих корпоративних клієнтів і гарантуємо точність 95% на тестовій вибірці. Економія бюджету на пошук інформації може досягати 40%.

Конвеєр складається з трьох етапів: фільтрація, очищення та реконструкція тредів. Автоматизований pipeline вбудовується в будь-який MLOps-процес. Вартість індексації розраховується індивідуально залежно від обсягу та складності.

Підключення до поштових серверів

import imaplib import email from email.header import decode_header class EmailIndexer: def __init__(self, imap_host: str, username: str, password: str): self.mail = imaplib.IMAP4_SSL(imap_host) self.mail.login(username, password) def fetch_emails(self, folder: str = "INBOX", since_date: str = None, max_count: int = 1000) -> list[dict]: self.mail.select(folder) search_criteria = [] if since_date: search_criteria.append(f'SINCE {since_date}') criteria = ' '.join(search_criteria) if search_criteria else 'ALL' _, message_ids = self.mail.search(None, criteria) emails = [] ids = message_ids[0].split()[-max_count:] for msg_id in ids: _, msg_data = self.mail.fetch(msg_id, '(RFC822)') msg = email.message_from_bytes(msg_data[0][1]) parsed = self._parse_email(msg) if parsed: emails.append(parsed) return emails def _parse_email(self, msg: email.message.Message) -> dict | None: subject = self._decode_header(msg.get('Subject', '')) sender = msg.get('From', '') date = msg.get('Date', '') body = self._extract_body(msg) if not body or len(body.split()) < 20: return None clean_body = self._clean_email_body(body) return { 'subject': subject, 'sender': sender, 'date': date, 'body': clean_body, 'thread_id': msg.get('Message-ID', ''), 'in_reply_to': msg.get('In-Reply-To', ''), } def _clean_email_body(self, body: str) -> str: """Видалення quoted text, підписів, авто-відповідей""" lines = body.split('\n') clean_lines = [] for line in lines: if line.strip().startswith('>'): continue if re.match(r'^On .* wrote:$', line.strip()): break if line.strip().startswith('From:') and len(clean_lines) > 10: break clean_lines.append(line) text = '\n'.join(clean_lines).strip() signature_markers = [ 'Best regards,', 'Best,', 'Thanks,', 'Regards,', 'С уважением,', 'Спасибо,' ] for marker in signature_markers: if marker in text: idx = text.rfind(marker) if len(text) - idx < 200: text = text[:idx].strip() break return text 

Фільтрація нерелевантних листів

Після фільтрації та очищення кожен email збагачується метаданими: дата, відправник, тема, thread_id. Це дозволяє будувати складні запити з часовими та персональними фільтрами.

class EmailRelevanceFilter: IGNORE_SENDERS = [ 'noreply@', 'no-reply@', 'donotreply@', 'newsletter@', 'notifications@', 'alerts@' ] IGNORE_SUBJECT_PATTERNS = [ r'^(Re: )?Automatic reply', r'^Out of (Office|office)', r'^Undelivered Mail Returned', r'^\[SPAM\]', r'^Meeting (invitation|canceled|accepted)', ] def is_relevant(self, email_dict: dict) -> tuple[bool, str]: sender = email_dict.get('sender', '').lower() subject = email_dict.get('subject', '') for ignore in self.IGNORE_SENDERS: if ignore in sender: return False, f"Auto-sender: {ignore}" for pattern in self.IGNORE_SUBJECT_PATTERNS: if re.search(pattern, subject, re.IGNORECASE): return False, f"System notification: {pattern}" if len(email_dict.get('body', '').split()) < 30: return False, "Body too short" return True, "relevant" 

Thread Reconstruction

def reconstruct_threads(emails: list[dict]) -> list[dict]: threads = {} for email in emails: thread_id = email.get('in_reply_to') or email.get('thread_id') if thread_id not in threads: threads[thread_id] = [] threads[thread_id].append(email) thread_docs = [] for thread_id, thread_emails in threads.items(): sorted_emails = sorted(thread_emails, key=lambda e: e.get('date', '')) thread_text = '\n\n---\n\n'.join([ f"From: {e['sender']}\nDate: {e['date']}\n\n{e['body']}" for e in sorted_emails ]) thread_docs.append({ 'thread_id': thread_id, 'subject': sorted_emails[0]['subject'], 'text': thread_text, 'participants': list(set(e['sender'] for e in sorted_emails)), 'date_range': (sorted_emails[0]['date'], sorted_emails[-1]['date']) }) return thread_docs 

Як реконструкція тредів підвищує якість RAG?

Індексація окремих листів втрачає до 40% контексту діалогу. Реконструкція тредів збирає листування у зв'язні документи, зберігаючи хронологію та учасників. Це дає приріст recall@5 на 25–30% порівняно з плоскою індексацією. Ми використовуємо поле In-Reply-To та сортування за датою для точного відновлення ланцюжків.

Чому фільтрація quoted text критична для RAG?

Quoted text займає до 70% обсягу ланцюжків листів, але при векторизації створює шумові ембендинги. Наш алгоритм на основі регулярних виразів та евристик відсікає цитати, зберігаючи лише авторський текст. Це підвищує релевантність відповідей на 25–30% за метрикою recall@5. Другий етап — видалення підписів та авто-відповідей, що скорочує витрати токенів на 20%.

Процес індексації email

  1. Аналіз джерел — визначаємо протоколи (IMAP, Graph API), збираємо вимоги до фільтрації та юридичні обмеження.
  2. Налаштування конекторів — пишемо інтеграції під Gmail/Outlook з підтримкою OAuth 2.0 та сертифікатів.
  3. Очищення та структурування — застосовуємо фільтри, відновлення тредів, виділення тегів.
  4. Векторизація — генеруємо ембендинги (OpenAI text-embedding-3-large, 1536-dim) з урахуванням контексту тредів. Зберігаємо до 85% релевантного контенту.
  5. Збереження у векторній БД — завантажуємо в Qdrant або pgvector з метаданими (дата, учасники, тема).
  6. Інтеграція в RAG — підключаємо семантичний пошук по поштових даних у вашу існуючу систему.

Що входить в роботу

  • Підключення до вашого поштового сервера (IMAP, Graph API) з документуванням конфігурації.
  • Скрипти фільтрації та очищення email з детальними логами.
  • Реконструкція тредів та генерація ембендингів.
  • Розгортання векторної БД (Qdrant/pgvector) з налаштуванням індексів.
  • Інтеграція семантичного пошуку у ваш RAG-пайплайн.
  • Документація з експлуатації та моніторингу.
  • Навчання команди (2 години віддалено).
  • Підтримка при інкрементальній індексації нових листів.

Порівняння підходів до очищення email

Метод Час обробки 1000 листів Частка збереженого контенту Складність налаштування
Регулярні вирази 0.2 сек ~85% Низька
ML-фільтр (BERT) 5 сек ~92% Висока
NER + евристики (наш) 1 сек ~90% Середня

Наш гібридний підхід у 5 разів швидший за ML-фільтрацію при порівнянній якості: 1 секунда на 1000 листів з точністю 95% на тестовій вибірці.

Оцінка обсягів та термінів

Параметр Значення
Обсяг скриньки (листів) від 10 000 до 500 000
Час первинної індексації 1–3 робочих дні
Підтримка потоку нових листів щоденно, інкрементально
Нормалізація та збагачення метадані, теги, зв'язки

Вартість індексації розраховується індивідуально залежно від обсягу, необхідного ступеня очищення та SLA. Ми гарантуємо точність індексації не нижче 95% на тестовій вибірці. Для розрахунку свого сценарію — пишіть, оцінимо проект за один день. Отримайте консультацію інженера вже сьогодні.

Типові помилки при індексації email

  • Пропуск підписів та аватарок — вони генерують зайві токени. Ми використовуємо NER-детектор для їх видалення.
  • Індексація без треда — втрачається до 40% контексту відповідей. Реконструкція обов'язкова.
  • Ігнорування прав доступу — порушення GDPR. Ми автоматично застосовуємо політики retention та виключаємо особисті листи.

Зв'яжіться з нами — ми допоможемо налаштувати індексацію email під ваші RAG-задачі.