| 
				
					
						 
				
	
				CI / lint_and_test (push) Successful in 2m30s
				
					Details
				
			 
		
	 | 
			||
|---|---|---|
| .gitea/workflows | ||
| src | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| Dockerfile | ||
| README.md | ||
| docker-compose.yml | ||
| requirements-dev.txt | ||
| requirements.txt | ||
		
			
				
				README.md
			
		
		
			
			
				
				
			
		
	
	AI Email Assistant
Сервис для генерации персонализированных холодных писем с использованием RAG и LangGraph.
О программе
- Персонализация писем под роль, индустрию и компанию лида
 - RAG-подход с базой знаний реальных кейсов
 - LangGraph пайплайн обработки запросов
 - Векторный поиск в ChromaDB
 - Поддержка OpenAI и Google Gemini
 - RESTful API для интеграции с CRM
 
Логика работы
Система обрабатывает запросы через 10-этапный LangGraph пайплайн:
- Валидация входа - проверка обязательных полей лида
 - Извлечение признаков - нормализация роли, индустрии из входных данных
 - Построение запроса - формирование поискового запроса на основе профиля лида
 - Векторный поиск - поиск релевантных кейсов в ChromaDB (top-30)
 - Ранжирование контекста - отбор лучших кейсов (top-6) и создание bullets
 - Построение промпта - формирование системного и пользовательского промптов
 - LLM генерация - создание письма через OpenAI/Gemini
 - Парсинг ответа - извлечение JSON с темой и телом письма
 - Проверка качества - валидация длины, структуры, наличия CTA
 - Формирование результата - финальная структура ответа с метаданными
 
Промпт-инжиниринг: обоснование подхода
Почему именно такая структура письма?
Структура Приветствие -> Хук -> Ценность -> Кейс -> CTA -> Подпись выбрана на основе исследований конверсии холодных писем:
1. Приветствие по имени
- Персонализация создает впечатление индивидуального подхода
 - Снижает восприятие письма как спама
 
2. Хук-вопрос в первых строках
- Привлекает внимание и активирует любопытство
 - Касается специфики индустрии лида -> создает ощущение релевантности
 
3. Конкретная ценность с цифрами
- Мозг лучше воспринимает конкретные числа vs абстракции
 - "15 минут" vs "быстро", "95%" vs "значительно"
 - Снижает скептицизм -> повышает доверие
 
4. Социальное доказательство с кейсом
- Название реальной компании + численный результат
 - Психология: "если помогли похожей компании, помогут и нам"
 - Снижает воспринимаемый риск принятия решения
 
5. Мягкий CTA без давления
- "Если интересно" vs "Давайте встретимся завтра"
 - Оставляет ощущение выбора -> снижает сопротивление
 - 15-минутный формат -> низкий барьер входа
 
6. Эмоциональные триггеры
- "Спокойствие", "контроль" -> позитивные эмоции от решения проблем
 - "Избежите", "снизьте риски" -> страх потерь (loss aversion)
 - Комбинация мотивирует к действию
 
Ограничения для максимальной эффективности
- ≤ 1000 символов: оптимальная длина письма для цифрового потребления. Согласно Nielsen Norman Group, пользователи читают в среднем 20–28% текста на веб-странице и быстро теряют внимание после первых 200–250 слов (~1000–1200 символов). Источник: 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. Загрузка базы знаний
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
- Убедитесь что все обязательные поля заполнены
 
Обновление базы знаний
Добавление кейсов
- 
Поместите
.mdфайлы вdata - 
Файлы должны содержать:
- Заголовки для структуры
 - Числовые метрики
 - Ключевые слова индустрии
 - Упоминания ролей
 
 - 
Дополните базу (инкрементально):
 
curl -X POST -H "Authorization: Bearer secret" \
  "http://localhost:8000/api/v1/admin/ingest"
- (альтернативно) Пересоздайте базу:
 
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