Back

Cómo Reducir las Alucinaciones de IA en Producción: Grounding, RAG y Guardrails que Realmente Funcionan

Tu feature de IA salió a producción el martes. Para el jueves, un usuario sacó screenshot de tu chatbot citando con total confianza un caso de la Corte Suprema que no existe. El viernes tenías 47 tickets de soporte sobre el chatbot inventando funcionalidades del producto que nunca existieron. El lunes, tu PM te pide "solo agregar un disclaimer."

¿Te suena? No estás solo. A principios de 2026, solo el 29% de los desarrolladores confía en los outputs de IA — bajó del 40% en 2024 — y casi la mitad del código generado por IA entra a los codebases sin review completo. El problema no es que los LLMs estén rotos — es que los estamos desplegando sin la infraestructura de ingeniería necesaria para detectar cuándo fallan.

Las alucinaciones no son un bug que se parchea. Son una propiedad fundamental del funcionamiento de los modelos de lenguaje. Pero eso no significa que estés indefenso. Hay toda una disciplina de ingeniería creciendo alrededor de hacer que los LLMs sean confiablemente útiles — y es mucho más que "ponle RAG y ya."

Esta guía cubre el stack completo de reducción de alucinaciones: entender por qué los modelos alucinan, implementar técnicas de grounding, construir pipelines RAG efectivos, agregar guardrails de salida y monitorear tasas de alucinación en producción. Cada técnica viene con código TypeScript que podés adaptar hoy.

Por qué los LLMs alucinan: El modelo mental para ingenieros

Antes de arreglar alucinaciones, necesitás entender por qué pasan. No la explicación académica — el modelo mental práctico que te ayuda a predecir cuándo van a ocurrir.

La máquina de autocompletar

Los LLMs no "saben" cosas. Predicen el token más probable dado los tokens anteriores. Cuando le preguntás a GPT-5 "¿Cuál es la capital de Francia?", no busca un dato — genera "París" porque es la continuación estadísticamente más probable de tu prompt en sus datos de entrenamiento.

Esta distinción importa porque explica cuándo ocurren las alucinaciones:

  1. Zonas de baja confianza. Cuando el modelo encuentra un prompt donde múltiples continuaciones son igual de probables, elige una. A veces elige mal.

  2. Vacíos en datos de entrenamiento. La información que no estaba (o era escasa) en los datos de entrenamiento fuerza al modelo a interpolar. No dice "no sé" por defecto — genera texto que suena plausible.

  3. Presión por seguir instrucciones. Cuando le decís al modelo "siempre da una respuesta", lo va a hacer — incluso cuando no debería. La instrucción de ser útil choca con la instrucción de ser preciso.

  4. Overflow de ventana de contexto. Cuando la información relevante está enterrada en un contexto largo, el modelo puede ignorarla y generar desde su memoria paramétrica. Es el problema de "perdido en el medio."

  5. Fabricación inducida por formato. Cuando pedís output estructurado (JSON, tablas), el modelo puede inventar valores para completar campos obligatorios en vez de dejarlos vacíos.

Taxonomía de alucinaciones

No todas las alucinaciones son iguales. Categorizarlas te ayuda a elegir la contramedida correcta:

TipoDescripciónEjemploPeligro
Fabricación factualInventa hechos que suenan reales"El hook useState de React se introdujo en la versión 15.3"🔴 Alto
Fabricación de fuentesCita fuentes inexistentes"Según la encuesta de StackOverflow 2024..." (datos incorrectos)🔴 Alto
Extrapolación confiadaExtiende hechos reales más allá de la verdad"PostgreSQL soporta tablas de hasta 100TB nativamente"🟡 Medio
Alucinación de instrucciónImagina capacidades"Busqué en tu base de datos y encontré 3 resultados" (no buscó)🔴 Alto
Drift de coherenciaSe contradiceDice "X es verdad" y después "X no es verdad"🟡 Medio
Confusión temporalMezcla períodos de tiempoAplica datos pre-entrenamiento a eventos post-entrenamiento🟡 Medio

