Quality Guardian
The Quality Guardian ensures only exceptional vendors surface to users. It uses multi-modal AI to assess visual quality, authenticity, and professionalism.
Overview
Mission: Ensure only exceptional vendors are surfaced
Key Insight: Quality ≠ Engagement. High-quality vendors may not be viral, but they deliver excellence.
Phase: Introduced in Phase 2 (Week 6)
Quality Dimensions
1. Visual Quality
Professional photography and composition:
async assessVisualQuality(vendor: Vendor): Promise<VisualQuality> {
const posts = await this.getVendorPosts(vendor, limit: 20);
const prompt = `
Analyze these wedding vendor photos for professional visual quality.
Evaluate:
1. Photography quality (composition, lighting, color grading)
2. Professional equipment usage (sharpness, depth of field)
3. Artistic vision and style consistency
4. Post-production quality
Score 0.0-1.0 for each dimension and overall.
Images: ${posts.map(p => p.imageUrl).join(', ')}
`;
const response = await this.callGPT4Vision(prompt, posts.map(p => p.imageUrl));
return {
composition: response.composition,
lighting: response.lighting,
consistency: response.consistency,
overall: response.overall,
reasoning: response.explanation
};
}
2. Content Authenticity
Real weddings vs styled shoots:
async detectAuthenticity(vendor: Vendor): Promise<AuthenticityScore> {
const posts = await this.getVendorPosts(vendor, limit: 30);
// Indicators of real weddings:
// - Candid moments, genuine emotions
// - Variety of venues and settings
// - Client testimonials in captions
// - Behind-the-scenes content
const realWeddingCount = posts.filter(p => {
return (
this.hasCandidMoments(p) ||
this.hasGuestInteractions(p) ||
this.hasClientTestimonials(p)
);
}).length;
const authenticityRatio = realWeddingCount / posts.length;
return {
score: authenticityRatio,
realWeddings: realWeddingCount,
styledShoots: posts.length - realWeddingCount,
confidence: this.computeConfidence(posts)
};
}
3. Professionalism Signals
Vendor communication and reliability:
async assessProfessionalism(vendor: Vendor): Promise<ProfessionalismScore> {
// Caption quality
const captionQuality = await this.analyzeCaptions(vendor);
// Response patterns
const responseTime = await this.analyzeResponseTime(vendor);
// Client feedback
const clientSentiment = await this.analyzeClientComments(vendor);
// Consistency
const postingConsistency = this.analyzePostingSchedule(vendor);
return {
captionQuality: captionQuality.score,
responsive: responseTime < 24, // < 24 hours
clientSatisfaction: clientSentiment,
consistent: postingConsistency > 0.7,
overall: this.computeOverall([
captionQuality.score,
responseTime < 24 ? 1 : 0.5,
clientSentiment,
postingConsistency
])
};
}
4. Portfolio Consistency
Quality across all work:
measureConsistency(posts: Post[]): number {
// Compute embeddings for all posts
const embeddings = posts.map(p => p.embedding);
// Measure clustering (tight cluster = consistent style)
const centroid = this.computeCentroid(embeddings);
const distances = embeddings.map(e => this.euclideanDistance(e, centroid));
// Consistency = inverse of variance
const variance = this.variance(distances);
const consistency = 1 / (1 + variance);
return consistency;
}
Implementation
Multi-Modal Evaluation
class QualityGuardian {
async evaluateVendor(vendor: Vendor): Promise<QualityScore> {
// Parallel evaluation of all dimensions
const [visual, authenticity, professionalism, consistency] = await Promise.all([
this.assessVisualQuality(vendor),
this.detectAuthenticity(vendor),
this.assessProfessionalism(vendor),
this.measureConsistency(vendor.posts)
]);
// Weighted overall score
const overall =
visual.overall * 0.4 +
authenticity.score * 0.3 +
professionalism.overall * 0.2 +
consistency * 0.1;
return {
overall,
visual,
authenticity,
professionalism,
consistency,
reasoning: this.explainScore(visual, authenticity, professionalism, consistency),
strengths: this.identifyStrengths(visual, authenticity, professionalism),
improvements: this.suggestImprovements(visual, authenticity, professionalism)
};
}
}
Tiered Evaluation Strategy
Cost optimization through tiered filtering:
async tieredEvaluation(vendor: Vendor): Promise<QualityScore> {
// Tier 1: Fast heuristics (free)
const heuristic = await this.heuristicFilter(vendor);
if (heuristic.score < 0.5) {
return heuristic; // Skip low-quality immediately
}
// Tier 2: Gemini Flash (cheap, $0.0001/request)
const gemini = await this.geminiEvaluation(vendor);
if (gemini.score < 0.7) {
return gemini; // Medium quality, no need for expensive eval
}
// Tier 3: GPT-4V (expensive, $0.003/request, only for edge cases)
return await this.gpt4vEvaluation(vendor);
}
private async heuristicFilter(vendor: Vendor): Promise<QualityScore> {
// Simple rules based on Instagram metrics
const score =
(vendor.followerCount > 500 ? 0.3 : 0) +
(vendor.engagementRate > 0.03 ? 0.3 : 0) +
(vendor.postCount > 50 ? 0.2 : 0) +
(vendor.bio.length > 50 ? 0.2 : 0);
return { overall: score, tier: 'heuristic' };
}
Batch Processing
Efficient Multi-Vendor Evaluation
async batchEvaluate(vendors: Vendor[]): Promise<Map<string, QualityScore>> {
const batches = this.chunk(vendors, 10); // 10 vendors per batch
const scores = new Map();
for (const batch of batches) {
const batchScores = await this.evaluateBatch(batch);
batch.forEach((vendor, i) => {
scores.set(vendor.id, batchScores[i]);
});
// Rate limiting
await this.delay(1000); // 1 second between batches
}
return scores;
}
async evaluateBatch(vendors: Vendor[]): Promise<QualityScore[]> {
const prompt = `
Evaluate these ${vendors.length} wedding vendors for quality.
For each vendor, provide scores (0.0-1.0) for:
- Visual quality (professional photography)
- Authenticity (real weddings vs styled shoots)
- Professionalism (captions, engagement, consistency)
- Overall quality
Vendors:
${vendors.map((v, i) => `
${i + 1}. @${v.username}
Bio: ${v.bio}
Followers: ${v.followerCount}
Recent posts: ${v.recentPosts.length}
`).join('\n')}
Return JSON array: [{visual: 0.8, authenticity: 0.9, ...}, ...]
`;
const response = await this.callGemini(prompt);
return JSON.parse(response);
}
Quality Thresholds
Vendor Tiers
enum VendorTier {
EXCEPTIONAL = 'exceptional', // 0.9+
HIGH_QUALITY = 'high_quality', // 0.75-0.89
GOOD = 'good', // 0.6-0.74
AVERAGE = 'average', // 0.4-0.59
LOW_QUALITY = 'low_quality' // < 0.4
}
function getVendorTier(score: number): VendorTier {
if (score >= 0.9) return VendorTier.EXCEPTIONAL;
if (score >= 0.75) return VendorTier.HIGH_QUALITY;
if (score >= 0.6) return VendorTier.GOOD;
if (score >= 0.4) return VendorTier.AVERAGE;
return VendorTier.LOW_QUALITY;
}
Feed Filtering
async filterFeedByQuality(
candidates: Content[],
minQuality: number = 0.75
): Promise<Content[]> {
// Get quality scores (cached for 24 hours)
const scores = await this.getQualityScores(
candidates.map(c => c.vendorId)
);
// Filter by minimum threshold
return candidates.filter(c => {
const score = scores.get(c.vendorId) || 0;
return score >= minQuality;
});
}
Quality Drift Detection
Monitoring Quality Over Time
async detectQualityDrift(vendor: Vendor): Promise<DriftSignal> {
// Compare recent posts vs historical
const recentPosts = await this.getRecentPosts(vendor, days: 30);
const historicalPosts = await this.getHistoricalPosts(vendor, days: 180);
const recentQuality = await this.averageQuality(recentPosts);
const historicalQuality = await this.averageQuality(historicalPosts);
const drift = recentQuality - historicalQuality;
if (Math.abs(drift) > 0.2) {
return {
drifted: true,
direction: drift > 0 ? 'improving' : 'declining',
magnitude: Math.abs(drift),
action: drift < 0 ? 'review_vendor' : 'boost_vendor'
};
}
return { drifted: false };
}
API Endpoints
Evaluate Vendor
Endpoint: POST /api/quality/evaluate
Request:
Response:
{
"overall": 0.87,
"visual": {
"overall": 0.92,
"composition": 0.95,
"lighting": 0.90,
"consistency": 0.85
},
"authenticity": {
"score": 0.85,
"realWeddings": 24,
"styledShoots": 6
},
"professionalism": {
"overall": 0.83,
"captionQuality": 0.80,
"responsive": true,
"clientSatisfaction": 0.88
},
"consistency": 0.79,
"tier": "high_quality",
"reasoning": "Strong visual quality with excellent composition...",
"strengths": ["Professional photography", "Authentic content"],
"improvements": ["More consistent posting schedule"]
}
Batch Evaluate
Endpoint: POST /api/quality/batch
Request:
Response:
{
"scores": {
"vendor-1": { "overall": 0.92, ... },
"vendor-2": { "overall": 0.78, ... },
"vendor-3": { "overall": 0.65, ... }
},
"metadata": {
"evaluationTime": "2.3s",
"costUsd": 0.003,
"cached": 1
}
}
Performance & Costs
Targets
| Metric | Target | Notes |
|---|---|---|
| Accuracy | 85%+ | vs human evaluation |
| Latency (single) | < 2s | Gemini tier |
| Latency (batch) | < 5s | 10 vendors |
| Cost per evaluation | < $0.01 | Tiered approach |
| False positive rate | < 5% | Low quality passed |
Cost Breakdown
// Tier 1: Heuristic (free)
// - 50% of vendors filtered here
// - $0 cost
// Tier 2: Gemini ($0.0001/request)
// - 45% of vendors evaluated here
// - $0.00001 per vendor
// Tier 3: GPT-4V ($0.003/request)
// - 5% of vendors (edge cases)
// - $0.003 per vendor
// Average cost per vendor
const avgCost = 0.5 * 0 + 0.45 * 0.0001 + 0.05 * 0.003;
// = $0.00019 per vendor (~$0.0002)
Monitoring
Key Metrics
// Quality scores distribution
metrics.histogram('vendor_quality_score', score);
metrics.gauge('avg_quality_score', avgScore);
// Evaluation metrics
metrics.increment('quality_evaluations', { tier });
metrics.histogram('evaluation_latency', latency);
metrics.increment('evaluation_cost_usd', cost);
// Accuracy tracking
metrics.gauge('quality_precision', precision);
metrics.gauge('quality_recall', recall);
Related Components
- Discovery Agent - Supplies vendors for evaluation
- Orchestrator - Uses quality scores in ranking
- Foundation Model - Provides embeddings for consistency