Diez formas de reducir las alucinaciones sin ajuste fino
Diez soluciones a nivel de sistema que reducen las alucinaciones de los LLM en un agente de comercio electrónico: mejor contexto, recuperación de información, grafos, herramientas, validación y evaluaciones, sin necesidad de ajuste fino.
Fuentes: 10 Ways to Reduce AI Hallucinations Without Fine-Tuning Your Model
Cuando un asistente le dice al cliente “su reembolso ya se ha procesado” cuando en realidad no existe ninguno, por lo general el modelo no es el culpable. El sistema que lo rodea dejó una laguna, y el modelo llenó esa laguna con una conjetura. Faltaba el estado del pedido, la consulta devolvía información incorrecta, el contexto estaba desorganizado, las llamadas a las herramientas no se verificaban y los éxitos anteriores se olvidaban.
Se pueden reducir en gran medida las alucinaciones sin tocar los pesos del modelo. El ejemplo que se muestra aquí es un asistente de comercio electrónico que verifica pedidos, emite reembolsos y actualiza las entregas, y lo reforzaremos capa por capa.
La versión ingenua
Un cliente escribe: “¿Dónde está mi pedido #48291?”. La versión inicial de la aplicación simplemente reenvía la pregunta:
const response = await client.responses.create({
model: "gpt-5",
input: `
Where is order #48291?
`
});
console.log(response.output_text);
El modelo podría responder con “Su pedido está en camino y llegará hoy”. Genial, excepto que no tiene idea de dónde se encuentra el pedido 48291. Generó una oración creíble a partir de patrones en sus datos de entrenamiento. Eso es una alucinación.
Lo que usted desea se parece más a esto:
Customer
↓
AI Agent
↓
Order System
↓
Actual Order State
↓
AI Agent
↓
Response
El modelo debe consultar la realidad, no inventarla.
1. Proporcione el contexto que ya posee
Regla uno: no haga que el modelo adivine lo que sabe su backend. Indique que el backend alberga este registro:
{
"orderId": "48291",
"status": "SHIPPED",
"carrier": "FedEx",
"trackingNumber": "784512963",
"estimatedDelivery": "2026-08-25"
}
Envíelo junto con la pregunta:
const context = {
order: {
id: "48291",
status: "SHIPPED",
carrier: "FedEx",
trackingNumber: "784512963",
estimatedDelivery: "2026-08-25"
}
};
const response = await client.responses.create({
model: "gpt-5",
input: `
Answer the customer using only the supplied order information.
Context:
${JSON.stringify(context, null, 2)}
Customer:
Where is my order #48291?
`
});
Ahora al modelo no se le pide que conozca el estado del pedido. Su tarea se reduce a explicar un hecho conocido al cliente, lo cual es mucho más seguro. Compare los dos flujos:
Bad:
Customer → LLM → Answer
Better:
Customer
↓
Retrieve state
↓
Build context
↓
LLM
↓
Answer
Este es el núcleo de la ingeniería de contexto.
2. Primero obtener la información, luego generarla
Otro cliente pregunta sobre la política de devoluciones para electrónicos adquiridos en una venta festiva. Esa respuesta no está en el modelo; se encuentra en su base de datos de políticas, documentos del producto, reglas de promoción y términos regionales. Por lo tanto, obténgala antes de generar la respuesta. Un flujo básico de RAG:
Customer Question
↓
Retrieve
↓
Rank
↓
Build Context
↓
LLM
↓
Answer
Y una implementación básica:
const results = await vectorStore.search({
query: customerQuestion,
topK: 10
});
const relevant = results
.filter(item => item.score > 0.8)
.slice(0, 5);
const context = relevant
.map(item => item.content)
.join("\n\n");
const answer = await generateAnswer(
customerQuestion,
context
);
Aquí la calidad supera a la cantidad. Proporcionarle al modelo 30 documentos no lo hace más inteligente. Si tres son importantes, los otros 27 son solo ruido.
3. Elimina el ruido
Imagínese al asistente recibiendo esta pila de documentos:
20 retrieved documents
+
15 previous messages
+
10 previous tool responses
+
customer profile
+
product catalog
+
order history
+
promotion metadata
Ahora el cliente solo pregunta: “¿Puedo devolver estos auriculares?”. El modelo no necesita las últimas quince conversaciones, pedidos no relacionados, otras líneas de productos, promociones vencidas ni reglas de envío antiguas. Un sistema más eficiente filtra la información antes de hacer la pregunta:
Available Information
↓
Relevance Filtering
↓
Metadata Filtering
↓
Ranking
↓
Deduplication
↓
Context Compression
↓
LLM
En código, algo como:
const context = results
.filter(x => x.score >= 0.82)
.filter(x => x.metadata.category === "returns")
.filter(x => x.metadata.region === customer.region)
.sort((a, b) => b.score - a.score)
.slice(0, 5)
.map(x => x.content);
Usted no está tratando de maximizar los tokens. Está intentando mejorar la relación señal-ruido. Una buena regla: darle al modelo todo lo que necesita y nada de lo que no necesita.
4. Utilice un grafo de conocimiento para hechos estructurados
RAG es adecuado para documentos. Muchas preguntas de comercio electrónico no tienen que ver con documentos en absoluto. Se refieren a relaciones: qué almacén procesa este pedido, qué pago lo cubrió, qué productos forman parte de una promoción, qué pedidos se ven afectados si un almacén queda fuera de servicio. Esos son problemas de grafos. Un grafo simplificado:
Customer
│
└── PLACED → Order
│
├── CONTAINS → Product
│
├── PAID_BY → Payment
│
├── FULFILLED_BY → Warehouse
│
└── SHIPPED_BY → Carrier
Cuando un usuario pregunta qué almacén está gestionando su pedido, el sistema puede seguir este camino:
Order #48291
↓
FULFILLED_BY
↓
Warehouse #17
con una consulta de gráfico como:
MATCH (o:Order {id: "48291"})
-[:FULFILLED_BY]->
(w:Warehouse)
RETURN w.id, w.name, w.location;
El resultado se convierte en un contexto fiable:
{
"orderId": "48291",
"warehouse": {
"id": "WH-17",
"name": "Delhi Fulfillment Center",
"location": "Delhi"
}
}
Coloca los dos enfoques uno al lado del otro. Sin estructura:
Without structured knowledge:
User → LLM
↓
Guess
Y con un gráfico en el bucle:
With Knowledge Graph:
User
↓
Entity Identification
↓
Graph Traversal
↓
Verified Relationship
↓
Context
↓
LLM
Da los mejores resultados cuando el asistente debe razonar a través de varios sistemas.
5. Pida pruebas
No solo pida la respuesta. Pida la respuesta junto con lo que la respalda. Una respuesta estructurada podría verse así:
{
"answer": "Your order is being fulfilled by Warehouse WH-17.",
"confidence": 0.98,
"evidence": [
{
"type": "order_record",
"source": "orders_db",
"orderId": "48291"
},
{
"type": "warehouse_relationship",
"source": "knowledge_graph",
"warehouseId": "WH-17"
}
]
}
Puedes fomentar el hábito a través de instrucciones:
For every factual claim:
1. Identify supporting evidence.
2. Use only available evidence.
3. Never invent a source.
4. If evidence is unavailable, say so.
5. Clearly distinguish facts from inference.
Luego, la aplicación puede actuar en función de la confianza:
if (result.confidence < 0.7) {
return escalateToHuman(result);
}
Eso cambia un flujo simple:
LLM → Answer
integrándolo con verificaciones en el proceso:
LLM
↓
Answer
↓
Evidence
↓
Confidence
↓
Decision
6. Proporcione al agente las herramientas necesarias en lugar de dejar que adivine
A veces, la mejor forma de prevenir problemas es evitar que el LLM responda en absoluto. Si un cliente pregunta si se ha procesado un reembolso, muestre una búsqueda correspondiente:
const tools = [{
name: "get_refund_status",
description: "Retrieve the current refund status for an order",
parameters: {
type: "object",
properties: {
orderId: {
type: "string"
}
},
required: ["orderId"]
}
}];
La ejecución se lleva a cabo de la siguiente manera:
Customer
↓
LLM
↓
get_refund_status()
↓
Payment System
↓
Actual Refund State
↓
LLM
↓
Customer
En lugar de una respuesta vaga como “su reembolso debería llegar en 5 a 7 días”, el agente puede indicar que el reembolso se inició ayer y aún está pendiente, ya que así es en realidad. Si el mundo real puede dar una respuesta, pregúnteselo al mundo real.
7. Validar las entradas y salidas de la herramienta
El simple hecho de llamar a una herramienta no garantiza que el agente sea fiable. El modelo aún puede generar una solicitud mal formada. Un cliente pide que se le reembolse $100 del pedido 48291, y el modelo emite:
{
"orderId": "48291",
"amount": "one hundred",
"currency": "dollars"
}
Pero tu API de pagos exige:
{
"orderId": "48291",
"amount": 100,
"currency": "USD"
}
Agregue validación de esquema, por ejemplo con Zod:
import { z } from "zod";
const RefundRequest = z.object({
orderId: z.string(),
amount: z.number().positive(),
currency: z.enum(["USD", "EUR", "GBP", "INR"])
});
const refundRequest = RefundRequest.parse(
modelOutput
);
No te detengas solo en los tipos. Revisa también las reglas de negocio:
if (refundRequest.amount > order.total) {
throw new Error(
"Refund amount exceeds order total"
);
}
El pipeline se convierte entonces en:
LLM
↓
Schema Validation
↓
Business Validation
↓
Permission Check
↓
Execute
↓
Response Validation
↓
Accept / Retry / Escalate
Esto es de suma importancia para los agentes que toman acciones reales. El LLM nunca debe tener la última palabra sobre los datos que se ejecutan.
8. Mantener los hechos separados del razonamiento
Un truco sutil pero efectivo es almacenar lo que el sistema sabe por separado de lo que el modelo concluyó:
{
"facts": [
"Order 48291 is shipped",
"Order 48291 is fulfilled by Warehouse WH-17",
"Warehouse WH-17 is currently operating"
],
"reasoning": [
"The order is likely to remain on schedule"
],
"conclusion": "The order is currently expected to arrive on time."
}
Ellos conllevan una certeza diferente. Las primeras afirmaciones provinieron de sistemas, mientras que la última es una inferencia. Cuando la respuesta final resulta incorrecta, se puede hacer una pregunta clara:
Was the fact wrong?
OR
Was the reasoning wrong?
Eso es mucho más fácil de depurar. Incluso puedes mostrar la división internamente:
FACT
→ Order shipped
FACT
→ Estimated delivery: Aug 25
INFERENCE
→ Delivery is currently expected on schedule
Un poco de separación te brinda mucha trazabilidad.
9. Aprende de las ejecuciones, no del entrenamiento
Procuremos que el agente mejore sin necesidad de ajustes finos. Un cliente solicita cancelar el pedido 48291. El agente verifica el pedido:
{
"orderId": "48291",
"status": "PROCESSING",
"cancelable": true
}
Luego llama:
cancel_order(48291)
y recibe de vuelta:
{
"success": true,
"cancellationId": "CAN-83921"
}
Ese resultado merece ser conservado. Guarde la ejecución exitosa:
{
"task": "Cancel order",
"orderState": "PROCESSING",
"action": "cancel_order",
"result": "SUCCESS",
"cancellationId": "CAN-83921"
}
Cuando aparezca una solicitud similar más tarde, busque primero el éxito anterior correspondiente:
New Request
↓
Find Similar Successful Execution
↓
Retrieve Relevant Pattern
↓
Check Current Order State
↓
Generate Action
↓
Validate
↓
Execute
Los fracasos también enseñan. Un intento que salió mal deja una huella:
Attempt 1
↓
cancel_order()
↓
Rejected: Order already shipped
↓
Agent retrieves shipping information
↓
Explains cancellation is unavailable
Esos rastros se convierten en conocimiento, y el bucle de retroalimentación se ve así:
Execute
↓
Observe
↓
Evaluate
↓
Store Experience
↓
Improve Future Context
Llámelo aprendizaje en tiempo de ejecución. No se requiere reentrenamiento.
10. Evaluar cada cambio
Finalmente, mida si algo fue de ayuda. Supongamos que modificó el método de recuperación. “Las respuestas parecen mejores” no es una prueba. Cree un conjunto de evaluación:
[
{
"question": "Where is order 48291?",
"expected": "SHIPPED"
},
{
"question": "Which warehouse fulfills order 48291?",
"expected": "WH-17"
},
{
"question": "Can order 48291 be cancelled?",
"expected": false
}
]
Luego, haga un seguimiento de métricas como estas:
Answer Accuracy
Groundedness
Retrieval Precision
Tool Selection Accuracy
Tool Success Rate
Task Success Rate
Recovery Rate
Hallucination Rate
Latency
Cost
Una tabla de antes y después hace que la mejora sea concreta:
Before After
Answer Accuracy 72% 95%
Groundedness 69% 97%
Tool Success 81% 98%
Hallucination Rate 17% 3%
Ahora no estás afirmando que el nuevo prompt funciona mejor, sino que lo demuestras. Aquí es donde la ingeniería de IA comienza a asemejarse a la ingeniería de software ordinaria:
Change
↓
Test
↓
Measure
↓
Compare
↓
Improve
Todas las diez capas juntas
Se apilan las diez capas alrededor del LLM y la arquitectura queda así:
┌──────────────────┐
│ Knowledge Graph │
└────────┬─────────┘
│
┌────────▼─────────┐
│ RAG / Search │
└────────┬─────────┘
│
Customer → Intent → Context Engine → LLM
↑ │
│ ▼
Memory Tool Selection
↑ │
│ ▼
│ Validation
│ │
│ ▼
│ Real Systems
│ │
│ ▼
└─────── Feedback
│
▼
Evaluation
Observe lo que no sucedió: el modelo no fue ajustado. Lo que sí mejoró fue el sistema que lo rodea.
Preguntas que hacer antes de culpar al modelo
La primera reacción ante una alucinación suele ser buscar un modelo mejor. A veces eso es cierto. Sin embargo, primero revise una lista de verificación.
- ¿Se proporcionó el contexto adecuado?
- ¿Se obtuvo la información correcta?
- ¿Se eliminó el material irrelevante?
- ¿Podría el conocimiento estructurado responder de manera determinista?
- ¿Podría una herramienta leer el estado real del sistema?
- ¿Se validó la salida del modelo?
- ¿Se mantuvieron los hechos separados del razonamiento?
- ¿Aprendió el sistema de ejecuciones anteriores?
- ¿Se proporcionaron pruebas?
- ¿Se miden los resultados?
Esas verificaciones suelen llevar a soluciones más económicas, seguras y fáciles de controlar que el ajuste fino del modelo.
El modelo es solo un componente
Una imagen útil:
┌───────────────────┐
│ Knowledge + RAG │
└─────────┬─────────┘
↓
Customer → Context → LLM → Tools → Real World
↑ ↓ ↓
Memory Reasoning Validation
↑ ↓
└──── Feedback
↓
Evaluation
El LLM genera posibilidades. Todo a su alrededor proporciona hechos, contexto, restricciones, herramientas, validación, memoria y retroalimentación, y eso es lo que hace que todo el sistema sea fiable.
Las alucinaciones suelen ser un problema de diseño del sistema más que un problema del modelo. Un mejor contexto, un mejor conocimiento, mejores herramientas, resultados validados y el aprendizaje de lo que realmente ocurrió te llevarán más lejos que un nuevo proceso de entrenamiento. La pregunta práctica pasa de qué modelo utilizar a qué debe saber el modelo, qué puede hacer y cómo determinar si ha acertado.
Leer más
- RAG, ajuste fino o agentes: elegir la capa adecuada y el orden para construirla — Una comparación práctica de la recuperación de información, el ajuste fino y los agentes que aborda costos, latencia, modos de fallo, reglas prácticas y un ejemplo de un asistente de recursos humanos que muestra el orden de construcción.
- Ingeniería de promptes: dirigir LLMs sin ajuste fino — Utiliza roles, ejemplos de pocos casos, razonamiento en cadena y restricciones de formato para guiar a los modelos, y sabe cuándo el uso exclusivo de promptes alcanza sus límites frente al ajuste fino.