Cada tipo requiere una mitigación distinta. Fabricación factual necesita grounding. Fabricación de fuentes necesita verificación de citas. Alucinación de instrucción necesita enforcement de uso de herramientas. Armemos las defensas.

Capa 1: Grounding — Anclar el modelo a la realidad

Grounding es la práctica de darle al modelo material fuente autoritativo y restringirlo para que genere respuestas basadas en ese material. Es la técnica más fundamental para reducir alucinaciones.

Ingeniería de system prompt para grounding

Tu system prompt es la primera línea de defensa. La mayoría de los devs escriben prompts que fomentan la alucinación sin darse cuenta:

// ❌ Este system prompt fomenta alucinación const badSystemPrompt = `Sos un agente de soporte al cliente amigable para Acme Corp. Respondé todas las preguntas de forma completa y útil.`; // ✅ Este system prompt reduce alucinación const goodSystemPrompt = `Sos un agente de soporte al cliente para Acme Corp. REGLAS CRÍTICAS: 1. SOLO respondé usando los documentos de contexto proporcionados. 2. Si la respuesta no está en el contexto, decí: "No tengo esa información. Dejame conectarte con un agente humano." 3. NUNCA inventes funcionalidades del producto, precios o políticas. 4. Cuando cites información, hacé referencia a la sección específica del documento. 5. Si no estás seguro de algún detalle, decilo explícitamente. Tu fecha de corte de conocimiento significa que podrías no tener información actual. En caso de duda, dirigí al usuario a docs.acme.com.`;

Los patrones clave:

  • Permiso explícito para decir "no sé." Los modelos generan algo por defecto. Tenés que autorizar explícitamente la abstención.
  • Especificar el límite de conocimiento. Decile al modelo exactamente de dónde viene su información.
  • Exigir citas. Cuando el modelo debe citar fuentes, alucina menos porque las citas fabricadas son más fáciles de detectar.
  • Penalizar la confianza. Frases como "si no estás seguro, decilo" activan los mecanismos de calibración del modelo.

Ejemplos few-shot de grounding

Los ejemplos few-shot son sorprendentemente efectivos para enseñarle al modelo cuándo abstenerse:

const groundingExamples = [ { role: "user", content: "¿Cuál es la garantía de uptime del plan Enterprise?" }, { role: "assistant", content: "Según la documentación de precios (Sección 3.2), el plan Enterprise incluye un SLA de uptime del 99.99% con créditos financieros por cualquier downtime que exceda ese umbral." }, { role: "user", content: "¿Soportan integración con SAP?" }, { role: "assistant", content: "No veo SAP listado en nuestra documentación de integraciones actual. Te recomiendo revisar nuestra página de integraciones en docs.acme.com/integrations para la lista más actualizada, o puedo conectarte con nuestro equipo de integraciones para discutir opciones de integración personalizada." } ];

El segundo ejemplo es el importante. Le enseña al modelo la forma de una respuesta honesta de "no sé" — una que sigue siendo útil sin fabricar información.

Controles de temperatura y sampling

Temperatura baja no elimina alucinaciones, pero reduce la fabricación creativa:

