Skip to main content

AI Message Intent Classification

Overview

Beyond literal keyword replies, the platform also supports a chatbot that understands the user's intent. This job takes an incoming user message, classifies it into an intent through three processing phases (keyword → vector → LLM), then hands it to the response sender to reply according to the intent that was matched.

It runs as part of a workflow: trigger rules with source_type = 'message_received' are grouped by workflow_id, and classification runs once per workflow.

Business Flow

  1. Receive a payload containing lineUserId, lineOaId, organizationId, messageText, messageType, replyToken, and timestamp.
  2. Load the trigger_rule rows for that (lineOaId, organizationId) pair where source_type='message_received', enabled=true, and the rule is not deleted. If none exist, the job ends.
  3. Group the rules by workflow_id, preserving insertion order to match the behaviour of a JS Map, then iterate through classifyAndRoute.
  4. Read the classifier configuration from the first rule's sourceConfig: intents, fallbackIntentId, confidenceThreshold (default 0.3), and conversationMemory.
  5. Split intents into two kinds: keyword and ai.
  6. Load conversation history — when memory is enabled, read from the Redis list keyed by the line OA and line user pair, up to maxMessages entries (default 10), then reverse the order so it runs oldest to newest.
  7. Three-phase classification
    • Phase 1 — Keyword: matchKeyword compares the message against each intent's keywords. A hit yields a confidence of 1.0 and ends classification immediately — the cheapest and fastest path.
    • Phase 2 — Vector: compare the message embedding against the intentVectors stored in the configuration using cosine similarity. If it clears the threshold, that result is used.
    • Phase 3 — AI: call the LLM through aix, which reads ai_config per OA and provider, asking it to classify the intent along with a confidence score. Anything below the threshold falls back to fallbackIntentId.
  8. Once an intent is determined, route to ResponseSenderService.SendResponse, which supports several modes:
    • reply with a plain text message, a rich message, or a configured flex message
    • ai_knowledge mode searches the knowledge base using Meilisearch combined with vectors — governed by topK, semanticRatio, directAnswer, and directAnswerThreshold — then has the LLM compose the answer
    • attach quick reply chips, resolve custom merge tags, and wrap URLs in the answer with a tracking redirect when tracking.enabled is on
    • store the assistant's reply back into memory via storeAssistantMemory
  9. Record the outcome to trigger_log through TriggerLogger.
note

message_received_trigger is the only queue in the system that is not DLQ-monitored — its topology is asserted, but nothing subscribes to its .dlq queue.

Key Files & Functions

  • internal/messagetrigger/service.go
    • MessageTriggerService.ProcessMessage(ctx, payload) — the entry point
    • fetchMessageReceivedRules(), classifyAndRoute(), matchKeyword(), parseClassificationResult(), cosineSimilarity(), decodeIntents()
    • the MessageReceivedPayload, classificationResult, IntentDefinition, and ConversationEntry structs
  • internal/messagetrigger/response_sender.go
    • ResponseSenderService.SendResponse(ctx, params)
    • applyTracking(), loadMergeUser(), storeAssistantMemory(), appendQuickReply()
    • IntentResponseConfig, whose Extra field carries knowledgeBaseId, retrievalConfig, and tracking
  • internal/messagetrigger/prompts.go — prompt templates for the classifier and the answer
  • internal/messagetrigger/transform.go — message object transformation
  • internal/messagetrigger/consumer.goConsumer.HandleMessageReceived
  • internal/aix/aix.go — the LLM client, with ConfigResolver reading from the ai_config table
  • internal/embedx/embedx.go — the embedding API client, and internal/meilix/meilix.go — the Meilisearch client
  • Queue: message_received_trigger (profile main)

Connections to Other Services

  • Receives jobs from: the line_webhook handler when priority is ai_classifier_only, or from Keyword Auto-response when fallbackToAi = true
  • Tables: trigger_rule (classifier configuration), ai_config (provider, model, and API key per OA), knowledge_chunk and knowledge_document (for knowledge-base answers), trigger_log, and line_user
  • Redis: a list holding conversation memory, keyed by the line OA and line user pair
  • External services: the LLM provider configured in ai_config, the embedding API (EMBEDDING_API_URL), and Meilisearch (MEILISEARCH_HOST)
  • LINE API: reply and push for delivering the answer
  • Connects to: Knowledge Base Indexing as the source of answers, and the action executor, which reuses the same response sender for the send_message action