Skip to main content

Followup Router Prompt

The Followup Router uses an LLM with a specialized prompt to classify how user followups should be handled. This document explains the prompt architecture, semantic reasoning approach, and deterministic fallback system.

Purpose

The followup router answers: "How should we handle this followup message?"

Followup KindExampleBehavior
pure_show_more"Näita veel"Paginate same results
soft_refinement"Odavamaid"Apply mild constraint
context_addition"See on emale"Add recipient/occasion context
new_constraint"Mitte raamatuid"Add exclusion constraint
hard_pivot"Do you have books"Switch product type
question_about_shown"Mis on see toode?"Ask about shown product

Architecture

Location: app/api/chat/orchestrators/context-orchestrator/followup-router.ts


The Core Problem: Semantic Similarity

Why This Matters

The system initially failed to understand that these queries are semantically equivalent:

QueryExpectedWas Misclassified As
"Do you have books"hard_pivotquestion_about_shown
"Do you have book recommendations"hard_pivothard_pivot
"Kas teil on raamatuid?"hard_pivotquestion_about_shown

Root Cause: The LLM saw "Do you have" as a question pattern and associated it with asking about shown products, rather than requesting a product type change.


Semantic Reasoning Guide

The prompt now includes a reasoning guide that teaches the LLM how to think about classification:

SEMANTIC REASONING GUIDE (apply BEFORE classifying):

1. Does the message reference a SPECIFIC SHOWN product?
- Look for: ordinals (first, second, esimene, teine),
pronouns (this, that, see, selle, seda)
- If YES → likely question_about_shown
- If NO → NOT question_about_shown

2. Does the message ask about a PRODUCT TYPE or CATEGORY?
- Product types: books/raamat, games/mäng, toys, electronics/tehnika
- Patterns: "do you have X", "show me X", "what about X"
- If asking about product TYPE without referencing shown product → hard_pivot

3. Key insight: "Do you have books" and "Do you have book recommendations"
are SEMANTICALLY EQUIVALENT.
Both mean: "Switch to showing book products" → hard_pivot

Why Semantic Reasoning Works


Prompt Structure

Followup Kinds with Examples

Followup kinds:
- pure_show_more: user just wants more of same results.
- soft_refinement: mild tweaks (cheaper, more expensive) without big shift.
- context_addition: user adds context info WITHOUT a new searchable product query.
* "See on emale" (This is for mother) → context_addition
* "Tema on 55-aastane" (They are 55) → context_addition

- new_constraint: adds a constraint but keeps main intent.
- hard_pivot: switches product type/category/intent. Examples:
* "Do you have books" → hard_pivot
* "Kas teil on raamatuid?" → hard_pivot (Estonian)
* "Show me games instead" → hard_pivot
* "What about toys?" → hard_pivot

- question_about_shown: asks about SPECIFIC already shown product(s).
REQUIRES ordinal or pronoun reference to a SHOWN product.

Critical Clarifications

The prompt includes critical rules to prevent common misclassifications:

CRITICAL: "Do you have X" patterns are NOT question_about_shown!
- "Do you have books" → hard_pivot (asks for a PRODUCT TYPE)
- "Kas teil on raamatuid?" → hard_pivot (same in Estonian)
- question_about_shown REQUIRES: ordinal (first/second) OR pronoun (this/that/see)
- If NO ordinal/pronoun and asking about PRODUCT TYPE → hard_pivot

Output Structure

The LLM outputs a JSON response with reasoning:

{
"reasoning": "User asks 'do you have books' - no pronouns or ordinals referencing shown products. 'Books' is a product type request, not asking about a specific shown product.",
"followup_kind": "hard_pivot",
"preserve": {
"context": true,
"excludes": true,
"categoryHints": false,
"productType": false
},
"needs_reextract": true,
"context_switch": true,
"context_switch_reason": "Product type change from games to books",
"confidence": 0.92,
"productInquiryMatch": { "productId": null, "productName": null }
}

Preservation Flags

FlagWhen trueWhen false
preserve.contextKeep recipient/occasionReset all context
preserve.excludesKeep exclude listClear excludes
preserve.categoryHintsKeep category hintsClear hints
preserve.productTypeKeep product typeAllow type change

Deterministic Corrections

Even with improved prompts, LLMs can misclassify. The system includes deterministic pattern matching as a safety net:

All 12 Product Types Covered

