Rubric
Contents — domains, guide and mocks

Designing a RAG pipeline

CCAR-P 3.513 min read · checked 21 September 2026

Task statementDesign a RAG pipeline with appropriate chunking and indexing strategies

The two halves of a RAG pipeline

  1. Split into chunkssize and boundaries follow the content
  2. Add chunk contextsay which document and section
  3. Index twiceembeddings + BM25, plus metadata
  4. Retrieve and fuseboth indexes, then deduplicate
  5. Rerank to top-Kbest chunks go to Claude
The first three steps run once when documents are ingested. The last two run on every query. Many quality problems start in the ingest half, even though they show up in the answers.

First decision: do you need RAG at all?

Retrieval adds moving parts: a chunker, an embedding model, a vector store, a keyword index, a reranker, and evals for each one. Anthropic’s Contextual Retrieval post says plainly that if a knowledge base is small enough (it suggests under about 200,000 tokens), you can put the whole thing in the prompt and skip RAG. Prompt caching makes that repeated prefix much faster and cheaper.

A design review should ask three questions before anything else. How big is the corpus, and how quickly is it growing? How often does it change? Do users ask for exact identifiers such as part numbers, clause numbers or error codes, or do they ask about concepts? The answers decide the architecture before chunk size comes up.

Choosing the retrieval architecture

What does the knowledge base look like?
  • Small, stable, fits comfortably
    Whole corpus in promptcache the prefix, no index
  • Large, conceptual questions
    Embedding indexwith contextualised chunks
  • Large, codes and exact terms
    Hybrid indexembeddings + BM25, fused
  • Changes hourly, lives in a system
    Query it live via a tooldon’t copy it to an index

Chunking: size, boundaries and self-containment

A chunk is the unit you retrieve, embed and eventually cite. Anthropic describes typical chunks as no more than a few hundred tokens, and lists chunk size, boundaries and overlap as choices that affect retrieval performance. There is no single right size. Small chunks give precise matches but lose surrounding meaning. Large chunks keep meaning, but their embedding blurs several topics together and each one uses more of the prompt.

StrategyCuts atGood forWatch out for
Fixed-sizeEvery N tokens, often with overlapUniform prose, quick first buildSplits tables, clauses and steps mid-thought
Structure-awareHeadings, sections, clauses, functionsPolicies, contracts, manuals, codeVery long sections need a second split
One record per chunkNatural record edgesFAQs, tickets, product entries, emailsVery short records carry little context
Parent–childSmall chunks for search, larger parent for the promptDense reference docsMore storage and a lookup step

Size matters less than whether a chunk still makes sense on its own. A clause that says “the Supplier shall pay the fee within 30 days” does not say which supplier, which contract or which fee. The embedding cannot contain information the text lacks. And BM25 cannot match a customer’s name that never appears in the chunk.

Contextual Retrieval: putting the context back

Anthropic’s fix is to prepend a short piece of context (usually 50–100 tokens) to each chunk before indexing it. Claude writes it, given the whole document and the chunk. The post’s example turns “The company’s revenue grew by 3% over the previous quarter” into a chunk that opens by naming ACME Corp’s Q2 2023 SEC filing and the previous quarter’s revenue. The same contextualised text goes into both the embedding index (Contextual Embeddings) and the keyword index (Contextual BM25).

Ingest step: situate each chunk in its documentpython
def contextualize(doc_text: str, chunk: str) -> str:
    response = client.messages.create(
        model=MODEL,
        max_tokens=200,
        # The whole document is the cached prefix, so every chunk
        # after the first reuses it instead of paying for it again.
        system=[{
            "type": "text",
            "text": f"<document>\n{doc_text}\n</document>",
            "cache_control": {"type": "ephemeral"},
        }],
        messages=[{"role": "user", "content": (
            f"<chunk>\n{chunk}\n</chunk>\n"
            "Give a short, succinct context that situates this chunk within "
            "the document, to improve search retrieval. Reply with the context only."
        )}],
    )
    context = response.content[0].text
    return f"{context}\n\n{chunk}"   # index THIS text in both indexes
Configuration (Anthropic’s tests)Retrieval failure rateReduction
Standard embeddings5.7%baseline
Contextual Embeddings3.7%35%
Contextual Embeddings + Contextual BM252.9%49%
Both, plus reranking1.9%67%

The failure rate there is 1 minus recall@20: the share of queries where a relevant chunk was not among the top 20 retrieved. Because the document is cached, the post puts the one-time cost of contextualising at about $1.02 per million document tokens. That is a one-off ingest cost, not a per-query cost, which is often the argument that wins a budget discussion.

