Skip to content

Assistant

Overview

The Assistant service powers the intelligent chatbot that handles visitor inquiries across the Sutomo platform. It processes natural language questions in multiple languages, matches them against known intents, and returns relevant answers.

The system uses a hybrid matching pipeline: ML classification (Rubix ML) → exact database match → fuzzy similarity scoring. This ensures both speed and accuracy while continuously learning from real user interactions.

Why It Exists

  • Provide instant self-service answers to common visitor questions (school hours, registration, fees, etc.)
  • Support multi-language inquiries (Indonesian, English, and others via Google Translate)
  • Continuously improve through automated training data collection
  • Reduce admin workload by handling repetitive questions

Architecture


Methods

Chatbot (Orchestrator)

MethodDescription
handleUserMessage(dialog, message)Main entry point — translates, normalizes, matches intent, returns reply
getOrCreateDialog(fingerprint, user)Get or create a dialog session
ensureGreeting(dialog, greeting)Insert greeting message if dialog is empty
resetDialog(dialog)Clear all messages in a dialog
getDefaultLanguage()Read default assistant language from settings
isLlmEnabled()Check if LLM feature is enabled

DialogManager

MethodDescription
getOrCreateFingerprint()Get or generate a persistent visitor fingerprint (session + cookie)
getOrCreateDialog(fingerprint, user)Find existing dialog or create new one
getMessages(dialog)Get all messages in chat format
ensureGreeting(dialog, greeting)Add greeting if first interaction
resetDialog(dialog)Delete all messages and reset state

Normalizer

MethodDescription
normalizeQuestion(question)Static — lowercases, removes punctuation, collapses whitespace
tokenize(text, language)Split, stem, remove stopwords, normalize synonyms
stemToken(token, language)Indonesian affix removal (prefixes & suffixes)
isStopword(token, language)Check against ID/EN stopword lists
normalizeSynonym(token, language)Map synonyms to canonical form (e.g., "mulai" → "jam")

MlClassifier (Rubix ML)

MethodDescription
train()Train Softmax Classifier on questions + confirmed training logs
predict(normalizedQuestion)Predict intent with confidence score
isTrained()Check if model file exists
getTrainingSampleCount()Count available training samples

SimilarityScorer

MethodDescription
scoreSimilarity(a, b, language)Composite score combining Jaccard, Overlap, Levenshtein, and Trigram Cosine
jaccardSimilarity(a, b)Token set intersection over union
overlapSimilarity(a, b)Twice intersection over total set size
levenshteinSimilarity(a, b)Character-level edit distance
trigramCosineSimilarity(a, b)N-gram (3-char) cosine similarity

Translator

MethodDescription
translateIntoDefaultLanguage(message, defaultLang)Detect source language and translate to default
translateFromDefaultLanguage(text, defaultLang, targetLang)Translate reply back to user's language
looksEnglish(text)Heuristic check if text is English
guessSourceLanguageFromScript(text)Detect CJK, Arabic, Cyrillic scripts
simpleDictionaryTranslateEnToId(text)Fallback dictionary-based EN→ID translation

Matching Pipeline

The following flowchart shows how a user message is processed through the hybrid matching pipeline:


How to Use

The Assistant service is used through the Chatbot class, which acts as the main orchestrator:

php
use App\Services\Assistant\Chatbot;

class YourController
{
    public function __construct(
        private readonly Chatbot $chatbot,
    ) {}

    public function handle(Request $request): array
    {
        $fingerprint = $this->chatbot->getOrCreateFingerprint();
        $dialog = $this->chatbot->getOrCreateDialog($fingerprint, $request->user());
        
        $this->chatbot->ensureGreeting($dialog, 'Hello! How can I help you?');
        
        [$reply, $intentCode, $matchInfo] = $this->chatbot->handleUserMessage(
            $dialog,
            $request->input('message'),
        );
        
        return [
            'reply' => $reply,
            'intent' => $intentCode,
            'match' => $matchInfo,
        ];
    }
}

Training the ML Model

php
use App\Services\Assistant\MlClassifier;

$classifier = app(MlClassifier::class);
$classifier->train(); // trains on all active questions + confirmed logs

Key Files

app/Services/Assistant/
├── Chatbot.php              # Main orchestrator — handles messages, coordinates pipeline
├── DialogManager.php         # Session management, fingerprinting, dialog CRUD
├── Normalizer.php            # Text preprocessing: stemming, stopwords, synonyms
├── MlClassifier.php          # Rubix ML Softmax Classifier — train & predict
├── SimilarityScorer.php      # Composite text similarity (Jaccard, Overlap, Levenshtein, Trigram)
└── Translator.php            # Multi-language translation via Google Translate

app/Models/Assistant/
├── BotDialog/BotDialog.php                    # Dialog session model
├── BotDialogMessage/BotDialogMessage.php       # Individual messages in a dialog
├── ChatbotIntent/ChatbotIntent.php             # Intent definitions
├── ChatbotQuestion/ChatbotQuestion.php         # Question variations per intent
├── ChatbotAnswer/ChatbotAnswer.php             # Answer content per intent
└── TrainingLog/TrainingLog.php                 # Training data from interactions

database/migrations/v1_4_0/
├── 005_create_otps_table.php                  # Assistant-related tables
└── ...