Lead Scoring Capabilities
Predictive Analytics
ML models trained on real estate conversion data to predict deal success
Behavioral Analysis
Real-time scoring based on website activity, email engagement, and interactions
Market Intelligence
Integration with market data and economic indicators for context-aware scoring
Continuous Learning
Models that improve over time with feedback and successful deal outcomes
AI Scoring Architecture
Machine Learning Pipeline
Enhanced Lead Scoring Engine
// lib/ai/enhanced-lead-scoring.ts
export class EnhancedLeadScoringEngine {
private models: MLModelEnsemble;
private featureExtractor: FeatureExtractor;
private marketDataService: MarketDataService;
constructor() {
this.models = new MLModelEnsemble([
new XGBoostModel(),
new RandomForestModel(),
new NeuralNetworkModel()
]);
this.featureExtractor = new FeatureExtractor();
this.marketDataService = new MarketDataService();
}
async calculateEnhancedScore(
personId: string,
organizationId: string
): Promise<EnhancedLeadScore> {
try {
// Gather comprehensive data
const person = await this.getPersonWithRelations(personId, organizationId);
const behavioralData = await this.getBehavioralData(personId);
const marketContext = await this.getMarketContext(person);
const historicalPerformance = await this.getHistoricalPerformance(organizationId);
// Extract features for ML models
const features = await this.featureExtractor.extractFeatures({
person,
behavioralData,
marketContext,
historicalPerformance
});
// Get predictions from ensemble
const predictions = await this.models.predict(features);
// Calculate final score with interpretability
const score = this.calculateWeightedScore(predictions, features);
// Generate insights and recommendations
const insights = await this.generateInsights(score, features, predictions);
// Store scoring result for feedback loop
await this.storeScoreResult(personId, score);
return {
personId,
totalScore: score.overall,
subscores: score.components,
confidence: score.confidence,
factors: score.factors,
insights: insights,
recommendations: score.recommendations,
lastUpdated: new Date(),
validUntil: this.calculateValidityPeriod(features)
};
} catch (error) {
log.error('Enhanced lead scoring failed', {
personId,
error: parseError(error)
});
throw error;
}
}
private async extractFeatures(data: LeadScoringData): Promise<FeatureVector> {
const features: FeatureVector = {
// Demographic Features
demographic: {
location_quality: this.calculateLocationQuality(data.person.city, data.person.state),
property_preference_alignment: this.calculatePropertyAlignment(data.person, data.marketContext),
timeline_urgency: this.extractTimelineUrgency(data.person.notes, data.behavioralData),
budget_qualification: this.assessBudgetQualification(data.person, data.marketContext)
},
// Behavioral Features
behavioral: {
email_engagement_rate: this.calculateEmailEngagement(data.behavioralData.emailActivity),
website_activity_score: this.calculateWebsiteActivity(data.behavioralData.websiteActivity),
response_rate: this.calculateResponseRate(data.behavioralData.communications),
property_interest_depth: this.calculatePropertyInterest(data.behavioralData.propertyViews),
search_frequency: this.calculateSearchFrequency(data.behavioralData.searches),
content_consumption: this.calculateContentConsumption(data.behavioralData.contentViews)
},
// Financial Features
financial: {
preapproval_status: data.person.preapprovalStatus || 0,
stated_budget_realism: this.assessBudgetRealism(data.person.budget, data.marketContext),
financing_readiness: this.assessFinancingReadiness(data.person, data.behavioralData),
investment_indicators: this.detectInvestmentIndicators(data.person, data.behavioralData)
},
// Communication Features
communication: {
sentiment_trend: this.calculateSentimentTrend(data.behavioralData.communications),
communication_preference_fit: this.assessCommunicationFit(data.person, data.behavioralData),
responsiveness_score: this.calculateResponsiveness(data.behavioralData.communications),
objection_patterns: this.analyzeObjectionPatterns(data.behavioralData.communications)
},
// Market Context Features
market: {
market_momentum: data.marketContext.momentum,
inventory_pressure: data.marketContext.inventory,
seasonal_factor: this.calculateSeasonalFactor(new Date()),
competition_level: data.marketContext.competition,
price_trend_favorability: data.marketContext.priceTrends
},
// Source Quality Features
source: {
lead_source_quality: this.getSourceQuality(data.person.source, data.historicalPerformance),
referral_quality: this.assessReferralQuality(data.person),
attribution_confidence: this.calculateAttributionConfidence(data.person),
channel_performance: this.getChannelPerformance(data.person.source, data.historicalPerformance)
},
// Temporal Features
temporal: {
recency_score: this.calculateRecencyScore(data.person.lastContactAt),
momentum_score: this.calculateMomentumScore(data.behavioralData),
lifecycle_stage: this.determineLifecycleStage(data.person, data.behavioralData),
decay_factor: this.calculateDecayFactor(data.person.createdAt, data.person.lastContactAt)
}
};
return features;
}
private calculateWeightedScore(
predictions: ModelPredictions,
features: FeatureVector
): ScoringResult {
// Ensemble weights based on model performance
const weights = {
xgboost: 0.4, // Best for tabular data
randomForest: 0.35, // Good for feature importance
neuralNetwork: 0.25 // Captures complex interactions
};
// Calculate weighted prediction
const weightedScore = (
predictions.xgboost * weights.xgboost +
predictions.randomForest * weights.randomForest +
predictions.neuralNetwork * weights.neuralNetwork
);
// Apply business logic adjustments
const adjustedScore = this.applyBusinessRules(weightedScore, features);
// Calculate component scores
const componentScores = {
behavioral: this.calculateBehavioralScore(features.behavioral),
demographic: this.calculateDemographicScore(features.demographic),
financial: this.calculateFinancialScore(features.financial),
communication: this.calculateCommunicationScore(features.communication),
market: this.calculateMarketScore(features.market),
source: this.calculateSourceScore(features.source),
temporal: this.calculateTemporalScore(features.temporal)
};
// Calculate confidence based on data quality
const confidence = this.calculateConfidence(features, predictions);
// Generate factor explanations
const factors = this.generateFactorExplanations(features, componentScores);
// Generate recommendations
const recommendations = this.generateRecommendations(adjustedScore, componentScores, features);
return {
overall: Math.min(100, Math.max(0, adjustedScore)),
components: componentScores,
confidence,
factors,
recommendations
};
}
private applyBusinessRules(score: number, features: FeatureVector): number {
let adjustedScore = score;
// Boost for high-value indicators
if (features.financial.stated_budget_realism > 0.8 && features.behavioral.property_interest_depth > 0.7) {
adjustedScore *= 1.1; // 10% boost for qualified, engaged leads
}
// Penalty for stale leads
if (features.temporal.recency_score < 0.3) {
adjustedScore *= 0.8; // 20% penalty for stale leads
}
// Boost for referrals from high-performing sources
if (features.source.referral_quality > 0.8) {
adjustedScore *= 1.05; // 5% boost for quality referrals
}
// Market condition adjustments
if (features.market.market_momentum > 0.7) {
adjustedScore *= 1.03; // Small boost in hot markets
}
// Communication quality boost
if (features.communication.sentiment_trend > 0.6 && features.communication.responsiveness_score > 0.7) {
adjustedScore *= 1.07; // Boost for positive, responsive leads
}
return adjustedScore;
}
private generateInsights(
score: ScoringResult,
features: FeatureVector,
predictions: ModelPredictions
): LeadScoringInsight[] {
const insights: LeadScoringInsight[] = [];
// High-value opportunity detection
if (score.overall > 80) {
insights.push({
type: 'HIGH_VALUE_OPPORTUNITY',
priority: 'HIGH',
title: 'High-Value Lead Detected',
description: 'This lead shows strong conversion potential across multiple indicators.',
action: 'Prioritize immediate outreach with personalized approach.',
impact: 'REVENUE'
});
}
// Behavioral insights
if (features.behavioral.website_activity_score > 0.8) {
insights.push({
type: 'HIGH_ENGAGEMENT',
priority: 'MEDIUM',
title: 'Highly Engaged Prospect',
description: 'Lead shows high website activity and content consumption.',
action: 'Share relevant property listings and market insights.',
impact: 'ENGAGEMENT'
});
}
// Financial readiness insights
if (features.financial.financing_readiness < 0.4) {
insights.push({
type: 'FINANCING_CONCERN',
priority: 'MEDIUM',
title: 'Potential Financing Issues',
description: 'Lead may need assistance with financing preparation.',
action: 'Provide financing resources and lender recommendations.',
impact: 'QUALIFICATION'
});
}
// Communication insights
if (features.communication.responsiveness_score < 0.3) {
insights.push({
type: 'LOW_RESPONSIVENESS',
priority: 'LOW',
title: 'Communication Challenge',
description: 'Lead has been less responsive to outreach attempts.',
action: 'Try different communication channels or timing.',
impact: 'COMMUNICATION'
});
}
// Market timing insights
if (features.market.seasonal_factor > 0.8 && features.temporal.momentum_score > 0.7) {
insights.push({
type: 'OPTIMAL_TIMING',
priority: 'HIGH',
title: 'Optimal Market Timing',
description: 'Market conditions and lead momentum align for action.',
action: 'Accelerate deal progression and scheduling.',
impact: 'TIMING'
});
}
return insights;
}
}
interface EnhancedLeadScore {
personId: string;
totalScore: number;
subscores: ComponentScores;
confidence: number;
factors: ScoringFactor[];
insights: LeadScoringInsight[];
recommendations: Recommendation[];
lastUpdated: Date;
validUntil: Date;
}
interface ComponentScores {
behavioral: number;
demographic: number;
financial: number;
communication: number;
market: number;
source: number;
temporal: number;
}
interface ScoringFactor {
category: string;
factor: string;
impact: 'POSITIVE' | 'NEGATIVE' | 'NEUTRAL';
weight: number;
description: string;
}
interface LeadScoringInsight {
type: string;
priority: 'HIGH' | 'MEDIUM' | 'LOW';
title: string;
description: string;
action: string;
impact: 'REVENUE' | 'ENGAGEMENT' | 'QUALIFICATION' | 'COMMUNICATION' | 'TIMING';
}
Real-time Scoring Updates
Behavioral Tracking System
// lib/ai/behavioral-tracking.ts
export class BehavioralTrackingService {
private scoringQueue: Queue<ScoringTask>;
private eventProcessor: EventProcessor;
constructor() {
this.scoringQueue = new Queue('lead-scoring-updates');
this.eventProcessor = new EventProcessor();
this.setupEventListeners();
}
private setupEventListeners(): void {
// Website activity tracking
this.eventProcessor.on('page_view', async (event: PageViewEvent) => {
await this.processPageView(event);
});
// Email engagement tracking
this.eventProcessor.on('email_opened', async (event: EmailEvent) => {
await this.processEmailEngagement(event, 'OPENED');
});
this.eventProcessor.on('email_clicked', async (event: EmailEvent) => {
await this.processEmailEngagement(event, 'CLICKED');
});
// Communication interactions
this.eventProcessor.on('call_completed', async (event: CallEvent) => {
await this.processCallInteraction(event);
});
this.eventProcessor.on('sms_responded', async (event: SMSEvent) => {
await this.processSMSResponse(event);
});
// Property interactions
this.eventProcessor.on('property_viewed', async (event: PropertyEvent) => {
await this.processPropertyInteraction(event);
});
this.eventProcessor.on('property_favorited', async (event: PropertyEvent) => {
await this.processPropertyFavorite(event);
});
// Search behavior
this.eventProcessor.on('search_performed', async (event: SearchEvent) => {
await this.processSearchBehavior(event);
});
}
private async processPageView(event: PageViewEvent): Promise<void> {
const person = await this.getPersonByEmail(event.email);
if (!person) return;
// Update behavioral metrics
await this.updateBehavioralMetric(person.id, 'website_activity', {
pageType: event.pageType,
timeOnPage: event.duration,
timestamp: event.timestamp,
referrer: event.referrer
});
// Queue score recalculation if significant activity
if (this.isSignificantActivity(event)) {
await this.queueScoreUpdate(person.id, 'BEHAVIORAL_CHANGE');
}
}
private async processEmailEngagement(
event: EmailEvent,
action: 'OPENED' | 'CLICKED'
): Promise<void> {
const person = await database.person.findUnique({
where: { id: event.personId }
});
if (!person) return;
// Calculate engagement score impact
const impactWeight = action === 'CLICKED' ? 2.0 : 1.0;
const timeDecay = this.calculateTimeDecay(event.timestamp);
const engagementImpact = impactWeight * timeDecay;
// Update engagement metrics
await this.updateBehavioralMetric(person.id, 'email_engagement', {
action,
impactScore: engagementImpact,
campaignId: event.campaignId,
timestamp: event.timestamp
});
// Immediate score boost for email clicks
if (action === 'CLICKED') {
await this.applyImmediateScoreBoost(person.id, 5, 'EMAIL_CLICK');
}
await this.queueScoreUpdate(person.id, 'EMAIL_ENGAGEMENT');
}
private async processCallInteraction(event: CallEvent): Promise<void> {
const person = await database.person.findUnique({
where: { id: event.personId },
include: { communications: { take: 5, orderBy: { createdAt: 'desc' } } }
});
if (!person) return;
// Analyze call quality metrics
const callQualityScore = this.calculateCallQualityScore({
duration: event.duration,
sentiment: event.sentiment,
clientParticipation: event.clientTalkRatio,
outcome: event.outcome
});
// Update communication metrics
await this.updateBehavioralMetric(person.id, 'call_interaction', {
duration: event.duration,
qualityScore: callQualityScore,
sentiment: event.sentiment,
outcome: event.outcome,
timestamp: event.timestamp
});
// Significant score impact for successful calls
if (callQualityScore > 0.7) {
await this.applyImmediateScoreBoost(person.id, 10, 'SUCCESSFUL_CALL');
}
await this.queueScoreUpdate(person.id, 'CALL_INTERACTION');
}
private async processPropertyInteraction(event: PropertyEvent): Promise<void> {
const person = await this.getPersonByIdentifier(event.identifier);
if (!person) return;
// Analyze property interest depth
const interestDepth = this.calculatePropertyInterestDepth({
viewDuration: event.duration,
galleryViews: event.galleryViews,
documentViews: event.documentViews,
mapInteraction: event.mapInteraction,
priceRange: event.property.price,
propertyType: event.property.type
});
// Update property interest metrics
await this.updateBehavioralMetric(person.id, 'property_interest', {
propertyId: event.propertyId,
interestDepth,
pricePoint: event.property.price,
propertyType: event.property.type,
timestamp: event.timestamp
});
// Boost score for properties in their stated price range
if (this.isInPriceRange(event.property.price, person.budget)) {
await this.applyImmediateScoreBoost(person.id, 3, 'RELEVANT_PROPERTY_VIEW');
}
await this.queueScoreUpdate(person.id, 'PROPERTY_INTERACTION');
}
private async queueScoreUpdate(
personId: string,
trigger: string
): Promise<void> {
await this.scoringQueue.add('update-lead-score', {
personId,
trigger,
timestamp: new Date(),
priority: this.getUpdatePriority(trigger)
});
}
private async applyImmediateScoreBoost(
personId: string,
boost: number,
reason: string
): Promise<void> {
await database.person.update({
where: { id: personId },
data: {
leadScore: {
increment: boost
},
lastContactAt: new Date()
}
});
// Broadcast real-time update
await this.broadcastScoreUpdate(personId, boost, reason);
}
}
Lead Scoring Dashboard
AI Insights Interface
// components/ai-lead-scoring-dashboard.tsx
export function AILeadScoringDashboard() {
const [scoreFilter, setScoreFilter] = useState<'all' | 'hot' | 'warm' | 'cold'>('all');
const [sortBy, setSortBy] = useState<'score' | 'activity' | 'potential'>('score');
const { data: scoringData } = useQuery({
queryKey: ['ai-lead-scoring', scoreFilter, sortBy],
queryFn: () => fetch(`/api/ai/lead-scoring?filter=${scoreFilter}&sort=${sortBy}`)
.then(res => res.json())
});
const { data: modelPerformance } = useQuery({
queryKey: ['scoring-model-performance'],
queryFn: () => fetch('/api/ai/scoring-performance').then(res => res.json())
});
return (
<div className="space-y-6">
{/* Model Performance Overview */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Brain className="h-5 w-5" />
<span>AI Model Performance</span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="text-center">
<p className="text-sm text-gray-500">Accuracy</p>
<p className="text-2xl font-bold text-green-600">
{(modelPerformance?.accuracy * 100 || 0).toFixed(1)}%
</p>
</div>
<div className="text-center">
<p className="text-sm text-gray-500">Precision</p>
<p className="text-2xl font-bold text-blue-600">
{(modelPerformance?.precision * 100 || 0).toFixed(1)}%
</p>
</div>
<div className="text-center">
<p className="text-sm text-gray-500">Recall</p>
<p className="text-2xl font-bold text-purple-600">
{(modelPerformance?.recall * 100 || 0).toFixed(1)}%
</p>
</div>
<div className="text-center">
<p className="text-sm text-gray-500">Leads Scored</p>
<p className="text-2xl font-bold text-orange-600">
{modelPerformance?.totalScored?.toLocaleString() || 0}
</p>
</div>
</div>
</CardContent>
</Card>
{/* Scoring Controls */}
<div className="flex items-center justify-between">
<div className="flex space-x-4">
<Select value={scoreFilter} onValueChange={(value: unknown) => setScoreFilter(value)}>
<SelectTrigger className="w-32">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Leads</SelectItem>
<SelectItem value="hot">Hot (80-100)</SelectItem>
<SelectItem value="warm">Warm (60-79)</SelectItem>
<SelectItem value="cold">Cold (0-59)</SelectItem>
</SelectContent>
</Select>
<Select value={sortBy} onValueChange={(value: unknown) => setSortBy(value)}>
<SelectTrigger className="w-40">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="score">Lead Score</SelectItem>
<SelectItem value="activity">Recent Activity</SelectItem>
<SelectItem value="potential">Revenue Potential</SelectItem>
</SelectContent>
</Select>
</div>
<Button onClick={() => window.location.reload()}>
<RefreshCw className="h-4 w-4 mr-2" />
Refresh Scores
</Button>
</div>
{/* Lead Scoring Grid */}
<div className="grid grid-cols-1 gap-4">
{scoringData?.leads?.map((lead: unknown) => (
<Card key={lead.id} className="hover:shadow-md transition-shadow">
<CardContent className="pt-4">
<div className="flex items-start justify-between">
<div className="flex-grow">
<div className="flex items-center space-x-3 mb-3">
<Avatar className="h-10 w-10">
<AvatarFallback>
{lead.firstName?.[0]}{lead.lastName?.[0]}
</AvatarFallback>
</Avatar>
<div>
<h3 className="font-semibold">
{lead.firstName} {lead.lastName}
</h3>
<p className="text-sm text-gray-500">
{lead.email} • {lead.phone}
</p>
</div>
</div>
{/* AI Insights */}
{lead.insights?.length > 0 && (
<div className="mb-3">
<h4 className="text-sm font-medium mb-2">AI Insights</h4>
<div className="space-y-1">
{lead.insights.slice(0, 2).map((insight: unknown, index: number) => (
<div key={index} className="flex items-center text-xs">
<div className={`
w-2 h-2 rounded-full mr-2
${insight.priority === 'HIGH'
? 'bg-red-500'
: insight.priority === 'MEDIUM'
? 'bg-yellow-500'
: 'bg-blue-500'
}
`} />
<span className="text-gray-600">{insight.title}</span>
</div>
))}
</div>
</div>
)}
{/* Score Components */}
<div className="grid grid-cols-3 gap-2 text-xs">
<div>
<p className="text-gray-500">Behavioral</p>
<p className="font-medium">{lead.subscores.behavioral}/100</p>
</div>
<div>
<p className="text-gray-500">Financial</p>
<p className="font-medium">{lead.subscores.financial}/100</p>
</div>
<div>
<p className="text-gray-500">Market</p>
<p className="font-medium">{lead.subscores.market}/100</p>
</div>
</div>
</div>
{/* Score Display */}
<div className="text-center">
<div className="relative">
<CircularProgressbar
value={lead.totalScore}
text={`${lead.totalScore}`}
styles={buildStyles({
textSize: '24px',
pathColor: getScoreColor(lead.totalScore),
textColor: getScoreColor(lead.totalScore),
trailColor: '#e5e7eb'
})}
className="w-16 h-16"
/>
</div>
<p className="text-xs text-gray-500 mt-1">
{lead.confidence}% confidence
</p>
{/* Quick Actions */}
<div className="flex space-x-1 mt-2">
<Button size="sm" variant="outline" className="text-xs px-2 py-1">
<Phone className="h-3 w-3" />
</Button>
<Button size="sm" variant="outline" className="text-xs px-2 py-1">
<Mail className="h-3 w-3" />
</Button>
<Button size="sm" variant="outline" className="text-xs px-2 py-1">
<Calendar className="h-3 w-3" />
</Button>
</div>
</div>
</div>
{/* Recommendations */}
{lead.recommendations?.length > 0 && (
<div className="mt-3 pt-3 border-t">
<p className="text-xs font-medium text-gray-700 mb-1">
💡 Recommended Actions:
</p>
<p className="text-xs text-gray-600">
{lead.recommendations[0].action}
</p>
</div>
)}
</CardContent>
</Card>
))}
</div>
{/* Scoring Analytics */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Card>
<CardHeader>
<CardTitle>Score Distribution</CardTitle>
</CardHeader>
<CardContent>
<ResponsiveContainer width="100%" height={200}>
<BarChart data={scoringData?.scoreDistribution || []}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="range" />
<YAxis />
<Tooltip />
<Bar dataKey="count" fill="#3b82f6" />
</BarChart>
</ResponsiveContainer>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Feature Importance</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3">
{modelPerformance?.featureImportance?.map((feature: unknown) => (
<div key={feature.name} className="flex items-center justify-between">
<span className="text-sm">{feature.name}</span>
<div className="flex items-center space-x-2">
<div className="w-20 h-2 bg-gray-200 rounded">
<div
className="h-2 bg-blue-500 rounded transition-all"
style={{ width: `${feature.importance * 100}%` }}
/>
</div>
<span className="text-xs text-gray-500 w-10">
{(feature.importance * 100).toFixed(0)}%
</span>
</div>
</div>
))}
</div>
</CardContent>
</Card>
</div>
</div>
);
}
const getScoreColor = (score: number): string => {
if (score >= 80) return '#10b981'; // Green
if (score >= 60) return '#f59e0b'; // Yellow
return '#ef4444'; // Red
};
Model Training & Optimization
Continuous Learning Pipeline
// lib/ai/model-training.ts
export class ModelTrainingPipeline {
private trainingScheduler: CronScheduler;
private dataPreprocessor: DataPreprocessor;
private modelValidator: ModelValidator;
constructor() {
this.trainingScheduler = new CronScheduler();
this.dataPreprocessor = new DataPreprocessor();
this.modelValidator = new ModelValidator();
this.setupTrainingSchedule();
}
private setupTrainingSchedule(): void {
// Retrain models weekly with new data
this.trainingScheduler.schedule('0 2 * * 1', async () => {
await this.performWeeklyRetraining();
});
// Quick model updates daily
this.trainingScheduler.schedule('0 1 * * *', async () => {
await this.performDailyModelUpdate();
});
// Real-time feedback incorporation
this.trainingScheduler.schedule('0 */4 * * *', async () => {
await this.incorporateFeedback();
});
}
async performWeeklyRetraining(): Promise<void> {
try {
log.info('Starting weekly model retraining');
// Gather new training data
const trainingData = await this.gatherTrainingData();
// Preprocess and validate data
const processedData = await this.dataPreprocessor.process(trainingData);
// Train new model versions
const newModels = await this.trainModelEnsemble(processedData);
// Validate model performance
const validation = await this.modelValidator.validate(newModels, processedData);
// Deploy if performance improves
if (validation.performanceImproved) {
await this.deployNewModels(newModels);
log.info('New models deployed successfully', {
performance: validation.metrics
});
} else {
log.info('Model performance did not improve, keeping existing models');
}
} catch (error) {
log.error('Weekly retraining failed', { error: parseError(error) });
}
}
private async gatherTrainingData(): Promise<TrainingDataset> {
const endDate = new Date();
const startDate = new Date(endDate.getTime() - 30 * 24 * 60 * 60 * 1000); // Last 30 days
// Gather positive examples (converted leads)
const convertedLeads = await database.person.findMany({
where: {
deals: {
some: {
status: 'WON',
actualCloseDate: {
gte: startDate,
lte: endDate
}
}
}
},
include: {
deals: true,
communications: true,
activities: true
}
});
// Gather negative examples (leads that went cold)
const coldLeads = await database.person.findMany({
where: {
status: 'CLOSED_LOST',
updatedAt: {
gte: startDate,
lte: endDate
}
},
include: {
deals: true,
communications: true,
activities: true
}
});
// Gather behavioral data
const behavioralData = await this.gatherBehavioralData(
[...convertedLeads, ...coldLeads].map(p => p.id)
);
return {
positiveExamples: convertedLeads,
negativeExamples: coldLeads,
behavioralData,
marketData: await this.gatherMarketData(startDate, endDate)
};
}
private async incorporateFeedback(): Promise<void> {
// Get recent agent feedback on scoring accuracy
const feedback = await database.leadScoringFeedback.findMany({
where: {
processed: false,
createdAt: {
gte: new Date(Date.now() - 4 * 60 * 60 * 1000) // Last 4 hours
}
}
});
if (feedback.length === 0) return;
// Process feedback for model improvement
for (const item of feedback) {
await this.processFeedbackItem(item);
}
// Mark feedback as processed
await database.leadScoringFeedback.updateMany({
where: {
id: { in: feedback.map(f => f.id) }
},
data: { processed: true }
});
log.info('Incorporated lead scoring feedback', {
feedbackItems: feedback.length
});
}
}
Next Steps
AI Assistant
Explore the AI-powered assistant and recommendations
Predictive Models
Learn about market prediction and forecasting models
Contact Management
See how AI scoring integrates with contact management
AI lead scoring transforms lead qualification from guesswork into data-driven science, helping agents focus their time on the most promising opportunities and maximize conversion rates.