B2B Sales Outreach Agent
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. They enable sales teams to focus on high-value conversations while automation handles research and initial contact.
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.
These agents transform cold outreach into warm, personalized engagement by leveraging prospect data and company intelligence.
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.
Difficulty: Advanced (requires understanding of sales methodology, personalization, and CRM integration)
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: Project Structure
sales-agent/
βββ leads/
β βββ __init__.py
β βββ scorer.py
βββ outreach/
β βββ __init__.py
β βββ researcher.py
β βββ email_generator.py
βββ sequences/
β βββ __init__.py
β βββ manager.py
βββ analytics/
β βββ __init__.py
β βββ tracker.py
βββ agent.py
βββ main.py
Step 3: Lead Scorer
# leads/scorer.py
from openai import OpenAI
from typing import Dict
import json
class LeadScorer:
def __init__(self, model: str = "gpt-4-turbo-preview"):
self.client = OpenAI()
self.model = model
def score(self, lead: Dict) -> Dict:
response = 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:
return {"score": 50, "grade": "C", "reasoning": "Unable to score"}
def score_batch(self, leads: list) -> list:
return [self.score(lead) for lead in leads]
def prioritize(self, leads: list) -> list:
scored = [{"lead": lead, "score": self.score(lead)} for lead in leads]
return sorted(scored, key=lambda x: x["score"].get("score", 0), reverse=True)
Step 4: Prospect Research and Email Generation
# outreach/researcher.py
from openai import OpenAI
from typing import Dict
import json
class ProspectResearcher:
def __init__(self, model: str = "gpt-4-turbo-preview"):
self.client = OpenAI()
self.model = model
def research(self, prospect: Dict) -> Dict:
response = 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:
return {"pain_points": [], "personalization_hooks": []}
# outreach/email_generator.py
from openai import OpenAI
from typing import Dict
class EmailGenerator:
def __init__(self, model: str = "gpt-4-turbo-preview"):
self.client = OpenAI()
self.model = model
def generate_cold_email(
self, prospect: Dict, research: Dict, product: str, tone: str = "professional"
) -> Dict:
response = 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", []),
}
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]}"
response = 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 response.choices[0].message.content
Step 5: Sequence Manager and Agent
# sequences/manager.py
from typing import Dict, List
from datetime import datetime, timedelta
class SequenceManager:
def __init__(self):
self.sequences: Dict[str, List[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"
# agent.py
from leads.scorer import LeadScorer
from outreach.researcher import ProspectResearcher
from outreach.email_generator import EmailGenerator
from sequences.manager import SequenceManager
from typing import Dict, List
class SalesOutreachAgent:
def __init__(self, model: str = "gpt-4-turbo-preview"):
self.scorer = LeadScorer(model)
self.researcher = ProspectResearcher(model)
self.email_gen = EmailGenerator(model)
self.sequence_mgr = SequenceManager()
def process_lead(self, lead: Dict, product: str) -> Dict:
score = self.scorer.score(lead)
research = self.researcher.research(lead)
email = self.email_gen.generate_cold_email(lead, research, product)
return {
"lead": lead,
"score": score,
"research": research,
"email": email,
}
def create_outreach_sequence(self, lead: Dict, product: str) -> str:
data = 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)
def batch_outreach(self, leads: List[Dict], product: str) -> List[Dict]:
prioritized = self.scorer.prioritize(leads)
results = []
for item in prioritized[:10]:
result = self.process_lead(item["lead"], product)
results.append(result)
return results
Mathematical Foundation
Lead Score Calculation:
Where each parameter means:
- β fit score (company/role match)
- β intent score (buying signals)
- β timing score (urgency indicators)
- β engagement score (interaction history)
Intuition: Weighted combination of fit, intent, timing, and engagement for holistic scoring.
Sequence Conversion Rate:
Intuition: Percentage of outreach that converts to desired outcome.
Testing & Evaluation
import pytest
from leads.scorer import LeadScorer
def test_lead_scoring():
scorer = LeadScorer()
lead = {"name": "John", "company": "TechCorp", "role": "CTO", "company_size": 500}
score = scorer.score(lead)
assert "score" in score
assert 1 <= score["score"] <= 100
def test_email_generation():
gen = EmailGenerator()
email = gen.generate_cold_email(
{"name": "John", "company": "TechCorp"},
{"pain_points": ["scaling issues"]},
"Cloud Platform"
)
assert "subject" in email
assert "body" in email
Performance Metrics
| Metric | Value | Notes |
|---|---|---|
| Lead Scoring Time | 2-5s | Per lead |
| Email Generation | 3-8s | Personalized |
| Research Time | 5-10s | Per prospect |
| Sequence Creation | 10-15s | Full sequence |
| Batch Processing | 50+ leads/hr | With prioritization |
Deployment
# main.py
from agent import SalesOutreachAgent
def main():
agent = SalesOutreachAgent()
print("Sales Outreach Agent Ready\n")
while True:
cmd = input("Command (score/outreach/batch/quit): ").strip()
if cmd == "quit":
break
elif cmd == "outreach":
name = input("Prospect name: ").strip()
company = input("Company: ").strip()
product = input("Your product: ").strip()
result = agent.process_lead({"name": name, "company": company}, product)
print(f"\nScore: {result['score']['score']}/100")
print(f"Email:\n{result['email']['body']}\n")
if __name__ == "__main__":
main()
Real-World Use Cases
- SaaS Sales: Outbound prospecting for software products
- Agency Business Development: Client acquisition campaigns
- Recruiting: Candidate outreach and engagement
- Partnership Development: Business development outreach
- Event Promotion: Conference and webinar invitations
Common Pitfalls & Solutions
| Pitfall | Solution |
|---|---|
| Generic emails feel spammy | Deep personalization with research |
| Low response rates | A/B test subject lines and CTAs |
| Email deliverability | Warm up domains, monitor reputation |
| CRM data quality | Regular data enrichment and validation |
| Compliance (CAN-SPAM) | Include unsubscribe, physical address |
Summary with Key Takeaways
- Lead scoring prioritizes outreach for highest conversion potential
- Prospect research enables genuine personalization at scale
- Multi-step sequences nurture leads over time
- Performance tracking enables continuous optimization
- Always comply with email regulations (CAN-SPAM, GDPR)