<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Maru EU</title>
    <description>The latest articles on DEV Community by Maru EU (@mariaeu).</description>
    <link>https://dev.to/mariaeu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3496981%2F9a1a6697-5f52-4105-8184-b697d975d9fa.jpg</url>
      <title>DEV Community: Maru EU</title>
      <link>https://dev.to/mariaeu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mariaeu"/>
    <language>en</language>
    <item>
      <title>Ayudando a otros... Día 1: Generador de Entrenamientos Garmin desde Train2Go: Híbrido Regex + LLM</title>
      <dc:creator>Maru EU</dc:creator>
      <pubDate>Wed, 11 Feb 2026 10:35:04 +0000</pubDate>
      <link>https://dev.to/mariaeu/ayudando-a-otros-dia-1-generador-de-entrenamientos-garmin-desde-train2go-hibrido-regex-llm-4po6</link>
      <guid>https://dev.to/mariaeu/ayudando-a-otros-dia-1-generador-de-entrenamientos-garmin-desde-train2go-hibrido-regex-llm-4po6</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykdkzs6ee39v9ns0ehmg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykdkzs6ee39v9ns0ehmg.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;He estado construyendo kaiord-helper, una herramienta que automatiza la generación de entrenamientos en formato Garmin Connect JSON desde Train2Go. Es un pequeño pero potente experimento sobre cómo combinar parsing tradicional (regex) con LLM para manejar la complejidad del lenguaje natural.&lt;br&gt;
Quería ayudar al repo de: github.com/pablo-albaladejo/kaiord&lt;/p&gt;

&lt;p&gt;El Problema&lt;br&gt;
Los entrenamientos en Train2Go se describen en prosa libre:&lt;/p&gt;

&lt;p&gt;10' z1 + 4x(3' Z4 + 2' Z1) + 5' CD&lt;br&gt;
Rodaje cómodo con cambios de ritmo: 2' fácil + 3x(30" rápido + 1'30" andando) + 3' CD&lt;br&gt;
Necesitaba convertir esto a JSON compatible con la API de Garmin Connect, que espera una estructura rígida (pasos ejecutables, grupos de repetición, targets de zona/pace, etc.).&lt;/p&gt;

&lt;p&gt;La Solución: Arquitectura Híbrida&lt;br&gt;
La herramienta usa dos estrategias en paralelo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parser Regex (por defecto)
Para casos simples y bien formados:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;// Reconoce: zonas (z1-z5), pace (5'40"), duración, repeticiones&lt;br&gt;
const stepPattern = /(\d+)&lt;a href="https://dev.to?:(d+)["&gt;':"&lt;/a&gt;?\s*([a-z0-9\s]*)/gi;&lt;br&gt;
Ventajas: Ultra rápido (&amp;lt;1ms), sin API keys, costo cero&lt;br&gt;
Limitación: Falla con variaciones naturales ("treinta segundos", "recuperación activa")&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Claude Haiku (fallback)
Cuando el regex falla, delegamos a Claude:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;const parsed = await parseWorkout(text, { fallbackToLLM: true });&lt;br&gt;
// Si regex falla → automáticamente intenta Claude&lt;br&gt;
Claude entiende contexto, variaciones de lenguaje y hasta lógica ("más rápido que la vez anterior").&lt;/p&gt;

&lt;p&gt;Ventajas: Robusto, flexible, natural&lt;br&gt;
Desventaja: ~$0.0001/workout, latencia ~500ms&lt;/p&gt;

&lt;p&gt;Stack Tecnológico&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "runtime": "Node.js",&lt;br&gt;
  "language": "TypeScript",&lt;br&gt;
  "apis": ["Train2Go", "Garmin Connect", "Anthropic (Claude)"],&lt;br&gt;
  "tools": ["Cheerio (web scraping)", "dotenv (config)"],&lt;br&gt;
  "architecture": "Hexagonal (Parser → Builder → Clients)"&lt;br&gt;
}&lt;br&gt;
Características Implementadas&lt;br&gt;
✅ Parsing flexible&lt;/p&gt;

&lt;p&gt;Zonas FC (z1-z5), pace, duración, repeticiones explícitas (4x) e implícitas&lt;br&gt;
Tipos de paso: warmup, cooldown, interval, recovery, rest&lt;br&gt;
Recuperaciones con modificadores (andando, activa)&lt;br&gt;
✅ Integración Train2Go&lt;/p&gt;

