Building an AI Sales Development Rep: Automated Lead Outreach and Qualification
Learn how to architect an AI-powered SDR agent that scores leads, generates personalized outreach messages, and manages multi-step follow-up sequences automatically.
Why Sales Needs Agentic AI
Sales development representatives spend the majority of their time on repetitive tasks: researching prospects, writing personalized emails, scoring leads, and scheduling follow-ups. An AI SDR agent automates this entire pipeline while maintaining the personalization that drives response rates. Unlike simple email templates, an agentic SDR reasons about each prospect individually, adapts messaging based on engagement signals, and decides when to escalate a warm lead to a human rep.
The architecture we will build has four components: a lead ingestion layer, a scoring engine, a message generation module, and a follow-up orchestrator.
Lead Ingestion and Enrichment
Before the agent can do anything useful, it needs structured data about each prospect. Raw leads from forms or CRM exports often lack the context needed for personalization. The ingestion layer enriches each lead with company information, recent news, and technology stack signals.
from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional
import httpx
@dataclass
class Lead:
email: str
name: str
company: str
title: Optional[str] = None
industry: Optional[str] = None
company_size: Optional[str] = None
enrichment_data: dict = field(default_factory=dict)
score: float = 0.0
stage: str = "new"
created_at: datetime = field(default_factory=datetime.utcnow)
async def enrich_lead(lead: Lead, api_key: str) -> Lead:
"""Enrich a lead with company data from an external API."""
async with httpx.AsyncClient() as client:
resp = await client.get(
"https://api.clearbit.com/v2/companies/find",
params={"domain": lead.email.split("@")[1]},
headers={"Authorization": f"Bearer {api_key}"},
)
if resp.status_code == 200:
data = resp.json()
lead.industry = data.get("category", {}).get("industry")
lead.company_size = data.get("metrics", {}).get("employeesRange")
lead.enrichment_data = data
return lead
Lead Scoring with an LLM
Traditional scoring uses weighted feature models. An LLM-augmented scorer can evaluate unstructured signals like job title relevance, company news sentiment, and technology fit that rule-based systems struggle with.
from openai import AsyncOpenAI
client = AsyncOpenAI()
SCORING_PROMPT = """You are a lead scoring assistant for a B2B SaaS company
that sells AI-powered customer service tools.
Evaluate this lead and return a JSON object with:
- "score": integer from 1 to 100
- "reasoning": one sentence explaining the score
- "priority": "hot", "warm", or "cold"
Lead data:
- Name: {name}
- Title: {title}
- Company: {company}
- Industry: {industry}
- Company size: {company_size}
"""
async def score_lead(lead: Lead) -> dict:
response = await client.chat.completions.create(
model="gpt-4o",
response_format={"type": "json_object"},
messages=[
{"role": "system", "content": "Return valid JSON only."},
{
"role": "user",
"content": SCORING_PROMPT.format(
name=lead.name,
title=lead.title or "Unknown",
company=lead.company,
industry=lead.industry or "Unknown",
company_size=lead.company_size or "Unknown",
),
},
],
)
import json
result = json.loads(response.choices[0].message.content)
lead.score = result["score"]
lead.stage = result["priority"]
return result
Personalized Outreach Generation
Generic emails get ignored. The agent uses enrichment data to craft messages that reference specific company details, recent events, or role-relevant pain points.
See AI Voice Agents Handle Real Calls
Book a free demo or calculate how much you can save with AI voice automation.
OUTREACH_PROMPT = """Write a short, personalized cold outreach email.
Prospect: {name}, {title} at {company}
Industry: {industry}
Company size: {company_size}
Key insight: {insight}
Rules:
- Keep it under 120 words
- Reference something specific about their company
- End with a clear, low-friction call to action
- Do NOT use generic phrases like "I hope this finds you well"
"""
async def generate_outreach(lead: Lead) -> str:
insight = lead.enrichment_data.get("description", "a growing company")
response = await client.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "user",
"content": OUTREACH_PROMPT.format(
name=lead.name,
title=lead.title,
company=lead.company,
industry=lead.industry,
company_size=lead.company_size,
insight=insight,
),
},
],
)
return response.choices[0].message.content
Follow-Up Sequence Orchestration
The most critical part of an SDR agent is persistence. Research shows that most conversions happen after the third or fourth touch. The orchestrator tracks engagement events and decides when and what to send next.
from datetime import timedelta
FOLLOW_UP_SCHEDULE = [
{"delay_days": 3, "strategy": "value_add"},
{"delay_days": 5, "strategy": "social_proof"},
{"delay_days": 7, "strategy": "breakup"},
]
async def process_follow_ups(leads: list[Lead], email_service):
now = datetime.utcnow()
for lead in leads:
if lead.stage == "cold":
continue
history = await email_service.get_history(lead.email)
if history.last_replied:
# Lead engaged — escalate to human rep
await email_service.notify_rep(lead, reason="reply_received")
continue
touches = history.touch_count
if touches >= len(FOLLOW_UP_SCHEDULE):
lead.stage = "exhausted"
continue
step = FOLLOW_UP_SCHEDULE[touches]
due_date = history.last_sent + timedelta(days=step["delay_days"])
if now >= due_date:
message = await generate_follow_up(lead, step["strategy"])
await email_service.send(lead.email, message)
Putting It All Together
The main agent loop pulls new leads, enriches them, scores them, and starts the outreach pipeline. Hot leads get immediate outreach while warm leads enter a nurture sequence.
async def run_sdr_agent(new_leads: list[Lead], email_service):
for lead in new_leads:
lead = await enrich_lead(lead, api_key="your-key")
scoring = await score_lead(lead)
if scoring["priority"] == "hot":
message = await generate_outreach(lead)
await email_service.send(lead.email, message)
elif scoring["priority"] == "warm":
await email_service.add_to_nurture(lead)
# Process existing follow-ups
active_leads = await email_service.get_active_leads()
await process_follow_ups(active_leads, email_service)
FAQ
How do I prevent the AI SDR from sending embarrassing or off-brand messages?
Implement a review layer. Score every generated message against your brand guidelines using a second LLM call that acts as a quality gate. Messages below a confidence threshold get queued for human review instead of sending automatically.
What CRM integrations are needed for a production SDR agent?
At minimum you need read access to contacts and deals (to avoid contacting existing customers), write access to log activities and update lead stages, and webhook support to receive engagement events like email opens and replies. HubSpot and Salesforce both provide robust APIs for this.
How do I measure the effectiveness of the AI SDR agent?
Track reply rate, meeting booked rate, and pipeline generated per lead. Compare these against your human SDR benchmarks. Also monitor negative signals like unsubscribe rate and spam complaints to ensure the agent is not damaging your domain reputation.
#SalesAI #SDRAgent #LeadQualification #OutreachAutomation #Python #AgenticAI #LearnAI #AIEngineering
CallSphere Team
Expert insights on AI voice agents and customer communication automation.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.