Inicio / Artículos

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.

Editor: REACTAPP.TOP9 min de lectura2156 palabras

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.

  1. ¿Se proporcionó el contexto adecuado?
  2. ¿Se obtuvo la información correcta?
  3. ¿Se eliminó el material irrelevante?
  4. ¿Podría el conocimiento estructurado responder de manera determinista?
  5. ¿Podría una herramienta leer el estado real del sistema?
  6. ¿Se validó la salida del modelo?
  7. ¿Se mantuvieron los hechos separados del razonamiento?
  8. ¿Aprendió el sistema de ejecuciones anteriores?
  9. ¿Se proporcionaron pruebas?
  10. ¿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