Pylarion Logo Pylarion Logo Pylarion
DESARROLLO WEB
13 MIN READ

Construyendo una aplicación RAG end-to-end con Nuxt y Gemini File Search

Pylarion Pylarion

Pylarion

Equipo de Pylarion. Construimos software confiable donde la tecnología no puede fallar. • 18 de julio de 2026

Construyendo una aplicación RAG end-to-end con Nuxt y Gemini File Search

Construyendo una aplicación RAG end-to-end con Nuxt y Gemini File Search

La inteligencia artificial generativa ha evolucionado de forma vertiginosa en los últimos años, pero uno de sus talones de Aquiles históricos ha sido la alucinación contextual: la tendencia de los modelos a generar respuestas plausibles pero incorrectas cuando carecen de información específica sobre un dominio. La técnica conocida como Retrieval-Augmented Generation (RAG) surge precisamente como respuesta a esta limitación, permitiendo anclar las respuestas del modelo en documentos reales, indexados y verificables. En este tutorial exploraremos cómo implementar una solución RAG completa utilizando el framework Nuxt en su vertiente fullstack y la API Gemini File Search de Google, combinando backend, frontend y orquestación de IA en una sola arquitectura coherente.

¿Qué es RAG y por qué importa en aplicaciones modernas?

RAG, o Generación Aumentada por Recuperación, es un patrón arquitectónico que divide el proceso de respuesta de un modelo de lenguaje grande (LLM) en dos fases diferenciadas: primero, la recuperación de fragmentos relevantes desde una base de conocimiento previamente indexada; segundo, la generación de una respuesta que sintetiza tanto la consulta del usuario como la evidencia recuperada. Esto contrasta radicalmente con el enfoque de fine-tuning, que requiere reentrenar el modelo con datos propios, un proceso costoso en tiempo y recursos computacionales.

Para comprender la arquitectura RAG es indispensable manejar tres conceptos nucleares que estructuran todo el pipeline:

  • Chunk: Fragmento o trozo de texto extraído de un documento más extenso. El proceso de chunking divide los documentos en unidades manejables que pueden ser vectorizadas e indexadas de forma independiente, permitiendo recuperaciones más granulares y precisas.
  • Store: Depósito o almacén donde se persisten los embeddings vectoriales y los metadatos asociados a cada chunk. En el contexto de Gemini File Search, este componente es gestionado directamente por la API de Google, abstrayendo la complejidad del almacenamiento vectorial.
  • Tool: Herramienta que el modelo LLM puede invocar durante la generación para recuperar información del Store. Gemini expone el File Search como una tool nativa, lo que permite al modelo decidir autónomamente cuándo consultar la base de conocimiento.

La ventaja competitiva de RAG frente a otras aproximaciones radica en su dinamismo: es posible actualizar la base de conocimiento sin necesidad de modificar ni reentrenar el modelo base. Para aplicaciones empresariales que manejan documentación técnica, políticas internas o bases de datos de productos que cambian con frecuencia, esto representa un diferencial tecnológico de primer orden.

Configuración del entorno y estructura del proyecto Nuxt

Nuxt se posiciona como un framework fullstack por excelencia gracias a su sistema de server routes, que permite definir endpoints de API directamente dentro del mismo repositorio que gestiona la interfaz de usuario. Esta característica lo convierte en una opción idónea para proyectos RAG donde la lógica de orquestación con la API de Gemini debe residir en el servidor, evitando exponer claves de API al cliente.

El primer paso consiste en inicializar un proyecto Nuxt e instalar el SDK oficial de Google Generative AI:

# Crear un nuevo proyecto Nuxt
npx nuxi@latest init rag-nuxt-app
cd rag-nuxt-app

# Instalar el SDK de Google Generative AI
npm install @google/generative-ai

# Instalar dependencias adicionales
npm install @nuxtjs/tailwindcss

La gestión segura de la clave de API es un aspecto crítico que no debe subestimarse. Nuxt provee un mecanismo nativo a través del archivo nuxt.config.ts y las variables de entorno del servidor, garantizando que el token nunca sea serializado en el bundle del cliente:

// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    // Variables privadas - solo accesibles en el servidor
    geminiApiKey: process.env.GEMINI_API_KEY,
    // Variables públicas - expuestas al cliente (evitar datos sensibles)
    public: {
      appName: 'RAG Assistant'
    }
  }
})

El archivo .env en la raíz del proyecto almacenará la clave real durante el desarrollo, mientras que en producción esta variable deberá configurarse directamente en el entorno de despliegue (Vercel, Railway, Netlify, entre otros). Es imperativo incluir .env en el archivo .gitignore para prevenir filtraciones accidentales en repositorios públicos.

