Building a Five-Stage AI Marketing Agent: From Raw Scraping to 100+ Personalized Developer Emails Daily
We engineered an AI marketing agent that automates developer outreach at scale. This post dissects our five-actor architecture—scraper, enricher, researcher, communicator, CRM sync—and how it sends 100+ hyper-personalized emails daily without sounding robotic.
Why We Ditched Mass Blasts for an AI Marketing Agent
After running 47 A/B tests on cold email campaigns targeting senior engineers, we hit a wall. Generic templates—even with “personalized” first names—yielded 0.3% reply rates. Developers smelled automation from the subject line. Our turning point came when we analyzed 12,000 conversations from GitHub Issues and HN comments: the most effective outreach referenced a specific commit, a debated language feature, or a recent Stack Overflow answer.
We needed a system that could process a prospect’s digital footprint in seconds, not hours. The solution? A five-stage AI pipeline where each actor handles a distinct cognitive load. Instead of a single LLM call trying to do everything, we decomposed the task into focused micro-agents. This approach cut our cost per email from $0.12 to $0.04 while increasing reply rates to 8.7% over 90 days.
Stage 1: The Scraper – Extracting Raw Signals from Public Sources
The pipeline starts with a Phoenix-based headless browser (Chrome DevTools Protocol via Playwright) that crawls three primary sources per target:
- GitHub profile: starred repos, recent PRs, merged commits, bio text
- LinkedIn (public view): current role, skills listed, recent activity
- Personal blog or dev.to: last three articles, tags, comment history
We use a retry-until-steady-state pattern. Each scraper session has a 15-second timeout per page. If a 429 or 403 appears, we back off with exponential jitter (base 2s, cap 30s). The raw output is JSON of max 2KB per source—no HTML, no bloat. Here’s a truncated example from a real scrape:
{
"github": {
"profile": "benawad",
"topics": ["typescript", "fullstack", "graphql"],
"recent_pr": "Added lazy loading to Apollo client examples",
"repos_starred": 24,
"bio": "building @thesecretorg"
},
"linkedin": {
"headline": "Senior Engineer at Stripe",
"skills": ["Python", "Rust", "APIs", "System Design"],
"recent_activity": "Shared article on idempotency in payments"
},
"blog": {
"last_title": "Building a Prisma-like ORM for Rust",
"tags": ["rust", "orm", "database"],
"comment_on_topic": "Postgres indexing strategies"
}
}
We discard any profile with fewer than 3 data points—those are too thin for meaningful personalization. This initial filter saves downstream costs by 22%.
Stage 2: The Enricher – Filling Gaps with Context-Aware Inference
Raw data is sparse. The enricher uses a Mixtral 8x7B local model (fine-tuned on 50,000 developer profiles) to infer missing attributes. It runs on an A100 with vLLM for sub-500ms inference per profile. We ask it three targeted questions:
- “What programming languages or frameworks does this person actively use, based on their recent PRs and posts?”
- “What technical problem are they likely trying to solve, given their starred repos and last 3 articles?”
- “What is a single, specific recent event in their work that could serve as a hook?”
The key is we do NOT ask for generic summaries. The enricher outputs only structured fields:
{
"inferred_stack": "TypeScript, React, Prisma, PostgreSQL",
"pain_point": "Optimizing N+1 queries in GraphQL resolvers without batching",
"hook_event": "They opened a PR two weeks ago to add DataLoader to their project's schema",
"confidence_score": 0.84
}
If confidence drops below 0.7, we skip that profile—better to send no email than a bad one. This quality gate rejects approximately 18% of profiles, but the remaining pool sees engagement rates above 10%.
Stage 3: The Researcher – Crafting a Data-Backed Narrative
This is our most advanced agent. It takes the enriched profile and performs a lightweight web search (via SerpAPI, limited to 2 requests per prospect) for recent mentions: a conference talk, a podcast appearance, or a major open-source contribution. We then feed all data into a GPT-4o-mini call with a strict template. The prompt constrains the output to exactly three sentences, no fluff, no questions:
You are a technical writer crafting a personalized email body.
Given the following context:
- Recent event: {hook_event}
- Inferred pain point: {pain_point}
- Technologies: {inferred_stack}
Write exactly 3 sentences:
1. Acknowledge their specific work (reference the event).
2. Connect your solution to their inferred pain point.
3. Show you understand their tech stack with a concrete example.
Do NOT use greetings, salutations, or calls to action.
Output example for a Stripe engineer: “Saw your PR on lazy-loading Apollo examples—clean work. We’ve been tackling the same N+1 problem in TypeScript resolvers and found that edge-fed batching reduces p95 latency by 40%. Curious if you’ve benchmarked DataLoader against your current schema.” This reads like a peer, not a vendor.
Stage 4: The Communicator – Delivering at the Right Cadence
Emails are not blasted simultaneously. We maintain a queue in Redis with a leaky bucket rate limiter: 12 emails per hour per sending domain, with randomized intervals of 3–9 minutes between sends. Each email is sent from a dedicated subdomain that has 6+ months of warm-up history (we used Mailwarm for gradual ramp-up).
The communicator also selects the best send time per prospect. We check their timezone from the enriched data (e.g., “America/Los_Angeles”) and schedule delivery for 10:30 AM local time on Tuesday or Wednesday. Our data shows these slots have 31% higher open rates than Monday mornings or Friday afternoons. The email uses plain text format—no HTML, no images—because developer inboxes often strip rich content.
We also set up a 2-step auto-followup: if no reply within 5 days, we send a single, different email that references the first (e.g., “Wanted to circle back on the DataLoader approach—did you try it?”). This second email converts an additional 4.2% of recipients.
Stage 5: CRM Sync – Closing the Loop with Structured Feedback
All interactions—sends, opens, clicks, replies, bounces—are piped via webhook into a Postgres-backed API that mirrors a lightweight CRM. We track two primary metrics per campaign:
- Positive reply rate (reply that asks for a demo or more info)
- Negative reply rate (opt-out or “not interested”)
We use this data to fine-tune our enricher and researcher. For example, after 3,000 sends, we noticed that emails referencing “N+1 queries” had a 2.3x higher positive rate than those referencing “optimization” in general. We then biased the enricher to surface specific technical terms over vague ones. The CRM also automatically suppresses any prospect who replied negatively, and we never email them again—respecting the developer’s time is non-negotiable.
Ready to automate your developer outreach without the spammy feel? Explore the full architecture at TormentNexus. We’ve open-sourced the rate limiter and enricher models so you can build your own AI marketing agent today.
Originally published at tormentnexus.site
Top comments (0)