Indexing: meaning and exact words

Embeddings capture meaning: “cancel my plan” lands near “terminate subscription”. They are weak on exact strings. The Contextual Retrieval post uses a support query for “Error code TS-999” as the case where BM25, which scores exact term matches, finds what embeddings miss. A hybrid index runs both and combines the ranked lists with a rank-fusion method (reciprocal rank fusion is a common choice), removing duplicates before the top-K go forward.

Embeddings-only versus hybrid indexing

Embeddings only

  • Finds paraphrases and related concepts
  • Often misses exact IDs like TS-999
  • Opaque ranking, hard to debug
  • One index to build and refresh

Hybrid: embeddings + BM25

  • Paraphrases and literal identifiers
  • Fused and deduplicated before rerank
  • Keyword hits are easy to explain
  • Two indexes kept in sync on every update

Anthropic does not offer its own embedding model. Its docs point to Voyage AI, which offers general models (the voyage-4 family), domain models for code, finance and law, and rerankers. Two details matter at design time. Embed documents with input_type="document" and queries with input_type="query". And weigh domain fit, latency at your scale, and whether the model can be customised to your vocabulary.

Documents and queries are embedded differentlypython
import voyageai
vo = voyageai.Client()                 # reads VOYAGE_API_KEY

doc_vecs = vo.embed(contextualized_chunks, model="voyage-4",
                    input_type="document").embeddings
query_vec = vo.embed([user_query], model="voyage-4",
                     input_type="query").embeddings[0]
# Voyage vectors are normalised, so a dot product is cosine similarity.

Store metadata alongside every chunk: document ID, title, section, version date, and the access-control attributes your permissions model needs. Metadata lets you filter before ranking (“only the current policy version”, “only documents this user may see”) and gives the generation step something to cite. Permission design itself is covered in 3.2.

Rerank, then hand chunks to Claude so they can be cited

Anthropic’s best configuration retrieved 150 candidates, scored each against the query with a reranking model, and passed the top 20 to Claude. Reranking adds some latency and cost per query. That is a trade-off to justify with numbers, and 3.3 covers it. Retrieving more chunks raises the chance the answer is present but costs more prompt, so tune K with an eval.

How you pass chunks to Claude is part of the design too. The Messages API has a search_result content block with a source, a title and text content. It can come back from a retrieval tool or be placed in a user turn. With citations enabled, Claude’s answer carries search_result_location citations pointing back to the exact result and block, parsed by the API rather than written as free text. Citations must be enabled on every search result in a request or on none.

Returning a retrieved chunk from your search toolpython
{
    "type": "tool_result",
    "tool_use_id": block.id,
    "content": [{
        "type": "search_result",
        "source": "kb://contracts/harlow-2024#clause-14.2",
        "title": "Harlow Logistics MSA, clause 14.2 (Termination)",
        "content": [{"type": "text", "text": chunk_text}],
        "citations": {"enabled": True},
    }],
}

Reviewing a proposed RAG design

  • Missing: Checked whether the corpus fits in contextdecide before building an index
  • Passes: Chunks cut on document structure
  • Fails: Each chunk says which document it came fromadd contextual prefix
  • Fails: Exact-match index for codes and namesadd BM25 and fuse
  • Check: Metadata for version and access filtersversion only, no ACLs
  • Missing: Recall@K eval separate from answer eval
Typical review of a first-draft design. Each fail or missing item is a stage-specific fix, not a reason to change models.

Traps the wrong answers are built from

Tempting but wrongDo this instead
Building a vector index for a corpus that fits comfortably in the promptLoad it whole with prompt caching, and use RAG only when size, churn or context rot justify it.
Chunking every document type the same way, every N tokensCut on the content’s own structure (clauses, headings, records) and keep each chunk self-contained.
Embedding bare chunks that never name their sourcePrepend generated context (document, entity, section) and index that text in both indexes.
Embeddings-only retrieval for users who search by code, SKU or nameAdd BM25 and fuse the two ranked lists before reranking.
Upgrading the answering model when the right chunk never reached itMeasure recall@K first; fix the stage that failed.

You should now be able to

  • Decide whether a knowledge base needs retrieval at all, or can be placed in a cached prompt.
  • Choose a chunking strategy that fits the structure of each document type.
  • Explain Contextual Embeddings and Contextual BM25, and what each adds over a standard pipeline.
  • Design a hybrid index with rank fusion, reranking and filterable metadata.
  • Pass retrieved chunks as search_result blocks so answers carry verifiable citations.
  • Separate retrieval metrics from generation metrics when diagnosing a RAG failure.