Implementación del backend: endpoints para gestionar el Store

El núcleo de la aplicación RAG reside en tres endpoints de servidor que orquestan las operaciones fundamentales del ciclo de vida del conocimiento. Nuxt estructura estos endpoints como archivos dentro del directorio server/api/, donde el nombre y la convención de directorio determinan la ruta HTTP resultante.

El primer endpoint se encarga de crear un nuevo depósito de búsqueda (corpus) en Gemini File Search. Este corpus actúa como contenedor lógico donde se agruparán todos los documentos relacionados con un dominio específico:

// server/api/corpus/create.post.ts
import { GoogleGenerativeAI } from '@google/generative-ai'

export default defineEventHandler(async (event) => {
  const config = useRuntimeConfig()
  const body = await readBody(event)

  const genAI = new GoogleGenerativeAI(config.geminiApiKey)
  
  // Inicializar el cliente de Semantic Retrieval
  const response = await $fetch(
    'https://generativelanguage.googleapis.com/v1beta/corpora',
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-goog-api-key': config.geminiApiKey
      },
      body: {
        display_name: body.displayName || 'Mi Base de Conocimiento'
      }
    }
  )

  return response
})

El segundo endpoint gestiona la indexación de documentos de texto plano. Recibe el contenido textual, lo fragmenta en chunks de tamaño apropiado y los persiste en el corpus previamente creado. La API de Gemini File Search automatiza el proceso de generación de embeddings, lo que simplifica considerablemente la implementación respecto a soluciones que requieren gestionar vectorstores de forma manual:

// server/api/corpus/[corpusId]/documents/index.post.ts
export default defineEventHandler(async (event) => {
  const config = useRuntimeConfig()
  const corpusId = getRouterParam(event, 'corpusId')
  const body = await readBody(event)

  // Crear documento dentro del corpus
  const document = await $fetch(
    `https://generativelanguage.googleapis.com/v1beta/${corpusId}/documents`,
    {
      method: 'POST',
      headers: { 'x-goog-api-key': config.geminiApiKey },
      body: {
        display_name: body.title,
        custom_metadata: [
          { key: 'source', string_value: body.source || 'manual_upload' }
        ]
      }
    }
  )

  // Indexar chunks del documento
  const chunks = splitIntoChunks(body.content, 500)
  const chunkRequests = chunks.map((text, index) => ({
    chunk: {
      data: { string_value: text },
      custom_metadata: [{ key: 'position', numeric_value: index }]
    }
  }))

  await $fetch(
    `https://generativelanguage.googleapis.com/v1beta/${document.name}/chunks:batchCreate`,
    {
      method: 'POST',
      headers: { 'x-goog-api-key': config.geminiApiKey },
      body: { requests: chunkRequests }
    }
  )

  return { success: true, documentName: document.name, chunksIndexed: chunks.length }
})

function splitIntoChunks(text: string, maxWords: number): string[] {
  const words = text.split(' ')
  const chunks: string[] = []
  for (let i = 0; i < words.length; i += maxWords) {
    chunks.push(words.slice(i, i + maxWords).join(' '))
  }
  return chunks
}

El tercer endpoint es el más sofisticado: el motor de consultas que combina la recuperación semántica con la generación de respuestas. Utiliza el modelo Gemini con la tool de retrieval habilitada, permitiendo al modelo consultar automáticamente el corpus antes de formular su respuesta:

// server/api/query.post.ts
import { GoogleGenerativeAI } from '@google/generative-ai'

export default defineEventHandler(async (event) => {
  const config = useRuntimeConfig()
  const body = await readBody(event)

  const genAI = new GoogleGenerativeAI(config.geminiApiKey)
  
  const model = genAI.getGenerativeModel({
    model: 'gemini-1.5-flash',
    tools: [
      {
        retrieval: {
          vertexAiSearch: undefined,
          googleSearchRetrieval: undefined,
          // Configurar corpus de Gemini Semantic Retrieval
        }
      }
    ]
  })

  // Configurar la tool de retrieval con el corpus específico
  const response = await $fetch(
    'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent',
    {
      method: 'POST',
      headers: { 'x-goog-api-key': config.geminiApiKey },
      body: {
        contents: [{ parts: [{ text: body.question }], role: 'user' }],
        tools: [{
          retrieval: {
            source: {
              semantic_retriever: {
                source: body.corpusName,
                query: { parts: [{ text: body.question }] },
                max_chunks_count: 5,
                minimum_relevance_score: 0.5
              }
            }
          }
        }]
      }
    }
  )

  return {
    answer: response.candidates[0].content.parts[0].text,
    groundingMetadata: response.candidates[0].groundingMetadata
  }
})

