Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01B38FGKKam5SCGgwgduVAh3
64 lines
4.0 KiB
Markdown
64 lines
4.0 KiB
Markdown
# 1. The shape of a RAG pipeline
|
|
|
|
[Index](../README.md) · next: [2. Chunking](02-chunking.md)
|
|
|
|
A language model only knows what it was trained on and what is in the prompt. Retrieval-augmented generation
|
|
(RAG) puts the right few paragraphs of *your* documents into the prompt, so the model can answer from them.
|
|
Everything else in this repository is a decision about which paragraphs, how they get there, and what to do
|
|
when the answer is not in them.
|
|
|
|
## Two phases, six stages
|
|
|
|
```
|
|
ingest (once per file) query (once per question)
|
|
────────────────────── ─────────────────────────
|
|
PDF ─► read pages ─► chunk ─► embed ─► store question ─► embed ─► search ─► rerank ─► prompt ─► model ─► check
|
|
IngestionService VectorStore └────── RetrievalAugmentationAdvisor ──────┘ RagQueryService
|
|
```
|
|
|
|
| Stage | Class here | Spring AI part it stands on |
|
|
|---|---|---|
|
|
| read | [`IngestionService`](../src/main/java/com/ankurm/rag/ingest/IngestionService.java) | `PagePdfDocumentReader` |
|
|
| chunk | `DocumentTransformer chunker` in [`RagConfig`](../src/main/java/com/ankurm/rag/config/RagConfig.java) | `TokenTextSplitter` (or [`RecursiveChunker`](../src/main/java/com/ankurm/rag/chunk/RecursiveChunker.java), [`SemanticChunker`](../src/main/java/com/ankurm/rag/chunk/SemanticChunker.java)) |
|
|
| embed + store | `VectorStore` bean | `PgVectorStore`, built by the pgvector starter |
|
|
| search | `retrievalAdvisor` bean | `VectorStoreDocumentRetriever` |
|
|
| rerank | [`LlmReranker`](../src/main/java/com/ankurm/rag/query/LlmReranker.java) | the `DocumentPostProcessor` hook (Spring AI ships no reranker) |
|
|
| prompt | `retrievalAdvisor` bean | `ContextualQueryAugmenter` |
|
|
| model + check | [`RagQueryService`](../src/main/java/com/ankurm/rag/query/RagQueryService.java) | `ChatClient`, `FactCheckingEvaluator` |
|
|
|
|
The beans as the application actually wires them are in [output 01](output/01-pipeline-beans.txt).
|
|
|
|
## The smallest thing that works
|
|
|
|
One advisor, no other class of ours:
|
|
|
|
```java
|
|
ChatClient.builder(chatModel)
|
|
.defaultAdvisors(QuestionAnswerAdvisor.builder(vectorStore)
|
|
.searchRequest(SearchRequest.builder().topK(2).similarityThreshold(0.3).build())
|
|
.build())
|
|
.build();
|
|
```
|
|
|
|
That is [`SimpleAdvisorTest`](../src/test/java/com/ankurm/rag/SimpleAdvisorTest.java). [Output 11](output/11-question-answer-advisor.txt)
|
|
shows the two prompts it produces. For a question the store can answer, the chunks are pasted between two lines of
|
|
dashes. For a question nothing matches, the prompt still has the same template with an **empty** block between the
|
|
dashes, and the model is left to follow the template's last sentence ("if the answer is not in the context, inform
|
|
the user that you can't answer"). Nothing in code decides that nothing was found. That is the first thing the
|
|
larger pipeline adds: see [chapter 4](04-retrieval-and-reranking.md).
|
|
|
|
## What this repository does not test
|
|
|
|
The chat model and the embedding model are scripted stand-ins
|
|
([`FakeChatModel`](../src/test/java/com/ankurm/rag/support/FakeChatModel.java),
|
|
[`HashingEmbeddingModel`](../src/test/java/com/ankurm/rag/support/HashingEmbeddingModel.java)). That is deliberate:
|
|
anyone can run every test with no API key, and what is asserted is what Spring AI *sends* and how this code
|
|
*handles* what comes back. It also means **nothing here shows how a real model answers, how a real embedding
|
|
model scores similarity, whether reranking improves answers, or how good a real judge is at spotting an
|
|
invented claim.** Similarity scores and thresholds in the outputs belong to the hashing model; do not carry them over.
|
|
|
|
The vector store is real: every test that touches persistence runs against PostgreSQL with pgvector
|
|
(`scripts/pg-up.sh` or `docker-compose.yml`), and the end-to-end test starts the whole application over HTTP.
|
|
|
|
Next: [2. Chunking](02-chunking.md)
|