← Files TeleCallARCHIVED FILE

src/agent/intent.ts

7.85 KB · Sep 30, 2026 · 23:16 UTC

↓ Download file

import { UserIntent, UserNeeds, IntentDetectionResult } from '../types/userNeeds';

export class IntentClassifier {
  /**
   * Analiza una consulta en lenguaje natural y extrae las intenciones y necesidades estructuradas
   */
  public parseQuery(query: string): IntentDetectionResult {
    const text = query.toLowerCase();
    const intents: Set<UserIntent> = new Set();
    const needs: UserNeeds = {};

    // 1. Detección de Fibra
    if (
      text.includes('fibra') ||
      text.includes('internet') ||
      text.includes('wifi') ||
      text.includes('adsl') ||
      text.includes('router') ||
      text.includes('en casa') ||
      text.includes('para casa')
    ) {
      needs.fiberRequired = true;
      intents.add('fiber');
    }

    // Velocidades de fibra
    if (text.includes('1 gb') || text.includes('1gb') || text.includes('1000 mb') || text.includes('1000mb') || text.includes('1 giga')) {
      needs.fiberRequired = true;
      needs.fiberSpeedRequired = 1000;
    } else if (text.includes('600 mb') || text.includes('600mb') || text.includes('600 megas')) {
      needs.fiberRequired = true;
      needs.fiberSpeedRequired = 600;
    } else if (text.includes('300 mb') || text.includes('300mb') || text.includes('300 megas')) {
      needs.fiberRequired = true;
      needs.fiberSpeedRequired = 300;
    }

    // 2. Detección de Móvil
    if (
      text.includes('móvil') ||
      text.includes('movil') ||
      text.includes('linea') ||
      text.includes('línea') ||
      text.includes('gigas') ||
      text.includes('gb') ||
      text.includes('llamadas')
    ) {
      intents.add('mobile');
      if (needs.mobileLines === undefined) {
        needs.mobileLines = 1;
      }
    }

    // Conteo de líneas y personas
    const familyMatch = text.match(/(somos|familia de)\s+(\w+|\d+)/);
    if (familyMatch) {
      const numWord = familyMatch[2];
      const parsedNum = this.parseNumberWord(numWord);
      if (parsedNum) {
        needs.people = parsedNum;
      }
    }

    const linesMatch = text.match(/(\w+|\d+)\s+(líneas|lineas|móviles|moviles)/);
    if (linesMatch) {
      const numWord = linesMatch[1];
      const parsedNum = this.parseNumberWord(numWord);
      if (parsedNum) {
        needs.mobileLines = parsedNum;
        if (parsedNum > 1) {
          intents.add('additional_line');
        }
      }
    }

    if (text.includes('tres líneas') || text.includes('3 líneas') || text.includes('tres moviles') || text.includes('3 moviles')) {
      needs.mobileLines = 3;
      intents.add('additional_line');
    } else if (text.includes('dos líneas') || text.includes('2 líneas') || text.includes('dos moviles') || text.includes('2 moviles')) {
      needs.mobileLines = 2;
      intents.add('additional_line');
    } else if (text.includes('cuatro líneas') || text.includes('4 líneas')) {
      needs.mobileLines = 4;
      intents.add('additional_line');
    }

    // Gigas requeridos
    if (text.includes('muchos gigas') || text.includes('más de 100 gb') || text.includes('100 gb') || text.includes('150 gb') || text.includes('300 gb')) {
      needs.mobileDataRequired = 100;
    } else if (text.includes('apenas uso datos') || text.includes('pocos gigas') || text.includes('barato')) {
      needs.mobileDataRequired = 50;
    }

    // 3. Convergencia Fibra + Móvil
    if (needs.fiberRequired && (needs.mobileLines || 0) > 0) {
      intents.add('fiber_mobile');
    }

    // 4. Streaming y Televisión
    if (text.includes('netflix')) {
      needs.netflix = true;
      needs.streaming = true;
      intents.add('tv');
    }

    if (text.includes('disney')) {
      needs.disneyPlus = true;
      needs.streaming = true;
      intents.add('tv');
    }

    if (text.includes('movistar plus') || text.includes('movistar+') || text.includes('televisión') || text.includes('television') || text.includes('tv')) {
      needs.movistarPlus = true;
      intents.add('tv');
    }

    if (text.includes('futbol') || text.includes('fútbol') || text.includes('liga') || text.includes('champions')) {
      needs.football = true;
      needs.sports = true;
      needs.movistarPlus = true;
      intents.add('tv');
    }

    // 5. Segunda Residencia
    if (
      text.includes('segunda residencia') ||
      text.includes('casa del pueblo') ||
      text.includes('casa en el pueblo') ||
      text.includes('casa de la playa') ||
      text.includes('casa de vacaciones') ||
      text.includes('casa de campo')
    ) {
      needs.secondResidence = true;
      intents.add('second_residence');
    }

    // 6. Roaming
    const roamingKeywords = ['roaming', 'viaje', 'viajo', 'viajar', 'fuera de españa', 'extranjero', 'tailandia', 'thailand', 'italia', 'francia', 'eeuu', 'estados unidos', 'uk', 'londres', 'alemania', 'japon', 'suiza', 'marruecos', 'andorra'];
    for (const kw of roamingKeywords) {
      if (text.includes(kw)) {
        intents.add('roaming');
        if (kw === 'tailandia' || kw === 'thailand') needs.country = 'Tailandia';
        else if (kw === 'italia') needs.country = 'Italia';
        else if (kw === 'francia') needs.country = 'Francia';
        else if (kw === 'eeuu' || kw === 'estados unidos') needs.country = 'Estados Unidos';
        else if (kw === 'uk' || kw === 'londres') needs.country = 'Reino Unido';
        else if (kw === 'suiza') needs.country = 'Suiza';
        else if (kw === 'japon') needs.country = 'Japón';
        else if (kw === 'marruecos') needs.country = 'Marruecos';
        else if (kw === 'andorra') needs.country = 'Andorra';
        break;
      }
    }

    // 7. Presupuesto
    const budgetMatch = text.match(/(no quiero pagar más de|máximo|presupuesto de|hasta|menos de|no más de)\s+(\d+)\s*(€|euros)?/);
    if (budgetMatch) {
      needs.budget = parseInt(budgetMatch[2], 10);
      intents.add('pricing');
    }

    if (text.includes('barata') || text.includes('económica') || text.includes('precio') || text.includes('cuesta') || text.includes('cuanto vale')) {
      intents.add('pricing');
    }

    // 8. Casos de uso específicos (teletrabajo, gaming, etc.)
    if (text.includes('teletrabajo') || text.includes('teletrabajar') || text.includes('trabajar desde casa')) {
      needs.teleworking = true;
      needs.fiberRequired = true;
      if (!needs.fiberSpeedRequired) needs.fiberSpeedRequired = 600;
    }

    if (text.includes('jugar') || text.includes('gaming') || text.includes('playstation') || text.includes('xbox') || text.includes('twitch')) {
      needs.gaming = true;
      needs.fiberRequired = true;
      if (!needs.fiberSpeedRequired) needs.fiberSpeedRequired = 600;
    }

    // 9. Cobertura
    if (text.includes('cobertura') || text.includes('llega la fibra') || text.includes('comprobar dirección') || text.includes('instalación en')) {
      intents.add('coverage');
    }

    // 10. Cliente actual
    if (text.includes('ya soy cliente') || text.includes('tengo o2') || text.includes('tengo contrato') || text.includes('añadir una línea') || text.includes('ya tengo')) {
      needs.existingCustomer = true;
      intents.add('existing_customer');
    }

    // 11. Comparación
    if (text.includes('compara') || text.includes('comparar') || text.includes('diferencia') || text.includes('diferencias') || text.includes('qué opciones hay')) {
      intents.add('comparison');
    }

    if (intents.size === 0) {
      intents.add('general_information');
    }

    return {
      intents: Array.from(intents),
      extractedNeeds: needs,
      confidence: 0.95,
      rawQuery: query
    };
  }

  private parseNumberWord(word: string): number | null {
    const clean = word.toLowerCase().trim();
    if (!isNaN(parseInt(clean, 10))) return parseInt(clean, 10);
    const map: Record<string, number> = {
      'un': 1, 'una': 1, 'uno': 1,
      'dos': 2,
      'tres': 3,
      'cuatro': 4,
      'cinco': 5,
      'seis': 6
    };
    return map[clean] || null;
  }
}

export const intentClassifier = new IntentClassifier();

SHA-256: 73199d76e7145fe23d4033e2dc79d5d3c06f348f83f08c6085e0864883901c1b