Practice questions

Original questions written for this lesson, in the exam’s style. Answer first, then open the reasoning — every option is explained, including why the wrong ones are tempting.

  1. Question 1

    A retailer’s product assistant answers “Is the TrailMax 44-B7 tent waterproof?” with details of a different tent. The product page for 44-B7 is in the corpus. The index is embeddings-only, built from 1,000-token fixed-size chunks.

    Which change most directly addresses the failure?

    1. AMove to a larger Claude model with stronger reasoning over product data.
    2. BDouble the chunk size so each chunk covers more of every product page.
    3. CAdd a BM25 index over the same chunks and fuse its results with the embeddings.
    4. DTell Claude in the system prompt to check product codes before answering.
    Show answer and reasoning
    1. AIncorrect. The right chunk never reaches the model, so a stronger model reasons over the wrong page just as confidently.
    2. BIncorrect. Bigger chunks blur embeddings further and don’t help the index match a literal model code.
    3. CCorrect. Exact identifiers such as 44-B7 are what keyword scoring matches and embeddings miss; fusion keeps the semantic matches too.
    4. DIncorrect. The prompt cannot make the model check a page that retrieval never supplied.
  2. Question 2

    An insurer’s claims handbook is 120,000 tokens, changes once a quarter, and every question may touch any section. A team proposes a vector database, a chunker and a reranker.

    What should the architect recommend first?

    1. ABuild the proposed pipeline, because RAG is the standard way to ground answers.
    2. BEvaluate loading the whole handbook into a cached prompt before building any index.
    3. CFine-tune a model on the handbook so it no longer needs the text at runtime.
    4. DSplit the handbook into 50-token chunks to maximise retrieval precision.
    Show answer and reasoning
    1. AIncorrect. RAG is a tool for corpora too big or too volatile to load. This one is neither, so it adds parts and failure points for no benefit.
    2. BCorrect. A small, stable corpus is the case Anthropic singles out for skipping RAG. Caching makes the repeated prefix cheap, and an eval confirms quality holds at that length.
    3. CIncorrect. Fine-tuning does not reliably make a model recall exact policy text, and a quarterly change would mean retraining each time.
    4. DIncorrect. Tiny chunks lose meaning and are still solving a retrieval problem that may not exist.
  3. Question 3

    A bank’s research assistant indexes analyst reports. Retrieval tests show chunks such as “Margins widened 40bps year on year” are found for the wrong company and period. The team wants the best improvement to recall without re-architecting.

    Which two changes should the architect prioritise? (Select 2.)

    1. APrepend generated context (company, report, period) to each chunk before embedding it.
    2. BIndex the same contextualised text in a BM25 index and fuse the results.
    3. CLower top-K from 20 to 5 so Claude sees less irrelevant text.
    4. DRaise the temperature so Claude considers more interpretations of each chunk.
    5. EAsk Claude to quote the relevant passage before answering.
    Show answer and reasoning
    1. ACorrect. This is Contextual Embeddings: the chunk now contains the entity and period the query mentions.
    2. BCorrect. Contextual BM25 lets literal company names and tickers match. In Anthropic’s tests, combining both cut failures further than embeddings alone.
    3. CIncorrect. Fewer chunks lowers the chance the right one is included, which makes a recall problem worse.
    4. DIncorrect. Sampling settings affect generation, not which chunks are retrieved.
    5. EIncorrect. Quoting helps the model use the chunks it has. It cannot help if the retrieved chunks are for the wrong company.
  4. Question 4

    Why does the Contextual Retrieval approach use prompt caching when generating chunk context?

    1. ACaching lets Claude remember earlier chunks, so each context line is consistent across the document.
    2. BThe full document is the same prefix for every chunk, so caching it makes the one-off ingest step cheap.
    3. CCached prompts are needed for BM25 indexing, which reads the cached tokens directly.
    4. DCaching raises the context window so larger documents can be contextualised.
    Show answer and reasoning
    1. AIncorrect. Caching does not change what the model knows or remembers. It reuses an identical prompt prefix to cut cost and latency.
    2. BCorrect. Each call sends the whole document plus one chunk. Caching the document is what brings the post’s cost to about $1.02 per million document tokens.
    3. CIncorrect. BM25 indexes the stored chunk text. It has no connection to the Claude prompt cache.
    4. DIncorrect. Caching does not change the context window size; it only reuses a prefix that is already processed.

Sources

Drafted with AI assistance and checked against the sources above; expert review is in progress. Spotted an error? Tell us and it gets fixed, dated and listed on how this is written.