LocalLens
Arquitectura

RAG vs LLM

Dos trabajos, dos modelos — el retrieval encuentra la evidencia, el LLM escribe la respuesta.

Las dos letras en RAG refieren a dos trabajos distintos. Retrieval encuentra evidencia. Generación escribe la respuesta. En LocalLens esos dos trabajos están separados en dos modelos distintos, en módulos distintos, a propósito.

LadoModeloQué hace
Retrieval (RAG)GTE_LARGE_FP16Embebe documentos y la pregunta. Devuelve los top-K chunks más cercanos.
Generación (LLM)QWEN3_1_7B_INST_Q4 (fallback QWEN3_600M_INST_Q4)Lee la pregunta + los chunks recuperados. Escribe la respuesta con citas.

El lado RAG es mecánico — similitud vectorial sobre el workspace, sin comprensión del lenguaje. El lado LLM es donde pasan el razonamiento y la prosa. Los chunks recuperados son evidencia; la respuesta es lo que el LLM hace con esa evidencia.

El retrieval encuentra la evidencia

El retrieval es el trabajo de la capa RAG de QVAC, manejado por el embedding model:

  • ragChunk parte un documento en ventanas de tokens con overlap.
  • ragIngest escribe esos chunks en un workspace, embebiendo cada uno con GTE_LARGE_FP16.
  • ragSearch embebe la pregunta con el mismo modelo y devuelve los top-K chunks más cercanos.

El retrieval no es una respuesta. Es una lista de excerpts que podrían contener la respuesta, rankeados por similitud semántica con la pregunta. Un resultado de búsqueda es útil incluso cuando nada hizo match — el LLM puede decirlo claramente en lugar de inventar algo que suene confiado.

El embedding model nunca lee historial de chat, nunca produce texto, y nunca ve la respuesta final. Su único output son vectores.

La generación escribe la respuesta

La generación es el trabajo del chat LLM. En LocalLens ese es QWEN3_1_7B_INST_Q4 por default, con QWEN3_600M_INST_Q4 como fallback cuando el 1.7B no logra cargar.

  • completion() recibe el ChatMessage[] que produjo el prompt builder y streamea texto como tokens contentDelta.
  • El system prompt obliga al modelo a responder desde los excerpts recuperados y a citarlos con corchetes.
  • El modelo nunca toca el disco y nunca embebe nada. Solo lee su context window — las reglas del system, los excerpts numerados, la pregunta.

Este es el paso donde la respuesta efectivamente existe. Todo lo anterior solo produjo evidencia y un prompt empaquetado. Sáltalo, y tienes un buscador, no un chat.

La separación es lo que mantiene chico al prompt builder y limpio al QVAC gateway. El prompt builder no sabe nada de embeddings ni de generación. El gateway tiene ambos modelos pero los expone por dos métodos independientes (search y answer).

La costura entre ambos

buildGroundedHistory(question, hits) es la costura. Toma una pregunta y una lista de hits y produce un prompt donde:

  • los excerpts están numerados ([1], [2], …) para que el LLM los pueda citar;
  • las reglas del system son explícitas y cortas;
  • hay exactamente un turno de usuario. No se reproduce historial conversacional, porque cada pregunta se trata como una ronda de retrieval fresca.

Si el search no devuelve nada útil, el prompt sigue pasando por el LLM, pero con "No matching chunks were found." en lugar de los excerpts. La primera regla del system prompt entonces se hace cargo y el LLM lo dice claramente:

Only use facts that appear in the excerpts. If the answer is not in them, say so plainly.

Esa es toda la estrategia anti-alucinación. Dos modelos, una regla de prompt, y funciona.

¿Por qué no hay historial de chat?

LocalLens trata cada pregunta como una ronda de retrieval independiente. El retrieval es fresco, el contexto del LLM es fresco, y los follow-ups no pueden heredar medias verdades de turnos previos. Si quieres diálogo multi-turno, agrégalo por encima de buildGroundedHistory, no dentro.

On this page