Go to file
itqop 0aa8964c6c
CI / lint_and_test (push) Successful in 2m34s Details
Add rate limiting with backoff
2025-07-19 20:40:25 +03:00
.gitea/workflows Add rules for CI 2025-07-19 17:36:58 +03:00
src Add rate limiting with backoff 2025-07-19 20:40:25 +03:00
.dockerignore Add docker deploy 2025-07-19 17:29:16 +03:00
.env.example Add .env example 2025-07-18 21:14:25 +03:00
.gitignore Edit gitignore 2025-07-19 17:56:08 +03:00
Dockerfile Try to fix docker [skip ci] 2025-07-19 19:07:11 +03:00
README.md Add rate limiting with backoff 2025-07-19 20:40:25 +03:00
docker-compose.yml Add rate limiting with backoff 2025-07-19 20:40:25 +03:00
requirements-dev.txt Fix CI 2025-07-19 00:18:00 +03:00
requirements.txt Fix email validator 2025-07-19 00:53:39 +03:00

README.md

AI Email Assistant

CI Code style: black Ruff

Сервис для генерации персонализированных холодных писем с использованием RAG и LangGraph.

О программе

  • Персонализация писем под роль, индустрию и компанию лида
  • RAG-подход с базой знаний реальных кейсов
  • LangGraph пайплайн обработки запросов
  • Векторный поиск в ChromaDB
  • Поддержка OpenAI и Google Gemini
  • RESTful API для интеграции с CRM

Логика работы

Система обрабатывает запросы через 10-этапный LangGraph пайплайн:

  1. Валидация входа - проверка обязательных полей лида
  2. Извлечение признаков - нормализация роли, индустрии из входных данных
  3. Построение запроса - формирование поискового запроса на основе профиля лида
  4. Векторный поиск - поиск релевантных кейсов в ChromaDB (top-30)
  5. Ранжирование контекста - отбор лучших кейсов (top-6) и создание bullets
  6. Построение промпта - формирование системного и пользовательского промптов
  7. LLM генерация - создание письма через OpenAI/Gemini
  8. Парсинг ответа - извлечение JSON с темой и телом письма
  9. Проверка качества - валидация длины, структуры, наличия CTA
  10. Формирование результата - финальная структура ответа с метаданными

Промпт-инжиниринг: обоснование подхода

Почему именно такая структура письма?

Структура Приветствие -> Хук -> Ценность -> Кейс -> CTA -> Подпись выбрана на основе исследований конверсии холодных писем:

1. Приветствие по имени

  • Персонализация создает впечатление индивидуального подхода
  • Снижает восприятие письма как спама

2. Хук-вопрос в первых строках

  • Привлекает внимание и активирует любопытство
  • Касается специфики индустрии лида -> создает ощущение релевантности

3. Конкретная ценность с цифрами

  • Мозг лучше воспринимает конкретные числа vs абстракции
  • "15 минут" vs "быстро", "95%" vs "значительно"
  • Снижает скептицизм -> повышает доверие

4. Социальное доказательство с кейсом

  • Название реальной компании + численный результат
  • Психология: "если помогли похожей компании, помогут и нам"
  • Снижает воспринимаемый риск принятия решения

5. Мягкий CTA без давления

  • "Если интересно" vs "Давайте встретимся завтра"
  • Оставляет ощущение выбора -> снижает сопротивление
  • 15-минутный формат -> низкий барьер входа

6. Эмоциональные триггеры

  • "Спокойствие", "контроль" -> позитивные эмоции от решения проблем
  • "Избежите", "снизьте риски" -> страх потерь (loss aversion)
  • Комбинация мотивирует к действию

