01 — Instalación & Configuración
Setup inicial para ReviewAI (React + Vite + TypeScript)
Estrategia: Lazy Loading
Los modelos se cargan bajo demanda (no en el bundle inicial). Esto mantiene el First Load bajo los 500KB requeridos. El primer análisis tarda ~3-5s en cargar el modelo; los siguientes son instantáneos.
# Instalar la librería npm install @huggingface/transformers # Para batch processing en Node.js también está disponible: # Compatible con Node.js 20+, Bun 1.x, Deno 2.x
import { env, LogLevel } from '@huggingface/transformers'; /** * Configuración global para ReviewAI * Cumplimiento GDPR: todos los modelos corren 100% local */ export function configureMLEnvironment() { // GDPR: modelos locales únicamente en producción if (import.meta.env.PROD) { env.allowRemoteModels = false; env.allowLocalModels = true; env.localModelPath = '/app/models/'; } else { // Dev: carga remota con caché env.allowRemoteModels = true; env.useBrowserCache = true; } // Reducir logs en producción env.logLevel = import.meta.env.PROD ? LogLevel.WARNING : LogLevel.INFO; }
02 — Análisis de Sentimiento en Tiempo Real
Clasifica reseñas de ReviewAI como POSITIVE/NEGATIVE al instante
import { pipeline, TextClassificationPipeline } from '@huggingface/transformers'; let sentimentPipeline: TextClassificationPipeline | null = null; /** Singleton: crea el pipeline una sola vez, reutiliza para todas las inferencias */ async function getSentimentPipeline() { if (!sentimentPipeline) { sentimentPipeline = await pipeline( 'sentiment-analysis', 'Xenova/bert-base-multilingual-uncased-sentiment', { dtype: 'q8', // 8-bit quantizado: 4x más rápido, ~1% menos accuracy device: 'wasm', // WASM: compatible con todos los navegadores } ); } return sentimentPipeline; } export async function analyzeReviews(reviews: string[]) { const pipe = await getSentimentPipeline(); // Batch processing: analiza múltiples reseñas a la vez const results = await pipe(reviews, { topk: 1 }); return reviews.map((text, i) => ({ text, label: results[i].label, score: results[i].score, sentiment: results[i].label === 'POSITIVE' ? 'positivo' : 'negativo', })); } // ⚠️ CRÍTICO: liberar memoria al cerrar el componente export async function disposeSentiment() { await sentimentPipeline?.dispose(); sentimentPipeline = null; }
03 — Clasificación Zero-Shot de Tickets
Categoriza automáticamente sin necesidad de datos de entrenamiento
Zero-shot: sin datos de entrenamiento propios
El modelo clasifica tickets en categorías arbitrarias definidas en tiempo de ejecución. ReviewAI puede cambiar las categorías sin reentrenar el modelo.
import { pipeline } from '@huggingface/transformers'; const SUPPORT_CATEGORIES = [ 'bug técnico', 'solicitud de nueva función', 'problema de facturación', 'onboarding y configuración', 'rendimiento y velocidad', 'pregunta general', ]; export async function classifyTickets(tickets: string[]) { const classifier = await pipeline( 'zero-shot-classification', 'Xenova/mDeBERTa-v3-base-mnli-xnli', // Multilingual { dtype: 'q4' } // 4-bit: máxima velocidad para clasificación ); const results = await Promise.all( tickets.map(ticket => classifier(ticket, SUPPORT_CATEGORIES, { multi_label: false }) ) ); await classifier.dispose(); // ← Siempre liberar memoria return results.map((r, i) => ({ ticket: tickets[i], category: r.labels[0], confidence: r.scores[0], allScores: r.labels.reduce((acc, label, j) => ({ ...acc, [label]: r.scores[j] }), {}), })); }
04 — Extracción de Entidades (NER)
Detecta organizaciones, productos, fechas y personas en reseñas
import { pipeline } from '@huggingface/transformers'; export async function extractEntities(text: string) { const ner = await pipeline( 'token-classification', 'Xenova/bert-base-multilingual-cased-ner-hrl', { dtype: 'q8', aggregation_strategy: 'simple', // Agrupa tokens del mismo entity } ); const entities = await ner(text); await ner.dispose(); // Agrupa entidades por tipo return (entities as any[]).reduce((acc, entity) => { const type = entity.entity_group; if (!acc[type]) acc[type] = []; acc[type].push({ text: entity.word, score: entity.score.toFixed(3), }); return acc; }, {} as Record<string, Array<{text: string, score: string}>>); }
05 — Resumen Automático de Reseñas
Condensa reseñas largas a 2-3 frases clave
import { pipeline } from '@huggingface/transformers'; export async function summarizeReview(longReview: string) { const summarizer = await pipeline( 'summarization', 'Xenova/distilbart-cnn-6-6', { dtype: 'q8' } ); const [result] = await summarizer(longReview, { max_length: 80, min_length: 20, do_sample: false, // Determinístico para consistencia early_stopping: true, }); await summarizer.dispose(); return { original_chars: longReview.length, summary: result.summary_text, summary_chars: result.summary_text.length, reduction: `${Math.round((1 - result.summary_text.length / longReview.length) * 100)}%`, }; }
06 — Gestión de Memoria & Ciclo de Vida
Patrón crítico: prevenir memory leaks en aplicaciones de larga duración
CRÍTICO: Siempre llamar pipe.dispose()
Los modelos ML consumen entre 100MB y varios GB de memoria GPU/CPU. Sin dispose(), una SPA puede quedarse sin memoria tras unas pocas horas de uso activo.
import { useEffect, useRef } from 'react'; import { pipeline } from '@huggingface/transformers'; export function useMLPipeline(task: string, model: string) { const pipeRef = useRef<any>(null); const loadingRef = useRef(false); const getPipe = async () => { if (pipeRef.current) return pipeRef.current; if (loadingRef.current) return null; // evitar carga doble loadingRef.current = true; pipeRef.current = await pipeline(task, model, { dtype: 'q8' }); loadingRef.current = false; return pipeRef.current; }; // ← Cleanup automático al desmontar el componente React useEffect(() => { return () => { pipeRef.current?.dispose().then(() => { pipeRef.current = null; console.log(`[ML] Disposed: ${model}`); }); }; }, [model]); return { getPipe }; }
07 — Modelos Recomendados para ReviewAI
Selección optimizada para los 4 casos de uso (browser + Node.js)
| Caso de Uso | Modelo HF Hub | Tamaño | Dtype | Rendimiento | Idiomas |
|---|---|---|---|---|---|
| Sentimiento | Xenova/bert-base-multilingual-uncased-sentiment | 66MB | q8 |
|
104 idiomas |
| Zero-Shot | Xenova/mDeBERTa-v3-base-mnli-xnli | 170MB | q4 |
|
Multilingue |
| NER | Xenova/bert-base-multilingual-cased-ner-hrl | 177MB | q8 |
|
9 idiomas |
| Resumen | Xenova/distilbart-cnn-6-6 | 290MB | q8 |
|
Inglés |
| Embeddings | onnx-community/all-MiniLM-L6-v2-ONNX | 22MB | fp32 |
|
Inglés |