Implementación de RAG en el Frontend con Angular para Chat con PDFs

implementacion-rag-angular

Implementación de RAG (Retrieval-Augmented Generation) en el Frontend con Angular

Tiempo estimado de lectura: 5 min

  • Ideas clave:
  • RAG combina recuperación semántica (vector DB) y generación (LLM); en el frontend debe evitar exponer claves y delegar embeddings/search/generation a una BFF/Edge Function.
  • Flujo: preprocesado offline → indexación (vector DB) → consulta runtime a Edge Function → streaming del LLM al frontend.
  • Angular actúa como orquestador UI: JWT al BFF, consumo de ReadableStream, y Signals para estado y streaming.
  • Seguridad multi-tenant: RLS/metadata.filter y nunca incluir keys en el bundle cliente.

Implementación de RAG (Retrieval-Augmented Generation) en el Frontend con Angular: aquí verás un diseño pragmático y seguro para que tus usuarios “chateen” con PDFs y bases de conocimiento sin exponer claves privadas, y con una experiencia de streaming y baja latencia.

Resumen rápido (lectores con prisa)

RAG une un índice vectorial para recuperar contexto relevante con un LLM que genera respuestas. Úsalo cuando necesites respuestas ancladas en documentación privada. Importa porque permite precisión y control de costos. Funciona: preprocesado e indexado server-side, BFF/Edge Function para retrieval + prompt building + streaming, y frontend que consume el stream y mantiene estado con Signals.

Implementación de RAG (Retrieval-Augmented Generation) en el Frontend con Angular — resumen arquitectónico

RAG combina recuperación semántica (vector DB) y generación (LLM). En el frontend esto se traduce en tres responsabilidades claras:

  • Orquestar la UI y el estado reactivo.
  • Llamar a una capa segura (BFF / Edge Function) que haga embeddings, búsqueda y generación.
  • Mostrar la respuesta en streaming con Signals para una UX fluida.

Nunca pongas claves de OpenAI, Pinecone o Supabase en el bundle. Usa BFF/Edge Functions. Patrones y herramientas: Supabase (pgvector + Edge Functions + RLS), Pinecone, OpenAI Embeddings, SSE/Streams MDN, Angular Signals.

Paso a paso: flujo de datos y responsabilidades

Preprocesado (server-side, offline)

  • Extrae texto del PDF (pdfminer, tika, or pdf-lib).
  • Segmenta en chunks (200–1000 tokens según modelo).
  • Calcula embeddings y guarda vectores con metadata: { documentId, chunkId, text, userId }.
  • Upsert en la vector DB (Pinecone o Supabase pgvector).

Consulta desde Angular (runtime)

  • Usuario pregunta en la UI.
  • Angular envía la petición a la Edge Function (BFF) con el JWT del usuario.
  • La Edge Function:
    • a) crea embedding de la consulta,
    • b) hace búsqueda semántica filtrada por metadata (userId) en la vector DB,
    • c) construye prompt con los top-K chunks,
    • d) llama al LLM (streaming) y reenvía el stream al cliente.

Presentación (cliente)

  • Angular consume el stream y muestra la respuesta en tiempo real.
  • Signals mantiene conversación, estados y métricas.

Código práctico: servicio Angular para consumir stream RAG

Este es el patrón cliente: Angular delega todo a una URL segura y procesa un ReadableStream en Signals.

<!-- rag-client.service.ts -->
import { Injectable, signal } from '@angular/core';

export interface ChatMessage { role: 'user'|'assistant'; content: string; }

@Injectable({ providedIn: 'root' })
export class RagClientService {
  public convo = signal<ChatMessage[]>([]);
  public loading = signal(false);

