RAG vs LLM
Dois trabalhos, dois modelos — o retrieval acha a evidência, o LLM escreve a resposta.
As duas letras em RAG referem-se a dois trabalhos distintos. Retrieval acha a evidência. Geração escreve a resposta. No LocalLens esses dois trabalhos estão divididos em dois modelos distintos, em módulos distintos, de propósito.
| Lado | Modelo | O que faz |
|---|---|---|
| Retrieval (RAG) | GTE_LARGE_FP16 | Embute documentos e a pergunta. Devolve os top-K chunks mais próximos. |
| Geração (LLM) | QWEN3_1_7B_INST_Q4 (fallback QWEN3_600M_INST_Q4) | Lê a pergunta + os chunks recuperados. Escreve a resposta com citações. |
O lado RAG é mecânico — similaridade vetorial no workspace, sem entendimento de linguagem. O lado LLM é onde acontecem o raciocínio e a prosa. Os chunks recuperados são evidência; a resposta é o que o LLM faz com essa evidência.
O retrieval acha a evidência
O retrieval é o trabalho da camada RAG do QVAC, conduzida pelo embedding model:
ragChunkdivide um documento em janelas de tokens com overlap.ragIngestescreve esses chunks num workspace, embutindo cada um comGTE_LARGE_FP16.ragSearchembute a pergunta com o mesmo modelo e devolve os top-K chunks mais próximos.
Retrieval não é uma resposta. É uma lista de excerpts que podem conter a resposta, rankeados por similaridade semântica com a pergunta. Um resultado de busca é útil mesmo quando nada deu match — o LLM pode dizer isso direto, em vez de inventar algo confiantemente errado.
O embedding model nunca lê histórico de chat, nunca produz texto, e nunca vê a resposta final. O único output dele são vetores.
A geração escreve a resposta
A geração é o trabalho do chat LLM. No LocalLens isso é o
QWEN3_1_7B_INST_Q4 por default, com QWEN3_600M_INST_Q4 como
fallback quando o 1.7B não consegue carregar.
completion()recebe oChatMessage[]que o prompt builder produziu e streama texto de volta como tokenscontentDelta.- O system prompt obriga o modelo a responder a partir dos excerpts recuperados e a citá-los entre colchetes.
- O modelo nunca toca o disco e nunca embute nada. Só lê a sua context window — regras do system, excerpts numerados, a pergunta.
Esse é o passo onde a resposta de fato existe. Tudo antes só produziu evidência e um prompt empacotado. Pula esse passo, e você tem um buscador, não um chat.
A separação é o que mantém o prompt builder pequeno e o QVAC
gateway limpo. O prompt builder não sabe nada de embeddings nem de
geração. O gateway tem os dois modelos mas expõe por dois métodos
independentes (search e answer).
A costura entre os dois
buildGroundedHistory(question, hits) é a costura. Pega a
pergunta e a lista de hits e produz um prompt onde:
- os excerpts vêm numerados (
[1],[2], …) pro LLM citar; - as regras do system são explícitas e curtas;
- existe exatamente um turno de usuário. Nenhum histórico conversacional é replicado, porque cada pergunta é tratada como uma rodada de retrieval nova.
Se o search não devolver nada útil, o prompt mesmo assim passa
pelo LLM, mas com "No matching chunks were found." no lugar dos
excerpts. A primeira regra do system prompt então entra em cena e
o LLM diz isso claramente:
Only use facts that appear in the excerpts. If the answer is not in them, say so plainly.
Essa é a estratégia anti-alucinação inteira. Dois modelos, uma regra de prompt, e funciona.
Por que não há histórico de chat?
O LocalLens trata cada pergunta como uma rodada de retrieval
independente. O retrieval é novo, o contexto do LLM é novo, e os
follow-ups não conseguem herdar meias-verdades de turnos
anteriores. Se você quer diálogo multi-turno, coloca acima do
buildGroundedHistory, não dentro.