&lt;p&gt;Obtiene entrenamientos de 7 días automáticamente&lt;br&gt;
Extrae descripciones en español&lt;br&gt;
Maneja sesiones con cookies&lt;br&gt;
✅ Publicación en Garmin Connect&lt;/p&gt;

&lt;p&gt;Genera y publica directamente: --publish&lt;br&gt;
Validación de sesión antes de subir&lt;br&gt;
ID de workout como respuesta&lt;br&gt;
✅ Modo offline para testing&lt;/p&gt;

&lt;p&gt;npx tsx src/index.ts --text "10' z1 + 4x(3' Z4 + 2' Z1) + 5' CD"&lt;br&gt;
Lo que Aprendí&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;La Trampa del Regex Puro
Mantener patrones para todas las variaciones naturales es imposible. Un deportista escribe:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;"5 minutos en zona 1"&lt;br&gt;
"cinco minutos z1"&lt;br&gt;
"5' z1"&lt;br&gt;
"5:00 fácil"&lt;br&gt;
Cada una necesita un patrón. Termina siendo un caos.&lt;/p&gt;

&lt;p&gt;Lección: Conocer cuándo regex es insuficiente. Un LLM es más mantenible que 100 regex.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;APIs y Autenticación Real
Aprendí sobre cookies de sesión, CSRF tokens, y cómo mantener credenciales seguras:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Train2Go requiere PHPSESSID + XSRF-TOKEN&lt;br&gt;
Garmin Connect usa GARMIN_SESSIONID + connect-csrf-token&lt;br&gt;
Las sesiones expiran → necesitas renovar desde el navegador&lt;br&gt;
Lección: La autenticación en APIs reales es frágil. Documentar bien qué datos necesitas y por qué.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Conversión entre Formatos
El flujo es: texto → estructura intermedia → JSON Garmin&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;"10' z1 + 4x(3' Z4 + 2' Z1)"&lt;br&gt;
    ↓&lt;br&gt;
{ type: "single", intensity: "warmup", durationSeconds: 600, target: { zone: 1 } }&lt;br&gt;
{ type: "repeat", iterations: 4, steps: [...] }&lt;br&gt;
    ↓&lt;br&gt;
[&lt;br&gt;
  { type: "ExecutableStepDTO", stepType: { stepTypeKey: "warmup" }, ... },&lt;br&gt;
  { type: "RepeatGroupDTO", numberOfIterations: 4, workoutSteps: [...] }&lt;br&gt;
]&lt;br&gt;
Esta abstracción intermedia es crucial para mantener el código agnóstico del formato de salida.&lt;/p&gt;

&lt;p&gt;Lección: Cada capa debe tener responsabilidad única. El builder no debe parsear, el parser no debe conocer Garmin.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Costos vs Complejidad
Cada LLM call cuesta ~$0.0001, pero evita mantener regexes complejas. En 100 workouts: ~$0.01 de costo, pero 0 deuda técnica.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Arquitectura Actual (Simplified)&lt;/p&gt;

&lt;p&gt;src/&lt;br&gt;
├── index.ts              ← CLI orchestrator&lt;br&gt;
├── train2go/client.ts    ← Obtiene entrenamientos&lt;br&gt;
├── garmin/client.ts      ← Publica en Garmin&lt;br&gt;
├── parser/&lt;br&gt;
│   ├── workout-parser.ts ← Regex + LLM fallback&lt;br&gt;
│   ├── llm-fallback.ts   ← Claude wrapper&lt;br&gt;
│   ├── tokenizer.ts      ← Tokenización&lt;br&gt;
│   └── patterns.ts       ← Regex patterns&lt;br&gt;
├── builder/&lt;br&gt;
│   └── garmin-builder.ts ← Estructura JSON Garmin&lt;br&gt;
├── types.ts&lt;br&gt;
└── utils.ts              ← Conversiones (pace, duración)&lt;/p&gt;

&lt;p&gt;La Próxima Mejora (En Backlog) --&amp;gt; &lt;strong&gt;REFACTORIZAR&lt;/strong&gt;&lt;br&gt;
Aunque la arquitectura actual funciona, hay un problema de mantenibilidad, extensibilidad y escalabilidad:&lt;/p&gt;

