Skip to content

Discovery Agent

The Discovery Agent finds exceptional wedding vendors on Instagram before they're popular. It leverages multiple curator accounts and Instagram's own recommendation algorithm to surface quality content.


Overview

Mission: Find exceptional vendors before they're popular

Strategy: Instagram-native discovery via curator accounts + AI quality filtering

Phase: Introduced in Phase 2 (Weeks 5-8)


Instagram Curator Strategy

Curator Accounts

We operate multiple Instagram accounts focused on specific regions and styles:

const CURATOR_ACCOUNTS = [
  {
    username: '@vows_au_modern',
    region: 'Australia',
    style: 'modern_minimalist',
    followers: 'quality_vendors'
  },
  {
    username: '@vows_au_boho',
    region: 'Australia',
    style: 'bohemian_rustic',
    followers: 'quality_vendors'
  },
  {
    username: '@vows_mel_luxury',
    region: 'Melbourne',
    style: 'luxury_elegant',
    followers: 'quality_vendors'
  },
  {
    username: '@vows_syd_garden',
    region: 'Sydney',
    style: 'garden_outdoor',
    followers: 'quality_vendors'
  }
];

Discovery Process

class DiscoveryAgent {
  async discoverVendors(
    region: string,
    style: string,
    limit: number = 50
  ): Promise<Vendor[]> {
    // 1. Select appropriate curator account
    const curator = this.selectCurator(region, style);

    // 2. Leverage Instagram's algorithm
    const suggestions = await this.getInstagramSuggestions(curator);

    // 3. Quality filter with AI
    const qualityScores = await this.evaluateQuality(suggestions);

    // 4. Return high-quality vendors
    return suggestions
      .filter((v, i) => qualityScores[i] > 0.75)
      .slice(0, limit);
  }

  private selectCurator(region: string, style: string): CuratorAccount {
    // Find best matching curator
    return CURATOR_ACCOUNTS.find(
      c => c.region === region && c.style === style
    ) || CURATOR_ACCOUNTS[0];  // Fallback to default
  }
}

Instagram Integration

Getting Suggestions

async getInstagramSuggestions(curator: CuratorAccount): Promise<Vendor[]> {
  const suggestions = [];

  // Method 1: Explore page suggestions
  const explorePage = await this.getExplorePage(curator);
  suggestions.push(...explorePage.accounts);

  // Method 2: "Suggested for you" on posts
  const suggestedAccounts = await this.getSuggestedAccounts(curator);
  suggestions.push(...suggestedAccounts);

  // Method 3: Hashtag exploration
  const hashtagVendors = await this.exploreHashtags(
    curator,
    ['#melbournewedding', '#weddingphotographer', '#weddingvenue']
  );
  suggestions.push(...hashtagVendors);

  // Deduplicate
  return this.deduplicate(suggestions);
}

Curator Account Management

class CuratorAccountManager {
  async engageWithContent(curator: CuratorAccount) {
    // Like high-quality posts (signal to Instagram algorithm)
    const posts = await this.getRecentPosts(curator.following);

    for (const post of posts.slice(0, 20)) {
      if (await this.isHighQuality(post)) {
        await this.like(curator, post);

        // Save exceptional content
        if (post.qualityScore > 0.9) {
          await this.save(curator, post);
        }
      }

      // Rate limiting
      await this.delay(30000);  // 30 seconds between actions
    }
  }

  async followQualityVendors(curator: CuratorAccount) {
    // Follow new quality vendors in target niche
    const candidates = await this.findVendorCandidates(curator);

    for (const vendor of candidates) {
      const quality = await this.evaluateVendor(vendor);

      if (quality > 0.8) {
        await this.follow(curator, vendor);
        await this.delay(60000);  // 1 minute between follows
      }
    }
  }
}

Quality Evaluation

Multi-Modal AI Filtering

async evaluateQuality(vendors: Vendor[]): Promise<number[]> {
  // Batch evaluation for efficiency
  const batches = this.chunk(vendors, 10);
  const scores = [];

  for (const batch of batches) {
    // Use Gemini for fast, cheap evaluation
    const batchScores = await this.batchEvaluate(batch);
    scores.push(...batchScores);
  }

  return scores;
}

async batchEvaluate(vendors: Vendor[]): Promise<number[]> {
  const prompt = `
Evaluate these wedding vendors on Instagram for quality and professionalism.
Score each from 0.0 to 1.0.

Criteria:
- Visual quality (professional photography)
- Portfolio consistency
- Genuine wedding work (not just styled shoots)
- Professionalism in captions and engagement

Vendors:
${vendors.map((v, i) => `${i + 1}. @${v.username} - ${v.bio}`).join('\n')}

Return JSON array of scores: [0.8, 0.6, 0.9, ...]
`;

  const response = await this.callGemini(prompt);
  return JSON.parse(response);
}

Emerging Vendor Detection

