El problema
La solicitud parece estándar: preparar un resumen corto de cambios en policy y añadir fuentes.
En trazas se ve otra cosa: en un run el agente devolvió 7 citas,
pero la verificación mostró que 3 fuentes nunca se fetchearon y 2 llevan a 404.
Para el usuario, la respuesta suena segura, pero no es reproducible.
El sistema no se cae.
Simplemente devuelve citas plausibles sin evidencia real.
Analogía: imagina un auditor que en su informe pone referencias a "carpetas en el archivo", pero nadie ha visto esas carpetas. El documento parece profesional hasta que alguien verifica las fuentes. En sistemas de agentes, las fuentes alucinadas funcionan igual.
Por qué pasa
Las fuentes alucinadas normalmente no aparecen por un solo error de modelo, sino por falta de control estricto de citas en runtime.
LLM tiene un sesgo fuerte hacia respuestas "completas", por eso sin verificación estricta el modelo tiende más a inventar una cita que a devolver una respuesta sin fuente.
En producción, normalmente pasa así:
- el agente genera citas como parte de una respuesta "completa";
- los snippets de búsqueda se toman como evidence aunque la página nunca se abrió;
source_idno está ligado a snapshots de evidence;- sin verificación de citas, runtime deja pasar fuentes unfetched o inválidas;
- si fail-closed no está configurado, las fuentes inventadas llegan al usuario.
En traza se ve como crecimiento de citations_count
mientras cae citation_validity_rate.
El problema no es una sola URL mala.
Runtime no bloquea citas no verificadas antes de la salida final.
Fallos más frecuentes
En producción aparecen sobre todo cuatro patrones repetidos de fuentes alucinadas.
Citas de URL no verificadas (Unfetched URL citations)
El agente cita una URL que nunca pasó por http.get o kb.read en ese run.
Causa típica: las citas no están restringidas a source_id del evidence store.
Snippet en lugar de evidencia (Search-as-evidence)
La respuesta incluye "fuentes" de resultados de búsqueda, pero el agente no tiene confirmación del contenido real de la página.
Causa típica: search results mezclados con la capa de evidence.
Deriva de citas entre pasos (Citation drift)
En un paso temprano la fuente era válida, pero tras retry o truncation la respuesta final apunta a otro documento.
Causa típica: no existe un enlace estable claim -> source_id -> snapshot hash.
Pseudocitas sin cobertura de claims (Claim-source mismatch)
La respuesta tiene bloque de citas, pero los claims clave no tienen fuente correspondiente.
Causa típica: la validación revisa solo "hay links", no cobertura de claims.
Cómo detectar estos problemas
Las fuentes alucinadas se detectan bien por la combinación de métricas de cita y retrieval.
| Métrica | Señal de fuentes alucinadas | Qué hacer |
|---|---|---|
citation_validity_rate | baja la proporción de citas validadas | introducir verificación fail-closed por source_id |
unfetched_source_rate | muchas URL unfetched en respuestas | prohibir URL-citations sin evidence snapshot |
source_404_rate | parte de las fuentes no abre | verificar estado y URL canónica durante fetch |
claim_without_citation_rate | claims sin vínculo a fuente | añadir claim-level coverage check |
citation_stop_reason_rate | citations:invalid frecuente en runtime | revisar calidad de retrieval y policy de tools |
Cómo distinguir fuentes alucinadas de una respuesta solo inexacta
No toda inexactitud de texto implica fuentes inventadas. La pregunta clave: si la fuente se puede reproducir técnicamente para cada claim crítico.
Normal si:
- cada cita apunta a
source_idque existe en evidence store; - hay metadatos de snapshot (URL, timestamp, hash);
- la verificación de claims muestra cobertura de las conclusiones clave.
Peligroso si:
- la respuesta contiene URL que no estuvieron en la fase de fetch;
- las citas existen "de forma" pero no cubren claims principales;
- las respuestas no son reproducibles al nivel run (
run_id->source_id-> snapshot).
Cómo frenar estos fallos
En la práctica, se ve así:
- todas las fuentes pasan por evidence store (snapshot + hash + timestamp);
- el modelo devuelve citas solo como
source_id, no URLs arbitrarias; - citation verifier comprueba que todos los
source_idexisten, fueron fetched y están permitidos por policy; - si la verificación falla, runtime devuelve stop reason y safe fallback.
Guard mínimo para validar citas:
from dataclasses import dataclass
import hashlib
import time
@dataclass(frozen=True)
class EvidenceMeta:
source_id: str
url: str
fetched_at: float
text_sha256: str
class EvidenceStore:
def __init__(self):
self.items: dict[str, EvidenceMeta] = {}
def add_snapshot(self, source_id: str, url: str, text: str) -> None:
self.items[source_id] = EvidenceMeta(
source_id=source_id,
url=url,
fetched_at=time.time(),
text_sha256=hashlib.sha256(text.encode("utf-8")).hexdigest(),
)
def has(self, source_id: str) -> bool:
return source_id in self.items
def verify_citations(cited_source_ids: list[str], store: EvidenceStore) -> str | None:
# cited_source_ids are expected to come from structured output
if not cited_source_ids:
return "citations:missing"
unknown = [sid for sid in cited_source_ids if not store.has(sid)]
if unknown:
return "citations:unknown_source_id"
return None
Este es un guard básico.
En producción suele ampliarse con claim-level coverage check,
allowlist para citation tools y stop reason separado para URL unfetched.
verify_citations(...) se llama antes del render final,
para que el usuario no vea fuentes inválidas.
Dónde se implementa en la arquitectura
En producción, el control de fuentes alucinadas casi siempre se reparte entre tres capas del sistema.
Tool Execution Layer se ocupa del fetch de evidence: estado de respuesta, normalización de URL, snapshots y hash. Si esta capa no guarda pruebas, las citas no pueden verificarse de forma fiable.
Agent Runtime controla structured output, citation verification, stop reasons y fallback fail-closed. Aquí se decide si la respuesta llega al usuario.
Memory Layer mantiene el vínculo run-evidence:
run_id, source_id, retention y reproducibilidad.
Sin esta capa, el equipo no puede hacer una auditoría sólida del incidente.
Autoevaluación
Verificación rápida antes del release. Marca los puntos y mira el estado abajo.
Este es un sanity-check corto, no una auditoría formal.
Progreso: 0/8
⚠ Hay señales de riesgo
Faltan controles básicos. Cierra los puntos clave del checklist antes del release.
FAQ
Q: ¿No basta con pedirle al modelo que "siempre añada fuentes"?
A: Puedes pedirlo, pero no es suficiente. Sin verificación runtime de citas, es formato, no evidencia.
Q: ¿Los search results pueden usarse como evidencia?
A: Normalmente no. Search solo da candidatos.
La evidencia es solo lo que se fetch y se guarda como snapshot.
Q: ¿Es obligatorio guardar el texto completo de la fuente?
A: No siempre. Mínimo para auditoría: URL, timestamp, hash y source_id estable. El texto completo se añade cuando hace falta replay o citas exactas.
Q: ¿Qué mostrar al usuario cuando las citas son inválidas?
A: Stop reason explícita, qué ya se verificó, y siguiente paso seguro: respuesta partial sin fuentes no verificadas o rerun con verificación.
Un incidente de fuentes alucinadas casi nunca parece un fallo ruidoso. Es una pérdida de confianza silenciosa que normalmente se descubre solo al revisar fuentes. Por eso los agentes de producción necesitan no solo buenas respuestas, sino disciplina estricta de citas.
Páginas relacionadas
Si este problema aparece en producción, también conviene revisar:
- Por qué fallan los agentes de IA - mapa general de fallos en producción.
- Context poisoning - cómo el contexto problemático empuja al agente a conclusiones erróneas.
- Tool failure - cómo herramientas inestables rompen la cadena de evidence.
- Agent Runtime - dónde aplicar structured output verification y stop reasons.
- Tool Execution Layer - dónde recolectar snapshots y validar fuentes.
- Memory Layer - dónde mantener la reproducibilidad de evidence entre runs.