Construcción de la interfaz de usuario con Nuxt y Vue

La capa frontend de la aplicación RAG debe cumplir dos funcionalidades primarias: permitir la carga e indexación de documentos de texto plano, y proporcionar una interfaz de conversación donde el usuario pueda realizar preguntas sobre el corpus indexado. Nuxt facilita esta implementación a través de sus composables reactivos y la integración nativa con Vue 3.

El componente de carga de documentos implementa un área de texto donde el usuario puede pegar contenido directamente, junto con metadatos opcionales como título y fuente. La interacción asíncrona se gestiona mediante useFetch o $fetch, manteniendo retroalimentación visual del estado de indexación:

<!-- components/DocumentUploader.vue -->
<template>
  <div class="uploader-container">
    <h3>Indexar nuevo documento</h3>
    <input v-model="documentTitle" placeholder="Título del documento" />
    <textarea 
      v-model="documentContent" 
      placeholder="Pega el contenido de texto plano aquí..."
      rows="10"
    />
    <button @click="indexDocument" :disabled="isIndexing">
      {{ isIndexing ? 'Indexando...' : 'Indexar documento' }}
    </button>
    <p v-if="indexingResult">✓ Indexados {{ indexingResult.chunksIndexed }} chunks</p>
  </div>
</template>

<script setup lang="ts">
const props = defineProps<{ corpusId: string }>()
const documentTitle = ref('')
const documentContent = ref('')
const isIndexing = ref(false)
const indexingResult = ref(null)

async function indexDocument() {
  if (!documentContent.value.trim()) return
  isIndexing.value = true
  try {
    indexingResult.value = await $fetch(
      `/api/corpus/${props.corpusId}/documents`,
      {
        method: 'POST',
        body: {
          title: documentTitle.value,
          content: documentContent.value
        }
      }
    )
  } finally {
    isIndexing.value = false
  }
}
</script>

La interfaz de chat presenta los mensajes del usuario y las respuestas del asistente en tiempo real, con soporte para mostrar las fuentes de grounding que Gemini retorna como metadatos. Esta transparencia sobre qué fragmentos del corpus fundamentaron cada respuesta es un elemento diferenciador que genera confianza en el usuario final y facilita la auditoría de la información:

<!-- pages/chat.vue -->
<template>
  <div class="chat-interface">
    <div class="messages-container" ref="messagesContainer">
      <div 
        v-for="message in messages" 
        :key="message.id"
        :class="['message', message.role]"
      >
        <p>{{ message.content }}</p>
        <div v-if="message.sources?.length" class="sources">
          <small>Fuentes consultadas: {{ message.sources.join(', ') }}</small>
        </div>
      </div>
    </div>
    <div class="input-area">
      <input 
        v-model="currentQuestion" 
        @keyup.enter="sendQuestion"
        placeholder="Haz una pregunta sobre tus documentos..."
      />
      <button @click="sendQuestion" :disabled="isLoading">Enviar</button>
    </div>
  </div>
</template>

Flujo completo y consideraciones de producción

El flujo de datos en la aplicación RAG construida sigue una secuencia bien definida que garantiza tanto la fidelidad de las respuestas como la trazabilidad del proceso. Al recibir una pregunta del usuario, el endpoint de consulta envía la interrogante al modelo Gemini junto con la configuración de la tool de retrieval. El modelo evalúa si necesita consultar el corpus —decisión autónoma basada en el contenido de la pregunta— y de ser así, emite una búsqueda semántica que recupera los chunks más relevantes. Finalmente, genera una respuesta fundamentada en esa evidencia documental.

Para entornos de producción, es importante considerar los siguientes aspectos técnicos que impactan directamente en el rendimiento y la experiencia de usuario:

  1. Estrategia de chunking: El tamaño óptimo de los chunks depende del dominio. Documentos técnicos densos pueden requerir chunks más pequeños (200-300 palabras) para maximizar la precisión semántica, mientras que narrativas o documentos con contexto amplio se benefician de chunks de 500-800 palabras.
  2. Gestión de límites de la API: La API de Gemini impone límites de requests por minuto (RPM) y tokens por minuto (TPM). Implementar un sistema de cola con reintentos exponenciales es recomendable para aplicaciones con múltiples usuarios concurrentes.
  3. Caché de respuestas: Preguntas frecuentes con alta similitud semántica pueden beneficiarse de un sistema de caché en memoria (Redis) para reducir latencia y costos de API.
  4. Validación de contenido: Antes de indexar, es recomendable sanitizar el texto para eliminar caracteres especiales que puedan interferir con el proceso de embedding.
  5. Monitoreo del corpus: Implementar endpoints de gestión que permitan listar, actualizar y eliminar documentos del corpus para mantener la base de conocimiento actualizada.