&lt;p&gt;❌ Mantenibilidad (Hoy)&lt;br&gt;
El parser híbrido (regex + LLM) es complejo:&lt;/p&gt;

&lt;p&gt;El regex es frágil y necesita mantenimiento&lt;br&gt;
Debugging es difícil: ¿falló regex o LLM?&lt;br&gt;
Test coverage: necesitas casos para ambas ramas&lt;br&gt;
🎯 Mantenibilidad (Objetivo)&lt;br&gt;
Usar solo LLM, simplificar radicalmente:&lt;/p&gt;

&lt;p&gt;Eliminar regex y todos sus patterns&lt;br&gt;
Delegar 100% del parsing a Claude&lt;br&gt;
Costo adicional negligible (~$0.0001 más por call)&lt;br&gt;
Código 50% más pequeño, más fácil de mantener&lt;br&gt;
❌ Extensibilidad (Hoy)&lt;br&gt;
Solo soporta running. Otras modalidades requieren:&lt;/p&gt;

&lt;p&gt;Nuevos targets (potencia para ciclismo, brazo para natación)&lt;br&gt;
Nuevas intensidades y patrones&lt;br&gt;
Regexes separados para cada deporte&lt;br&gt;
🎯 Extensibilidad (Objetivo)&lt;br&gt;
Arquitectura agnóstica de deporte:&lt;/p&gt;

&lt;p&gt;Parser trabaja con cualquier descripción (training peak también las usa)&lt;br&gt;
Builder adapta a cualquier formato (Zwift ZWO, TCX, etc.)&lt;br&gt;
Deporte como parámetro, no hardcoding&lt;br&gt;
❌ Escalabilidad (Hoy)&lt;br&gt;
Solo Garmin Connect. Integrar Training Peaks requeriría:&lt;/p&gt;

&lt;p&gt;Nuevo cliente de API&lt;br&gt;
Nuevo builder (Training Peaks tiene schema diferente)&lt;br&gt;
Duplicar lógica&lt;br&gt;
🎯 Escalabilidad (Objetivo)&lt;br&gt;
Arquitectura de adaptadores:&lt;/p&gt;

&lt;p&gt;Core Parser + Builder reutilizable&lt;br&gt;
Adaptadores por plataforma (Garmin, Training Peaks, Zwift)&lt;br&gt;
CLI flexible que selecciona outputs&lt;br&gt;
Roadmap&lt;/p&gt;

&lt;p&gt;v1.0 (Actual)       → Hybrid parser (regex+LLM), Garmin only, Running only&lt;br&gt;
↓&lt;br&gt;
v2.0 (Próximo)      → Pure LLM parser, multi-sport, multi-output&lt;br&gt;
↓&lt;br&gt;
v3.0 (Futuro)       → Training Peaks integration, web UI, cloud sync&lt;br&gt;
Instalación y Uso&lt;/p&gt;

&lt;h1&gt;
  
  
  Setup
&lt;/h1&gt;

&lt;p&gt;npm install&lt;/p&gt;

&lt;h1&gt;
  
  
  Offline test
&lt;/h1&gt;

&lt;p&gt;npx tsx src/index.ts --text "10' z1 + 4x(3' Z4 + 2' Z1) + 5' CD"&lt;/p&gt;

&lt;h1&gt;
  
  
  Online mode (necesita TRAIN2GO_COOKIE)
&lt;/h1&gt;

&lt;p&gt;npx tsx src/index.ts --date "2026-02-12" --fallback&lt;/p&gt;

&lt;h1&gt;
  
  
  Con publicación (necesita GARMIN_COOKIE + GARMIN_CSRF_TOKEN)
&lt;/h1&gt;

&lt;p&gt;npx tsx src/index.ts --date "2026-02-12" --fallback --publish&lt;/p&gt;

&lt;p&gt;Reflexión Final&lt;br&gt;
Este proyecto me enseñó que la mejor solución no siempre es el patrón más "puro". Un regex perfecto no existe. Pero un LLM + una arquitectura clara sí.&lt;/p&gt;

