mirror of
https://github.com/f/awesome-chatgpt-prompts.git
synced 2026-02-12 15:52:47 +00:00
Add prompt: Sales Research
This commit is contained in:
886
PROMPTS.md
886
PROMPTS.md
@@ -66526,3 +66526,889 @@ This skill provides expert guidance on analyzing financial statements...
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Sales Research</strong></summary>
|
||||
|
||||
## Sales Research
|
||||
|
||||
Contributed by [@TomsTools11](https://github.com/TomsTools11)
|
||||
|
||||
```md
|
||||
---
|
||||
name: sales-research
|
||||
description: This skill provides methodology and best practices for researching sales prospects.
|
||||
---
|
||||
|
||||
# Sales Research
|
||||
|
||||
## Overview
|
||||
|
||||
This skill provides methodology and best practices for researching sales prospects. It covers company research, contact profiling, and signal detection to surface actionable intelligence.
|
||||
|
||||
## Usage
|
||||
|
||||
The company-researcher and contact-researcher sub-agents reference this skill when:
|
||||
- Researching new prospects
|
||||
- Finding company information
|
||||
- Profiling individual contacts
|
||||
- Detecting buying signals
|
||||
|
||||
## Research Methodology
|
||||
|
||||
### Company Research Checklist
|
||||
|
||||
1. **Basic Profile**
|
||||
- Company name, industry, size (employees, revenue)
|
||||
- Headquarters and key locations
|
||||
- Founded date, growth stage
|
||||
|
||||
2. **Recent Developments**
|
||||
- Funding announcements (last 12 months)
|
||||
- M&A activity
|
||||
- Leadership changes
|
||||
- Product launches
|
||||
|
||||
3. **Tech Stack**
|
||||
- Known technologies (BuiltWith, StackShare)
|
||||
- Job postings mentioning tools
|
||||
- Integration partnerships
|
||||
|
||||
4. **Signals**
|
||||
- Job postings (scaling = opportunity)
|
||||
- Glassdoor reviews (pain points)
|
||||
- News mentions (context)
|
||||
- Social media activity
|
||||
|
||||
### Contact Research Checklist
|
||||
|
||||
1. **Professional Background**
|
||||
- Current role and tenure
|
||||
- Previous companies and roles
|
||||
- Education
|
||||
|
||||
2. **Influence Indicators**
|
||||
- Reporting structure
|
||||
- Decision-making authority
|
||||
- Budget ownership
|
||||
|
||||
3. **Engagement Hooks**
|
||||
- Recent LinkedIn posts
|
||||
- Published articles
|
||||
- Speaking engagements
|
||||
- Mutual connections
|
||||
|
||||
## Resources
|
||||
|
||||
- `resources/signal-indicators.md` - Taxonomy of buying signals
|
||||
- `resources/research-checklist.md` - Complete research checklist
|
||||
|
||||
## Scripts
|
||||
|
||||
- `scripts/company-enricher.py` - Aggregate company data from multiple sources
|
||||
- `scripts/linkedin-parser.py` - Structure LinkedIn profile data
|
||||
FILE:company-enricher.py
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
company-enricher.py - Aggregate company data from multiple sources
|
||||
|
||||
Inputs:
|
||||
- company_name: string
|
||||
- domain: string (optional)
|
||||
|
||||
Outputs:
|
||||
- profile:
|
||||
name: string
|
||||
industry: string
|
||||
size: string
|
||||
funding: string
|
||||
tech_stack: [string]
|
||||
recent_news: [news items]
|
||||
|
||||
Dependencies:
|
||||
- requests, beautifulsoup4
|
||||
"""
|
||||
|
||||
# Requirements: requests, beautifulsoup4
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
from dataclasses import dataclass, asdict
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
class NewsItem:
|
||||
title: str
|
||||
date: str
|
||||
source: str
|
||||
url: str
|
||||
summary: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class CompanyProfile:
|
||||
name: str
|
||||
domain: str
|
||||
industry: str
|
||||
size: str
|
||||
location: str
|
||||
founded: str
|
||||
funding: str
|
||||
tech_stack: list[str]
|
||||
recent_news: list[dict]
|
||||
competitors: list[str]
|
||||
description: str
|
||||
|
||||
|
||||
def search_company_info(company_name: str, domain: str = None) -> dict:
|
||||
"""
|
||||
Search for basic company information.
|
||||
In production, this would call APIs like Clearbit, Crunchbase, etc.
|
||||
"""
|
||||
# TODO: Implement actual API calls
|
||||
# Placeholder return structure
|
||||
return {
|
||||
"name": company_name,
|
||||
"domain": domain or f"{company_name.lower().replace(' ', '')}.com",
|
||||
"industry": "Technology", # Would come from API
|
||||
"size": "Unknown",
|
||||
"location": "Unknown",
|
||||
"founded": "Unknown",
|
||||
"description": f"Information about {company_name}"
|
||||
}
|
||||
|
||||
|
||||
def search_funding_info(company_name: str) -> dict:
|
||||
"""
|
||||
Search for funding information.
|
||||
In production, would call Crunchbase, PitchBook, etc.
|
||||
"""
|
||||
# TODO: Implement actual API calls
|
||||
return {
|
||||
"total_funding": "Unknown",
|
||||
"last_round": "Unknown",
|
||||
"last_round_date": "Unknown",
|
||||
"investors": []
|
||||
}
|
||||
|
||||
|
||||
def search_tech_stack(domain: str) -> list[str]:
|
||||
"""
|
||||
Detect technology stack.
|
||||
In production, would call BuiltWith, Wappalyzer, etc.
|
||||
"""
|
||||
# TODO: Implement actual API calls
|
||||
return []
|
||||
|
||||
|
||||
def search_recent_news(company_name: str, days: int = 90) -> list[dict]:
|
||||
"""
|
||||
Search for recent news about the company.
|
||||
In production, would call news APIs.
|
||||
"""
|
||||
# TODO: Implement actual API calls
|
||||
return []
|
||||
|
||||
|
||||
def main(
|
||||
company_name: str,
|
||||
domain: str = None
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
Aggregate company data from multiple sources.
|
||||
|
||||
Args:
|
||||
company_name: Company name to research
|
||||
domain: Company domain (optional, will be inferred)
|
||||
|
||||
Returns:
|
||||
dict with company profile including industry, size, funding, tech stack, news
|
||||
"""
|
||||
# Get basic company info
|
||||
basic_info = search_company_info(company_name, domain)
|
||||
|
||||
# Get funding information
|
||||
funding_info = search_funding_info(company_name)
|
||||
|
||||
# Detect tech stack
|
||||
company_domain = basic_info.get("domain", domain)
|
||||
tech_stack = search_tech_stack(company_domain) if company_domain else []
|
||||
|
||||
# Get recent news
|
||||
news = search_recent_news(company_name)
|
||||
|
||||
# Compile profile
|
||||
profile = CompanyProfile(
|
||||
name=basic_info["name"],
|
||||
domain=basic_info["domain"],
|
||||
industry=basic_info["industry"],
|
||||
size=basic_info["size"],
|
||||
location=basic_info["location"],
|
||||
founded=basic_info["founded"],
|
||||
funding=funding_info.get("total_funding", "Unknown"),
|
||||
tech_stack=tech_stack,
|
||||
recent_news=news,
|
||||
competitors=[], # Would be enriched from industry analysis
|
||||
description=basic_info["description"]
|
||||
)
|
||||
|
||||
return {
|
||||
"profile": asdict(profile),
|
||||
"funding_details": funding_info,
|
||||
"enriched_at": datetime.now().isoformat(),
|
||||
"sources_checked": ["company_info", "funding", "tech_stack", "news"]
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
# Example usage
|
||||
result = main(
|
||||
company_name="DataFlow Systems",
|
||||
domain="dataflow.io"
|
||||
)
|
||||
print(json.dumps(result, indent=2))
|
||||
FILE:linkedin-parser.py
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
linkedin-parser.py - Structure LinkedIn profile data
|
||||
|
||||
Inputs:
|
||||
- profile_url: string
|
||||
- or name + company: strings
|
||||
|
||||
Outputs:
|
||||
- contact:
|
||||
name: string
|
||||
title: string
|
||||
tenure: string
|
||||
previous_roles: [role objects]
|
||||
mutual_connections: [string]
|
||||
recent_activity: [post summaries]
|
||||
|
||||
Dependencies:
|
||||
- requests
|
||||
"""
|
||||
|
||||
# Requirements: requests
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
from dataclasses import dataclass, asdict
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
class PreviousRole:
|
||||
title: str
|
||||
company: str
|
||||
duration: str
|
||||
description: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class RecentPost:
|
||||
date: str
|
||||
content_preview: str
|
||||
engagement: int
|
||||
topic: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class ContactProfile:
|
||||
name: str
|
||||
title: str
|
||||
company: str
|
||||
location: str
|
||||
tenure: str
|
||||
previous_roles: list[dict]
|
||||
education: list[str]
|
||||
mutual_connections: list[str]
|
||||
recent_activity: list[dict]
|
||||
profile_url: str
|
||||
headline: str
|
||||
|
||||
|
||||
def search_linkedin_profile(name: str = None, company: str = None, profile_url: str = None) -> dict:
|
||||
"""
|
||||
Search for LinkedIn profile information.
|
||||
In production, would use LinkedIn API or Sales Navigator.
|
||||
"""
|
||||
# TODO: Implement actual LinkedIn API integration
|
||||
# Note: LinkedIn's API has strict terms of service
|
||||
|
||||
return {
|
||||
"found": False,
|
||||
"name": name or "Unknown",
|
||||
"title": "Unknown",
|
||||
"company": company or "Unknown",
|
||||
"location": "Unknown",
|
||||
"headline": "",
|
||||
"tenure": "Unknown",
|
||||
"profile_url": profile_url or ""
|
||||
}
|
||||
|
||||
|
||||
def get_career_history(profile_data: dict) -> list[dict]:
|
||||
"""
|
||||
Extract career history from profile.
|
||||
"""
|
||||
# TODO: Implement career extraction
|
||||
return []
|
||||
|
||||
|
||||
def get_mutual_connections(profile_data: dict, user_network: list = None) -> list[str]:
|
||||
"""
|
||||
Find mutual connections.
|
||||
"""
|
||||
# TODO: Implement mutual connection detection
|
||||
return []
|
||||
|
||||
|
||||
def get_recent_activity(profile_data: dict, days: int = 30) -> list[dict]:
|
||||
"""
|
||||
Get recent posts and activity.
|
||||
"""
|
||||
# TODO: Implement activity extraction
|
||||
return []
|
||||
|
||||
|
||||
def main(
|
||||
name: str = None,
|
||||
company: str = None,
|
||||
profile_url: str = None
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
Structure LinkedIn profile data for sales prep.
|
||||
|
||||
Args:
|
||||
name: Person's name
|
||||
company: Company they work at
|
||||
profile_url: Direct LinkedIn profile URL
|
||||
|
||||
Returns:
|
||||
dict with structured contact profile
|
||||
"""
|
||||
if not profile_url and not (name and company):
|
||||
return {"error": "Provide either profile_url or name + company"}
|
||||
|
||||
# Search for profile
|
||||
profile_data = search_linkedin_profile(
|
||||
name=name,
|
||||
company=company,
|
||||
profile_url=profile_url
|
||||
)
|
||||
|
||||
if not profile_data.get("found"):
|
||||
return {
|
||||
"found": False,
|
||||
"name": name or "Unknown",
|
||||
"company": company or "Unknown",
|
||||
"message": "Profile not found or limited access",
|
||||
"suggestions": [
|
||||
"Try searching directly on LinkedIn",
|
||||
"Check for alternative spellings",
|
||||
"Verify the person still works at this company"
|
||||
]
|
||||
}
|
||||
|
||||
# Get career history
|
||||
previous_roles = get_career_history(profile_data)
|
||||
|
||||
# Find mutual connections
|
||||
mutual_connections = get_mutual_connections(profile_data)
|
||||
|
||||
# Get recent activity
|
||||
recent_activity = get_recent_activity(profile_data)
|
||||
|
||||
# Compile contact profile
|
||||
contact = ContactProfile(
|
||||
name=profile_data["name"],
|
||||
title=profile_data["title"],
|
||||
company=profile_data["company"],
|
||||
location=profile_data["location"],
|
||||
tenure=profile_data["tenure"],
|
||||
previous_roles=previous_roles,
|
||||
education=[], # Would be extracted from profile
|
||||
mutual_connections=mutual_connections,
|
||||
recent_activity=recent_activity,
|
||||
profile_url=profile_data["profile_url"],
|
||||
headline=profile_data["headline"]
|
||||
)
|
||||
|
||||
return {
|
||||
"found": True,
|
||||
"contact": asdict(contact),
|
||||
"research_date": datetime.now().isoformat(),
|
||||
"data_completeness": calculate_completeness(contact)
|
||||
}
|
||||
|
||||
|
||||
def calculate_completeness(contact: ContactProfile) -> dict:
|
||||
"""Calculate how complete the profile data is."""
|
||||
fields = {
|
||||
"basic_info": bool(contact.name and contact.title and contact.company),
|
||||
"career_history": len(contact.previous_roles) > 0,
|
||||
"mutual_connections": len(contact.mutual_connections) > 0,
|
||||
"recent_activity": len(contact.recent_activity) > 0,
|
||||
"education": len(contact.education) > 0
|
||||
}
|
||||
|
||||
complete_count = sum(fields.values())
|
||||
return {
|
||||
"fields": fields,
|
||||
"score": f"{complete_count}/{len(fields)}",
|
||||
"percentage": int((complete_count / len(fields)) * 100)
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
# Example usage
|
||||
result = main(
|
||||
name="Sarah Chen",
|
||||
company="DataFlow Systems"
|
||||
)
|
||||
print(json.dumps(result, indent=2))
|
||||
FILE:priority-scorer.py
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
priority-scorer.py - Calculate and rank prospect priorities
|
||||
|
||||
Inputs:
|
||||
- prospects: [prospect objects with signals]
|
||||
- weights: {deal_size, timing, warmth, signals}
|
||||
|
||||
Outputs:
|
||||
- ranked: [prospects with scores and reasoning]
|
||||
|
||||
Dependencies:
|
||||
- (none - pure Python)
|
||||
"""
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
# Default scoring weights
|
||||
DEFAULT_WEIGHTS = {
|
||||
"deal_size": 0.25,
|
||||
"timing": 0.30,
|
||||
"warmth": 0.20,
|
||||
"signals": 0.25
|
||||
}
|
||||
|
||||
# Signal score mapping
|
||||
SIGNAL_SCORES = {
|
||||
# High-intent signals
|
||||
"recent_funding": 10,
|
||||
"leadership_change": 8,
|
||||
"job_postings_relevant": 9,
|
||||
"expansion_news": 7,
|
||||
"competitor_mention": 6,
|
||||
|
||||
# Medium-intent signals
|
||||
"general_hiring": 4,
|
||||
"industry_event": 3,
|
||||
"content_engagement": 3,
|
||||
|
||||
# Relationship signals
|
||||
"mutual_connection": 5,
|
||||
"previous_contact": 6,
|
||||
"referred_lead": 8,
|
||||
|
||||
# Negative signals
|
||||
"recent_layoffs": -3,
|
||||
"budget_freeze_mentioned": -5,
|
||||
"competitor_selected": -7,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class ScoredProspect:
|
||||
company: str
|
||||
contact: str
|
||||
call_time: str
|
||||
raw_score: float
|
||||
normalized_score: int
|
||||
priority_rank: int
|
||||
score_breakdown: dict
|
||||
reasoning: str
|
||||
is_followup: bool
|
||||
|
||||
|
||||
def score_deal_size(prospect: dict) -> tuple[float, str]:
|
||||
"""Score based on estimated deal size."""
|
||||
size_indicators = prospect.get("size_indicators", {})
|
||||
|
||||
employee_count = size_indicators.get("employees", 0)
|
||||
revenue_estimate = size_indicators.get("revenue", 0)
|
||||
|
||||
# Simple scoring based on company size
|
||||
if employee_count > 1000 or revenue_estimate > 100_000_000:
|
||||
return 10.0, "Enterprise-scale opportunity"
|
||||
elif employee_count > 200 or revenue_estimate > 20_000_000:
|
||||
return 7.0, "Mid-market opportunity"
|
||||
elif employee_count > 50:
|
||||
return 5.0, "SMB opportunity"
|
||||
else:
|
||||
return 3.0, "Small business"
|
||||
|
||||
|
||||
def score_timing(prospect: dict) -> tuple[float, str]:
|
||||
"""Score based on timing signals."""
|
||||
timing_signals = prospect.get("timing_signals", [])
|
||||
|
||||
score = 5.0 # Base score
|
||||
reasons = []
|
||||
|
||||
for signal in timing_signals:
|
||||
if signal == "budget_cycle_q4":
|
||||
score += 3
|
||||
reasons.append("Q4 budget planning")
|
||||
elif signal == "contract_expiring":
|
||||
score += 4
|
||||
reasons.append("Contract expiring soon")
|
||||
elif signal == "active_evaluation":
|
||||
score += 5
|
||||
reasons.append("Actively evaluating")
|
||||
elif signal == "just_funded":
|
||||
score += 3
|
||||
reasons.append("Recently funded")
|
||||
|
||||
return min(score, 10.0), "; ".join(reasons) if reasons else "Standard timing"
|
||||
|
||||
|
||||
def score_warmth(prospect: dict) -> tuple[float, str]:
|
||||
"""Score based on relationship warmth."""
|
||||
relationship = prospect.get("relationship", {})
|
||||
|
||||
if relationship.get("is_followup"):
|
||||
last_outcome = relationship.get("last_outcome", "neutral")
|
||||
if last_outcome == "positive":
|
||||
return 9.0, "Warm follow-up (positive last contact)"
|
||||
elif last_outcome == "neutral":
|
||||
return 7.0, "Follow-up (neutral last contact)"
|
||||
else:
|
||||
return 5.0, "Follow-up (needs re-engagement)"
|
||||
|
||||
if relationship.get("referred"):
|
||||
return 8.0, "Referred lead"
|
||||
|
||||
if relationship.get("mutual_connections", 0) > 0:
|
||||
return 6.0, f"{relationship['mutual_connections']} mutual connections"
|
||||
|
||||
if relationship.get("inbound"):
|
||||
return 7.0, "Inbound interest"
|
||||
|
||||
return 4.0, "Cold outreach"
|
||||
|
||||
|
||||
def score_signals(prospect: dict) -> tuple[float, str]:
|
||||
"""Score based on buying signals detected."""
|
||||
signals = prospect.get("signals", [])
|
||||
|
||||
total_score = 0
|
||||
signal_reasons = []
|
||||
|
||||
for signal in signals:
|
||||
signal_score = SIGNAL_SCORES.get(signal, 0)
|
||||
total_score += signal_score
|
||||
if signal_score > 0:
|
||||
signal_reasons.append(signal.replace("_", " "))
|
||||
|
||||
# Normalize to 0-10 scale
|
||||
normalized = min(max(total_score / 2, 0), 10)
|
||||
|
||||
reason = f"Signals: {', '.join(signal_reasons)}" if signal_reasons else "No strong signals"
|
||||
return normalized, reason
|
||||
|
||||
|
||||
def calculate_priority_score(
|
||||
prospect: dict,
|
||||
weights: dict = None
|
||||
) -> ScoredProspect:
|
||||
"""Calculate overall priority score for a prospect."""
|
||||
weights = weights or DEFAULT_WEIGHTS
|
||||
|
||||
# Calculate component scores
|
||||
deal_score, deal_reason = score_deal_size(prospect)
|
||||
timing_score, timing_reason = score_timing(prospect)
|
||||
warmth_score, warmth_reason = score_warmth(prospect)
|
||||
signal_score, signal_reason = score_signals(prospect)
|
||||
|
||||
# Weighted total
|
||||
raw_score = (
|
||||
deal_score * weights["deal_size"] +
|
||||
timing_score * weights["timing"] +
|
||||
warmth_score * weights["warmth"] +
|
||||
signal_score * weights["signals"]
|
||||
)
|
||||
|
||||
# Compile reasoning
|
||||
reasons = []
|
||||
if timing_score >= 8:
|
||||
reasons.append(timing_reason)
|
||||
if signal_score >= 7:
|
||||
reasons.append(signal_reason)
|
||||
if warmth_score >= 7:
|
||||
reasons.append(warmth_reason)
|
||||
if deal_score >= 8:
|
||||
reasons.append(deal_reason)
|
||||
|
||||
return ScoredProspect(
|
||||
company=prospect.get("company", "Unknown"),
|
||||
contact=prospect.get("contact", "Unknown"),
|
||||
call_time=prospect.get("call_time", "Unknown"),
|
||||
raw_score=round(raw_score, 2),
|
||||
normalized_score=int(raw_score * 10),
|
||||
priority_rank=0, # Will be set after sorting
|
||||
score_breakdown={
|
||||
"deal_size": {"score": deal_score, "reason": deal_reason},
|
||||
"timing": {"score": timing_score, "reason": timing_reason},
|
||||
"warmth": {"score": warmth_score, "reason": warmth_reason},
|
||||
"signals": {"score": signal_score, "reason": signal_reason}
|
||||
},
|
||||
reasoning="; ".join(reasons) if reasons else "Standard priority",
|
||||
is_followup=prospect.get("relationship", {}).get("is_followup", False)
|
||||
)
|
||||
|
||||
|
||||
def main(
|
||||
prospects: list[dict],
|
||||
weights: dict = None
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
Calculate and rank prospect priorities.
|
||||
|
||||
Args:
|
||||
prospects: List of prospect objects with signals
|
||||
weights: Optional custom weights for scoring components
|
||||
|
||||
Returns:
|
||||
dict with ranked prospects and scoring details
|
||||
"""
|
||||
weights = weights or DEFAULT_WEIGHTS
|
||||
|
||||
# Score all prospects
|
||||
scored = [calculate_priority_score(p, weights) for p in prospects]
|
||||
|
||||
# Sort by raw score descending
|
||||
scored.sort(key=lambda x: x.raw_score, reverse=True)
|
||||
|
||||
# Assign ranks
|
||||
for i, prospect in enumerate(scored, 1):
|
||||
prospect.priority_rank = i
|
||||
|
||||
# Convert to dicts for JSON serialization
|
||||
ranked = []
|
||||
for s in scored:
|
||||
ranked.append({
|
||||
"company": s.company,
|
||||
"contact": s.contact,
|
||||
"call_time": s.call_time,
|
||||
"priority_rank": s.priority_rank,
|
||||
"score": s.normalized_score,
|
||||
"reasoning": s.reasoning,
|
||||
"is_followup": s.is_followup,
|
||||
"breakdown": s.score_breakdown
|
||||
})
|
||||
|
||||
return {
|
||||
"ranked": ranked,
|
||||
"weights_used": weights,
|
||||
"total_prospects": len(prospects)
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
# Example usage
|
||||
example_prospects = [
|
||||
{
|
||||
"company": "DataFlow Systems",
|
||||
"contact": "Sarah Chen",
|
||||
"call_time": "2pm",
|
||||
"size_indicators": {"employees": 200, "revenue": 25_000_000},
|
||||
"timing_signals": ["just_funded", "active_evaluation"],
|
||||
"signals": ["recent_funding", "job_postings_relevant"],
|
||||
"relationship": {"is_followup": False, "mutual_connections": 2}
|
||||
},
|
||||
{
|
||||
"company": "Acme Manufacturing",
|
||||
"contact": "Tom Bradley",
|
||||
"call_time": "10am",
|
||||
"size_indicators": {"employees": 500},
|
||||
"timing_signals": ["contract_expiring"],
|
||||
"signals": [],
|
||||
"relationship": {"is_followup": True, "last_outcome": "neutral"}
|
||||
},
|
||||
{
|
||||
"company": "FirstRate Financial",
|
||||
"contact": "Linda Thompson",
|
||||
"call_time": "4pm",
|
||||
"size_indicators": {"employees": 300},
|
||||
"timing_signals": [],
|
||||
"signals": [],
|
||||
"relationship": {"is_followup": False}
|
||||
}
|
||||
]
|
||||
|
||||
result = main(prospects=example_prospects)
|
||||
print(json.dumps(result, indent=2))
|
||||
FILE:research-checklist.md
|
||||
# Prospect Research Checklist
|
||||
|
||||
## Company Research
|
||||
|
||||
### Basic Information
|
||||
- [ ] Company name (verify spelling)
|
||||
- [ ] Industry/vertical
|
||||
- [ ] Headquarters location
|
||||
- [ ] Employee count (LinkedIn, website)
|
||||
- [ ] Revenue estimate (if available)
|
||||
- [ ] Founded date
|
||||
- [ ] Funding stage/history
|
||||
|
||||
### Recent News (Last 90 Days)
|
||||
- [ ] Funding announcements
|
||||
- [ ] Acquisitions or mergers
|
||||
- [ ] Leadership changes
|
||||
- [ ] Product launches
|
||||
- [ ] Major customer wins
|
||||
- [ ] Press mentions
|
||||
- [ ] Earnings/financial news
|
||||
|
||||
### Digital Footprint
|
||||
- [ ] Website review
|
||||
- [ ] Blog/content topics
|
||||
- [ ] Social media presence
|
||||
- [ ] Job postings (careers page + LinkedIn)
|
||||
- [ ] Tech stack (BuiltWith, job postings)
|
||||
|
||||
### Competitive Landscape
|
||||
- [ ] Known competitors
|
||||
- [ ] Market position
|
||||
- [ ] Differentiators claimed
|
||||
- [ ] Recent competitive moves
|
||||
|
||||
### Pain Point Indicators
|
||||
- [ ] Glassdoor reviews (themes)
|
||||
- [ ] G2/Capterra reviews (if B2B)
|
||||
- [ ] Social media complaints
|
||||
- [ ] Job posting patterns
|
||||
|
||||
## Contact Research
|
||||
|
||||
### Professional Profile
|
||||
- [ ] Current title
|
||||
- [ ] Time in role
|
||||
- [ ] Time at company
|
||||
- [ ] Previous companies
|
||||
- [ ] Previous roles
|
||||
- [ ] Education
|
||||
|
||||
### Decision Authority
|
||||
- [ ] Reports to whom
|
||||
- [ ] Team size (if manager)
|
||||
- [ ] Budget authority (inferred)
|
||||
- [ ] Buying involvement history
|
||||
|
||||
### Engagement Hooks
|
||||
- [ ] Recent LinkedIn posts
|
||||
- [ ] Published articles
|
||||
- [ ] Podcast appearances
|
||||
- [ ] Conference talks
|
||||
- [ ] Mutual connections
|
||||
- [ ] Shared interests/groups
|
||||
|
||||
### Communication Style
|
||||
- [ ] Post tone (formal/casual)
|
||||
- [ ] Topics they engage with
|
||||
- [ ] Response patterns
|
||||
|
||||
## CRM Check (If Available)
|
||||
|
||||
- [ ] Any prior touchpoints
|
||||
- [ ] Previous opportunities
|
||||
- [ ] Related contacts at company
|
||||
- [ ] Notes from colleagues
|
||||
- [ ] Email engagement history
|
||||
|
||||
## Time-Based Research Depth
|
||||
|
||||
| Time Available | Research Depth |
|
||||
|----------------|----------------|
|
||||
| 5 minutes | Company basics + contact title only |
|
||||
| 15 minutes | + Recent news + LinkedIn profile |
|
||||
| 30 minutes | + Pain point signals + engagement hooks |
|
||||
| 60 minutes | Full checklist + competitive analysis |
|
||||
FILE:signal-indicators.md
|
||||
# Signal Indicators Reference
|
||||
|
||||
## High-Intent Signals
|
||||
|
||||
### Job Postings
|
||||
- **3+ relevant roles posted** = Active initiative, budget allocated
|
||||
- **Senior hire in your domain** = Strategic priority
|
||||
- **Urgency language ("ASAP", "immediate")** = Pain is acute
|
||||
- **Specific tool mentioned** = Competitor or category awareness
|
||||
|
||||
### Financial Events
|
||||
- **Series B+ funding** = Growth capital, buying power
|
||||
- **IPO preparation** = Operational maturity needed
|
||||
- **Acquisition announced** = Integration challenges coming
|
||||
- **Revenue milestone PR** = Budget available
|
||||
|
||||
### Leadership Changes
|
||||
- **New CXO in your domain** = 90-day priority setting
|
||||
- **New CRO/CMO** = Tech stack evaluation likely
|
||||
- **Founder transition to CEO** = Professionalizing operations
|
||||
|
||||
## Medium-Intent Signals
|
||||
|
||||
### Expansion Signals
|
||||
- **New office opening** = Infrastructure needs
|
||||
- **International expansion** = Localization, compliance
|
||||
- **New product launch** = Scaling challenges
|
||||
- **Major customer win** = Delivery pressure
|
||||
|
||||
### Technology Signals
|
||||
- **RFP published** = Active buying process
|
||||
- **Vendor review mentioned** = Comparison shopping
|
||||
- **Tech stack change** = Integration opportunity
|
||||
- **Legacy system complaints** = Modernization need
|
||||
|
||||
### Content Signals
|
||||
- **Blog post on your topic** = Educating themselves
|
||||
- **Webinar attendance** = Interest confirmed
|
||||
- **Whitepaper download** = Problem awareness
|
||||
- **Conference speaking** = Thought leadership, visibility
|
||||
|
||||
## Low-Intent Signals (Nurture)
|
||||
|
||||
### General Activity
|
||||
- **Industry event attendance** = Market participant
|
||||
- **Generic hiring** = Company growing
|
||||
- **Positive press** = Healthy company
|
||||
- **Social media activity** = Engaged leadership
|
||||
|
||||
## Signal Scoring
|
||||
|
||||
| Signal Type | Score | Action |
|
||||
|-------------|-------|--------|
|
||||
| Job posting (relevant) | +3 | Prioritize outreach |
|
||||
| Recent funding | +3 | Reference in conversation |
|
||||
| Leadership change | +2 | Time-sensitive opportunity |
|
||||
| Expansion news | +2 | Growth angle |
|
||||
| Negative reviews | +2 | Pain point angle |
|
||||
| Content engagement | +1 | Nurture track |
|
||||
| No signals | 0 | Discovery focus |
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
874
prompts.csv
874
prompts.csv
@@ -52186,3 +52186,877 @@ This skill provides expert guidance on analyzing financial statements...
|
||||
- [ ] Consistent output format
|
||||
- [ ] Resistant to jailbreaks
|
||||
```",FALSE,TEXT,TomsTools11
|
||||
Sales Research,"---
|
||||
name: sales-research
|
||||
description: This skill provides methodology and best practices for researching sales prospects.
|
||||
---
|
||||
|
||||
# Sales Research
|
||||
|
||||
## Overview
|
||||
|
||||
This skill provides methodology and best practices for researching sales prospects. It covers company research, contact profiling, and signal detection to surface actionable intelligence.
|
||||
|
||||
## Usage
|
||||
|
||||
The company-researcher and contact-researcher sub-agents reference this skill when:
|
||||
- Researching new prospects
|
||||
- Finding company information
|
||||
- Profiling individual contacts
|
||||
- Detecting buying signals
|
||||
|
||||
## Research Methodology
|
||||
|
||||
### Company Research Checklist
|
||||
|
||||
1. **Basic Profile**
|
||||
- Company name, industry, size (employees, revenue)
|
||||
- Headquarters and key locations
|
||||
- Founded date, growth stage
|
||||
|
||||
2. **Recent Developments**
|
||||
- Funding announcements (last 12 months)
|
||||
- M&A activity
|
||||
- Leadership changes
|
||||
- Product launches
|
||||
|
||||
3. **Tech Stack**
|
||||
- Known technologies (BuiltWith, StackShare)
|
||||
- Job postings mentioning tools
|
||||
- Integration partnerships
|
||||
|
||||
4. **Signals**
|
||||
- Job postings (scaling = opportunity)
|
||||
- Glassdoor reviews (pain points)
|
||||
- News mentions (context)
|
||||
- Social media activity
|
||||
|
||||
### Contact Research Checklist
|
||||
|
||||
1. **Professional Background**
|
||||
- Current role and tenure
|
||||
- Previous companies and roles
|
||||
- Education
|
||||
|
||||
2. **Influence Indicators**
|
||||
- Reporting structure
|
||||
- Decision-making authority
|
||||
- Budget ownership
|
||||
|
||||
3. **Engagement Hooks**
|
||||
- Recent LinkedIn posts
|
||||
- Published articles
|
||||
- Speaking engagements
|
||||
- Mutual connections
|
||||
|
||||
## Resources
|
||||
|
||||
- `resources/signal-indicators.md` - Taxonomy of buying signals
|
||||
- `resources/research-checklist.md` - Complete research checklist
|
||||
|
||||
## Scripts
|
||||
|
||||
- `scripts/company-enricher.py` - Aggregate company data from multiple sources
|
||||
- `scripts/linkedin-parser.py` - Structure LinkedIn profile data
|
||||
FILE:company-enricher.py
|
||||
#!/usr/bin/env python3
|
||||
""""""
|
||||
company-enricher.py - Aggregate company data from multiple sources
|
||||
|
||||
Inputs:
|
||||
- company_name: string
|
||||
- domain: string (optional)
|
||||
|
||||
Outputs:
|
||||
- profile:
|
||||
name: string
|
||||
industry: string
|
||||
size: string
|
||||
funding: string
|
||||
tech_stack: [string]
|
||||
recent_news: [news items]
|
||||
|
||||
Dependencies:
|
||||
- requests, beautifulsoup4
|
||||
""""""
|
||||
|
||||
# Requirements: requests, beautifulsoup4
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
from dataclasses import dataclass, asdict
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
class NewsItem:
|
||||
title: str
|
||||
date: str
|
||||
source: str
|
||||
url: str
|
||||
summary: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class CompanyProfile:
|
||||
name: str
|
||||
domain: str
|
||||
industry: str
|
||||
size: str
|
||||
location: str
|
||||
founded: str
|
||||
funding: str
|
||||
tech_stack: list[str]
|
||||
recent_news: list[dict]
|
||||
competitors: list[str]
|
||||
description: str
|
||||
|
||||
|
||||
def search_company_info(company_name: str, domain: str = None) -> dict:
|
||||
""""""
|
||||
Search for basic company information.
|
||||
In production, this would call APIs like Clearbit, Crunchbase, etc.
|
||||
""""""
|
||||
# TODO: Implement actual API calls
|
||||
# Placeholder return structure
|
||||
return {
|
||||
""name"": company_name,
|
||||
""domain"": domain or f""{company_name.lower().replace(' ', '')}.com"",
|
||||
""industry"": ""Technology"", # Would come from API
|
||||
""size"": ""Unknown"",
|
||||
""location"": ""Unknown"",
|
||||
""founded"": ""Unknown"",
|
||||
""description"": f""Information about {company_name}""
|
||||
}
|
||||
|
||||
|
||||
def search_funding_info(company_name: str) -> dict:
|
||||
""""""
|
||||
Search for funding information.
|
||||
In production, would call Crunchbase, PitchBook, etc.
|
||||
""""""
|
||||
# TODO: Implement actual API calls
|
||||
return {
|
||||
""total_funding"": ""Unknown"",
|
||||
""last_round"": ""Unknown"",
|
||||
""last_round_date"": ""Unknown"",
|
||||
""investors"": []
|
||||
}
|
||||
|
||||
|
||||
def search_tech_stack(domain: str) -> list[str]:
|
||||
""""""
|
||||
Detect technology stack.
|
||||
In production, would call BuiltWith, Wappalyzer, etc.
|
||||
""""""
|
||||
# TODO: Implement actual API calls
|
||||
return []
|
||||
|
||||
|
||||
def search_recent_news(company_name: str, days: int = 90) -> list[dict]:
|
||||
""""""
|
||||
Search for recent news about the company.
|
||||
In production, would call news APIs.
|
||||
""""""
|
||||
# TODO: Implement actual API calls
|
||||
return []
|
||||
|
||||
|
||||
def main(
|
||||
company_name: str,
|
||||
domain: str = None
|
||||
) -> dict[str, Any]:
|
||||
""""""
|
||||
Aggregate company data from multiple sources.
|
||||
|
||||
Args:
|
||||
company_name: Company name to research
|
||||
domain: Company domain (optional, will be inferred)
|
||||
|
||||
Returns:
|
||||
dict with company profile including industry, size, funding, tech stack, news
|
||||
""""""
|
||||
# Get basic company info
|
||||
basic_info = search_company_info(company_name, domain)
|
||||
|
||||
# Get funding information
|
||||
funding_info = search_funding_info(company_name)
|
||||
|
||||
# Detect tech stack
|
||||
company_domain = basic_info.get(""domain"", domain)
|
||||
tech_stack = search_tech_stack(company_domain) if company_domain else []
|
||||
|
||||
# Get recent news
|
||||
news = search_recent_news(company_name)
|
||||
|
||||
# Compile profile
|
||||
profile = CompanyProfile(
|
||||
name=basic_info[""name""],
|
||||
domain=basic_info[""domain""],
|
||||
industry=basic_info[""industry""],
|
||||
size=basic_info[""size""],
|
||||
location=basic_info[""location""],
|
||||
founded=basic_info[""founded""],
|
||||
funding=funding_info.get(""total_funding"", ""Unknown""),
|
||||
tech_stack=tech_stack,
|
||||
recent_news=news,
|
||||
competitors=[], # Would be enriched from industry analysis
|
||||
description=basic_info[""description""]
|
||||
)
|
||||
|
||||
return {
|
||||
""profile"": asdict(profile),
|
||||
""funding_details"": funding_info,
|
||||
""enriched_at"": datetime.now().isoformat(),
|
||||
""sources_checked"": [""company_info"", ""funding"", ""tech_stack"", ""news""]
|
||||
}
|
||||
|
||||
|
||||
if __name__ == ""__main__"":
|
||||
import sys
|
||||
|
||||
# Example usage
|
||||
result = main(
|
||||
company_name=""DataFlow Systems"",
|
||||
domain=""dataflow.io""
|
||||
)
|
||||
print(json.dumps(result, indent=2))
|
||||
FILE:linkedin-parser.py
|
||||
#!/usr/bin/env python3
|
||||
""""""
|
||||
linkedin-parser.py - Structure LinkedIn profile data
|
||||
|
||||
Inputs:
|
||||
- profile_url: string
|
||||
- or name + company: strings
|
||||
|
||||
Outputs:
|
||||
- contact:
|
||||
name: string
|
||||
title: string
|
||||
tenure: string
|
||||
previous_roles: [role objects]
|
||||
mutual_connections: [string]
|
||||
recent_activity: [post summaries]
|
||||
|
||||
Dependencies:
|
||||
- requests
|
||||
""""""
|
||||
|
||||
# Requirements: requests
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
from dataclasses import dataclass, asdict
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
class PreviousRole:
|
||||
title: str
|
||||
company: str
|
||||
duration: str
|
||||
description: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class RecentPost:
|
||||
date: str
|
||||
content_preview: str
|
||||
engagement: int
|
||||
topic: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class ContactProfile:
|
||||
name: str
|
||||
title: str
|
||||
company: str
|
||||
location: str
|
||||
tenure: str
|
||||
previous_roles: list[dict]
|
||||
education: list[str]
|
||||
mutual_connections: list[str]
|
||||
recent_activity: list[dict]
|
||||
profile_url: str
|
||||
headline: str
|
||||
|
||||
|
||||
def search_linkedin_profile(name: str = None, company: str = None, profile_url: str = None) -> dict:
|
||||
""""""
|
||||
Search for LinkedIn profile information.
|
||||
In production, would use LinkedIn API or Sales Navigator.
|
||||
""""""
|
||||
# TODO: Implement actual LinkedIn API integration
|
||||
# Note: LinkedIn's API has strict terms of service
|
||||
|
||||
return {
|
||||
""found"": False,
|
||||
""name"": name or ""Unknown"",
|
||||
""title"": ""Unknown"",
|
||||
""company"": company or ""Unknown"",
|
||||
""location"": ""Unknown"",
|
||||
""headline"": """",
|
||||
""tenure"": ""Unknown"",
|
||||
""profile_url"": profile_url or """"
|
||||
}
|
||||
|
||||
|
||||
def get_career_history(profile_data: dict) -> list[dict]:
|
||||
""""""
|
||||
Extract career history from profile.
|
||||
""""""
|
||||
# TODO: Implement career extraction
|
||||
return []
|
||||
|
||||
|
||||
def get_mutual_connections(profile_data: dict, user_network: list = None) -> list[str]:
|
||||
""""""
|
||||
Find mutual connections.
|
||||
""""""
|
||||
# TODO: Implement mutual connection detection
|
||||
return []
|
||||
|
||||
|
||||
def get_recent_activity(profile_data: dict, days: int = 30) -> list[dict]:
|
||||
""""""
|
||||
Get recent posts and activity.
|
||||
""""""
|
||||
# TODO: Implement activity extraction
|
||||
return []
|
||||
|
||||
|
||||
def main(
|
||||
name: str = None,
|
||||
company: str = None,
|
||||
profile_url: str = None
|
||||
) -> dict[str, Any]:
|
||||
""""""
|
||||
Structure LinkedIn profile data for sales prep.
|
||||
|
||||
Args:
|
||||
name: Person's name
|
||||
company: Company they work at
|
||||
profile_url: Direct LinkedIn profile URL
|
||||
|
||||
Returns:
|
||||
dict with structured contact profile
|
||||
""""""
|
||||
if not profile_url and not (name and company):
|
||||
return {""error"": ""Provide either profile_url or name + company""}
|
||||
|
||||
# Search for profile
|
||||
profile_data = search_linkedin_profile(
|
||||
name=name,
|
||||
company=company,
|
||||
profile_url=profile_url
|
||||
)
|
||||
|
||||
if not profile_data.get(""found""):
|
||||
return {
|
||||
""found"": False,
|
||||
""name"": name or ""Unknown"",
|
||||
""company"": company or ""Unknown"",
|
||||
""message"": ""Profile not found or limited access"",
|
||||
""suggestions"": [
|
||||
""Try searching directly on LinkedIn"",
|
||||
""Check for alternative spellings"",
|
||||
""Verify the person still works at this company""
|
||||
]
|
||||
}
|
||||
|
||||
# Get career history
|
||||
previous_roles = get_career_history(profile_data)
|
||||
|
||||
# Find mutual connections
|
||||
mutual_connections = get_mutual_connections(profile_data)
|
||||
|
||||
# Get recent activity
|
||||
recent_activity = get_recent_activity(profile_data)
|
||||
|
||||
# Compile contact profile
|
||||
contact = ContactProfile(
|
||||
name=profile_data[""name""],
|
||||
title=profile_data[""title""],
|
||||
company=profile_data[""company""],
|
||||
location=profile_data[""location""],
|
||||
tenure=profile_data[""tenure""],
|
||||
previous_roles=previous_roles,
|
||||
education=[], # Would be extracted from profile
|
||||
mutual_connections=mutual_connections,
|
||||
recent_activity=recent_activity,
|
||||
profile_url=profile_data[""profile_url""],
|
||||
headline=profile_data[""headline""]
|
||||
)
|
||||
|
||||
return {
|
||||
""found"": True,
|
||||
""contact"": asdict(contact),
|
||||
""research_date"": datetime.now().isoformat(),
|
||||
""data_completeness"": calculate_completeness(contact)
|
||||
}
|
||||
|
||||
|
||||
def calculate_completeness(contact: ContactProfile) -> dict:
|
||||
""""""Calculate how complete the profile data is.""""""
|
||||
fields = {
|
||||
""basic_info"": bool(contact.name and contact.title and contact.company),
|
||||
""career_history"": len(contact.previous_roles) > 0,
|
||||
""mutual_connections"": len(contact.mutual_connections) > 0,
|
||||
""recent_activity"": len(contact.recent_activity) > 0,
|
||||
""education"": len(contact.education) > 0
|
||||
}
|
||||
|
||||
complete_count = sum(fields.values())
|
||||
return {
|
||||
""fields"": fields,
|
||||
""score"": f""{complete_count}/{len(fields)}"",
|
||||
""percentage"": int((complete_count / len(fields)) * 100)
|
||||
}
|
||||
|
||||
|
||||
if __name__ == ""__main__"":
|
||||
import sys
|
||||
|
||||
# Example usage
|
||||
result = main(
|
||||
name=""Sarah Chen"",
|
||||
company=""DataFlow Systems""
|
||||
)
|
||||
print(json.dumps(result, indent=2))
|
||||
FILE:priority-scorer.py
|
||||
#!/usr/bin/env python3
|
||||
""""""
|
||||
priority-scorer.py - Calculate and rank prospect priorities
|
||||
|
||||
Inputs:
|
||||
- prospects: [prospect objects with signals]
|
||||
- weights: {deal_size, timing, warmth, signals}
|
||||
|
||||
Outputs:
|
||||
- ranked: [prospects with scores and reasoning]
|
||||
|
||||
Dependencies:
|
||||
- (none - pure Python)
|
||||
""""""
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
# Default scoring weights
|
||||
DEFAULT_WEIGHTS = {
|
||||
""deal_size"": 0.25,
|
||||
""timing"": 0.30,
|
||||
""warmth"": 0.20,
|
||||
""signals"": 0.25
|
||||
}
|
||||
|
||||
# Signal score mapping
|
||||
SIGNAL_SCORES = {
|
||||
# High-intent signals
|
||||
""recent_funding"": 10,
|
||||
""leadership_change"": 8,
|
||||
""job_postings_relevant"": 9,
|
||||
""expansion_news"": 7,
|
||||
""competitor_mention"": 6,
|
||||
|
||||
# Medium-intent signals
|
||||
""general_hiring"": 4,
|
||||
""industry_event"": 3,
|
||||
""content_engagement"": 3,
|
||||
|
||||
# Relationship signals
|
||||
""mutual_connection"": 5,
|
||||
""previous_contact"": 6,
|
||||
""referred_lead"": 8,
|
||||
|
||||
# Negative signals
|
||||
""recent_layoffs"": -3,
|
||||
""budget_freeze_mentioned"": -5,
|
||||
""competitor_selected"": -7,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class ScoredProspect:
|
||||
company: str
|
||||
contact: str
|
||||
call_time: str
|
||||
raw_score: float
|
||||
normalized_score: int
|
||||
priority_rank: int
|
||||
score_breakdown: dict
|
||||
reasoning: str
|
||||
is_followup: bool
|
||||
|
||||
|
||||
def score_deal_size(prospect: dict) -> tuple[float, str]:
|
||||
""""""Score based on estimated deal size.""""""
|
||||
size_indicators = prospect.get(""size_indicators"", {})
|
||||
|
||||
employee_count = size_indicators.get(""employees"", 0)
|
||||
revenue_estimate = size_indicators.get(""revenue"", 0)
|
||||
|
||||
# Simple scoring based on company size
|
||||
if employee_count > 1000 or revenue_estimate > 100_000_000:
|
||||
return 10.0, ""Enterprise-scale opportunity""
|
||||
elif employee_count > 200 or revenue_estimate > 20_000_000:
|
||||
return 7.0, ""Mid-market opportunity""
|
||||
elif employee_count > 50:
|
||||
return 5.0, ""SMB opportunity""
|
||||
else:
|
||||
return 3.0, ""Small business""
|
||||
|
||||
|
||||
def score_timing(prospect: dict) -> tuple[float, str]:
|
||||
""""""Score based on timing signals.""""""
|
||||
timing_signals = prospect.get(""timing_signals"", [])
|
||||
|
||||
score = 5.0 # Base score
|
||||
reasons = []
|
||||
|
||||
for signal in timing_signals:
|
||||
if signal == ""budget_cycle_q4"":
|
||||
score += 3
|
||||
reasons.append(""Q4 budget planning"")
|
||||
elif signal == ""contract_expiring"":
|
||||
score += 4
|
||||
reasons.append(""Contract expiring soon"")
|
||||
elif signal == ""active_evaluation"":
|
||||
score += 5
|
||||
reasons.append(""Actively evaluating"")
|
||||
elif signal == ""just_funded"":
|
||||
score += 3
|
||||
reasons.append(""Recently funded"")
|
||||
|
||||
return min(score, 10.0), ""; "".join(reasons) if reasons else ""Standard timing""
|
||||
|
||||
|
||||
def score_warmth(prospect: dict) -> tuple[float, str]:
|
||||
""""""Score based on relationship warmth.""""""
|
||||
relationship = prospect.get(""relationship"", {})
|
||||
|
||||
if relationship.get(""is_followup""):
|
||||
last_outcome = relationship.get(""last_outcome"", ""neutral"")
|
||||
if last_outcome == ""positive"":
|
||||
return 9.0, ""Warm follow-up (positive last contact)""
|
||||
elif last_outcome == ""neutral"":
|
||||
return 7.0, ""Follow-up (neutral last contact)""
|
||||
else:
|
||||
return 5.0, ""Follow-up (needs re-engagement)""
|
||||
|
||||
if relationship.get(""referred""):
|
||||
return 8.0, ""Referred lead""
|
||||
|
||||
if relationship.get(""mutual_connections"", 0) > 0:
|
||||
return 6.0, f""{relationship['mutual_connections']} mutual connections""
|
||||
|
||||
if relationship.get(""inbound""):
|
||||
return 7.0, ""Inbound interest""
|
||||
|
||||
return 4.0, ""Cold outreach""
|
||||
|
||||
|
||||
def score_signals(prospect: dict) -> tuple[float, str]:
|
||||
""""""Score based on buying signals detected.""""""
|
||||
signals = prospect.get(""signals"", [])
|
||||
|
||||
total_score = 0
|
||||
signal_reasons = []
|
||||
|
||||
for signal in signals:
|
||||
signal_score = SIGNAL_SCORES.get(signal, 0)
|
||||
total_score += signal_score
|
||||
if signal_score > 0:
|
||||
signal_reasons.append(signal.replace(""_"", "" ""))
|
||||
|
||||
# Normalize to 0-10 scale
|
||||
normalized = min(max(total_score / 2, 0), 10)
|
||||
|
||||
reason = f""Signals: {', '.join(signal_reasons)}"" if signal_reasons else ""No strong signals""
|
||||
return normalized, reason
|
||||
|
||||
|
||||
def calculate_priority_score(
|
||||
prospect: dict,
|
||||
weights: dict = None
|
||||
) -> ScoredProspect:
|
||||
""""""Calculate overall priority score for a prospect.""""""
|
||||
weights = weights or DEFAULT_WEIGHTS
|
||||
|
||||
# Calculate component scores
|
||||
deal_score, deal_reason = score_deal_size(prospect)
|
||||
timing_score, timing_reason = score_timing(prospect)
|
||||
warmth_score, warmth_reason = score_warmth(prospect)
|
||||
signal_score, signal_reason = score_signals(prospect)
|
||||
|
||||
# Weighted total
|
||||
raw_score = (
|
||||
deal_score * weights[""deal_size""] +
|
||||
timing_score * weights[""timing""] +
|
||||
warmth_score * weights[""warmth""] +
|
||||
signal_score * weights[""signals""]
|
||||
)
|
||||
|
||||
# Compile reasoning
|
||||
reasons = []
|
||||
if timing_score >= 8:
|
||||
reasons.append(timing_reason)
|
||||
if signal_score >= 7:
|
||||
reasons.append(signal_reason)
|
||||
if warmth_score >= 7:
|
||||
reasons.append(warmth_reason)
|
||||
if deal_score >= 8:
|
||||
reasons.append(deal_reason)
|
||||
|
||||
return ScoredProspect(
|
||||
company=prospect.get(""company"", ""Unknown""),
|
||||
contact=prospect.get(""contact"", ""Unknown""),
|
||||
call_time=prospect.get(""call_time"", ""Unknown""),
|
||||
raw_score=round(raw_score, 2),
|
||||
normalized_score=int(raw_score * 10),
|
||||
priority_rank=0, # Will be set after sorting
|
||||
score_breakdown={
|
||||
""deal_size"": {""score"": deal_score, ""reason"": deal_reason},
|
||||
""timing"": {""score"": timing_score, ""reason"": timing_reason},
|
||||
""warmth"": {""score"": warmth_score, ""reason"": warmth_reason},
|
||||
""signals"": {""score"": signal_score, ""reason"": signal_reason}
|
||||
},
|
||||
reasoning=""; "".join(reasons) if reasons else ""Standard priority"",
|
||||
is_followup=prospect.get(""relationship"", {}).get(""is_followup"", False)
|
||||
)
|
||||
|
||||
|
||||
def main(
|
||||
prospects: list[dict],
|
||||
weights: dict = None
|
||||
) -> dict[str, Any]:
|
||||
""""""
|
||||
Calculate and rank prospect priorities.
|
||||
|
||||
Args:
|
||||
prospects: List of prospect objects with signals
|
||||
weights: Optional custom weights for scoring components
|
||||
|
||||
Returns:
|
||||
dict with ranked prospects and scoring details
|
||||
""""""
|
||||
weights = weights or DEFAULT_WEIGHTS
|
||||
|
||||
# Score all prospects
|
||||
scored = [calculate_priority_score(p, weights) for p in prospects]
|
||||
|
||||
# Sort by raw score descending
|
||||
scored.sort(key=lambda x: x.raw_score, reverse=True)
|
||||
|
||||
# Assign ranks
|
||||
for i, prospect in enumerate(scored, 1):
|
||||
prospect.priority_rank = i
|
||||
|
||||
# Convert to dicts for JSON serialization
|
||||
ranked = []
|
||||
for s in scored:
|
||||
ranked.append({
|
||||
""company"": s.company,
|
||||
""contact"": s.contact,
|
||||
""call_time"": s.call_time,
|
||||
""priority_rank"": s.priority_rank,
|
||||
""score"": s.normalized_score,
|
||||
""reasoning"": s.reasoning,
|
||||
""is_followup"": s.is_followup,
|
||||
""breakdown"": s.score_breakdown
|
||||
})
|
||||
|
||||
return {
|
||||
""ranked"": ranked,
|
||||
""weights_used"": weights,
|
||||
""total_prospects"": len(prospects)
|
||||
}
|
||||
|
||||
|
||||
if __name__ == ""__main__"":
|
||||
import sys
|
||||
|
||||
# Example usage
|
||||
example_prospects = [
|
||||
{
|
||||
""company"": ""DataFlow Systems"",
|
||||
""contact"": ""Sarah Chen"",
|
||||
""call_time"": ""2pm"",
|
||||
""size_indicators"": {""employees"": 200, ""revenue"": 25_000_000},
|
||||
""timing_signals"": [""just_funded"", ""active_evaluation""],
|
||||
""signals"": [""recent_funding"", ""job_postings_relevant""],
|
||||
""relationship"": {""is_followup"": False, ""mutual_connections"": 2}
|
||||
},
|
||||
{
|
||||
""company"": ""Acme Manufacturing"",
|
||||
""contact"": ""Tom Bradley"",
|
||||
""call_time"": ""10am"",
|
||||
""size_indicators"": {""employees"": 500},
|
||||
""timing_signals"": [""contract_expiring""],
|
||||
""signals"": [],
|
||||
""relationship"": {""is_followup"": True, ""last_outcome"": ""neutral""}
|
||||
},
|
||||
{
|
||||
""company"": ""FirstRate Financial"",
|
||||
""contact"": ""Linda Thompson"",
|
||||
""call_time"": ""4pm"",
|
||||
""size_indicators"": {""employees"": 300},
|
||||
""timing_signals"": [],
|
||||
""signals"": [],
|
||||
""relationship"": {""is_followup"": False}
|
||||
}
|
||||
]
|
||||
|
||||
result = main(prospects=example_prospects)
|
||||
print(json.dumps(result, indent=2))
|
||||
FILE:research-checklist.md
|
||||
# Prospect Research Checklist
|
||||
|
||||
## Company Research
|
||||
|
||||
### Basic Information
|
||||
- [ ] Company name (verify spelling)
|
||||
- [ ] Industry/vertical
|
||||
- [ ] Headquarters location
|
||||
- [ ] Employee count (LinkedIn, website)
|
||||
- [ ] Revenue estimate (if available)
|
||||
- [ ] Founded date
|
||||
- [ ] Funding stage/history
|
||||
|
||||
### Recent News (Last 90 Days)
|
||||
- [ ] Funding announcements
|
||||
- [ ] Acquisitions or mergers
|
||||
- [ ] Leadership changes
|
||||
- [ ] Product launches
|
||||
- [ ] Major customer wins
|
||||
- [ ] Press mentions
|
||||
- [ ] Earnings/financial news
|
||||
|
||||
### Digital Footprint
|
||||
- [ ] Website review
|
||||
- [ ] Blog/content topics
|
||||
- [ ] Social media presence
|
||||
- [ ] Job postings (careers page + LinkedIn)
|
||||
- [ ] Tech stack (BuiltWith, job postings)
|
||||
|
||||
### Competitive Landscape
|
||||
- [ ] Known competitors
|
||||
- [ ] Market position
|
||||
- [ ] Differentiators claimed
|
||||
- [ ] Recent competitive moves
|
||||
|
||||
### Pain Point Indicators
|
||||
- [ ] Glassdoor reviews (themes)
|
||||
- [ ] G2/Capterra reviews (if B2B)
|
||||
- [ ] Social media complaints
|
||||
- [ ] Job posting patterns
|
||||
|
||||
## Contact Research
|
||||
|
||||
### Professional Profile
|
||||
- [ ] Current title
|
||||
- [ ] Time in role
|
||||
- [ ] Time at company
|
||||
- [ ] Previous companies
|
||||
- [ ] Previous roles
|
||||
- [ ] Education
|
||||
|
||||
### Decision Authority
|
||||
- [ ] Reports to whom
|
||||
- [ ] Team size (if manager)
|
||||
- [ ] Budget authority (inferred)
|
||||
- [ ] Buying involvement history
|
||||
|
||||
### Engagement Hooks
|
||||
- [ ] Recent LinkedIn posts
|
||||
- [ ] Published articles
|
||||
- [ ] Podcast appearances
|
||||
- [ ] Conference talks
|
||||
- [ ] Mutual connections
|
||||
- [ ] Shared interests/groups
|
||||
|
||||
### Communication Style
|
||||
- [ ] Post tone (formal/casual)
|
||||
- [ ] Topics they engage with
|
||||
- [ ] Response patterns
|
||||
|
||||
## CRM Check (If Available)
|
||||
|
||||
- [ ] Any prior touchpoints
|
||||
- [ ] Previous opportunities
|
||||
- [ ] Related contacts at company
|
||||
- [ ] Notes from colleagues
|
||||
- [ ] Email engagement history
|
||||
|
||||
## Time-Based Research Depth
|
||||
|
||||
| Time Available | Research Depth |
|
||||
|----------------|----------------|
|
||||
| 5 minutes | Company basics + contact title only |
|
||||
| 15 minutes | + Recent news + LinkedIn profile |
|
||||
| 30 minutes | + Pain point signals + engagement hooks |
|
||||
| 60 minutes | Full checklist + competitive analysis |
|
||||
FILE:signal-indicators.md
|
||||
# Signal Indicators Reference
|
||||
|
||||
## High-Intent Signals
|
||||
|
||||
### Job Postings
|
||||
- **3+ relevant roles posted** = Active initiative, budget allocated
|
||||
- **Senior hire in your domain** = Strategic priority
|
||||
- **Urgency language (""ASAP"", ""immediate"")** = Pain is acute
|
||||
- **Specific tool mentioned** = Competitor or category awareness
|
||||
|
||||
### Financial Events
|
||||
- **Series B+ funding** = Growth capital, buying power
|
||||
- **IPO preparation** = Operational maturity needed
|
||||
- **Acquisition announced** = Integration challenges coming
|
||||
- **Revenue milestone PR** = Budget available
|
||||
|
||||
### Leadership Changes
|
||||
- **New CXO in your domain** = 90-day priority setting
|
||||
- **New CRO/CMO** = Tech stack evaluation likely
|
||||
- **Founder transition to CEO** = Professionalizing operations
|
||||
|
||||
## Medium-Intent Signals
|
||||
|
||||
### Expansion Signals
|
||||
- **New office opening** = Infrastructure needs
|
||||
- **International expansion** = Localization, compliance
|
||||
- **New product launch** = Scaling challenges
|
||||
- **Major customer win** = Delivery pressure
|
||||
|
||||
### Technology Signals
|
||||
- **RFP published** = Active buying process
|
||||
- **Vendor review mentioned** = Comparison shopping
|
||||
- **Tech stack change** = Integration opportunity
|
||||
- **Legacy system complaints** = Modernization need
|
||||
|
||||
### Content Signals
|
||||
- **Blog post on your topic** = Educating themselves
|
||||
- **Webinar attendance** = Interest confirmed
|
||||
- **Whitepaper download** = Problem awareness
|
||||
- **Conference speaking** = Thought leadership, visibility
|
||||
|
||||
## Low-Intent Signals (Nurture)
|
||||
|
||||
### General Activity
|
||||
- **Industry event attendance** = Market participant
|
||||
- **Generic hiring** = Company growing
|
||||
- **Positive press** = Healthy company
|
||||
- **Social media activity** = Engaged leadership
|
||||
|
||||
## Signal Scoring
|
||||
|
||||
| Signal Type | Score | Action |
|
||||
|-------------|-------|--------|
|
||||
| Job posting (relevant) | +3 | Prioritize outreach |
|
||||
| Recent funding | +3 | Reference in conversation |
|
||||
| Leadership change | +2 | Time-sensitive opportunity |
|
||||
| Expansion news | +2 | Growth angle |
|
||||
| Negative reviews | +2 | Pain point angle |
|
||||
| Content engagement | +1 | Nurture track |
|
||||
| No signals | 0 | Discovery focus |",FALSE,TEXT,TomsTools11
|
||||
|
||||
|
Can't render this file because it is too large.
|
Reference in New Issue
Block a user