Cursos/Claude Code + IA Local/Web app tipo Lexia

Crea una web app tipo Lexia con RAG local

En esta lección vas a construir el patrón técnico de Lexia: una aplicación web que no se limita a chatear, sino que consulta una biblioteca propia, recupera fuentes relevantes y genera respuestas citadas con un modelo local compatible con OpenAI.

Objetivos de aprendizaje
  • Separar interfaz, API, corpus, embeddings y modelo generativo.
  • Crear una arquitectura RAG local reutilizable para distintos dominios.
  • Diseñar prompts que obliguen a responder con fuentes y límites claros.
  • Preparar la app para una demo local y para publicar la parte web en Vercel.
En cristiano: una app tipo Lexia. Lexia es una IA jurídico-administrativa local. Lo importante no es copiar el dominio legal, sino su arquitectura: el usuario pregunta, la app busca en documentos controlados, entrega fragmentos al modelo y exige una respuesta verificable con citas.

El mapa de la aplicación

Antes de programar, mira el sistema como una cadena de piezas pequeñas:

Terminal
usuario -> interfaz web -> API -> recuperador RAG -> fuentes -> prompt con reglas -> LLM -> respuesta citada
Idea clave. Una app de IA fiable no nace de un prompt largo. Nace de controlar qué documentos entran, qué fragmentos se recuperan, qué reglas recibe el modelo y qué se muestra al usuario.

Qué vamos a construir

El proyecto final tendrá estas piezas:

  • Una interfaz web con caja de consulta, respuesta, fuentes e historial.
  • Un backend Node.js con endpoints para consultar, buscar y ver el estado del índice.
  • Una carpeta de corpus con documentos fragmentados.
  • Embeddings locales generados con LM Studio, Ollama o un servidor OpenAI-compatible.
  • Recuperación semántica y búsqueda por términos.
  • Un prompt de sistema que exige citas y abstención cuando faltan fuentes.
Repositorio de referencia
Puedes estudiar el patrón completo en github.com/raym33/lexia. En esta lección lo usamos como inspiración práctica: public/ para la interfaz, server.mjs para la API, ingest.mjs para preparar corpus y embed.mjs para generar embeddings.

Paso 1: define tu dominio

No empieces por el código. Empieza por el producto. Rellena esta ficha:

Terminal
Nombre de la app:
Usuario:
Problema:
Fuentes:
Tono:
Formato de respuesta:
Límites:

Ejemplo para una versión educativa:

Terminal
Nombre: EduLexia
Usuario: profesores y alumnos de FP
Problema: resolver dudas sobre materiales del curso
Fuentes: apuntes, rubricas, programaciones y ejercicios
Tono: claro, práctico y pedagógico
Formato: respuesta breve, pasos, fuentes y tarea sugerida
Límites: no inventar temario ni calificaciones
Cuidado. Si tu dominio toca salud, derecho, fiscalidad, psicología, finanzas o datos personales, añade desde el principio una regla de revisión humana. RAG reduce errores, pero no convierte la app en autoridad profesional ni sustituye asesoramiento profesional.

Paso 2: crea la base del proyecto

Para aprender el patrón desde Lexia, clona el repositorio y arráncalo en local:

Terminal
git clone https://github.com/raym33/lexia.git
cd lexia
cp .env.example .env
npm start

Variables clave de configuración:

Terminal
PORT=5174
LM_BASE=http://127.0.0.1:1234/v1
CHAT_MODEL=gemma-3-12b-it
EMBED_MODEL=bge-m3
USE_RERANK=0
TOP_K=6
Comprueba que funciona. Abre http://localhost:5174. Si la app carga pero el índice aparece vacío, no pasa nada: todavía falta corpus y embeddings.

Paso 3: conecta un runtime local de modelos

En LM Studio, carga un modelo de chat y un modelo de embeddings. Activa el servidor local compatible con OpenAI en http://127.0.0.1:1234/v1.

Terminal
curl http://127.0.0.1:1234/v1/models

Prueba embeddings:

Terminal
curl -s http://127.0.0.1:1234/v1/embeddings \
  -H 'content-type: application/json' \
  -d '{"model":"bge-m3","input":["prueba de embeddings"]}'
En cristiano: embeddings. Un embedding convierte texto en una lista de números. Textos con significado parecido quedan cerca. Gracias a eso, el sistema encuentra fragmentos útiles aunque el usuario no use las mismas palabras del documento.

Paso 4: prepara tu biblioteca documental

Para un primer prototipo, usa un corpus pequeño en JSON. Lo importante es que cada fragmento tenga texto y cita.

Terminal
corpus/
  documentos.json
Terminal
[
  {
    "id": "doc-001#p1",
    "titulo": "Manual del curso",
    "cita": "Manual del curso, apartado 1",
    "texto": "Contenido del fragmento...",
    "materia": "curso"
  }
]
Idea clave. Un buen fragmento contiene una idea completa. Como regla práctica: entre 500 y 1.500 caracteres, con metadatos suficientes para citarlo.

Paso 5: genera el índice de embeddings

Terminal
npm run embed

Ficheros esperados:

Terminal
data/embeddings.bin
data/embeddings.ids.txt
data/embeddings.meta.json
Cuidado. Si cambias el modelo de embeddings, borra o aparta los embeddings anteriores y vuelve a generarlos. Mezclar vectores de modelos distintos rompe la recuperación.

Paso 6: construye la API de consulta

La ruta central de una app tipo Lexia es una consulta RAG:

Terminal
POST /api/consulta
Terminal
async function consulta(pregunta) {
  const fuentes = await retrieve(pregunta, 6);
  const prompt = buildPrompt(pregunta, fuentes);
  const respuesta = await chat(prompt);
  return { respuesta, fuentes };
}
Prompt base del sistema
Eres un asistente experto. Responde SOLO con la información de las fuentes. Si las fuentes no bastan, dilo claramente. Cada afirmación importante debe incluir una cita como [1] o [2]. No inventes articulos, cifras, pasos ni requisitos. Termina con una sección "Siguiente paso recomendado".

Paso 7: añade recuperación híbrida

Lexia combina búsqueda semántica y búsqueda por términos. Es una decisión importante porque los dominios técnicos tienen vocabulario exacto.

  • Embeddings: captan significado.
  • BM25 o búsqueda léxica: captura palabras exactas.
  • Pesos de autoridad: priorizan fuentes canónicas.
  • Reranker opcional: reordena candidatos cuando necesitas más precisión.
En cristiano: recuperación híbrida. Es como buscar en una biblioteca con dos estrategias a la vez: una persona entiende lo que quieres decir, la otra busca las palabras exactas en el índice. Juntas fallan menos.

Paso 8: diseña la interfaz

La interfaz mínima debe mostrar:

  • Caja de consulta.
  • Selector de modo: responder, redactar o buscar.
  • Respuesta principal.
  • Fuentes recuperadas con snippet.
  • Historial local.
  • Estado del índice y mensajes de error útiles.
Terminal
Recuperando fuentes...
Generando respuesta citada...
No hay indice disponible. Ejecuta npm run ingest y npm run embed.
El modelo local no responde. Revisa LM Studio.
Idea clave. Las fuentes deben estar visibles junto a la respuesta. Si las escondes, el usuario vuelve a confiar en el texto como si fuera magia. La gracia de Lexia es que se puede verificar.

Paso 9: guardrails responsables

Bloque de límites
Límites: - No diagnostiques. - No prometas resultados. - No inventes fuentes. - Si hay riesgo o falta información, recomienda revisión humana. - Si la pregunta queda fuera del corpus, dilo y pide documentos adecuados.
Cuidado. No guardes consultas sensibles en logs durante la demo. No subas a Git data/users.json, data/embeddings.*, .env ni documentos privados.

Paso 10: evalúa antes de publicar

Crea cinco preguntas conocidas y anota qué fuente debería aparecer. No evalúes solo si la respuesta suena bien.

Terminal
[
  {
    "pregunta": "Que plazo tengo para entregar la actividad?",
    "fuente_esperada": "rubrica-entrega#p2"
  },
  {
    "pregunta": "Como se califica el proyecto final?",
    "fuente_esperada": "rubrica-final#p1"
  }
]

Métricas sencillas:

  • Hit@3: la fuente esperada aparece entre las tres primeras.
  • Hit@6: aparece entre las seis primeras.
  • Respuesta citada: la respuesta incluye referencias.
  • Abstención correcta: si no hay fuente, la app lo reconoce.
Comprueba que funciona. Prueba una pregunta fácil, una ambigua, una fuera del corpus, una que pida inventar y otra que solicite consejo profesional definitivo. La app debe responder con matices distintos.

Paso 11: preparar para Vercel

Vercel es perfecto para publicar la parte web y el contenido educativo. Para una app RAG con modelos locales, recuerda esta separación:

  • La web estática o Next.js puede desplegarse en Vercel.
  • El modelo local y los embeddings viven en tu máquina o servidor privado.
  • Si necesitas una demo pública, usa datos de ejemplo y un backend remoto controlado.
Terminal
npm run build
vercel deploy
Arquitectura recomendada para producción
Separa la app web, el servicio RAG, el runtime de modelos, el almacenamiento del corpus y la observabilidad. Documenta versión de modelo, versión de corpus y fecha de generación de embeddings.
Fuentes y repositorios para contrastar
Revisa el repositorio de referencia raym33/lexia, la documentación oficial de LM Studio, Ollama, Qdrant y OWASP Top 10 for LLM Applications antes de usar este patrón con datos reales.
Guardar y reabrir el proyecto.
Tu proyecto queda como una plantilla reutilizable. Para otro dominio, cambia corpus, prompt, textos de interfaz, criterios de evaluación y guardrails. Mantén intacta la disciplina: fuentes visibles, respuestas citadas y revisión humana.

Reto final

Crea una mini Lexia para un dominio propio:

  1. Elige un dominio.
  2. Prepara 15 fragmentos de corpus sin datos sensibles.
  3. Configura modelo local y embeddings.
  4. Crea cinco preguntas de evaluación.
  5. Personaliza el prompt.
  6. Graba una demo de dos minutos mostrando respuesta y fuentes.