Acabo de terminar un proyecto en el que creé un nutricionista bot en Telegram usando RAG (Retrieval Augmented Generation) con Claude 3.5 Haiku. Quería compartir lo que aprendí en el camino, especialmente sobre cómo armar un sistema de IA que sea útil, seguro y escalable.
El Proyecto: Bot Nutricionista en Telegram
Básicamente, creé un bot que:
- Recopila datos del usuario (peso, altura, objetivo deportivo)
- Calcula calorías y macronutrientes personalizados
- Responde preguntas sobre nutrición usando una base de conocimiento
- Genera planes semanales y recomienda suplementos
- Mantiene todo seguro con guardrails contra prompt injection
Suena simple, pero bajo el capó hay bastante arquitectura interesante.
Stack Tecnológico
LLM y APIs
- Claude 3.5 Haiku (Anthropic) - Elegí Haiku por rapidez y costo, no perdía capacidades
- Anthropic SDK - Para llamadas a la API
Vector Store y Búsqueda
- FAISS - Para almacenar 990 chunks de documentos (guías de nutrición)
- Sentence-Transformers (HuggingFace) - Embeddings multilingües en español
- CrossEncoder - Reranking de resultados para mejorar relevancia
Framework y Backend
- LangChain - Orquestación del pipeline RAG
- Flask - Servidor para el webhook de Telegram
- SQLite - Base de datos con 6 tablas para perfiles y conversaciones
Infraestructura
- Docker - Containerización
- ngrok - Para exponer el servidor local a Telegram
Lo Que Aprendí (y lo más interesante)
1. RAG No Es Solo Embeddings + LLM
Al principio pensé: "Recupero chunks, los mando al modelo y listo". Spoiler: no listo.
El problema: recuperar no es lo mismo que encontrar lo realmente relevante. FAISS es rápido pero aproximado - usa búsqueda por similitud coseno en vectores, que es como buscar con los ojos cerrados.
La solución: Reranking con CrossEncoder
Flujo naive:
Query → Embedding → FAISS (búsqueda k-NN) → Top 3 chunks → LLM
Flujo mejorado:
Query → Embedding → FAISS (búsqueda k-NN) → Top 9 candidatos → CrossEncoder
→ Reranking por relevancia real → Top 3 chunks → LLM
El CrossEncoder (mmarco-mMiniLMv2-L12-H384-v1) evalúa cada pareja (query, documento) de forma conjunta. Sí, es más lento, pero los resultados mejoraron un 169% en "Context Precision".
Metrics que lo prueban (evaluación RAGas):
- Context Precision: 28% → 77% 📈
- Answer Relevancy: 40% → 85% 📈
- Faithfulness: 39% → 67% 📈
2. Embeddings Multilingües Son Críticos
Usé paraphrase-multilingual-MiniLM-L12-v2 porque mi base de conocimiento y usuarios son en español. El modelo por defecto en muchos tutoriales es inglés.
La diferencia es brutal. Cuando pedí "¿cuánta proteína necesito?" con embeddings en inglés, recuperaba documentos sobre tipos de prótidos en química. Con multilingüe, recuperaba guías de proteína para atletas.
Lección: No ignores la dimensión lingüística de tu proyecto. Los modelos tienen sesgo hacia los datos en los que fueron entrenados.
3. Guardrails No Son Paranoia, Son Arquitectura
Implementé 3 capas de validación:
Capa 1 - Entrada (pre-LLM)
# Detectar prompt injection
if detect_prompt_injection(user_message):
return "No puedo procesar esa solicitud"
# Detectar off-topic
if is_off_topic(user_message):
return "Eso está fuera de mi dominio de nutrición"
Capa 2 - System Prompt Reforzado
El system prompt de Claude incluye 6 reglas explícitas: solo nutrición, sin revelar info técnica, resistencia a jailbreaks, etc.
Capa 3 - Salida (post-LLM)
# Filtrar respuesta para no exponer info sensible
if contains_sensitive_patterns(llm_response):
return generic_safe_response()
¿Por qué 3 capas? Porque cada capa falla de forma diferente:
- La inyección de prompts puede pasar el filtro de entrada (lenguaje creativo)
- El LLM puede "olvidar" el system prompt (alucinaciones)
- La respuesta puede contener accidentalmente una ruta de archivo o SQL query
Redundancia = resilencia.
4. El Costo Real de los Modelos en Producción
Comparé dos estrategias:
Opción A: Fine-tuning en GPU
- Fine-tuning: $5-20 (barato ✓)
- Servir 24/7 en A100: $641/mes
- Total: ~$650/mes
Opción B: Claude Haiku API
- 100 queries/día (nuestro caso)
- Costo mensual: ~$4.80
Diferencia: 135x más barato con API.
Además, fine-tuning hubiera requiere MLOps, versionado de modelos, pipeline de datos. Para un MVP, la complejidad operativa no vale la pena.
Cuándo sí haría fine-tuning: 50k+ queries/día, latencia crítica (<100ms), datos propietarios que no pueden salir de la infraestructura.
5. Las Herramientas Son Mejor Que Embeddings Especializados
En lugar de usar embeddings específicos para "cálculo de macros" u "búsqueda de recetas", creé 5 herramientas especializadas que el agente invoca según la conversación:
tools = {
"calcular_macros_objetivo": formula_cientifica(),
"buscar_recetas_por_deporte": json_lookup(),
"generar_menu_diario": rule_based(),
"recomendar_suplementos": calculo_personalizado(),
"calcular_ajuste_revision": analisis_progreso()
}
El LLM decide cuál usar. Es más interpretable (sé exactamente qué hace cada tool) y auditable (puedo verificar los cálculos).
Ejemplo:
User: "Recomendame suplementos para crossfit"
↓
Agent decide: invoke_tool("recomendar_suplementos", deporte="crossfit")
↓
Tool devuelve JSON con dosis personalizadas (peso, edad, objetivo)
↓
Agent formatea respuesta natural
Sin tools, todo sería "preguntale al LLM y espera a ver qué responde". Con tools, tengo garantías.
6. SQLite Escala Mejor de Lo Que Pensaba
Diseñé 6 tablas:
-
users- Info básica -
user_profiles- Perfil nutricional -
conversation_history- Chats -
weekly_schedules- Planes -
user_revisions- Seguimiento de progreso -
onboarding_progress- Estado temporal
Para producción a escala, obvio usaría PostgreSQL. Pero para un bot con 100 usuarios activos, SQLite es simple, suficiente y sin overhead operativo. El archivo .db cabe en un commit de Git.
Resultados de Evaluación
Corrí evaluación formal con RAGas (framework para evaluar RAG):
| Métrica | Sin Mejoras | Con Mejoras | Delta |
|---|---|---|---|
| Context Precision | 28.7% | 77.3% | +169.7% |
| Context Recall | 25.7% | 68.3% | +166.0% |
| Answer Relevancy | 39.7% | 84.8% | +113.4% |
| Faithfulness | 39.1% | 67.1% | +71.5% |
Qué cambió: Pasé de embeddings en inglés sin reranking → embeddings multilingüe + CrossEncoder.
Eso prueba que los detalles de implementación importan. No es "RAG genérico", es RAG bien hecho.
3 Aprendizajes Clave Para Tu Próximo Proyecto RAG
Reranking es tuya mejor amiga: Los embeddings recuperan candidatos rápido, pero el reranking elige los correctos. No lo saltes.
Herramientas > Más contexto en el prompt: En lugar de meter toda la lógica de negocio en el prompt o en RAG, crea tools que el LLM invoque. Es más mantenible.
Validación en 3 capas no es paranoia: Input → LLM → Output. Cada capa debe validar. Los guardrails son arquitectura, no un addon.
Stack Mínimo para un RAG Serio
Si empezas hoy un proyecto RAG, aquí está la receta:
LLM: Claude (Anthropic) o GPT-4o
Vector Store: FAISS (local) o Pinecone (cloud)
Embeddings: Sentence-Transformers multilingües
Reranking: CrossEncoder (mismo repo que embeddings)
Framework: LangChain (abstracción decente)
DB: SQLite o PostgreSQL según escala
Backend: FastAPI (mejor que Flask para producción)
Código y Docs
Todo el proyecto está en GitHub con documentación completa:
hands-on-coding-llm-aiengineering
Incluye:
- Arquitectura detallada
- Scripts para actualizar la knowledge base
- Evaluación RAGas con código
- Docker para deployar en 2 comandos
Final
Este proyecto me enseñó que hacer un chatbot "que funciona" es fácil. Hacer uno que funciona bien requiere diseño:
- Recuperación semántica correcta (RAG + reranking)
- Herramientas para lógica determinista
- Guardrails en múltiples capas
- Evaluación formal de resultados
Espero que la experiencia sea útil si estás explorando RAG. Los números (Context Precision +169%) hablan solos.
¿Preguntas o sugerencias? Déjalas en los comentarios, estaré encantada de ayudarte
Top comments (0)