3-агентный пайплайн: RSS вакансий + Claude + Gmail MCP
Как собрать трёхагентный пайплайн на Python и Claude: парсинг RSS с бирж вакансий, AI-скоринг и черновики писем через Gmail MCP.
3-агентный пайплайн: парсинг RSS вакансий, AI-скоринг и черновики писем через Gmail MCP
Представьте: каждое утро ваш скрипт сам обходит десятки RSS-лент с биржами вакансий, просматривает сотни позиций, оценивает каждую по вашему профилю и кладёт в Gmail уже готовые черновики персонализированных писем. Никакого ручного копипаста, никаких забытых вкладок браузера.
Именно это делает трёхагентный пайплайн на Python + Claude + Gmail MCP. В этой статье — подробная архитектура, ключевые решения и весь необходимый код.
Зачем три агента вместо одного скрипта?
Можно написать один монолитный скрипт: «скачай RSS → отправь в Claude → сохрани письмо». Это работает. Но сразу появляются проблемы:
- Агент делает слишком много и теряет фокус
- Промпты разбухают: контекст скоринга и контекст написания письма мешают друг другу
- При ошибке на шаге 3 всё нужно перезапускать с шага 1
- Масштабировать или заменять один этап невозможно без рефакторинга всего
Подход с несколькими специализированными агентами решает всё это. Каждый агент получает чёткую роль, минимальный контекст и единственную задачу. Это принцип Single Responsibility, перенесённый на уровень AI-оркестрации.
«Каждый агент в пайплайне должен иметь чёткую роль, инструменты и зону ответственности — иначе координация превращается в хаос.»
Архитектура пайплайна
Пайплайн состоит из трёх агентов, выстроенных в цепочку:
graph TD
A[🌐 RSS-ленты бирж вакансий] --> B[Агент 1: Ingestor\nПарсинг и нормализация]
B --> C[(SQLite / JSON Store)]
C --> D[Агент 2: Scorer\nAI-скоринг через Claude]
D --> E[Отфильтрованные вакансии\nScore ≥ порога]
E --> F[Агент 3: Mailer\nClaude + Gmail MCP]
F --> G[📧 Черновики в Gmail]
G --> H[👤 Человек — ревью и отправка]
Агент 1 — Ingestor: парсинг и нормализация RSS
Первый агент — самый простой и при этом самый критичный. Его задача: обойти список RSS-URL, скачать ленты, распарсить и сложить нормализованные записи в локальное хранилище.
import feedparser
import hashlib
import json
from datetime import datetime
from pathlib import Path
RSS_FEEDS = [
"https://remoteok.com/remote-jobs.rss",
"https://weworkremotely.com/categories/remote-programming-jobs.rss",
"https://stackoverflow.com/jobs/feed?r=true&tech=python",
]
def ingest_feeds(feeds: list[str], store_path: str = "jobs.json") -> list[dict]:
existing = load_store(store_path)
seen_ids = {j["id"] for j in existing}
new_jobs = []
for url in feeds:
feed = feedparser.parse(url)
for entry in feed.entries:
job_id = hashlib.md5(entry.get("link", "").encode()).hexdigest()
if job_id in seen_ids:
continue
job = {
"id": job_id,
"title": entry.get("title", ""),
"company": entry.get("author", "Unknown"),
"description": entry.get("summary", ""),
"url": entry.get("link", ""),
"published": entry.get("published", str(datetime.now())),
"score": None,
"email_drafted": False,
}
new_jobs.append(job)
seen_ids.add(job_id)
save_store(store_path, existing + new_jobs)
print(f"[Ingestor] Добавлено {len(new_jobs)} новых вакансий")
return new_jobs
Ключевые детали:
- Дедупликация по MD5 от URL — одна вакансия не попадёт дважды
- Хранилище в JSON/SQLite — лёгкий персистентный слой без внешних зависимостей
- Нормализованная схема — все поля одинаковые вне зависимости от источника
Агент 2 — Scorer: AI-скоринг через Claude
Второй агент — сердце системы. Он берёт каждую вакансию из хранилища без оценки и отправляет её в Claude с вашим профилем кандидата.
import anthropic
import json
CLIENT = anthropic.Anthropic() # читает ANTHROPIC_API_KEY из env
CANDIDATE_PROFILE = """
Опыт: 5 лет Python, FastAPI, PostgreSQL, Docker.
Ищу: удалённую позицию senior/lead backend разработчика.
Не интересует: стартапы до seed-раунда, позиции с релокацией, .NET/Java.
Желаемая зарплата: от $120k.
"""
SCORING_PROMPT = """
Ты — рекрутинговый AI-ассистент. Оцени вакансию для кандидата.
Профиль кандидата:
{profile}
Вакансия:
Название: {title}
Компания: {company}
Описание: {description}
Верни JSON:
{{
"score": <число от 1 до 10>,
"reasoning": "<краткое обоснование>",
"red_flags": ["<список проблем если есть>"]
}}
"""
def score_job(job: dict) -> dict:
prompt = SCORING_PROMPT.format(
profile=CANDIDATE_PROFILE,
title=job["title"],
company=job["company"],
description=job["description"][:2000], # обрезаем длинные описания
)
message = CLIENT.messages.create(
model="claude-opus-4-5",
max_tokens=512,
messages=[{"role": "user", "content": prompt}],
)
result = json.loads(message.content[0].text)
job["score"] = result["score"]
job["scoring_reasoning"] = result["reasoning"]
job["red_flags"] = result.get("red_flags", [])
return job
def score_all_jobs(store_path: str = "jobs.json", min_score: int = 7):
jobs = load_store(store_path)
to_score = [j for j in jobs if j["score"] is None]
print(f"[Scorer] Оцениваем {len(to_score)} вакансий...")
for job in to_score:
scored = score_job(job)
print(f" {scored['title']} @ {scored['company']} → {scored['score']}/10")
save_store(store_path, jobs)
return [j for j in jobs if j.get("score", 0) >= min_score]
Агент 3 — Mailer: черновики писем через Gmail MCP
Третий агент — самый интересный с точки зрения архитектуры. Он использует Model Context Protocol (MCP) для прямого доступа к Gmail.
Gmail MCP Server — это сервер Model Context Protocol для интеграции Gmail в Claude Desktop с поддержкой авто-аутентификации, который позволяет AI-ассистентам управлять Gmail через взаимодействие на естественном языке.
Gmail MCP сервер предоставляет структурированный и защищённый доступ к почте, позволяя агенту искать, читать, создавать черновики, организовывать письма и управлять контактами прямо в вашем почтовом ящике от вашего имени.
Настройка Gmail MCP
# Установка
npm install -g @gongrzhe/server-gmail-autoauth-mcp
# Первичная авторизация (потребует gcp-oauth.keys.json)
mkdir -p ~/.gmail-mcp
mv gcp-oauth.keys.json ~/.gmail-mcp/
npx @gongrzhe/server-gmail-autoauth-mcp auth
После авторизации токены хранятся глобально в ~/.gmail-mcp/ и переиспользуются автоматически.
Агент-мейлер
import anthropic
import subprocess
import json
EMAIL_PROMPT = """
Ты — карьерный ассистент. Напиши персонализированное письмо о кандидатуре на вакансию.
Кандидат: {profile}
Вакансия:
- Название: {title}
- Компания: {company}
- Описание: {description}
- Почему подходит: {reasoning}
Требования к письму:
1. Тема (subject): конкретная, упоминает позицию
2. Тело: 3-4 абзаца, без шаблонных фраз
3. Подчеркни 2-3 релевантных навыка с конкретными примерами
4. Вежливый, уверенный тон
5. Создай как черновик — НЕ отправляй
Используй Gmail MCP для создания черновика.
"""
def draft_email_for_job(job: dict, recipient: str = "hiring@company.com"):
"""Создаёт черновик письма через Claude + Gmail MCP."""
# Запускаем MCP-сервер как subprocess
mcp_process = subprocess.Popen(
["npx", "@gongrzhe/server-gmail-autoauth-mcp"],
stdin=subprocess.PIPE, stdout=subprocess.PIPE
)
client = anthropic.Anthropic()
# Claude получает доступ к Gmail-инструментам через MCP
response = client.beta.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=[{
"type": "computer_use_20250124",
# В реальном проекте здесь — инструменты Gmail MCP
}],
messages=[{"role": "user", "content": EMAIL_PROMPT.format(
profile=CANDIDATE_PROFILE,
title=job["title"],
company=job["company"],
description=job["description"][:1500],
reasoning=job.get("scoring_reasoning", ""),
)}]
)
job["email_drafted"] = True
return response
Сравнение подходов к интеграции Gmail
Существует несколько способов дать Claude доступ к Gmail. Вот сравнение:
| Подход | Сложность настройки | Стоимость | Контроль | Рекомендуется для |
|---|---|---|---|---|
| Gmail MCP Server (self-hosted) | Средняя | Только API Claude | Полный | Разработчики, приватность |
| Composio Tool Router | Низкая | Freemium + платный план | Частичный | Быстрый старт |
| Zapier + Claude | Очень низкая | $20+/мес за Zapier | Минимальный | Нетехнические пользователи |
| Gmail API напрямую | Высокая | Только API Claude | Полный | Продакшн, кастомизация |
С отдельным Gmail MCP сервером агенты имеют доступ только к фиксированному набору инструментов Gmail. Однако с Composio Tool Router агенты могут динамически загружать инструменты из Gmail и других приложений в зависимости от задачи — всё через единую MCP-точку доступа.
Для нашего пайплайна выбор — self-hosted Gmail MCP: максимальный контроль, данные не покидают вашу машину.
Оркестрация: запускаем пайплайн
Главный скрипт связывает трёх агентов в единый поток:
import time
import schedule
from agents.ingestor import ingest_feeds
from agents.scorer import score_all_jobs
from agents.mailer import draft_emails_for_top_jobs
RSS_FEEDS = [
"https://remoteok.com/remote-jobs.rss",
"https://weworkremotely.com/categories/remote-programming-jobs.rss",
]
def run_pipeline():
print("🚀 Запуск пайплайна...")
# Шаг 1: Парсинг RSS
new_jobs = ingest_feeds(RSS_FEEDS)
if not new_jobs:
print("Нет новых вакансий. Выходим.")
return
# Шаг 2: AI-скоринг (порог — 7 из 10)
top_jobs = score_all_jobs(min_score=7)
print(f"✅ Топ вакансий: {len(top_jobs)}")
# Шаг 3: Черновики писем
drafted = draft_emails_for_top_jobs(top_jobs)
print(f"📧 Создано черновиков: {drafted}")
print("✨ Пайплайн завершён. Проверьте черновики в Gmail.")
# Запуск каждый день в 8:00
schedule.every().day.at("08:00").do(run_pipeline)
if __name__ == "__main__":
run_pipeline() # Первый запуск сразу
while True:
schedule.run_pending()
time.sleep(60)
Структура репозитория
job-pipeline/
├── agents/
│ ├── ingestor.py # Агент 1: RSS → normalized JSON
│ ├── scorer.py # Агент 2: Claude scoring
│ └── mailer.py # Агент 3: Gmail MCP drafts
├── data/
│ └── jobs.json # Персистентное хранилище
├── config/
│ ├── feeds.yaml # Список RSS-лент
│ └── profile.md # Профиль кандидата
├── main.py # Оркестратор + планировщик
├── requirements.txt
└── README.md
requirements.txt
anthropomorphic>=0.34.0
feedparser>=6.0.11
schedule>=1.2.2
python-dotenv>=1.0.0
Подводные камни и как их обойти
1. Rate limits Claude API
При большом числе вакансий Агент-2 быстро упирается в лимиты. Решение — батч-обработка с паузами:
import time
def score_with_backoff(jobs, batch_size=10, pause_sec=2):
for i in range(0, len(jobs), batch_size):
batch = jobs[i:i+batch_size]
for job in batch:
score_job(job)
time.sleep(pause_sec) # пауза между батчами
2. Качество RSS-описаний
Многие job boards дают в RSS только тизер описания, а не полный текст. Если Claude видит 50 слов вместо 500 — скоринг будет неточным. Решение: для важных лент добавить запрос к полной странице через httpx + BeautifulSoup.
3. Контекст письма
Продвинутые системы генерируют персонализированные черновики ответов, учитывающих стиль общения с конкретными контактами, распознают важные даты и задачи, и рассматривают другие релевантные письма при составлении ответа. Добавьте в промпт Агента-3 несколько примеров ваших прошлых писем — это значительно повысит соответствие вашему стилю.
Добавьте в начало системного промпта:
Вот примеры писем кандидата в похожих ситуациях:
[письмо 1: ...]
[письмо 2: ...]
Пиши в том же стиле: кратко, конкретно, без клише.
Метрики и что ожидать
Из реальных экспериментов с подобными пайплайнами:
| Метрика | Типичное значение |
|---|---|
| Вакансий в RSS-лентах (в день) | 50–300 |
| Проходят порог скоринга ≥7 | 5–15% |
| Время работы пайплайна | 3–8 минут |
| Стоимость Claude API (100 вакансий) | ~$0.15–$0.40 |
| Черновиков писем в день | 3–20 |
Самое ценное здесь — не автоматизация самого написания (письмо всё равно нужно проверить), а автоматическая фильтрация шума. Система помогает найти те немногие предложения, которые действительно заслуживают вашего времени из сотен доступных — это фильтр, а не инструмент для массовой рассылки.
Заключение
Трёхагентный пайплайн решает реальную проблему: рынок вакансий огромен, а время на анализ — ограничено. Разбив задачу на три специализированных агента (парсинг → скоринг → черновик), мы получаем систему, которую легко дебажить, расширять и настраивать под себя.
Ключевые выводы:
- MCP — не магия, а стандарт: MCP-сервер — это лёгкая программа, которая предоставляет специфические возможности через стандартизированный Model Context Protocol. Gmail MCP — один из многих таких серверов.
- Human-in-the-loop обязателен: агент создаёт черновики, человек — отправляет. Всегда.
- Специализация агентов повышает качество: каждый агент делает одно и делает это хорошо.
- Стоимость минимальна: менее $0.50 в день при разумных объёмах.
Начните с малого: один RSS-фид, один агент-скорер, ручная проверка результатов. Затем добавляйте слои сложности по мере роста доверия к системе.