Ограничения для максимальной эффективности

  • ≤ 1000 символов: оптимальная длина письма для цифрового потребления. Согласно Nielsen Norman Group, пользователи читают в среднем 2028% текста на веб-странице и быстро теряют внимание после первых 200250 слов (~10001200 символов). Источник: How Users Read on the Web
  • Микро-абзацы 1-2 предложения: улучшают восприятие текста на экранах смартфонов. Люди сканируют информацию взглядом, и короткие абзацы помогают быстрее вычленять ключевые мысли. Источник: F-Shaped Pattern of Reading Web Content
  • 1 конкретный CTA: письма с одним призывом к действию получают на 371% больше кликов и могут увеличить доход на 1617% по сравнению с письмами с несколькими CTA.
    Источник: Unlayer - Call to Action in Emails

Настройка промптов

Системные промпты: src/services/prompt_templates.py

  • Тон: деловой, лаконичный, дружелюбный
  • Длина: до 1000 символов
  • Структура: Приветствие -> Хук -> Ценность -> Кейс -> CTA -> Подпись

Быстрый старт

1. Подготовка окружения

git clone https://git.itqop.pw/itqop/ai-email-assistant.git
cd ai-email-assistant

# Создание конфигурации
cp .env.example .env
# Или создайте .env файл сами

2. Базовая конфигурация .env

LLM_PROVIDER=openai
OPENAI_API_KEY=your_openai_api_key_here
API_SECRET_KEY=your_admin_token

3. Запуск через Docker

docker compose up --build

4. Загрузка базы знаний

Полоджите файлы/папку с md в data/, а после выполните запрос

curl -X POST "http://localhost:8000/api/v1/admin/ingest" \
  -H "Authorization: Bearer your_admin_token"

5. Использование

curl -X POST "http://localhost:8000/api/v1/generate_email" \
  -H "Content-Type: application/json" \
  -d '{
    "contact": "Помящий Никита",
    "position": "Технический директор",
    "company_name": "FIVE",
    "segment": "маркетинговое агентство"
  }'

Альтернативный запуск (без Docker)

# Создание виртуального окружения
python -m venv .venv
.venv\Scripts\activate  # Windows
source .venv/bin/activate  # Linux/Mac

# Установка зависимостей
pip install -r requirements.txt

# Загрузка данных
python -m src.ingest.ingest_cli --data-dir articles_konsol_pro --recreate

# Запуск сервиса
python -m src.app.main

API документация

Основные эндпоинты

  • GET /healthz - Проверка здоровья
  • GET /readiness - Готовность к работе
  • GET /docs - Swagger документация
  • POST /api/v1/generate_email - Генерация письма

Формат запроса

Обязательные поля:

{
  "contact": "string",
  "position": "string", 
  "company_name": "string",
  "segment": "string"
}

Полный формат:

{
  "contact": "Помящий Никита",
  "position": "Технический директор",
  "company_name": "FIVE", 
  "segment": "маркетинговое агентство",
  "email": "nikita@five.agency",
  "locale": "ru",
  "notes": "Дополнительная информация"
}

Формат ответа

{
  "subject": "Как упростить работу с самозанятыми в FIVE?",
  "body": "Здравствуйте, Никита!....",
  "meta": {
    "locale": "ru",
    "lead_normalized": {
      "contact_name": "Помящий Никита",
      "contact_first_name": "Никита",
      "contact_last_name": "Помящий",
      "role_title": "Технический директор",
      "role_category": "tech",
      "company_name": "FIVE",
      "industry_segment": "маркетинговое агентство",
      "industry_tag": "marketing_agency",
      "email": null,
      "locale": "ru",
      "notes": null
    },
    "used_chunks": [
      "sds_podkluchenie_k_konsoli#sds_podkluchenie_k_konsoli#c7",
      "vysotnik_rental#vysotnik_rental#c2"
    ],
    "model": "gpt-4o",
    "tokens_prompt": 1132,
    "tokens_completion": 289,
    "guardrails_violations": 0,
    "context_chunks_used": 5,
    "context_quality_score": 0.87
  }
}