async detectEmergingVendors(region: string): Promise<Vendor[]> {
  // Find vendors with:
  // - High quality content
  // - Growing follower count
  // - Low current follower count (<5K)
  // - High engagement rate

  const candidates = await this.getRecentVendors(region);

  const emerging = candidates.filter(v => {
    const quality = v.qualityScore > 0.8;
    const growing = v.followerGrowthRate > 0.1;  // 10% monthly
    const small = v.followerCount < 5000;
    const engaged = v.engagementRate > 0.05;  // 5%

    return quality && growing && small && engaged;
  });

  return emerging;
}

Vendor Search by Description

async searchVendors(
  query: string,
  region: string,
  limit: number = 20
): Promise<Vendor[]> {
  // Embed search query
  const queryEmbedding = await this.embedQuery(query);

  // Vector search in Qdrant
  const results = await this.qdrant.search({
    collection: 'vendors',
    vector: queryEmbedding,
    filter: {
      must: [
        { key: 'region', match: { value: region } },
        { key: 'qualityScore', range: { gte: 0.7 } }
      ]
    },
    limit
  });

  return results.map(r => r.payload);
}

Content-Based Vendor Discovery

async findSimilarVendors(
  vendorId: string,
  limit: number = 10
): Promise<Vendor[]> {
  // Get vendor's content embeddings
  const vendor = await this.getVendor(vendorId);
  const vendorEmbedding = await this.computeVendorEmbedding(vendor);

  // Find similar vendors in embedding space
  const similar = await this.qdrant.search({
    collection: 'vendors',
    vector: vendorEmbedding,
    filter: {
      must_not: [
        { key: 'id', match: { value: vendorId } }  // Exclude self
      ]
    },
    limit
  });

  return similar.map(r => r.payload);
}

Relationship Graph Analysis

Vendor Network Discovery

async discoverViaNetwork(seedVendor: Vendor): Promise<Vendor[]> {
  const network = new Set<Vendor>();

  // Level 1: Who seed vendor follows
  const following = await this.getFollowing(seedVendor);
  network.add(...following);

  // Level 2: Who those vendors follow (filtered)
  for (const vendor of following) {
    if (vendor.qualityScore > 0.8) {
      const secondLevel = await this.getFollowing(vendor);
      network.add(...secondLevel.slice(0, 10));  // Limit spread
    }
  }

  // Filter by quality
  return Array.from(network).filter(v => v.qualityScore > 0.75);
}

Collaborative Filtering

async findCollaborativeVendors(userId: string): Promise<Vendor[]> {
  // Find users with similar taste
  const similarUsers = await this.findSimilarUsers(userId);

  // Get vendors they love but our user hasn't seen
  const candidateVendors = new Set<Vendor>();

  for (const user of similarUsers) {
    const theirVendors = await this.getUserVendors(user.id);

    for (const vendor of theirVendors) {
      if (!await this.hasSeenVendor(userId, vendor.id)) {
        candidateVendors.add(vendor);
      }
    }
  }

  return Array.from(candidateVendors)
    .sort((a, b) => b.qualityScore - a.qualityScore)
    .slice(0, 20);
}

API Endpoints

Discover Vendors

Endpoint: POST /api/discovery/vendors

Request:

{
  "region": "Melbourne",
  "style": "modern",
  "category": "photographer",
  "limit": 50
}

Response:

{
  "vendors": [
    {
      "id": "vendor-123",
      "username": "modernweddingsmelb",
      "displayName": "Modern Weddings Melbourne",
      "category": "photographer",
      "qualityScore": 0.92,
      "followerCount": 3500,
      "engagementRate": 0.08,
      "isEmerging": true,
      "portfolioUrl": "https://instagram.com/modernweddingsmelb"
    }
  ],
  "metadata": {
    "curatorAccount": "@vows_mel_modern",
    "discoveryMethod": "instagram_suggestions",
    "totalCandidates": 120,
    "filtered": 50
  }
}

Search Vendors

Endpoint: GET /api/discovery/search

Request:

GET /api/discovery/search?q=garden+venue+melbourne&limit=20

Response:

{
  "vendors": [...],
  "query": "garden venue melbourne",
  "semanticMatch": true
}


Performance & Costs

Targets

Metric Target Notes
Discovery rate 50+ vendors/week Per region
Quality precision 80%+ % high quality
API cost < $0.01/vendor Gemini batch eval
Instagram rate limits 0 violations Stay within ToS

Cost Optimization

// Tiered evaluation
async evaluateVendor(vendor: Vendor): Promise<number> {
  // Tier 1: Cheap heuristics (free)
  const heuristic = this.heuristicScore(vendor);
  if (heuristic < 0.5) return heuristic;  // Skip bad ones

  // Tier 2: Gemini (cheap)
  const gemini = await this.geminiScore(vendor);
  if (gemini < 0.7) return gemini;

  // Tier 3: GPT-4V (expensive, only for edge cases)
  return await this.gpt4vScore(vendor);
}

Monitoring

Key Metrics

// Track discovery performance
metrics.increment('vendors_discovered', { region, style });
metrics.histogram('vendor_quality_score', qualityScore);
metrics.gauge('emerging_vendors_found', emergingCount);

// Track API usage
metrics.increment('instagram_api_calls', { curator });
metrics.increment('quality_evaluations', { model: 'gemini' });

// Track costs
metrics.increment('discovery_cost_usd', cost);

Resources