Email-triggered PhD outreach automation agent
Send one email with your CV → OfferOpenClaw scrapes faculty pages, analyzes research fit, and drafts personalized cold emails → Results delivered back to your inbox.
You send an email (attach your CV)
↓
OfferOpenClaw receives it and replies immediately with a confirmation
↓
Claude Agent begins:
1. Reads your CV → extracts research interests, skills, goals
2. Scrapes faculty listings from target departments
3. Fetches each professor's recent papers (Semantic Scholar)
4. Scores research fit 0–10
5. Drafts personalized cold emails for professors scoring ≥ 6.5
↓
Results emailed back to you (faculty list + email drafts)
pip install anthropic requests beautifulsoup4 lxml python-dotenv \
click rich pymupdf python-docxCopy .env.example to .env and fill in the following:
# Anthropic API Key (required)
# Get one at: console.anthropic.com → API Keys
ANTHROPIC_API_KEY=sk-ant-...
# Gmail credentials
IMAP_HOST=imap.gmail.com
IMAP_PORT=993
IMAP_USER=you@gmail.com
IMAP_PASS=xxxx xxxx xxxx xxxx # Gmail App Password (16 characters)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=you@gmail.com
SMTP_PASS=xxxx xxxx xxxx xxxx
# Sender whitelist — only process emails from these addresses
ALLOWED_SENDERS=you@university.edu,you@gmail.comGetting a Gmail App Password: Gmail Settings → Security → 2-Step Verification (must be enabled) → App Passwords → Generate
# Run as a daemon (polls inbox every 60 seconds)
python app.py serve
# Run once — process all pending emails then exit (good for testing)
python app.py serve --onceSend an email to your own Gmail with your CV attached (PDF).
Subject must contain one of these keywords:
PhD Outreach Pipeline,faculty,professor,pipeline,cold email
Example email:
Subject: PhD Outreach Pipeline — GeoAI & 3D Urban Modeling, Top 5 Universities
Please run the full PhD outreach pipeline for me.
Target universities:
1. MIT
2. Stanford University
3. Carnegie Mellon University
4. University of Southern California
5. New York University
Extra instructions: Focus on faculty working on GeoAI, urban computing,
or vision-language models. I am looking for PhD/RA positions starting Fall 2026.
My CV is attached.
You'll receive an acknowledgement immediately and results within 10–30 minutes.
| Email intent | Agent behavior |
|---|---|
| Full pipeline (CV + multiple universities) | Scrape all faculty → score → draft cold emails |
| Analyze a specific professor | Fetch papers → return research summary |
| Generate an email for a specific professor | Fetch papers → write one cold email |
| Unclear intent | Reply asking for clarification |
# List recent jobs
python app.py jobs list
# Show details for a specific job
python app.py jobs show <job_id>Example output:
┌──────────┬──────────┬──────────────────────┬──────────────────┬──────────────────────────────────────┐
│ ID │ Status │ From │ Type │ Subject │
├──────────┼──────────┼──────────────────────┼──────────────────┼──────────────────────────────────────┤
│ 5bab73a0 │ done │ you@tamu.edu │ full_pipeline │ PhD Outreach Pipeline — GeoAI... │
│ bf4ca411 │ failed │ you@gmail.com │ generate_email │ Email for Prof. Smith at MIT │
└──────────┴──────────┴──────────────────────┴──────────────────┴──────────────────────────────────────┘
offeropenclaw/
├── app.py # CLI entry point (serve / jobs)
├── config.py # Global configuration
├── .env # Environment variables (never commit)
│
├── agent/
│ ├── orchestrator.py # Claude agentic loop
│ ├── tools/
│ │ ├── cv_reader.py # Read CV text from storage
│ │ ├── faculty_scraper.py # Scrape faculty pages (heuristic + Claude fallback)
│ │ ├── semantic_scholar.py # Fetch papers via Semantic Scholar API
│ │ └── io_utils.py # Save / load output files
│ └── skills/
│ ├── student_profile/ # CV → structured student profile
│ ├── research_analysis/ # Papers → research summary
│ ├── match_scoring/ # Research fit score 0–10
│ └── cold_email/ # Cold email writing rules
│
├── email_gateway/
│ ├── inbox_reader.py # IMAP inbox polling
│ ├── email_parser.py # Parse email → TaskRequest
│ ├── reply_sender.py # Send replies via SMTP
│ └── attachment_handler.py # Extract text from PDF / DOCX / TXT
│
├── jobs/
│ ├── queue.py # SQLite-backed job queue
│ └── worker.py # Background worker thread
│
└── storage/
├── requests/<job_id>/cv.txt # Uploaded CV
├── outputs/<job_id>/ # Agent output files
└── logs/<job_id>.log # Per-job run log
Agent-generated emails follow these guidelines:
- Length: 200–280 words (body only)
- Structure: hook → background → specific research connection → ask → closing
- Must reference at least one specific paper title or research result
- Ask: request a 20-minute call (specific, not vague)
- Avoid:
cutting-edge,passionate about,I hope this email finds you well, generic openers
Cold emails are only generated for professors scoring ≥ 6.5 / 10.
- Gmail free accounts have an SMTP sending limit of ~500 emails/day
- Semantic Scholar API requires no key but has rate limits (retry logic built in)
- First run: use
--oncemode to verify the pipeline works end-to-end - Only unread emails in your inbox are processed — already-read emails are skipped
- Marketing / spam emails are filtered out automatically (subject keyword matching + Gmail inbox-only search)
| Package | Purpose |
|---|---|
anthropic |
Claude API (agent loop, email parsing, scraper fallback) |
requests + beautifulsoup4 + lxml |
Web scraping |
pymupdf |
PDF parsing |
python-docx |
DOCX parsing |
python-dotenv |
Environment variable loading |
click + rich |
CLI interface |

