Portafolio · Parte de nutri.

Producto personal · nutri.

RAG que solo cita lo recuperado — búsqueda semántica y preguntas al diario de alimentos

Dos funciones de recuperación bajo una misma regla: el modelo nunca genera una cifra. Búsqueda híbrida con pgvector en el catálogo y RAG estructurado en tres pasos sobre el diario, verificado contra la base de datos hasta el tercer decimal.

RAG · pgvector · Búsqueda híbrida

La prioridad central de nutri. es la exactitud de los datos nutricionales almacenados, una condición que hereda cada función de IA. Estas dos se entregaron juntas: una capa semántica para buscar en el catálogo y «Pregunta a tu registro», un flujo de preguntas en lenguaje natural sobre el diario de alimentos. Ambas siguen la misma regla: el LLM decide qué consultar y cómo explicarlo, pero nunca es la fuente de una cifra.

Búsqueda semántica: palabras clave primero, vectores cuando aportan

La búsqueda usaba solo ilike: «fruta cítrica» no devolvía nada aunque el catálogo tuviera naranja, toronja y limón. Con 634 alimentos —490 creados por el usuario y una base compartida de 144 elementos de USDA—, el vocabulario de la persona dejó de coincidir con los nombres de las filas.

El diseño es deliberadamente conservador:

La carga inicial también es parte del caso: unos 4 MB de vectores nunca pasaron por el contexto del agente de IA. Dos RPC temporales con security definer —listar pendientes y aplicar un lote, con protección para no sobrescribir valores existentes— y un script local reanudable enviaron los 634 registros por HTTP, en lotes de 20 y con pausas de 65 segundos porque el nivel gratuito del servicio limita llamadas por minuto. El proceso tardó unos diez minutos y ambas RPC se eliminaron después.

Búsqueda semántica de nutri.: una consulta sobre algo dulce para desayunar devuelve avena, plátano, miel y cereal sin palabras en común
«Algo dulce para desayunar»: avena, plátano, miel y cereal, sin coincidencia literal de palabras.

Pruebas en vivo registradas: «fruta cítrica» → naranja, toronja y limón, sin coincidencias literales; «pescado azul» → atún, huachinango y salmón, incluido un resultado entre idiomas para «Canned tuna in water».

Pregunta a tu registro: RAG cuya recuperación es SQL

La solución obvia —convertir el diario en vectores y recuperar por similitud— habría sido peor en precisión y costo. Los datos del diario son tabulares y ya son exactos en las vistas SQL. Por eso el flujo usa RAG estructurado en tres pasos:

  1. Planificar. Un modelo convierte la pregunta en {date_from, date_to, nutrients[], need_detail} como salida estructurada. Las claves válidas de nutrientes se incorporan al prompt desde la única fuente de verdad de la app. Un saneador puro y probado corrige las posibles fallas del modelo: fechas faltantes → últimos 30 días, fechas futuras → hoy, rangos → máximo 92 días —se informa en la interfaz— y nutrientes desconocidos → se eliminan.
  2. Recuperar. Los totales diarios del rango vienen de las mismas vistas SQL que lee el panel; el detalle por entrada solo se consulta si el plan lo solicita; los objetivos se resuelven por día con la lógica existente. No se recalcula ni se estima nada.
  3. Responder. Las filas recuperadas se entregan como contexto compacto tipo CSV —hasta 400 entradas de mayor aporte calórico, con aviso si se alcanza el límite— y el modelo recibe la instrucción de usar exclusivamente cifras presentes en ese contexto, citar días y alimentos concretos, y describir sin prescribir.
Pregunta a tu registro de nutri.: respuesta sobre proteína semanal con cifras y alimentos de cada día, citando gramos
Cada cifra de la respuesta —gramos registrados, gramos de proteína y objetivo de 165 g— existe en el contexto recuperado.

Verificación hasta el tercer decimal

La afirmación «solo cita cifras recuperadas» se probó de punta a punta. Ante «¿qué alimentos me aportaron más sodio en las últimas dos semanas?», la respuesta citó «pan integral, 91 g → 409.5 mg» el 2 de julio. La vista de la base de datos indica 409.500. Las piezas puras del flujo —normalización de vectores, unión de resultados, saneamiento del plan y formato del contexto— tienen pruebas unitarias, 17 de las 186 de la suite. La hoja muestra un aviso fijo: Respuesta generada por IA; verifique en el panel. Las cifras reales están en el contexto, pero la prosa sigue viniendo de un modelo; la mitigación consiste en reconocerlo, no en fingir que el riesgo es cero.

Límites documentados

Los alimentos creados fuera de la app —por MCP o REST— no tienen embedding hasta que alguien los edita y guarda; mientras tanto, la búsqueda híbrida los encuentra por palabra clave. El límite de distancia acepta algo de ruido a cambio de coincidencias entre idiomas y es un parámetro de ejecución, no una migración. La hoja de preguntas no conserva una conversación: cada pregunta es independiente. Las tres son decisiones documentadas con posibles rutas de mejora.


El código está en el repositorio público github.com/vryahn/nutri: src/lib/ai.js y supabase/migrations/017_foods_embedding.sql. Contexto del sistema completo: nutri. — aplicación en producción con un entorno agéntico.

BR / CONTACTO

Hablemos

Para oportunidades profesionales y conversaciones sobre producto, pagos e IA aplicada.