Структура meta:

  • locale - Язык, на котором сгенерировано письмо (например, ru)
  • lead_normalized - Объект с нормализованной и обогащенной информацией о лиде:
    • contact_first_name/contact_last_name - Распознанные имя и фамилия
    • role_category/industry_tag - Машинно-читаемые теги для должности и сегмента компании (например, tech, marketing_agency)
  • used_chunks - Массив с ID фрагментов из базы знаний, которые были использованы для генерации ответа
  • model - Использованная LLM модель
  • tokens_prompt/tokens_completion - Расход токенов на запрос и ответ для мониторинга затрат
  • guardrails_violations - Количество нарушений правил безопасности (guardrails). 0 означает, что все проверки пройдены
  • context_chunks_used - Общее количество фрагментов контекста, которые были использованы для генерации
  • context_quality_score - Оценка релевантности найденного контекста (0-1):
    • 1.0 - отличное качество контекста
    • 0.8+ - хорошее качество (высокая релевантность)
    • 0.6+ - приемлемое качество (достаточная релевантность)
    • <0.6 - низкое качество (слабая релевантность)

Конфигурация

Переменные окружения

Параметр Описание По умолчанию
LLM_PROVIDER openai или gemini openai
LLM_MODEL Модель генерации gpt-4o
EMBEDDING_MODEL Модель векторизации text-embedding-3-large
TOP_K Чанков для поиска 30
TOP_N_CONTEXT Чанков в контексте 6
CHUNK_SIZE Размер чанка (токены) 500
CHUNK_OVERLAP Перекрытие чанков 100

Мониторинг

Качество контекста

Метрика context_quality_score вычисляется на основе:

  • Средний similarity score (40%) - релевантность найденных кейсов
  • Доля высококачественных chunks (25%) - процент кейсов с similarity > 0.7
  • Консистентность результатов (20%) - равномерность качества chunks
  • Фактор количества (15%) - достаточность объема контекста

Интерпретация для мониторинга:

  • context_quality_score < 0.5 - критически низкое качество, требует обновления базы знаний
  • context_quality_score < 0.7 - предупреждение о качестве контекста
  • context_quality_score >= 0.8 - хорошее качество генерации

Логи

Все запросы логируются в JSON:

{
  "request_id": "1641234567890",
  "method": "POST",
  "url": "http://localhost:8000/api/v1/generate_email", 
  "status_code": 200,
  "process_time": 2.341
}

Проверка состояния

# Статус сервиса
curl http://localhost:8000/healthz

# Статистика базы знаний  
curl -H "Authorization: Bearer secret" \
  http://localhost:8000/api/v1/admin/knowledge-base/stats

# Docker логи
docker-compose logs -f ai-email-assistant

Устранение ошибок

Частые проблемы

404 No relevant knowledge found

# Загрузите базу знаний
curl -X POST -H "Authorization: Bearer secret" \
  "http://localhost:8000/api/v1/admin/ingest"

502 External service error

  • Проверьте API ключи LLM провайдера в .env

400 Validation failed

  • Убедитесь что все обязательные поля заполнены

Обновление базы знаний

Добавление кейсов

  1. Поместите .md файлы в data

  2. Файлы должны содержать:

    • Заголовки для структуры
    • Числовые метрики
    • Ключевые слова индустрии
    • Упоминания ролей
  3. Дополните базу (инкрементально):

curl -X POST -H "Authorization: Bearer secret" \
  "http://localhost:8000/api/v1/admin/ingest"
  1. (альтернативно) Пересоздайте базу:
curl -X POST -H "Authorization: Bearer secret" \
  "http://localhost:8000/api/v1/admin/ingest?recreate=true"

Формат кейсов

# Кейс компании FIVE

Маркетинговое агентство автоматизировало работу с подрядчиками.

## Результаты
- Онбординг: с 2 дней до 15 минут
- Снижение ошибок: на 95% 
- Выплаты: мгновенные

Технический директор Никита отмечает эффективность.

Тестирование

# Все тесты
pytest src/tests/

# Только API
pytest src/tests/test_api.py

TODO:

  • Улучшить валидацию входных данных (почта, защита хака llm)
  • Увеличить покрытие кода тестами до 80%, доавить unit тесты для бизнес-логики
  • Сделать качественную докуентацию