  async ask(question: string, docId: string, token: string) {
    this.convo.update(c => [...c, { role: 'user', content: question }]);
    this.convo.update(c => [...c, { role: 'assistant', content: '' }]);
    this.loading.set(true);

    const res = await fetch(`/api/rag?doc=${docId}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
      body: JSON.stringify({ question })
    });

    if (!res.body) { this.loading.set(false); throw new Error('No stream'); }
    const reader = res.body.getReader();
    const dec = new TextDecoder();
    let done = false;

    while (!done) {
      const { value, done: rDone } = await reader.read();
      done = rDone;
      if (value) {
        const chunk = dec.decode(value, { stream: true });
        this.append(chunk);
      }
    }
    this.loading.set(false);
  }

  private append(text: string) {
    this.convo.update(c => {
      const last = [...c];
      const idx = last.length - 1;
      last[idx] = { ...last[idx], content: last[idx].content + text };
      return last;
    });
  }
}

Seguridad y multi-tenant: cómo proteger datos y consultas

  • Edge Functions (Supabase / Vercel) firman y validan JWT. Angular solo envía el token del usuario.
  • En Supabase usa Row Level Security (RLS) para que la consulta vectorial devuelva solo vectores del userId (https://supabase.com/docs).
  • En Pinecone añade metadata.filter (userId) en las queries y realiza autorización en tu backend.
  • Nunca aceptes keys “hosted” en el cliente; si necesitas BYOK (Bring Your Own Key), que el usuario lo suministre y limite permisos.

Buenas prácticas de diseño

  • Chunking y contexto: corta por sentencias y 200–500 tokens. Guarda overlap para preservar contexto.
  • Top-K + score threshold: recupera 3–8 chunks y descarta con score bajo para evitar ruido.
  • Fallback y control de costos: si la DB no devuelve contexto útil, responde con “No encontré información” en lugar de llamar al LLM.
  • Telemetría: registra latencia de retrieval vs generation, porcentaje de respuestas sin contexto.
  • UX: muestra progreso de retrieval y luego streaming del LLM; evita spinners largos.

Resumen y criterio

Implementación de RAG en el frontend con Angular no es “cliente habla con Pinecone”. Es dividir responsabilidades: preprocesado e indexación en backend, retrieval y prompt-building en BFF/Edge Function, y presentación + streaming en Angular. Esa separación protege claves, permite RLS/tenant isolation y ofrece una UX moderna con Signals y ReadableStreams.

Si quieres ejemplos de Edge Functions (Supabase) o plantillas para indexado de PDF, lo siguiente es lo lógico: un script server-side que extrae texto, chunkea, crea embeddings (OpenAI) y hace upsert a la vector DB. Cuando tengas ese bloque, el frontend es trivial: JWT + fetch streaming + Signals. Implementa eso y tendrás un “chat” con tus PDFs que realmente sirve en producción.

Para recursos citados en el artículo: Supabase (pgvector + Edge Functions + RLS), Pinecone, OpenAI Embeddings, SSE/Streams MDN, Angular Signals.

Continúa explorando plantillas y laboratorios técnicos en Dominicode Labs para ver implementaciones prácticas y scripts de indexado que complementan este flujo.

FAQ

¿Qué es RAG y cuándo debería usarlo?

RAG (Retrieval-Augmented Generation) combina una base de conocimiento indexada en vectores para recuperar contexto relevante y un LLM para generar respuestas. Úsalo cuando necesites respuestas ancladas en documentación privada, control de factualidad y reducción de costos frente a enviar todo el prompt al LLM.

¿Dónde deben vivír las claves de OpenAI / Pinecone?

Las claves deben residir en el backend (BFF o Edge Functions). Nunca las incluyas en el bundle cliente. El frontend solo envía el JWT del usuario y la Edge Function realiza llamadas a los servicios con las claves seguras.

¿Cómo evito fugas de datos entre tenants?

Usa Row Level Security (RLS) en Supabase o filtros de metadata (por ejemplo userId) en Pinecone y valida JWT en la Edge Function para asegurar que las queries devuelvan solo vectores autorizados.

¿Qué tamaño de chunk es recomendado?

Segmenta entre 200–1000 tokens según el modelo; una recomendación práctica es 200–500 tokens con overlap para preservar contexto y mantener relevancia.

¿Qué hacer si la vector DB no devuelve contexto útil?

Implementa un fallback: responde con “No encontré información” en lugar de llamar al LLM para evitar costos y respuestas potencialmente incorrectas sin contexto.

¿Cómo mostrar streaming en Angular?

Consume el ReadableStream desde fetch, lee chunks con un TextDecoder y actualiza el estado reactivo (Signals) a medida que llegan datos para una experiencia en tiempo real.

¿Qué telemetría es esencial?

Registra latencia de retrieval vs generation, porcentaje de respuestas sin contexto y métricas de coste por llamada al LLM para evaluar trade-offs y optimizaciones.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *