Un agent que alucina durante la ejecucion es catastrofico: podria fabricar parametros de API, inventar confirmaciones de exito tras fallos, o ejecutar acciones basadas en creencias falsas.
Este articulo explora como el Retrieval-Augmented Generation (RAG) tradicional provoca que los AI agents alucinen estadisticas y agregaciones, en contraste con GraphRAG usando knowledge graphs de Neo4j. Demostramos las diferencias a traves de un agent de reservas de viajes que compara RAG (FAISS) frente a GraphRAG sobre 300 documentos FAQ de hoteles.
Esta demo utiliza Strands Agents. Patrones similares se pueden aplicar en LangGraph, AutoGen u otros frameworks de agents.
Vision General de la Serie
Esta es la Parte 1 de una serie sobre como detener las hallucinations en AI agents:
- RAG vs Graph-RAG (este articulo) — Los knowledge graphs previenen agregaciones alucinadas
- Semantic Tool Selection — El filtrado vectorial reduce las elecciones incorrectas de tools
- Multi-Agent Validation — Los swarms detectan hallucinations antes de que lleguen a los usuarios
- AI Agent Guardrails — Reglas simbolicas que el LLM no puede eludir
- Runtime Guardrails — Dirigir en lugar de bloquear
El Problema: Tres Tipos de Hallucinations en RAG
La investigacion (MetaRAG: Metamorphic Testing for Hallucination Detection) identifica tres tipos de hallucination en sistemas RAG:
- Estadisticas fabricadas — Los sistemas RAG producen numeros estimados en lugar de valores calculados. El LLM adivina agregaciones a partir de unos pocos documentos recuperados en vez de calcularlas sobre el conjunto completo de datos.
- Recuperacion incompleta — La busqueda vectorial recupera los top-k fragmentos mas similares, pero los datos necesarios para una respuesta pueden estar dispersos en muchos documentos. El LLM llena los vacios con fabricaciones que suenan plausibles.
- Fabricacion fuera de dominio — Cuando no existen datos relevantes, RAG devuelve los resultados de apariencia mas similar de todos modos. El LLM trata estas coincidencias parciales como contexto valido y fabrica una respuesta con confianza.
La Solucion: GraphRAG con Knowledge Graphs
Los knowledge graphs resuelven estos problemas a nivel arquitectonico:
- Las agregaciones son calculadas por el motor de base de datos (AVG, COUNT, SUM) — no adivinadas por el LLM
- Las relaciones son aristas explicitas en el grafo — no inferidas por similitud de texto
- Los datos faltantes devuelven resultados vacios — el LLM recibe "no se encontraron resultados" en lugar de coincidencias parciales de las cuales alucinar
La diferencia clave: RAG siempre devuelve algo. GraphRAG devuelve resultados vacios cuando los datos no existen.
Requisitos Previos
- Python 3.9+
- Neo4j ejecutandose localmente con el plugin APOC
- Clave de API de OpenAI (o cualquier proveedor de modelos compatible con Strands)
git clone https://github.com/aws-samples/sample-why-agents-fail
cd stop-ai-agent-hallucinations/01-faq-graphrag-demo
uv venv && uv pip install -r requirements.txt
Implementacion
Agent 1: RAG Tradicional (FAISS)
from strands import Agent, tool
@tool
def search_faqs(query: str) -> str:
"""Search hotel FAQs using vector similarity."""
query_embedding = embed_model.encode([query])
distances, indices = index.search(query_embedding.astype('float32'), 3)
return "\n".join([documents[idx]['text'][:500] for idx in indices[0]])
rag_agent = Agent(tools=[search_faqs], model=model)
El agent RAG recupera los 3 documentos mas similares y permite que el LLM los resuma. No puede agregar sobre el conjunto completo de datos, recorrer relaciones ni reconocer cuando los datos estan ausentes.
Agent 2: GraphRAG (Neo4j + Cypher)
@tool
def query_knowledge_graph(cypher_query: str) -> str:
"""Execute a Cypher query against the hotel knowledge graph.
Node labels: Hotel, Room, Amenity, Policy, Service
Relationships: (Hotel)-[:HAS_ROOM]->(Room), (Hotel)-[:OFFERS_AMENITY]->(Amenity),
(Hotel)-[:HAS_POLICY]->(Policy), (Hotel)-[:PROVIDES_SERVICE]->(Service)
Properties:
- Hotel: name, location, rating, price_range
- Room: type, capacity, price_per_night
- Amenity: name, category
"""
with driver.session() as session:
result = session.run(cypher_query)
records = list(result)
if not records:
return "No results found."
return f"Found {len(records)} results:\n" + "\n".join(str(dict(r.items())) for r in records[:15])
graph_agent = Agent(tools=[query_knowledge_graph], model=model)
El schema en el docstring habilita Text2Cypher — el LLM genera consultas Cypher precisas en lugar de adivinar a partir de fragmentos de texto.
Resultados: 4 Escenarios de Prueba
Prueba 1: Agregacion — "What is the average hotel rating in Paris?"
| Enfoque | Resultado | Por que |
|---|---|---|
| RAG | 4.7 (de solo 2 documentos) | Recupero 2 documentos similares y promedio esas calificaciones |
| GraphRAG | 4.7 (a nivel de base de datos) ✅ |
MATCH (h:Hotel {location:'Paris'}) RETURN avg(h.rating) — calculado sobre todos los hoteles |
RAG tuvo suerte aqui — mismo numero, pero de 2 documentos en vez del conjunto completo de datos. Con datos diferentes, esto divergiria.
Prueba 2: Conteo — "How many hotels have a swimming pool?"
| Enfoque | Resultado | Por que |
|---|---|---|
| RAG | "No tengo los datos" ❌ | Los 3 documentos principales no mencionaban piscinas; no pudo contar entre 300 documentos |
| GraphRAG | "133 hoteles" ✅ | MATCH (h)-[:OFFERS_AMENITY]->(a {name:'pool'}) RETURN count(h) |
Prueba 3: Multi-hop — "What room types does the highest-rated hotel offer?"
| Enfoque | Resultado | Por que |
|---|---|---|
| RAG | Incompleto ⚠️ | Encontro informacion del hotel pero no pudo recorrer hasta los datos de habitaciones |
| GraphRAG | Completo con datos de habitaciones ✅ | MATCH (h:Hotel) WITH h ORDER BY h.rating DESC LIMIT 1 MATCH (h)-[:HAS_ROOM]->(r) RETURN r |
Prueba 4: Fuera de dominio — "Find hotels in Antarctica"
| Enfoque | Resultado | Por que |
|---|---|---|
| RAG | Fabrico "Estaciones de Investigacion" como hoteles ❌ | Devolvio documentos de apariencia similar sobre alojamiento en clima frio |
| GraphRAG | "No se encontraron hoteles" ✅ |
MATCH (h:Hotel {location:'Antarctica'}) RETURN h — resultado vacio |
Cuando Usar Cada Enfoque
| Criterio | RAG | GraphRAG |
|---|---|---|
| Estructura de datos | Texto no estructurado, documentos | Estructurado, con relaciones |
| Tipo de consulta | Similitud semantica, busqueda difusa | Agregaciones precisas, recorridos |
| Datos faltantes | Devuelve resultados similares (riesgo de hallucination) | Devuelve vacio (fallo honesto) |
| Complejidad de configuracion | Menor (embeddings + vector store) | Mayor (knowledge graph + schema) |
| Ideal para | Busqueda de contenido, Q&A sobre documentos | Analitica, razonamiento multi-hop, verificacion |
Conclusiones Clave
- RAG siempre devuelve algo — incluso cuando los datos no existen, creando oportunidades de hallucination
- GraphRAG devuelve resultados vacios cuando los datos estan ausentes — forzando respuestas honestas de "No lo se"
- Las agregaciones deben ser calculadas por la base de datos, no adivinadas por el LLM
- El schema en el docstring del tool habilita Text2Cypher — consultas precisas en lugar de similitud de texto
- Ambos enfoques tienen su lugar — usa RAG para busqueda semantica, GraphRAG para recuperacion precisa de datos
Siguiente Paso
GraphRAG previene estadisticas y agregaciones alucinadas. Pero los agents aun pueden seleccionar el tool incorrecto o desperdiciar tokens procesando descripciones de tools irrelevantes.
Parte 2: Semantic Tool Selection muestra como el filtrado vectorial reduce los errores de seleccion de tools y los costos de tokens.
Pruebalo Tu Mismo
git clone https://github.com/aws-samples/sample-why-agents-fail
cd stop-ai-agent-hallucinations/01-faq-graphrag-demo
uv venv && uv pip install -r requirements.txt
# Start Neo4j with APOC plugin first
uv run test_graphrag_hallucinations.py
Puedes cambiar a cualquier proveedor compatible con Strands — consulta Strands Model Providers para la configuracion.
Referencias
Investigacion
Strands Agents
Codigo
Gracias!
Top comments (0)