const PRODUCT_TYPE_KEYWORDS = [
// Raamat (Books)
'(?:book|books|raamat|raamatuid|novel)',
// Mängud (Games)
'(?:game|games|mäng|mängud|toy|puzzle)',
// Kinkekaart (Gift Cards)
'(?:gift\\s*card|kinkekaart|voucher)',
// Kingitused (Gifts)
'(?:gift|gifts|kingitus|present)',
// Kodu ja aed (Home & Garden)
'(?:home|garden|kodu|aed|candle|küünal)',
// Kontorikaup (Office Supplies)
'(?:office|stationery|kontori|notebook|märkmik)',
// Ilu ja stiil (Beauty & Style)
'(?:beauty|cosmetic|kosmeetika|parfüüm|jewelry)',
// Tehnika (Electronics)
'(?:electronic|electronics|tehnika|gadget|e-reader)',
// Film (Movies)
'(?:movie|movies|film|dvd)',
// Muusika (Music)
'(?:music|muusika|album|vinyl)',
// Joodav ja söödav (Food & Drink)
'(?:food|drink|toit|chocolate|tea|coffee)',
// Sport ja harrastused (Sports & Hobbies)
'(?:sport|sports|fitness|hobby)',
];

Request Patterns

const PRODUCT_TYPE_PIVOT_PATTERNS = [
// "Do you have X" patterns (English)
/\b(?:do\s+you\s+have|have\s+you\s+got|got\s+any)\s+PRODUCT_TYPE/i,

// "Show me X" / "I want X" patterns
/\b(?:show\s+me|i\s+want|looking\s+for)\s+PRODUCT_TYPE/i,

// "What about X" patterns
/\b(?:what\s+about|how\s+about|instead)\s+PRODUCT_TYPE/i,

// Estonian: "Kas teil on X"
/\bkas\s+teil\s+on\s+PRODUCT_TYPE/i,

// Estonian: "Näita mulle X" / "Otsin X"
/\b(?:näita\s+mulle|otsin|tahan)\s+PRODUCT_TYPE/i,

// Direct requests: "Books?" or "Games please"
/^PRODUCT_TYPE(?:\s+please)?[?!.]?$/i,
];

Correction Logic


Complete Example Flow

Scenario: Gift for 10-year-old → Books

Previously Broken (Now Fixed)


Configuration

Model Selection

const ROUTER_MODEL = 'meta-llama/llama-4-scout-17b-16e-instruct';

Why Llama 4 Scout 17B?

  • Fast inference (~100-200ms)
  • Good classification accuracy
  • Low cost via Groq

Confidence Threshold

export const FOLLOWUP_ACTIONABLE_THRESHOLD = 0.35;

Classifications below this threshold are treated as uncertain and may trigger fallback behavior.

Environment Variables

# Disable followup router entirely
ENABLE_FOLLOWUP_ROUTER=false

# Enable debug logging
CHAT_DEBUG_LOGS=true

Debugging

Log Output

When CHAT_DEBUG_LOGS=true:

🔬 FOLLOWUP_ROUTER: Entry point {
userMessage: 'Do you have books',
hasContextToRestore: true,
shouldRunRouter: true
}

🔬 FOLLOWUP_ROUTER: LLM result {
status: 'applied',
followupKind: 'hard_pivot'
}

⚠️ FOLLOWUP ROUTER CORRECTION: Overriding question_about_shown → hard_pivot {
userMessage: 'Do you have books',
pattern: 'Do you have [product type] detected'
}

Common Issues

SymptomCauseSolution
"I don't have info about this product"Misclassified as question_about_shownDeterministic patterns correct this
Context lost on followuppreserve.context: falseCheck LLM reasoning for why
Product type not switchingpreserve.productType: trueEnsure hard_pivot classification

Performance

Latency Breakdown

ComponentTime
Prompt building~2ms
LLM inference100-200ms
Response parsing~3ms
Pattern correction<1ms
Total~150-200ms

Prompt Architecture

Conversational Systems

Context Systems


Key Takeaways

  1. Semantic reasoning in the prompt teaches the LLM how to think, not just what to classify
  2. Explicit examples for each classification type prevent bias toward over-documented categories
  3. Critical clarifications address common misclassification patterns directly
  4. Deterministic corrections catch any remaining LLM errors for known patterns
  5. Defense in depth ensures robust handling even when LLM makes mistakes

Last Updated: November 2025
Version: 2.0 - Semantic Reasoning Enhancement
Status: ✅ Production Ready