B2B Sales Outreach AI Agent
Why This Matters
B2B sales teams spend 60%+ of their time on research and initial outreach rather than closing deals. An AI sales agent automates lead scoring, prospect research, and personalized email generation, enabling sales reps to focus on high-value conversations while the agent handles the top-of-funnel pipeline at scale.
Real-World Analogy
Think of a Sales Outreach Agent as a talent scout for your business. Just as a scout identifies promising athletes, researches their backgrounds, and tailors recruitment pitches, this agent scores leads, researches prospects, and crafts personalized outreach that resonates with each individual's pain points and context.
What is a Sales Outreach Agent?
Sales outreach agents automate the prospecting and engagement process by scoring leads, personalizing outreach, managing CRM data, and automating follow-up sequences. The key pipeline is: lead research β scoring β personalized outreach β response tracking β follow-up automation. Each step is optimized using data from previous campaigns to improve conversion rates.
Project Overview
We will build a sales outreach agent that:
- Scores leads based on fit and engagement signals
- Researches prospects from public data
- Generates personalized outreach emails
- Manages follow-up sequences
- Tracks campaign performance
- Integrates with CRM systems
Expected outcome: An agent that increases response rates through personalized, data-driven outreach.
Architecture
Tools & Setup
| Tool | Version | Purpose |
|---|---|---|
| Python | 3.11+ | Core language |
| openai | 1.0+ | LLM backbone |
| httpx | 0.27+ | API calls |
| pydantic | 2.0+ | Data models |
| pandas | 2.0+ | Analytics |
Step 1: Environment Setup
python -m venv venv
source venv/bin/activate
pip install openai httpx pydantic pandas
export OPENAI_API_KEY="sk-your-key"
Step 2: Lead Scorer
import json
import logging
from typing import Any, Dict, List, Optional
from openai import AsyncOpenAI
logger = logging.getLogger(__name__)
class LeadScorer:
"""Score B2B leads using LLM analysis of fit, intent, and timing."""
def __init__(self, model: str = "gpt-4o"):
self.client = AsyncOpenAI()
self.model = model
async def score(self, lead: Dict[str, Any]) -> Dict[str, Any]:
response = await self.client.chat.completions.create(
model=self.model,
messages=[
{
"role": "system",
"content": """Score this B2B sales lead.
Consider: company size, industry fit, role seniority, engagement signals, intent.
Return JSON:
{
"score": 1-100,
"fit_score": 1-10,
"intent_score": 1-10,
"timing_score": 1-10,
"grade": "A|B|C|D",
"reasoning": "explanation",
"recommended_approach": "strategy suggestion"
}""",
},
{"role": "user", "content": json.dumps(lead, indent=2)},
],
temperature=0.0,
)
try:
return json.loads(response.choices[0].message.content)
except (json.JSONDecodeError, IndexError):
return {"score": 50, "grade": "C", "reasoning": "Unable to score"}
async def score_batch(self, leads: List[Dict]) -> List[Dict]:
import asyncio
tasks = [self.score(lead) for lead in leads]
return await asyncio.gather(*tasks)
async def prioritize(self, leads: List[Dict]) -> List[Dict]:
scored = []
for lead in leads:
score = await self.score(lead)
scored.append({"lead": lead, "score": score})
return sorted(scored, key=lambda x: x["score"].get("score", 0), reverse=True)
Step 3: Prospect Research and Email Generation
class ProspectResearcher:
"""Research prospects using LLM for personalized outreach."""
def __init__(self, model: str = "gpt-4o"):
self.client = AsyncOpenAI()
self.model = model
async def research(self, prospect: Dict[str, Any]) -> Dict[str, Any]:
response = await self.client.chat.completions.create(
model=self.model,
messages=[
{
"role": "system",
"content": """Research this prospect and provide insights for personalized outreach.
Return JSON:
{
"pain_points": ["likely challenges"],
"interests": ["professional interests"],
"recent_news": "any relevant company news",
"personalization_hooks": ["things to mention"],
"best_approach": "recommended outreach strategy"
}""",
},
{"role": "user", "content": f"Prospect: {json.dumps(prospect, indent=2)}"},
],
temperature=0.2,
)
try:
return json.loads(response.choices[0].message.content)
except (json.JSONDecodeError, IndexError):
return {"pain_points": [], "personalization_hooks": []}
class EmailGenerator:
"""Generate personalized cold outreach emails."""
def __init__(self, model: str = "gpt-4o"):
self.client = AsyncOpenAI()
self.model = model
async def generate_cold_email(
self,
prospect: Dict[str, Any],
research: Dict[str, Any],
product: str,
tone: str = "professional",
) -> Dict[str, Any]:
response = await self.client.chat.completions.create(
model=self.model,
messages=[
{
"role": "system",
"content": f"""Write a personalized cold outreach email.
Tone: {tone}
Product: {product}
Rules:
- Subject line under 50 characters
- Opening that references something specific about them
- Value proposition tied to their pain points
- Clear, low-friction CTA
- Under 150 words
- No generic phrases like "I hope this finds you well\"""",
},
{
"role": "user",
"content": f"Prospect: {prospect.get('name', 'Unknown')} at {prospect.get('company', 'Unknown')}\nRole: {prospect.get('role', 'Unknown')}\n\nResearch insights:\n{research}",
},
],
temperature=0.7,
)
content = response.choices[0].message.content
return {
"subject": content.split("\n")[0].replace("Subject:", "").strip() if "\n" in content else "Quick question",
"body": content,
"personalization_hooks": research.get("personalization_hooks", []),
}
async def generate_followup(self, original_email: str, response: str = None, step: int = 1) -> str:
prompt = f"Write follow-up email #{step} for this sequence."
if response:
prompt += f"\n\nTheir response: {response}"
prompt += f"\n\nOriginal email: {original_email[:500]}"
result = await self.client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": "Write brief, value-adding follow-up emails. Under 100 words."},
{"role": "user", "content": prompt},
],
temperature=0.7,
)
return result.choices[0].message.content
Step 4: Sequence Manager and Agent
from datetime import datetime
class SequenceManager:
"""Manage multi-step outreach sequences for leads."""
def __init__(self):
self.sequences: Dict[str, Dict] = {}
def create_sequence(self, lead_id: str, steps: List[Dict]) -> str:
sequence_id = f"seq_{lead_id}_{datetime.now().strftime('%Y%m%d')}"
self.sequences[sequence_id] = {
"lead_id": lead_id,
"steps": steps,
"current_step": 0,
"status": "active",
"created_at": datetime.now().isoformat(),
}
return sequence_id
def get_next_action(self, sequence_id: str) -> Dict:
seq = self.sequences.get(sequence_id)
if not seq or seq["status"] != "active":
return {"action": "none"}
current = seq["current_step"]
if current >= len(seq["steps"]):
seq["status"] = "completed"
return {"action": "none"}
step = seq["steps"][current]
seq["current_step"] = current + 1
return step
def pause_sequence(self, sequence_id: str) -> None:
if sequence_id in self.sequences:
self.sequences[sequence_id]["status"] = "paused"
def resume_sequence(self, sequence_id: str) -> None:
if sequence_id in self.sequences:
self.sequences[sequence_id]["status"] = "active"
class SalesOutreachAgent:
"""Orchestrate the complete sales outreach pipeline."""
def __init__(self, model: str = "gpt-4o"):
self.scorer = LeadScorer(model)
self.researcher = ProspectResearcher(model)
self.email_gen = EmailGenerator(model)
self.sequence_mgr = SequenceManager()
async def process_lead(self, lead: Dict[str, Any], product: str) -> Dict[str, Any]:
score = await self.scorer.score(lead)
research = await self.researcher.research(lead)
email = await self.email_gen.generate_cold_email(lead, research, product)
return {"lead": lead, "score": score, "research": research, "email": email}
async def create_outreach_sequence(self, lead: Dict[str, Any], product: str) -> str:
data = await self.process_lead(lead, product)
steps = [
{"type": "email", "content": data["email"]["body"], "delay_days": 0},
{"type": "email", "content": "followup_1", "delay_days": 3},
{"type": "email", "content": "followup_2", "delay_days": 7},
{"type": "task", "content": "LinkedIn connect", "delay_days": 5},
{"type": "email", "content": "breakup_email", "delay_days": 14},
]
return self.sequence_mgr.create_sequence(lead.get("email", "unknown"), steps)
async def batch_outreach(self, leads: List[Dict], product: str) -> List[Dict]:
prioritized = await self.scorer.prioritize(leads)
return [await self.process_lead(item["lead"], product) for item in prioritized[:10]]
Mathematical Foundation
Lead Score Calculation:
Where (fit), (intent), (timing), (engagement) are normalized scores 0-1, and weights sum to 1.
Sequence Conversion Rate:
Industry benchmarks: 2-5% for cold outreach. Personalized subject lines increase open rates by 26%.
Email Open Rate Optimization:
Target: 20-25% for cold outreach. Personalized subject lines increase this by 26%.
Performance Considerations
| Metric | Latency | Cost | Accuracy |
|---|---|---|---|
| Lead scoring | 3-6s per lead | $0.01-0.03 | High |
| Prospect research | 5-10s | $0.02-0.05 | Medium |
| Email generation | 3-8s per email | $0.01-0.03 | High |
| Sequence creation | <1s | Free | Exact |
| Batch processing (10) | 30-60s | $0.10-0.30 | High |
Security Considerations
- Never store raw email credentials; use OAuth or app passwords
- Implement CAN-SPAM compliance (unsubscribe link, physical address)
- Maintain suppression lists for opt-outs
- Use SPF, DKIM, DMARC for email authentication
- Encrypt prospect data at rest and in transit
- Implement rate limiting to prevent email provider bans
- Audit all outbound communications for compliance
Interview Q&A
Q1: How does the lead scorer balance fit vs intent?
Fit measures how well the prospect matches your ideal customer profile (company size, industry, role). Intent measures buying signals (website visits, content downloads). A high-fit, low-intent lead needs nurturing; a low-fit, high-intent lead may not be a good customer. The weighted formula allows tuning based on sales strategy.
Q2: How do you prevent email deliverability issues with cold outreach?
Warm up new sending domains over 2-4 weeks. Start with 5-10 emails/day and gradually increase. Use SPF, DKIM, and DMARC authentication. Monitor bounce rates (<2%) and spam complaints (<0.1%). Use email verification services before sending.
Q3: What's the optimal follow-up sequence length?
3-5 touchpoints over 14-21 days is optimal. More than 5 follow-ups feel spammy; fewer miss prospects. Each follow-up should add new value (case study, insight, different angle) rather than repeating the original ask.
Q4: How does personalization at scale differ from one-to-one outreach?
The agent automates research and uses it to personalize templates. Instead of writing each email from scratch, it fills personalization slots in proven frameworks. This achieves 80% of the personalization impact at 10% of the time cost.
Q5: How do you measure the ROI of the sales outreach agent?
Track: cost per email (LLM API cost), response rate, meeting conversion rate, pipeline generated, and closed deals. Compare against manual benchmarks. Typical ROI: 3-5x increase in outreach volume with 30-50% higher response rates.
Q6: How would you handle email replies and conversation handoff?
Implement reply detection that monitors incoming emails. Use sentiment analysis to categorize replies (interested, not interested, out of office). Auto-respond to simple replies; route complex conversations to human sales reps with full context.
Q7: What compliance requirements apply to B2B cold outreach?
CAN-SPAM (US): include physical address, unsubscribe link, accurate subject line. GDPR (EU): requires legitimate interest basis or consent. Include opt-out in every email. Maintain suppression lists. Never use purchased email lists without consent verification.
Q8: How do you A/B test email subject lines and content?
Split leads into randomized groups of similar score/grade. Test one variable at a time (subject line, opening, CTA). Run for statistical significance (100+ emails per variant). Use the winner for future campaigns.
Common Pitfalls & Solutions
| Pitfall | Solution |
|---|---|
| Generic emails feel spammy | Deep personalization with prospect research |
| Low response rates | A/B test subject lines and CTAs; improve personalization |
| Email deliverability issues | Warm up domains; authenticate (SPF/DKIM/DMARC); verify addresses |
| CRM data quality | Regular data enrichment and validation |
| CAN-SPAM/GDPR violations | Include unsubscribe; maintain suppression lists; document consent |
| Follow-up fatigue | Limit sequence to 3-5 touches; add value each time |
| Inconsistent brand voice | Template-based approach with personalization slots |
| No response tracking | Implement reply detection and categorization |
Knowledge Check
Q1: In the lead score formula, what does the timing component measure? A) How old the lead is B) Urgency indicators C) Time of day D) Response time
Answer
B) Urgency indicators (job changes, funding rounds, pain points).Q2: What is the recommended maximum number of follow-up emails? A) 1-2 B) 3-5 C) 10+ D) Unlimited
Answer
B) 3-5. More than 5 feel spammy and damage brand perception.Q3: What is the typical cold email open rate benchmark? A) 5-10% B) 20-25% C) 50-60% D) 90%+
Answer
B) 20-25%. Personalized subject lines increase this by 26%.Q4: Why is email warm-up important for new domains? A) Required by law B) Builds sender reputation C) Reduces API costs D) Improves content quality
Answer
B) Builds sender reputation with email providers to avoid spam folders.Q5: What does a lead grade of "A" indicate? A) Unlikely to convert B) High fit, intent, and timing C) Oldest lead D) Most engagement
Answer
B) The lead has high fit, intent, and timing scoresβhighest priority for outreach.Q6: What is the purpose of the "breakup email"? A) End relationship B) Create urgency with final touchpoint C) Request refund D) Close deal
Answer
B) Create urgency with a final touchpoint before pausing outreach.Summary with Key Takeaways
- Lead scoring prioritizes outreach for highest conversion potential using fit, intent, timing, and engagement
- Prospect research enables genuine personalization at scale, increasing response rates by 30-50%
- Multi-step sequences nurture leads over time without being spammy (3-5 touchpoints optimal)
- Performance tracking enables continuous optimization through A/B testing and metric analysis
- Always comply with email regulations (CAN-SPAM, GDPR)βinclude unsubscribe and physical address
- Email warm-up is essential for new domains to build sender reputation