import OpenAI from 'openai'; const openai = new OpenAI(); async function groundedCompletion( systemPrompt: string, context: string, userQuery: string ) { const response = await openai.chat.completions.create({ model: 'gpt-5', temperature: 0.1, // Temperatura baja para tareas factuales top_p: 0.9, // Nucleus sampling ligeramente restringido frequency_penalty: 0.3, // Desincentivar patrones repetitivos presence_penalty: 0.0, // No forzar diversidad (queremos precisión) messages: [ { role: 'system', content: systemPrompt }, { role: 'user', content: `Contexto:\n${context}\n\nPregunta: ${userQuery}` } ], }); return response.choices[0].message.content; }

Matiz importante: Temperatura 0 no significa "cero alucinaciones." Significa que el modelo elige el token más probable en cada paso. Si la continuación más probable es una alucinación (porque el modelo genuinamente no tiene la información correcta), temperatura 0 va a alucinar con máxima confianza.

Capa 2: RAG — Alimentar al modelo con lo que necesita

Retrieval-Augmented Generation es la técnica más adoptada para reducir alucinaciones. La idea es simple: en vez de depender de la memoria paramétrica del modelo, buscás documentos relevantes y los inyectás en el contexto.

Pero la mayoría de las implementaciones RAG son mediocres para reducir alucinaciones porque se enfocan en la calidad de la búsqueda e ignoran la otra mitad: cómo el modelo usa el contexto recuperado.

El pipeline RAG que realmente reduce alucinaciones

// Paso 1: La estrategia de chunking importa más que el modelo de embedding function intelligentChunk(document: string, metadata: Record<string, any>) { const sections = document.split(/\n## /); return sections.map((section, index) => ({ content: index === 0 ? section : `## ${section}`, metadata: { ...metadata, sectionIndex: index, documentTitle: metadata.title, sectionTitle: section.split('\n')[0]?.trim() || 'Introduction', previousSection: index > 0 ? sections[index - 1].slice(-200) : null, nextSection: index < sections.length - 1 ? sections[index + 1].slice(0, 200) : null, } })); } // Paso 2: Búsqueda con scoring de relevancia async function retrieveWithScoring( query: string, vectorStore: SupabaseVectorStore, options: { topK: number; scoreThreshold: number } ) { const results = await vectorStore.similaritySearchWithScore( query, options.topK * 2 ); const relevant = results .filter(([_, score]) => score >= options.scoreThreshold) .slice(0, options.topK); if (relevant.length === 0) { return { documents: [], confidence: 'none', message: 'No se encontraron documentos suficientemente relevantes' }; } return { documents: relevant.map(([doc, score]) => ({ content: doc.pageContent, metadata: doc.metadata, relevanceScore: score })), confidence: relevant[0][1] > 0.85 ? 'high' : 'moderate', message: null }; } // Paso 3: Generación con enforcement de citas async function generateWithRAG( query: string, retrievalResult: Awaited<ReturnType<typeof retrieveWithScoring>> ) { if (retrievalResult.confidence === 'none') { return { answer: "No tengo suficiente información en mi base de conocimiento para responder con precisión. ¿Podrías reformular la pregunta, o preferís que te conecte con un experto?", citations: [], confidence: 'none' }; } const contextBlock = retrievalResult.documents .map((doc, i) => `[Fuente ${i + 1}: ${doc.metadata.documentTitle} > ${doc.metadata.sectionTitle}]\n${doc.content}`) .join('\n\n---\n\n'); const response = await openai.chat.completions.create({ model: 'gpt-5', temperature: 0.1, messages: [ { role: 'system', content: `Sos un asistente preciso. Respondé SOLO basándote en las fuentes proporcionadas. Reglas: - Citá fuentes usando la notación [Fuente N] - Si las fuentes no responden completamente, decí qué PODÉS y qué NO PODÉS responder - Nunca extrapolés más allá de lo que las fuentes dicen explícitamente - Si las fuentes se contradicen, presentá ambos puntos de vista con sus citas` }, { role: 'user', content: `Fuentes:\n${contextBlock}\n\nPregunta: ${query}` } ] }); return { answer: response.choices[0].message.content, citations: retrievalResult.documents.map(d => d.metadata), confidence: retrievalResult.confidence }; }

Los cinco errores de RAG que causan alucinaciones

Incluso con RAG, la mayoría de las implementaciones siguen alucinando. Acá está por qué:

1. Sin umbral de relevancia. Si tu pipeline RAG siempre devuelve algo, el modelo va a intentar responder desde contexto irrelevante. Eso es peor que no tener contexto — le da falsa confianza al modelo.

// ❌ Siempre devuelve resultados, incluso irrelevantes const results = await vectorStore.similaritySearch(query, 5); // ✅ Solo devuelve resultados por encima del umbral const results = await vectorStore.similaritySearchWithScore(query, 10); const filtered = results.filter(([_, score]) => score > 0.75);

2. Chunks demasiado chicos. Cuando cortás documentos en fragmentos de 200 tokens, perdés contexto. El modelo ve "la tasa es del 4.5%" pero no ve que era de una sección de precios de 2023 ya actualizada.

3. Sin metadata de documentos. Sin metadata (título, fecha, jerarquía), el modelo no puede evaluar la autoridad o vigencia de la fuente. Trata un blog post de hace 3 años igual que la documentación oficial de ayer.

4. Demasiado contexto. Más contexto no siempre es mejor. Cuando inyectás 15 chunks, el modelo tiene problemas para identificar el relevante. El efecto "perdido en el medio" hace que la información en posiciones 4-12 de un contexto de 15 chunks se ignore frecuentemente.

5. Sin camino de "no sé". Si tu pipeline siempre genera una respuesta, va a alucinar cuando el contexto no contenga la respuesta. Necesitás un camino explícito de abstención.

Búsqueda híbrida: Lo mejor de ambos mundos

La búsqueda por similitud vectorial pura pierde matches exactos de keywords. La búsqueda por keywords pierde similitud semántica. La búsqueda híbrida combina ambas:

async function hybridSearch( query: string, supabase: any, options: { topK: number; vectorWeight: number } ) { const vectorResults = await supabase.rpc('match_documents', { query_embedding: await embedQuery(query), match_threshold: 0.7, match_count: options.topK * 2, }); const keywordResults = await supabase.rpc('search_documents', { search_query: query, match_count: options.topK * 2, }); // Reciprocal Rank Fusion (RRF) para combinar resultados const scores = new Map<string, number>(); const k = 60; vectorResults.data?.forEach((doc: any, rank: number) => { const id = doc.id; const score = (scores.get(id) || 0) + options.vectorWeight / (k + rank + 1); scores.set(id, score); }); keywordResults.data?.forEach((doc: any, rank: number) => { const id = doc.id; const weight = 1 - options.vectorWeight; const score = (scores.get(id) || 0) + weight / (k + rank + 1); scores.set(id, score); }); return Array.from(scores.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, options.topK); }

Capa 3: Guardrails de Output — Atrapar alucinaciones después de la generación

Grounding y RAG reducen alucinaciones. Los guardrails atrapan las que se filtran. Esta es tu red de seguridad.

Validación estructural

El guardrail más simple y efectivo: validar que el output coincida con la estructura esperada.

import { z } from 'zod'; const ProductRecommendationSchema = z.object({ productName: z.string().min(1), productId: z.string().regex(/^PROD-\d{6}$/), price: z.number().positive(), inStock: z.boolean(), reasoning: z.string().min(20), }); async function validateOutput( llmOutput: string, schema: z.ZodSchema, knownProducts: Map<string, any> ) { let parsed; try { parsed = schema.parse(JSON.parse(llmOutput)); } catch (e) { return { valid: false, error: 'Validación estructural falló', data: null }; } const realProduct = knownProducts.get(parsed.productId); if (!realProduct) { return { valid: false, error: `Product ID ${parsed.productId} no existe`, data: null }; } const issues: string[] = []; if (Math.abs(parsed.price - realProduct.price) > 0.01) { issues.push(`Precio incorrecto: LLM dijo ${parsed.price}, real es ${realProduct.price}`); } if (parsed.inStock !== realProduct.inStock) { issues.push(`Stock incorrecto: LLM dijo ${parsed.inStock}, real es ${realProduct.inStock}`); } return issues.length > 0 ? { valid: false, error: issues.join('; '), data: parsed } : { valid: true, error: null, data: parsed }; }

LLM-as-Judge: Auto-verificación

Usá una segunda llamada al LLM para verificar el primer output. Es más caro pero atrapa alucinaciones sutiles que la validación estructural no detecta:

async function selfVerify( originalQuery: string, context: string, generatedAnswer: string ): Promise<{ isGrounded: boolean; issues: string[]; confidence: number }> { const verificationPrompt = `Sos un fact-checker. Tu trabajo es verificar si una respuesta generada por IA está completamente respaldada por el contexto proporcionado. Contexto: ${context} Pregunta original: ${originalQuery} Respuesta generada: ${generatedAnswer} Analizá la respuesta oración por oración. Para cada afirmación: 1. ¿Está directamente respaldada por el contexto? (SUPPORTED) 2. ¿Es una inferencia razonable del contexto? (INFERRED) 3. ¿No se encuentra en el contexto? (UNSUPPORTED) 4. ¿Contradice el contexto? (CONTRADICTED) Respondé en JSON: { "claims": [{ "claim": "...", "status": "...", "evidence": "..." }], "overallGrounded": true/false, "confidence": 0.0-1.0, "issues": ["..."] }`; const verification = await openai.chat.completions.create({ model: 'gpt-5', temperature: 0, response_format: { type: 'json_object' }, messages: [ { role: 'system', content: 'Sos un fact-checker preciso. Sé estricto.' }, { role: 'user', content: verificationPrompt } ] }); return JSON.parse(verification.choices[0].message.content!); }

Optimización de costos: No necesitás verificar cada respuesta. Implementá verificación selectiva:

function shouldVerify(response: string, context: any): boolean { // Siempre verificar respuestas de alto riesgo if (context.category === 'medical' || context.category === 'legal') return true; // Verificar respuestas con números (propensas a fabricación) if (/\d+%|\$\d+|\d+ (users|customers|times)/.test(response)) return true; // Verificar respuestas que citan fuentes específicas if (/according to|según|la documentación dice/.test(response)) return true; // Saltear verificación para confirmaciones simples if (response.length < 100) return false; // Sampling aleatorio para el resto (10%) return Math.random() < 0.1; }

Verificación de citas

Cuando el modelo dice citar una fuente, verificá que la cita exista y respalde la afirmación:

async function verifyCitations( answer: string, providedSources: Array<{ id: string; content: string; title: string }> ): Promise<{ verified: boolean; fabricatedCitations: string[]; unsupportedClaims: string[]; }> { const citationPattern = /\[Fuente (\d+)\]/g; const citedSources = new Set<number>(); let match; while ((match = citationPattern.exec(answer)) !== null) { citedSources.add(parseInt(match[1])); } const fabricatedCitations: string[] = []; for (const sourceNum of citedSources) { if (sourceNum > providedSources.length || sourceNum < 1) { fabricatedCitations.push(`[Fuente ${sourceNum}] no existe`); } } return { verified: fabricatedCitations.length === 0, fabricatedCitations, unsupportedClaims: [] }; }

Capa 4: Scoring de Confianza — Saber cuándo no sabés

Una de las técnicas más poderosas pero menos usadas: hacer que el modelo reporte su propia confianza y usar esa señal para filtrar respuestas.

Confianza a nivel de token

GPT-5 de OpenAI soporta logprobs — probabilidades logarítmicas para cada token generado. (Nota: la API de Claude de Anthropic no soporta logprobs actualmente; usá confianza auto-reportada para Claude.) Tokens de baja probabilidad son señales de alucinación:

⚠️ Nota sobre GPT-5: logprobs solo está disponible cuando reasoning_effort está en "none". Usar logprobs con otros niveles de razonamiento va a tirar error.

async function getConfidenceScore(prompt: string, systemPrompt: string) { const completion = await openai.chat.completions.create({ model: 'gpt-5', temperature: 0, logprobs: true, top_logprobs: 3, messages: [ { role: 'system', content: systemPrompt }, { role: 'user', content: prompt } ] }); const logprobs = completion.choices[0].logprobs?.content || []; const avgLogProb = logprobs.reduce((sum, t) => sum + t.logprob, 0) / logprobs.length; const confidence = Math.exp(avgLogProb); const lowConfidenceSpans: Array<{ text: string; probability: number }> = []; let currentSpan = ''; let spanMinProb = 1; for (const token of logprobs) { const prob = Math.exp(token.logprob); if (prob < 0.5) { currentSpan += token.token; spanMinProb = Math.min(spanMinProb, prob); } else if (currentSpan) { lowConfidenceSpans.push({ text: currentSpan, probability: spanMinProb }); currentSpan = ''; spanMinProb = 1; } } return { response: completion.choices[0].message.content!, confidence, lowConfidenceSpans }; }

Confianza auto-reportada

Pedile al modelo que califique su propia confianza. Es imperfecto pero sorprendentemente útil combinado con otras señales:

async function generateWithConfidence(query: string, context: string) { const response = await openai.chat.completions.create({ model: 'gpt-5', temperature: 0.1, response_format: { type: 'json_object' }, messages: [ { role: 'system', content: `Respondé la pregunta basándote en el contexto proporcionado. Para cada parte de tu respuesta, calificá tu confianza: - HIGH: Directamente indicado en el contexto - MEDIUM: Inferencia razonable del contexto - LOW: No bien respaldado por el contexto - NONE: Especulación pura Respondé en JSON: { "answer": "tu respuesta", "confidence_breakdown": [ { "claim": "...", "confidence": "HIGH|MEDIUM|LOW|NONE", "source": "..." } ], "overall_confidence": "HIGH|MEDIUM|LOW|NONE", "caveats": ["limitaciones importantes"] }` }, { role: 'user', content: `Contexto:\n${context}\n\nPregunta: ${query}` } ] }); const result = JSON.parse(response.choices[0].message.content!); if (result.overall_confidence === 'LOW' || result.overall_confidence === 'NONE') { return { ...result, shouldEscalate: true, userMessage: `Tengo confianza limitada en esta respuesta. ${result.caveats?.join(' ') || 'La información disponible puede no cubrir completamente tu pregunta.'}` }; } return { ...result, shouldEscalate: false }; }

Capa 5: Monitoreo en Producción — Medir lo que importa

No podés mejorar lo que no medís. Así se rastrean las tasas de alucinación en producción.

Tracking de tasa de alucinación

interface HallucinationEvent { id: string; timestamp: Date; query: string; response: string; hallucinationType: 'factual' | 'source' | 'instruction' | 'coherence' | 'temporal'; severity: 'critical' | 'moderate' | 'minor'; detectionMethod: 'user_report' | 'guardrail' | 'self_verify' | 'citation_check'; context: { model: string; temperature: number; ragUsed: boolean; retrievalScore: number | null; tokenConfidence: number; }; } class HallucinationMonitor { private events: HallucinationEvent[] = []; async trackResponse(params: { query: string; response: string; context: string; model: string; ragScore: number | null; confidence: number; }) { const verificationResult = await selfVerify( params.query, params.context, params.response ); if (!verificationResult.isGrounded) { const event: HallucinationEvent = { id: crypto.randomUUID(), timestamp: new Date(), query: params.query, response: params.response, hallucinationType: this.classifyHallucination(verificationResult.issues), severity: this.assessSeverity(verificationResult), detectionMethod: 'self_verify', context: { model: params.model, temperature: 0.1, ragUsed: params.ragScore !== null, retrievalScore: params.ragScore, tokenConfidence: params.confidence, } }; this.events.push(event); if (event.severity === 'critical') { console.error(`🚨 Alucinación crítica detectada: ${event.hallucinationType}`); } } return verificationResult; } getMetrics(timeWindow: { start: Date; end: Date }) { const windowEvents = this.events.filter( e => e.timestamp >= timeWindow.start && e.timestamp <= timeWindow.end ); return { totalResponses: windowEvents.length, hallucinationRate: windowEvents.length / (windowEvents.length || 1), byType: this.groupBy(windowEvents, 'hallucinationType'), bySeverity: this.groupBy(windowEvents, 'severity'), }; } private classifyHallucination(issues: string[]): HallucinationEvent['hallucinationType'] { const issueText = issues.join(' ').toLowerCase(); if (issueText.includes('source') || issueText.includes('citation')) return 'source'; if (issueText.includes('contradict')) return 'coherence'; return 'factual'; } private assessSeverity(result: any): HallucinationEvent['severity'] { if (result.confidence < 0.3) return 'critical'; if (result.confidence < 0.6) return 'moderate'; return 'minor'; } private groupBy<T>(arr: T[], key: keyof T) { return arr.reduce((acc, item) => { const k = String(item[key]); acc[k] = (acc[k] || 0) + 1; return acc; }, {} as Record<string, number>); } }

Feedback de usuarios

La señal más honesta: dejá que los usuarios reporten alucinaciones y usá esos datos para mejorar tu pipeline.

async function handleFeedback(feedback: { responseId: string; feedbackType: 'hallucination' | 'incorrect' | 'helpful' | 'not_helpful'; userComment?: string; correction?: string; }) { await db.insert(feedbackTable).values({ responseId: feedback.responseId, type: feedback.feedbackType, comment: feedback.userComment, correction: feedback.correction, timestamp: new Date(), }); if (feedback.feedbackType === 'hallucination') { await db.insert(knownHallucinationsTable).values({ responseId: feedback.responseId, userCorrection: feedback.correction, reviewStatus: 'pending', }); } }

Todo junto: Pipeline de defensa en profundidad

Ninguna técnica sola elimina alucinaciones. El enfoque de producción es defensa en profundidad:

async function safeAIResponse( query: string, userContext: { userId: string; category: string } ) { // Capa 1: Búsqueda y scoring de contexto (RAG) const retrieval = await retrieveWithScoring(query, vectorStore, { topK: 5, scoreThreshold: 0.7, }); // Capa 2: Generación grounded const generation = await generateWithRAG(query, retrieval); // Capa 3: Scoring de confianza const confidence = await getConfidenceScore(query, groundedSystemPrompt); // Capa 4: Verificación selectiva if (shouldVerify(generation.answer, userContext)) { const verification = await selfVerify( query, retrieval.documents.map(d => d.content).join('\n'), generation.answer ); if (!verification.isGrounded) { return { response: "Quiero asegurarme de darte información precisa. " + "Algunas partes de la respuesta inicial no pudieron verificarse completamente. " + "Te paso solo lo que puedo confirmar.", confidence: 'low', citations: generation.citations, verified: false, }; } } // Capa 5: Monitoreo await hallucinationMonitor.trackResponse({ query, response: generation.answer, context: retrieval.documents.map(d => d.content).join('\n'), model: 'gpt-5', ragScore: retrieval.documents[0]?.relevanceScore || null, confidence: confidence.confidence, }); return { response: generation.answer, confidence: generation.confidence, citations: generation.citations, verified: true, }; }

Checklist de reducción de alucinaciones

Pre-lanzamiento

  • System prompt permite explícitamente respuestas "no sé"
  • Temperatura ≤ 0.3 para tareas factuales
  • Pipeline RAG con umbral de relevancia
  • Chunks con metadata (título, fecha, sección)
  • Validación de schema para outputs estructurados
  • Verificación de citas implementada
  • Scoring de confianza integrado (logprobs o auto-reportado)
  • Respuestas de fallback para outputs de baja confianza

Post-lanzamiento

  • Dashboard de monitoreo de alucinaciones activo
  • Mecanismo de feedback de usuarios implementado
  • Dataset de alucinaciones conocidas creciendo
  • Review semanal de métricas
  • Framework de A/B testing para mejoras de prompts
  • Alertas configuradas para picos críticos

Conclusión

Las alucinaciones no van a desaparecer. La arquitectura fundamental de los LLMs — predecir continuaciones probables — significa que siempre van a tener el potencial de generar texto que suena plausible pero es falso. Ninguna cantidad de RLHF, fine-tuning o prompts ingeniosos va a cambiar esto por completo.

Pero eso no significa que no puedas construir features de IA confiables. Los equipos que están teniendo éxito con productos de IA en 2026 no encontraron un prompt mágico. Construyeron infraestructura de ingeniería alrededor de sus modelos: grounding, retrieval, guardrails, scoring de confianza, monitoreo y feedback loops.

El insight clave: tratá los outputs del LLM como input de usuario. No confiarías en input arbitrario de un usuario sin validación. No confíes en output del LLM sin verificación tampoco.

Empezá por la capa que atienda tu mayor riesgo. Si tus usuarios ven hechos fabricados, implementá RAG con umbrales de relevancia. Si ven citas fabricadas, agregá verificación de citas. Si no sabés qué están viendo, empezá por el monitoreo.

El objetivo no es la perfección. Es ingeniería de una tasa de error aceptable — y tener la infraestructura para detectarla, medirla y mejorarla con el tiempo.

AILLMhallucinationsRAGguardrailsproductionTypeScriptOpenAIClaudegrounding

Explora herramientas relacionadas

Prueba estas herramientas gratuitas de Pockit