• Skip to main content
  • Skip to navigation
  • Skip to search
    Petunia™
    FeaturesPricingIntegrationsAboutContact
    Log inStart free trialSign up
    Loading
    Petunia™

    Reimagining customer communication for the modern business.

    Product

    • Features
    • Pricing
    • Integrations
    • Roadmap
    • What's New

    Resources

    • Help Center
    • Documentation
    • Guides
    • API Reference
    • Community
    • Support

    Company

    • About Us
    • Careers
    • Blog
    • Press
    • Contact

    © 2026 Gray Group International LLC. All rights reserved.·
    Made by gardenpatch 🌱

    Privacy PolicyTerms of ServiceCookie Policy

    Petunia™ is a trademark of Gray Group International LLC. The Petunia name, brand, product design, and content are proprietary. Unauthorized use, imitation, or copying is prohibited.

    Documentation

    KNOWLEDGE_ARCHITECTURE

    docs/KNOWLEDGE_ARCHITECTURE.md
    Docs homeGuidesSupport
    Quick links
    Start here
    How the docs are organized.
    Environment setup
    Configure env + run locally.
    Unified Inbox
    Inbox concepts & behavior.
    Voice AI setup
    Providers, Twilio, testing.
    Pricing model
    Source-of-truth pricing.
    Operations runbook
    How to operate safely.

    Knowledge Management Architecture

    Overview

    Petunia's knowledge management system is a multi-layered architecture that combines vector embeddings, semantic search, and retrieval-augmented generation (RAG) to deliver intelligent, context-aware responses. The system learns from every interaction and continuously improves accuracy through feedback loops.

    Version: 1.0.0 Last Modified: 2025-12-13 Status: Production

    Architecture Layers

    1. Data Storage Layer

    KnowledgeBaseEntry (PostgreSQL)

    Primary relational storage for structured knowledge entries.

    Schema:

    model KnowledgeBaseEntry {
      id           String   @id
      portalId     String
      category     String
      title        String
      question     String
      answer       String
      keywords     String[]
      metadata     Json?
      isPublic     Boolean  @default(false)
      requiresAuth Boolean  @default(false)
      channels     String[]
      embedding    Float[]       // Vector embedding for semantic search
      searchText   String        // Full-text search optimization
      createdAt    DateTime @default(now())
      updatedAt    DateTime
      Portal       Portal   @relation(fields: [portalId], references: [id], onDelete: Cascade)
    
      @@index([category])
      @@index([portalId])
      @@index([searchText])
    }
    

    Key Features:

    • Multi-tenant isolation via portalId
    • Category-based organization
    • Channel-specific knowledge (Yelp, SMS, email, etc.)
    • Access control (public/private, auth requirements)
    • Vector embeddings stored inline for quick access
    • Optimized search text for keyword fallback

    Location: /prisma/schema.prisma (line 3637-3658)

    Vector Storage (Upstash Vector)

    High-performance vector database for semantic similarity search.

    Storage Types:

    enum KnowledgeType {
      BUSINESS_INFO = 'business_info',           // Core business details
      CUSTOMER_PROFILE = 'customer_profile',     // Customer preferences
      CONVERSATION_PATTERN = 'conversation_pattern', // Past interactions
      COMPETITOR_INSIGHT = 'competitor_insight', // Competitive intelligence
      PRODUCT_SERVICE = 'product_service',       // Products/services
      FAQ = 'faq',                              // Common questions
      REVIEW_SENTIMENT = 'review_sentiment',    // Review analysis
      CAMPAIGN_PERFORMANCE = 'campaign_performance', // Marketing data
      YELP_REVIEW = 'yelp_review',             // Yelp reviews
      YELP_MESSAGE = 'yelp_message',           // Yelp messages
      SOCIAL_MENTION = 'social_mention',       // Social media
    }
    

    Metadata Structure:

    interface KnowledgeMetadata {
      portalId: string;          // Multi-tenant isolation
      type: KnowledgeType;       // Knowledge category
      category?: string;         // Subcategory
      source: string;            // Origin (onboarding, conversation, review)
      timestamp: string;         // When stored
      relevanceScore?: number;   // Quality score
      tags?: string[];          // Searchable tags
      content: string;          // Original text
      [key: string]: any;       // Flexible metadata
    }
    

    Location: /lib/services/knowledge/upstash-knowledge.ts

    Search Index (Upstash Redis)

    Fast full-text search for messages, customers, and documents.

    Document Types:

    enum SearchDocumentType {
      MESSAGE = 'message',
      CUSTOMER = 'customer',
      LEAD = 'lead',
      REVIEW = 'review',
      CAMPAIGN = 'campaign',
      FAQ = 'faq',
      KNOWLEDGE = 'knowledge',
    }
    

    Indexing Strategy:

    • Word tokenization with 3-character minimum
    • Timeline-based sorting (zset)
    • Customer-specific indexes
    • Channel-based filtering
    • Real-time indexing on write

    Location: /lib/services/knowledge/upstash-search.ts

    Pinecone Vector Database

    Long-term vector storage for voice knowledge and specialized contexts.

    Features:

    • Namespace isolation
    • Metadata filtering
    • Voice-optimized retrieval
    • Batch operations (100 vectors per batch)
    • Automatic fallback mode for testing

    Location: /lib/services/ai/pinecone.ts


    2. Embedding Layer

    OpenAI Embeddings

    Model: text-embedding-3-large Dimensions: 3072 Use Cases:

    • Knowledge base entries
    • Customer queries
    • Business context
    • Reviews and feedback

    Caching Strategy:

    // 1-hour cache for embeddings
    const cacheKey = `embedding:${text.substring(0, 100)}`;
    const cached = await caches.api.get<number[]>(cacheKey);
    if (cached) return cached;
    
    // Generate and cache
    const embedding = await openai.embeddings.create({
      model: 'text-embedding-3-large',
      input: text,
    });
    await caches.api.set(cacheKey, embedding, 3600);
    

    Deduplication:

    // Prevent duplicate embeddings
    const dedupeKey = `dedupe:${portalId}:${customerId}:${message.substring(0, 50)}`;
    const isDuplicate = await caches.temp.get(dedupeKey);
    if (isDuplicate) return;
    await caches.temp.set(dedupeKey, true, 3600);
    

    Location: /lib/services/knowledge/upstash-knowledge.ts (generateEmbedding)


    3. RAG (Retrieval Augmented Generation)

    Knowledge-Enhanced AI Service

    Purpose: Combines multiple knowledge sources to generate contextually rich responses.

    Context Gathering Pipeline:

    async function gatherContext(query: string, context: AIContext) {
      // 1. Business Information
      const businessContext = await businessKnowledge.getRelevantContext(
        portalId, query,
        { types: [KnowledgeType.BUSINESS_INFO, KnowledgeType.PRODUCT_SERVICE] }
      );
    
      // 2. Customer History
      const customerInsights = await customerKnowledge.getCustomerInsights(
        portalId, customerId
      );
    
      // 3. Similar Conversations
      const similarInteractions = await messageSearch.search(
        portalId, query,
        { types: [SearchDocumentType.MESSAGE], limit: 3 }
      );
    
      // 4. Relevant FAQs
      const relevantFAQs = await customerSearch.search(
        portalId, query,
        { types: [SearchDocumentType.FAQ], limit: 2 }
      );
    
      // 5. Active Campaigns/Offers
      const relevantOffers = await customerSearch.search(
        portalId, query,
        { types: [SearchDocumentType.CAMPAIGN], limit: 1 }
      );
    
      // 6. Competitor Intelligence
      const competitorContext = await businessKnowledge.getRelevantContext(
        portalId, query,
        { types: [KnowledgeType.COMPETITOR_INSIGHT], limit: 2 }
      );
    
      return { businessContext, customerInsights, similarInteractions,
               relevantFAQs, relevantOffers, competitorContext };
    }
    

    Enhanced Prompt Construction:

    const prompt = `
    You are a helpful assistant for ${businessName}.
    
    About the business:
    ${businessContext.content}
    
    Customer history:
    ${customerInsights.map(i => i.content).join('\n')}
    
    Similar past responses:
    ${similarInteractions.map(i => i.content).join('\n')}
    
    Relevant FAQs:
    ${faqs.map(f => `${f.title}: ${f.content}`).join('\n')}
    
    Current offers:
    ${offers.map(o => o.content).join('\n')}
    
    Use a ${tone} tone.
    Customer question: ${query}
    
    Provide a helpful, accurate response based on the context above.
    `;
    

    AI Models:

    • Primary: Claude 3 Opus (Anthropic)
    • Fallback: GPT-4 Turbo (OpenAI)
    • Deterministic Fallback: Context-based template responses

    Confidence Scoring:

    function calculateConfidence(context: KnowledgeContextData): number {
      let score = 0.5; // Base confidence
      if (context.business?.content) score += 0.2;
      if (context.customer?.insights?.length > 0) score += 0.1;
      if (context.similar?.length > 0) score += 0.1;
      if (context.faqs?.length > 0) score += 0.05;
      if (context.offers?.length > 0) score += 0.05;
      return Math.min(1.0, score);
    }
    

    Location: /lib/services/ai/knowledge-ai.ts


    4. Knowledge Indexing & Search

    Semantic Search

    Similarity Algorithm: Cosine similarity

    function cosineSimilarity(a: number[], b: number[]): number {
      if (!a || !b || a.length !== b.length) return 0;
    
      let dotProduct = 0;
      let normA = 0;
      let normB = 0;
    
      for (let i = 0; i < a.length; i++) {
        dotProduct += a[i] * b[i];
        normA += a[i] * a[i];
        normB += b[i] * b[i];
      }
    
      return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
    }
    

    Search Process:

    1. Generate query embedding (3072 dimensions)
    2. Apply metadata filters (portalId, type, categories)
    3. Compute similarity scores
    4. Filter by minimum score (default: 0.7)
    5. Sort by relevance
    6. Return top-K results (default: 10)

    Upstash Vector Query:

    const results = await vectorIndex.query({
      vector: queryEmbedding,
      topK: options?.limit || 10,
      includeMetadata: true,
      filter: { portalId, type: { $in: types } }
    });
    

    Location: /lib/services/knowledge/upstash-knowledge.ts (getRelevantContext)

    Keyword Search Fallback

    Tokenization:

    function tokenize(text: string): string[] {
      return text
        .toLowerCase()
        .replace(/[^\w\s]/g, ' ')
        .split(/\s+/)
        .filter(word => word.length > 2)
        .slice(0, 100); // Limit to prevent excessive indexing
    }
    

    Relevance Scoring:

    function calculateRelevance(query: string, content: string): number {
      const contentLower = content.toLowerCase();
      let score = 0;
    
      // Exact match bonus
      if (contentLower.includes(query.toLowerCase())) {
        score += 10;
      }
    
      // Word match scoring
      const queryWords = tokenize(query);
      for (const word of queryWords) {
        const matches = contentLower.match(new RegExp(`\\b${word}\\b`, 'gi'));
        if (matches) {
          score += matches.length * 2;
        }
      }
    
      return Math.min(100, score);
    }
    

    Location: /lib/services/knowledge/upstash-search.ts


    5. Multi-Tenant Knowledge Isolation

    Portal-Level Isolation

    All knowledge operations require portalId:

    // Storage
    await businessKnowledge.storeBusinessKnowledge(portalId, data);
    
    // Retrieval
    const context = await businessKnowledge.getRelevantContext(portalId, query);
    
    // Search
    const results = await messageSearch.search(portalId, query);
    
    // Vector query
    await vectorIndex.query({
      vector: embedding,
      filter: { portalId }, // REQUIRED
    });
    

    Database Constraints:

    model KnowledgeBaseEntry {
      Portal Portal @relation(fields: [portalId], references: [id], onDelete: Cascade)
      @@index([portalId])
    }
    

    Benefits:

    • Hard isolation at data layer
    • Cascade deletes when portal removed
    • Optimized queries via indexes
    • Prevents cross-tenant data leakage

    Namespace Isolation (Upstash)

    Namespaces:

    • business - Business context and services
    • customer - Customer profiles and history
    • messages - Conversation search
    • customers - Customer search
    • Portal-specific namespaces for specialized data

    Client Override Support:

    // Use client-specific API keys when available
    const service = await UpstashKnowledgeService.getInstance('business', clientId);
    

    Location: /lib/services/knowledge/upstash-knowledge.ts


    6. File Upload & Processing Pipeline

    Upload Service

    Supported File Types:

    • Images: JPEG, PNG, GIF
    • Documents: PDF, TXT
    • Maximum size: 10MB (configurable)

    Processing Pipeline:

    async function upload(file: Buffer, filename: string, userId: string) {
      // 1. Rate limiting (10 uploads/minute)
      await uploadRateLimiter.check(userId);
    
      // 2. File validation
      const fileType = await detectFileType(file);
      validateFileType(fileType, allowedTypes);
      validateFileSize(file.length, maxSize);
    
      // 3. Security scanning
      await scanFile(file);
    
      // 4. Image processing (if applicable)
      if (fileType.startsWith('image/')) {
        file = await compressImage(file);
        thumbnailKey = await createThumbnail(file, key, userId);
      }
    
      // 5. Upload to S3
      await s3Client.send(new PutObjectCommand({
        Bucket: bucket,
        Key: key,
        Body: file,
        ContentType: fileType,
        ServerSideEncryption: 'AES256',
      }));
    
      return { url, key, size, type, thumbnailUrl };
    }
    

    Image Optimization:

    // Compression
    const compressed = await sharp(file)
      .jpeg({ quality: 85, progressive: true })
      .toBuffer();
    
    // Thumbnail generation
    const thumbnail = await sharp(file)
      .resize(200, 200, { fit: 'cover' })
      .jpeg({ quality: 80 })
      .toBuffer();
    

    Location: /lib/services/file-upload.ts

    Document Processing

    OCR Integration:

    • Endpoint: /app/api/uploads/ocr/route.ts
    • Use case: Extract text from images and PDFs
    • Service: AWS Textract or similar

    Knowledge Extraction:

    1. Upload document
    2. Extract text via OCR
    3. Split into chunks (8000 chars max)
    4. Generate embeddings for each chunk
    5. Store in vector database with metadata
    6. Index for search

    7. Knowledge Integration Layer

    Real-Time Learning Hooks

    AutoResponder Integration:

    async function onAutoResponderMessage(data: {
      portalId: string;
      customerId: string;
      incomingMessage: string;
      generatedResponse: string;
      sentiment?: string;
    }) {
      // Queue for batch processing
      queueInteraction(portalId, {
        customerId, message: incomingMessage,
        response: generatedResponse, sentiment
      });
    
      // Index immediately for search
      await messageSearch.indexDocument({
        id: `auto_${portalId}_${Date.now()}`,
        portalId, customerId, type: SearchDocumentType.MESSAGE,
        content: incomingMessage
      });
    }
    

    Yelp Integration:

    // Reviews
    async function onYelpReview(data: {
      portalId: string;
      reviewId: string;
      rating: number;
      text: string;
    }) {
      // Store review with sentiment
      await businessKnowledge.storeYelpReview(portalId, data);
    
      // Extract competitor mentions
      const competitors = await getCompetitorKeywords(portalId);
      for (const competitor of competitors) {
        if (text.toLowerCase().includes(competitor)) {
          await businessKnowledge.storeCompetitorInsight(portalId, {
            competitorName: competitor,
            observation: text
          });
        }
      }
    
      // Analyze negative reviews
      if (rating <= 2) {
        await analyzeNegativeFeedback(portalId, text);
      }
    }
    
    // Messages
    async function onYelpMessage(data: {
      portalId: string;
      conversationId: string;
      message: string;
      isFromBusiness: boolean;
    }) {
      if (isFromBusiness) return; // Only learn from customers
    
      const conversation = await prisma.yelpConversation.findUnique({
        where: { id: conversationId }
      });
    
      queueInteraction(portalId, {
        customerId: conversation.userId,
        message: data.message,
        channel: 'yelp'
      });
    }
    

    Social Media Integration:

    async function onSocialMention(data: {
      portalId: string;
      platform: 'facebook' | 'instagram' | 'twitter';
      content: string;
      authorId: string;
      sentiment?: string;
    }) {
      queueInteraction(portalId, {
        customerId: authorId,
        message: content,
        channel: platform,
        sentiment
      });
    }
    

    Campaign Learning:

    async function onCampaignComplete(data: {
      portalId: string;
      campaignName: string;
      performance: { sent, opened, clicked, converted };
      bestPerformingMessage?: string;
    }) {
      const context = `
        Campaign "${campaignName}" Results:
        Open Rate: ${(opened/sent * 100).toFixed(1)}%
        Click Rate: ${(clicked/opened * 100).toFixed(1)}%
        Conversion: ${(converted/clicked * 100).toFixed(1)}%
        Best Message: ${bestPerformingMessage}
      `;
    
      await businessKnowledge.storeBusinessKnowledge(portalId, {
        description: context,
        source: 'campaign_performance'
      });
    }
    

    Location: /lib/services/knowledge/knowledge-integration.ts

    Batch Processing

    Queue-Based Processing:

    class KnowledgeIntegration {
      private processingQueue: Map<string, Interaction[]> = new Map();
      private batchTimer: NodeJS.Timeout;
    
      startBatchProcessor() {
        // Process every 30 seconds
        this.batchTimer = setInterval(async () => {
          for (const [portalId, interactions] of this.processingQueue) {
            // Filter trivial interactions
            const meaningful = interactions.filter(i =>
              i.message && i.message.length > 10
            );
    
            if (meaningful.length > 0) {
              await businessKnowledge.batchStoreInteractions(
                portalId, meaningful
              );
            }
          }
          this.processingQueue.clear();
        }, 30000);
      }
    }
    

    Benefits:

    • Reduces API calls
    • Prevents rate limiting
    • Improves throughput
    • Batches embeddings

    8. Feedback Learning System

    Response Tracking

    Track Every AI Response:

    async function trackResponse(data: {
      portalId: string;
      customerId: string;
      query: string;
      response: string;
      confidence: number;
    }): Promise<string> {
      const responseId = `resp_${Date.now()}_${random()}`;
    
      await AIResponseTracking.create({
        id: responseId,
        portalId, customerId, query, response,
        confidence, status: 'pending_feedback'
      });
    
      // Check implicit feedback after 5 minutes
      setTimeout(() => {
        checkImplicitFeedback(responseId, portalId, customerId);
      }, 5 * 60 * 1000);
    
      return responseId;
    }
    

    Explicit Feedback

    User Corrections:

    interface ResponseFeedback {
      responseId: string;
      portalId: string;
      originalQuery: string;
      generatedResponse: string;
      wasHelpful: boolean;
      humanCorrection?: string;
      customerSatisfaction?: 1 | 2 | 3 | 4 | 5;
      resolutionTime?: number;
      requiredHumanIntervention: boolean;
    }
    
    async function recordFeedback(feedback: ResponseFeedback) {
      // Queue for batch processing
      learningQueue.get(portalId).push(feedback);
    
      // Update tracking record
      await AIResponseTracking.update({
        where: { id: feedback.responseId },
        data: { wasHelpful, humanCorrection, satisfaction }
      });
    
      // Learn from correction immediately
      if (humanCorrection && !wasHelpful) {
        await learnFromCorrection(feedback);
      }
    }
    

    Implicit Feedback

    Detect Satisfaction from Behavior:

    async function checkImplicitFeedback(
      responseId: string,
      portalId: string,
      customerId: string
    ) {
      // Check if customer sent more messages
      const followUpMessages = await prisma.message.findMany({
        where: { portalId, contactId: customerId,
          createdAt: { gte: new Date(Date.now() - 5 * 60 * 1000) }
        }
      });
    
      // No follow-up = satisfactory
      const wasHelpful = followUpMessages.length === 0;
    
      // Check for clarification requests
      const needsClarification = detectClarificationPhrases(
        followUpMessages.map(m => m.content).join(' ')
      );
    
      await recordFeedback({
        responseId, portalId,
        wasHelpful: wasHelpful && !needsClarification,
        requiredHumanIntervention: needsClarification
      });
    }
    
    const clarificationPhrases = [
      'what do you mean', 'i don\'t understand',
      'can you explain', 'not what i asked',
      'that doesn\'t help', 'confused'
    ];
    

    Learning from Mistakes

    Pattern Identification:

    function identifyMistakePattern(
      query: string,
      wrongResponse: string,
      correctResponse: string
    ): { type: string; severity: 'low' | 'medium' | 'high' } {
      // Factual errors
      if (wrongResponse.includes('open') && correctResponse.includes('closed')) {
        return { type: 'factual_error', severity: 'high' };
      }
    
      // Missing information
      if (correctResponse.length > wrongResponse.length * 1.5) {
        return { type: 'incomplete_response', severity: 'medium' };
      }
    
      // Missed intent
      if (query.includes('price') && !wrongResponse.includes('price')) {
        return { type: 'missed_intent', severity: 'high' };
      }
    
      // Tone issues
      if (wrongResponse.includes('sorry') && !correctResponse.includes('sorry')) {
        return { type: 'over_apologetic', severity: 'low' };
      }
    
      return { type: 'general_mistake', severity: 'medium' };
    }
    

    Store Corrections:

    async function learnFromCorrection(feedback: ResponseFeedback) {
      // Store as high-priority learning example
      await businessKnowledge.storeBusinessKnowledge(portalId, {
        businessName: 'Learning Example',
        industry: 'correction',
        description: `
          When asked: "${feedback.originalQuery}",
          DON'T say: "${feedback.generatedResponse}",
          INSTEAD say: "${feedback.humanCorrection}"
        `
      });
    
      // Track mistake patterns
      const pattern = identifyMistakePattern(
        feedback.originalQuery,
        feedback.generatedResponse,
        feedback.humanCorrection
      );
    
      const patterns = await caches.config.get<MistakePattern[]>(
        `mistakes:${portalId}`
      ) || [];
    
      const existing = patterns.find(p => p.pattern === pattern.type);
      if (existing) {
        existing.frequency++;
        existing.examples.push({
          query: feedback.originalQuery,
          wrong: feedback.generatedResponse,
          correct: feedback.humanCorrection
        });
      } else {
        patterns.push({ pattern: pattern.type, frequency: 1, examples: [...] });
      }
    
      await caches.config.set(`mistakes:${portalId}`, patterns, 86400 * 7);
    }
    

    Automatic Retraining

    Trigger When Accuracy Drops:

    async function updateMetrics(portalId: string) {
      const feedback = await AIResponseTracking.findMany({
        where: { portalId, status: 'feedback_received',
          createdAt: { gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) }
        }
      });
    
      const accuracy = feedback.filter(f => f.wasHelpful).length / feedback.length;
    
      // Alert if accuracy < 70% with sufficient data
      if (accuracy < 0.7 && feedback.length > 20) {
        logger.warn('AI accuracy below threshold', { portalId, accuracy });
        await triggerRetraining(portalId);
      }
    }
    
    async function triggerRetraining(portalId: string) {
      // Gather all corrections
      const corrections = await AIResponseTracking.findMany({
        where: { portalId, humanCorrection: { not: null }, wasHelpful: false },
        take: 50,
        orderBy: { createdAt: 'desc' }
      });
    
      // Create training examples
      for (const correction of corrections) {
        await businessKnowledge.storeBusinessKnowledge(portalId, {
          businessName: 'Training Data',
          industry: 'retraining',
          description: `Q: ${correction.query}\nA: ${correction.humanCorrection}`
        });
      }
    
      // Clear caches to use new knowledge
      await caches.api.invalidatePattern(`context:${portalId}:*`);
    }
    

    Location: /lib/services/knowledge/feedback-learning.ts


    API Routes

    Knowledge Base Management

    GET /api/autoresponder/knowledge

    • Get all knowledge entries or specific entry by ID
    • Query params: businessId, id (optional)
    • Returns: { data: { knowledge | knowledgeBase }, success: true }

    POST /api/autoresponder/knowledge

    • Create or update knowledge entry
    • Body: { businessId, entry: { title, content, category, tags } }
    • Tier limits enforced (prevents bypass via fake IDs)
    • Returns: { data: { entry }, success: true }

    DELETE /api/autoresponder/knowledge

    • Delete knowledge entry
    • Query params: businessId, id
    • Returns: { data: { success: true }, success: true }

    Location: /app/api/autoresponder/knowledge/route.ts


    Services & Components

    Core Services

    KnowledgeBaseService

    • /lib/services/knowledgeBaseService.ts
    • PostgreSQL-backed knowledge management
    • Semantic search with OpenAI embeddings
    • Import/export (CSV, JSON)
    • Category statistics

    UpstashKnowledgeService

    • /lib/services/knowledge/upstash-knowledge.ts
    • Vector-based knowledge storage
    • Real-time embedding generation
    • Business/customer namespace isolation
    • Sentiment analysis

    UpstashSearchService

    • /lib/services/knowledge/upstash-search.ts
    • Full-text search with Redis
    • Real-time indexing
    • Multi-field queries
    • Autocomplete suggestions

    KnowledgeEnhancedAI

    • /lib/services/ai/knowledge-ai.ts
    • RAG implementation
    • Multi-source context aggregation
    • Claude/GPT-4 integration
    • Confidence scoring

    KnowledgeIntegration

    • /lib/services/knowledge/knowledge-integration.ts
    • Real-time learning hooks
    • Batch processing queue
    • Cross-platform integration (Yelp, social, campaigns)

    FeedbackLearningSystem

    • /lib/services/knowledge/feedback-learning.ts
    • Response tracking
    • Explicit/implicit feedback
    • Mistake pattern detection
    • Automatic retraining

    UI Components

    KnowledgeBaseManager

    • /components/knowledge-base/KnowledgeBaseManager.tsx
    • CRUD interface for knowledge entries
    • Category management
    • Bulk import/export

    Performance Optimizations

    Caching Strategy

    Embedding Cache:

    // 1-hour cache for embeddings
    caches.api.set(`embedding:${text.substring(0, 100)}`, embedding, 3600);
    

    Context Cache:

    // 5-minute cache for context queries
    caches.api.set(`context:${portalId}:${query}`, results, 300);
    

    Search Results Cache:

    // 2-minute cache for search results
    caches.api.set(`search:${portalId}:${query}`, results, 120);
    

    Category Stats Cache:

    // 5-minute in-memory cache
    this.categoryCache.set(organizationId, stats);
    setTimeout(() => this.categoryCache.delete(organizationId), 5 * 60 * 1000);
    

    Batch Operations

    Embedding Generation:

    • Process 10 embeddings in parallel
    • Prevents API rate limiting
    • Reduces latency

    Vector Storage:

    • Batch upsert: 100 vectors per request
    • Reduces network overhead
    • Improves throughput

    Search Indexing:

    • Queue interactions (30-second batches)
    • Filters trivial messages
    • Reduces write load

    Security & Access Control

    Multi-Tenant Isolation

    Database Level:

    • All queries filtered by portalId
    • Cascade deletes on portal removal
    • Indexed for performance

    Vector Storage:

    • Metadata-based filtering
    • Namespace isolation
    • Client override support

    Access Control

    Knowledge Entry Permissions:

    isPublic: boolean;      // Public vs private entries
    requiresAuth: boolean;  // Authentication required
    channels: string[];     // Channel-specific access
    

    API Authentication:

    export const GET = withAuth(async (request, session) => {
      // session.user.id validated
      // portalId ownership verified
    });
    

    Rate Limiting

    File Uploads:

    • 10 uploads per minute per user
    • Configurable limits

    API Requests:

    • Standard rate limiting via middleware
    • Tier-based quotas

    Monitoring & Metrics

    Learning Metrics

    interface LearningMetrics {
      portalId: string;
      totalResponses: number;
      helpfulResponses: number;
      accuracy: number;                    // % helpful responses
      averageSatisfaction: number;        // 1-5 scale
      averageResolutionTime: number;      // seconds
      commonMistakes: Array<{
        pattern: string;
        frequency: number;
        correction: string;
      }>;
    }
    

    Alerts:

    • Accuracy < 70% with 20+ responses
    • Triggers automatic retraining
    • Logs to analytics dashboard

    Performance Metrics

    • Embedding generation time
    • Vector search latency
    • Cache hit rates
    • Queue processing time
    • Batch operation throughput

    Future Enhancements

    Planned Features

    1. Advanced Document Processing:

      • PDF parsing with layout preservation
      • Table extraction
      • Multi-language OCR
    2. Knowledge Graph:

      • Entity relationships
      • Automatic link discovery
      • Visual knowledge explorer
    3. Active Learning:

      • Uncertainty sampling
      • Human-in-the-loop labeling
      • Semi-supervised learning
    4. Fine-Tuned Models:

      • Domain-specific embeddings
      • Portal-specific language models
      • Custom entity recognition
    5. Knowledge Versioning:

      • Track knowledge evolution
      • Rollback capabilities
      • A/B testing for responses

    Technical Debt

    1. Vector Storage Optimization:

      • Migrate to dedicated vector DB (Pinecone/Weaviate)
      • Implement approximate nearest neighbor
      • Add vector compression
    2. Search Improvements:

      • Implement BM25 ranking
      • Add faceted search
      • Support fuzzy matching
    3. Scalability:

      • Partition large knowledge bases
      • Implement sharding strategy
      • Add read replicas

    References

    Related Documentation

    • /docs/ARCHITECTURE.md - Overall system architecture
    • /TESTING.md - Testing knowledge services
    • /docs/CLIENT_INTEGRATIONS.md - Third-party integrations

    Key Files

    Schema:

    • /prisma/schema.prisma - KnowledgeBaseEntry model

    Services:

    • /lib/services/knowledgeBaseService.ts
    • /lib/services/knowledge/upstash-knowledge.ts
    • /lib/services/knowledge/upstash-search.ts
    • /lib/services/knowledge/knowledge-integration.ts
    • /lib/services/knowledge/feedback-learning.ts
    • /lib/services/ai/knowledge-ai.ts
    • /lib/services/ai/pinecone.ts
    • /lib/services/file-upload.ts

    API Routes:

    • /app/api/autoresponder/knowledge/route.ts
    • /app/api/uploads/ocr/route.ts

    Components:

    • /components/knowledge-base/KnowledgeBaseManager.tsx

    External Services

    • Upstash Vector: Vector similarity search
    • Upstash Redis: Full-text search and caching
    • Pinecone: Long-term vector storage
    • OpenAI: Embedding generation (text-embedding-3-large)
    • Anthropic: Claude Opus for RAG responses
    • AWS S3: File storage and serving

    Changelog

    1.0.0 (2025-12-13):

    • Initial comprehensive documentation
    • Documented all architecture layers
    • Added code examples and diagrams
    • Included performance optimizations
    • Security and access control details
    On this page
    OverviewArchitecture Layers1. Data Storage Layer2. Embedding Layer3. RAG (Retrieval Augmented Generation)4. Knowledge Indexing & Search5. Multi-Tenant Knowledge Isolation6. File Upload & Processing Pipeline7. Knowledge Integration Layer8. Feedback Learning SystemAPI RoutesKnowledge Base ManagementServices & ComponentsCore ServicesUI ComponentsPerformance OptimizationsCaching StrategyBatch OperationsSecurity & Access ControlMulti-Tenant IsolationAccess ControlRate LimitingMonitoring & MetricsLearning MetricsPerformance MetricsFuture EnhancementsPlanned FeaturesTechnical DebtReferencesRelated DocumentationKey FilesExternal Services