&lt;p&gt;A veces es más smart usar tecnología para eliminar complejidad, no añadirla.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>llm</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Construyendo cosas... Día 1: Bot de nutrición deportiva con RAG</title>
      <dc:creator>Maru EU</dc:creator>
      <pubDate>Tue, 10 Feb 2026 10:28:11 +0000</pubDate>
      <link>https://dev.to/mariaeu/construyendo-cosas-dia-1-bot-de-nutricion-deportiva-con-rag-4nop</link>
      <guid>https://dev.to/mariaeu/construyendo-cosas-dia-1-bot-de-nutricion-deportiva-con-rag-4nop</guid>
      <description>&lt;p&gt;Acabo de terminar un proyecto en el que creé un nutricionista bot en Telegram usando &lt;strong&gt;RAG (Retrieval Augmented Generation)&lt;/strong&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  El Proyecto: Bot Nutricionista en Telegram
&lt;/h2&gt;

&lt;p&gt;Básicamente, creé un bot que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recopila datos del usuario (peso, altura, objetivo deportivo)&lt;/li&gt;
&lt;li&gt;Calcula calorías y macronutrientes personalizados&lt;/li&gt;
&lt;li&gt;Responde preguntas sobre nutrición usando una base de conocimiento&lt;/li&gt;
&lt;li&gt;Genera planes semanales y recomienda suplementos&lt;/li&gt;
&lt;li&gt;Mantiene todo seguro con guardrails contra prompt injection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suena simple, pero bajo el capó hay bastante arquitectura interesante.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stack Tecnológico
&lt;/h2&gt;

&lt;h3&gt;
  
  
  LLM y APIs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude 3.5 Haiku&lt;/strong&gt; (Anthropic) - Elegí Haiku por rapidez y costo, no perdía capacidades&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anthropic SDK&lt;/strong&gt; - Para llamadas a la API&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Vector Store y Búsqueda
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FAISS&lt;/strong&gt; - Para almacenar 990 chunks de documentos (guías de nutrición)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sentence-Transformers&lt;/strong&gt; (HuggingFace) - Embeddings multilingües en español&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CrossEncoder&lt;/strong&gt; - Reranking de resultados para mejorar relevancia&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Framework y Backend
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LangChain&lt;/strong&gt; - Orquestación del pipeline RAG&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flask&lt;/strong&gt; - Servidor para el webhook de Telegram&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQLite&lt;/strong&gt; - Base de datos con 6 tablas para perfiles y conversaciones&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Infraestructura
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt; - Containerización&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ngrok&lt;/strong&gt; - Para exponer el servidor local a Telegram&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Lo Que Aprendí (y lo más interesante)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. RAG No Es Solo Embeddings + LLM
&lt;/h3&gt;

&lt;p&gt;Al principio pensé: "Recupero chunks, los mando al modelo y listo". &lt;strong&gt;Spoiler: no listo&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;El problema: recuperar no es lo mismo que encontrar lo &lt;em&gt;realmente&lt;/em&gt; relevante. &lt;strong&gt;FAISS es rápido pero aproximado&lt;/strong&gt; - usa búsqueda por similitud coseno en vectores, que es como buscar con los ojos cerrados.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La solución: Reranking con CrossEncoder&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El CrossEncoder (&lt;code&gt;mmarco-mMiniLMv2-L12-H384-v1&lt;/code&gt;) evalúa &lt;em&gt;cada pareja (query, documento)&lt;/em&gt; de forma conjunta. Sí, es más lento, pero los resultados mejoraron un 169% en "Context Precision".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metrics que lo prueban&lt;/strong&gt; (evaluación RAGas):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Context Precision: 28% → 77% 📈&lt;/li&gt;
&lt;li&gt;Answer Relevancy: 40% → 85% 📈&lt;/li&gt;
&lt;li&gt;Faithfulness: 39% → 67% 📈&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Embeddings Multilingües Son Críticos
&lt;/h3&gt;

&lt;p&gt;Usé &lt;code&gt;paraphrase-multilingual-MiniLM-L12-v2&lt;/code&gt; porque mi base de conocimiento y usuarios son en español. El modelo por defecto en muchos tutoriales es inglés.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lección&lt;/strong&gt;: No ignores la dimensión lingüística de tu proyecto. Los modelos tienen sesgo hacia los datos en los que fueron entrenados.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Guardrails No Son Paranoia, Son Arquitectura
&lt;/h3&gt;

