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.
| Lado | Modelo | Qué hace |
|---|---|---|
| Retrieval (RAG) | GTE_LARGE_FP16 | Embebe 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:
ragChunkparte un documento en ventanas de tokens con overlap.ragIngestescribe esos chunks en un workspace, embebiendo cada uno conGTE_LARGE_FP16.ragSearchembebe 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 elChatMessage[]que produjo el prompt builder y streamea texto como tokenscontentDelta.- 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.