"La arquitectura RAG no es simplemente una mejora incremental sobre los LLMs estándar; representa un cambio paradigmático en cómo las organizaciones pueden democratizar el acceso a su conocimiento institucional, combinando la fluidez lingüística de los modelos generativos con la precisión y auditabilidad de sistemas de recuperación estructurados."

La combinación de Nuxt como framework fullstack y Gemini File Search como motor de retrieval ofrece un equilibrio notable entre productividad de desarrollo y capacidad técnica. El desarrollador puede construir una aplicación RAG funcional en pocas horas sin necesidad de gestionar infraestructura vectorial propia, bases de datos especializadas como Pinecone o Weaviate, ni pipelines complejos de generación de embeddings. Esto democratiza significativamente el acceso a la tecnología RAG para equipos de desarrollo web que no cuentan con especialización en machine learning, abriendo la puerta a una nueva generación de aplicaciones web inteligentes fundamentadas en datos verificables y trazables.

0
Pylarion Pylarion

Pylarion

Desarrollo Web Expert

Equipo de Pylarion. Construimos software confiable donde la tecnología no puede fallar.

Blog / Desarrollo Web / Construyendo una aplicación RAG end-to-end con Nuxt y Gemini File Search

Comentarios

(0)
Categoría: Desarrollo Web

Artículos Recomendados

Creando un editor colaborativo con Nuxt UI, Jazz e IA
Desarrollo Web
3
0
0

Creando un editor colaborativo con Nuxt UI, Jazz e IA

El autor comparte su experiencia desarrollando un editor de texto enriquecido colaborativo en tiempo real utilizando Nuxt UI y Jazz, asistido en gran medida por inteligencia artificial. Inicialmente planeado como un tutorial paso a paso, el artículo evoluciona hacia una reflexión sobre cómo la IA facilita la programación, una práctica conocida como vibe coding. Se detalla la motivación de construir una solución propia dentro del ecosistema Nuxt para mantener el control completo del código, superando así las limitaciones de varias opciones prefabricadas. El proyecto resultante incluye características clave como soporte multiusuario, comentarios contextuales estilo Notion, carga de imágenes y resolución de respuestas. Además de proporcionar el código base de código abierto para que la comunidad lo clone, el autor ofrece importantes lecciones sobre el desarrollo guiado por agentes de IA, destacando la necesidad de forzarlos a verificar iterativamente su trabajo mediante el navegador.

Leer artículo
Construyendo una aplicación RAG con Nuxt y Gemini File Search
Desarrollo Web
5
0
0

Construyendo una aplicación RAG con Nuxt y Gemini File Search

Este artículo es un tutorial técnico paso a paso para construir una aplicación de Generación Aumentada por Recuperación (RAG) utilizando el framework Nuxt y la API Gemini File Search de Google. Explica cómo indexar documentos propios, recuperar el contexto más relevante en fragmentos y generar respuestas precisas y fundamentadas por inteligencia artificial. El proyecto abarca la creación de un backend en Nuxt con utilidades y endpoints para interactuar con Gemini, gestionar los procesos de indexación y solicitar información. También describe una interfaz de usuario completa para probar el flujo integrado, permitiendo cargar textos, visualizar almacenes de datos y leer los estados del sistema. Además, el contenido define conceptos esenciales sobre la tecnología RAG y detalla exactamente la configuración inicial del proyecto, la gestión de dependencias y el manejo de variables de entorno.

Leer artículo
Gestión de Estado en Vue: Composables, Provide/Inject y Pinia
Desarrollo Web
2
0
0

Gestión de Estado en Vue: Composables, Provide/Inject y Pinia

Esta guía práctica explica cómo elegir la herramienta adecuada para compartir estado y lógica en aplicaciones Vue.js, evitando arquitecturas complejas. Destaca tres enfoques clave: Los Composables, que utilizan la Composition API para encapsular y reutilizar comportamiento donde cada consumidor obtiene su propio estado independiente, ideales para lógica local como validaciones o fetch de datos. Provide/Inject resulta útil para pasar o inyectar estado a lo largo de un árbol de componentes descendente sin necesidad de recurrir al tedioso paso de props múltiples ('prop drilling'). Por último, Pinia es la solución estándar recomendada para gestionar el estado global y centralizado de la aplicación (como sesiones de usuario o datos del carrito de compras), ya que vive fuera de la jerarquía de los componentes y ofrece integración completa con Devtools.

Leer artículo