&lt;p&gt;Implementé 3 capas de validación:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Capa 1 - Entrada (pre-LLM)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Detectar prompt injection
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;detect_prompt_injection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No puedo procesar esa solicitud&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Detectar off-topic
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;is_off_topic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Eso está fuera de mi dominio de nutrición&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Capa 2 - System Prompt Reforzado&lt;/strong&gt;&lt;br&gt;
El system prompt de Claude incluye 6 reglas explícitas: solo nutrición, sin revelar info técnica, resistencia a jailbreaks, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Capa 3 - Salida (post-LLM)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Filtrar respuesta para no exponer info sensible
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;contains_sensitive_patterns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm_response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;generic_safe_response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;¿Por qué 3 capas? Porque cada capa falla de forma diferente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;La inyección de prompts puede pasar el filtro de entrada (lenguaje creativo)&lt;/li&gt;
&lt;li&gt;El LLM puede "olvidar" el system prompt (alucinaciones)&lt;/li&gt;
&lt;li&gt;La respuesta puede contener accidentalmente una ruta de archivo o SQL query&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Redundancia = resilencia.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. El Costo Real de los Modelos en Producción
&lt;/h3&gt;

&lt;p&gt;Comparé dos estrategias:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Opción A: Fine-tuning en GPU&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fine-tuning: $5-20 (barato ✓)&lt;/li&gt;
&lt;li&gt;Servir 24/7 en A100: $641/mes &lt;/li&gt;
&lt;li&gt;Total: ~$650/mes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Opción B: Claude Haiku API&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;100 queries/día (nuestro caso)&lt;/li&gt;
&lt;li&gt;Costo mensual: ~$4.80 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Diferencia: 135x más barato con API&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Además, fine-tuning hubiera requiere MLOps, versionado de modelos, pipeline de datos. Para un MVP, la complejidad operativa no vale la pena.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cuándo sí haría fine-tuning&lt;/strong&gt;: 50k+ queries/día, latencia crítica (&amp;lt;100ms), datos propietarios que no pueden salir de la infraestructura.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Las Herramientas Son Mejor Que Embeddings Especializados
&lt;/h3&gt;

&lt;p&gt;En lugar de usar embeddings específicos para "cálculo de macros" u "búsqueda de recetas", creé &lt;strong&gt;5 herramientas especializadas&lt;/strong&gt; que el agente invoca según la conversación:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;calcular_macros_objetivo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;formula_cientifica&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;buscar_recetas_por_deporte&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;json_lookup&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;generar_menu_diario&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rule_based&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;recomendar_suplementos&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;calculo_personalizado&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;calcular_ajuste_revision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;analisis_progreso&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El LLM decide cuál usar. Es más &lt;strong&gt;interpretable&lt;/strong&gt; (sé exactamente qué hace cada tool) y &lt;strong&gt;auditable&lt;/strong&gt; (puedo verificar los cálculos).&lt;/p&gt;

&lt;p&gt;Ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sin tools, todo sería "preguntale al LLM y espera a ver qué responde". Con tools, tengo garantías.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. SQLite Escala Mejor de Lo Que Pensaba
&lt;/h3&gt;

&lt;p&gt;Diseñé 6 tablas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;users&lt;/code&gt; - Info básica&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;user_profiles&lt;/code&gt; - Perfil nutricional&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;conversation_history&lt;/code&gt; - Chats&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;weekly_schedules&lt;/code&gt; - Planes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;user_revisions&lt;/code&gt; - Seguimiento de progreso&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onboarding_progress&lt;/code&gt; - Estado temporal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para producción a escala, obvio usaría PostgreSQL. Pero para un bot con 100 usuarios activos, SQLite es &lt;strong&gt;simple, suficiente y sin overhead operativo&lt;/strong&gt;. El archivo &lt;code&gt;.db&lt;/code&gt; cabe en un commit de Git.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resultados de Evaluación
&lt;/h2&gt;

&lt;p&gt;Corrí evaluación formal con &lt;strong&gt;RAGas&lt;/strong&gt; (framework para evaluar RAG):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Métrica&lt;/th&gt;
&lt;th&gt;Sin Mejoras&lt;/th&gt;
&lt;th&gt;Con Mejoras&lt;/th&gt;
&lt;th&gt;Delta&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Context Precision&lt;/td&gt;
&lt;td&gt;28.7%&lt;/td&gt;
&lt;td&gt;77.3%&lt;/td&gt;
&lt;td&gt;+169.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context Recall&lt;/td&gt;
&lt;td&gt;25.7%&lt;/td&gt;
&lt;td&gt;68.3%&lt;/td&gt;
&lt;td&gt;+166.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Answer Relevancy&lt;/td&gt;
&lt;td&gt;39.7%&lt;/td&gt;
&lt;td&gt;84.8%&lt;/td&gt;
&lt;td&gt;+113.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Faithfulness&lt;/td&gt;
&lt;td&gt;39.1%&lt;/td&gt;
&lt;td&gt;67.1%&lt;/td&gt;
&lt;td&gt;+71.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Qué cambió&lt;/strong&gt;: Pasé de embeddings en inglés sin reranking → embeddings multilingüe + CrossEncoder.&lt;/p&gt;

&lt;p&gt;Eso prueba que los detalles de implementación importan. No es "RAG genérico", es &lt;strong&gt;RAG bien hecho&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  3 Aprendizajes Clave Para Tu Próximo Proyecto RAG
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reranking es tuya mejor amiga&lt;/strong&gt;: Los embeddings recuperan candidatos rápido, pero el reranking elige los correctos. No lo saltes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Herramientas &amp;gt; Más contexto en el prompt&lt;/strong&gt;: 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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Validación en 3 capas no es paranoia&lt;/strong&gt;: Input → LLM → Output. Cada capa debe validar. Los guardrails son arquitectura, no un addon.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Stack Mínimo para un RAG Serio
&lt;/h2&gt;

&lt;p&gt;Si empezas hoy un proyecto RAG, aquí está la receta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Código y Docs
&lt;/h2&gt;

&lt;p&gt;Todo el proyecto está en GitHub con documentación completa:&lt;br&gt;
&lt;a href="https://github.com/mariaeugenia-alvarez/hands-on-coding-llm-aiengineering" rel="noopener noreferrer"&gt;hands-on-coding-llm-aiengineering&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Incluye:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arquitectura detallada&lt;/li&gt;
&lt;li&gt;Scripts para actualizar la knowledge base&lt;/li&gt;
&lt;li&gt;Evaluación RAGas con código&lt;/li&gt;
&lt;li&gt;Docker para deployar en 2 comandos&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final
&lt;/h2&gt;

&lt;p&gt;Este proyecto me enseñó que hacer un chatbot "que funciona" es fácil. Hacer uno que &lt;strong&gt;funciona bien&lt;/strong&gt; requiere diseño:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recuperación semántica correcta (RAG + reranking)&lt;/li&gt;
&lt;li&gt;Herramientas para lógica determinista&lt;/li&gt;
&lt;li&gt;Guardrails en múltiples capas&lt;/li&gt;
&lt;li&gt;Evaluación formal de resultados&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Espero que la experiencia sea útil si estás explorando RAG. Los números (Context Precision +169%) hablan solos.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Preguntas o sugerencias?&lt;/strong&gt; Déjalas en los comentarios, estaré encantada de ayudarte&lt;/p&gt;

&lt;h1&gt;
  
  
  RAG #AI #LLM #Claude #Python #Telegram
&lt;/h1&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>llm</category>
      <category>rag</category>
    </item>
    <item>
      <title>Creación de un TO-DO app con Python y Postgres SQL.</title>
      <dc:creator>Maru EU</dc:creator>
      <pubDate>Tue, 16 Sep 2025 09:43:16 +0000</pubDate>
      <link>https://dev.to/mariaeu/creacion-de-un-to-do-app-con-python-y-postgres-sql-3bhm</link>
      <guid>https://dev.to/mariaeu/creacion-de-un-to-do-app-con-python-y-postgres-sql-3bhm</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Crear un repositorio en gitHub y clonarlo.&lt;/li&gt;
&lt;li&gt;Crear los archivos de setup: README.md, .gitignore, requirements.txt, main.py&lt;/li&gt;
&lt;li&gt;Añadir funcionalidad de conexión y desconexión a la base de datos.&lt;/li&gt;
&lt;li&gt;Creación de una variable de entorno con python-dotenv: DATABASE_URL=postgresql://user:password@host&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
  </channel>
</rss>
