
{"id":197240,"date":"2026-07-13T07:57:57","date_gmt":"2026-07-13T07:57:57","guid":{"rendered":"https:\/\/mycryptomania.com\/?p=197240"},"modified":"2026-07-13T07:57:57","modified_gmt":"2026-07-13T07:57:57","slug":"contextual-retrieval-anthropics-method-for-cutting-rag-failures","status":"publish","type":"post","link":"https:\/\/mycryptomania.com\/?p=197240","title":{"rendered":"Contextual Retrieval: Anthropic\u2019s Method for Cutting RAG Failures"},"content":{"rendered":"<h3>Introduction<\/h3>\n<p>Welcome back!!! If you have been following my RAG series, you already know that chunking is where most retrieval pipelines quietly break. You split a document into neat little pieces, embed each one, store it in a vector database, and assume the system now understands your content. It does not. Each chunk sits alone, stripped of the paragraph before it and the paragraph after it, and that missing context is exactly where your RAG system starts\u00a0failing.<\/p>\n<p>In September 2024, Anthropic published a technique called Contextual Retrieval that tackles this problem directly. It is simple, it is cheap to run, and it cut retrieval failures by 49%, and by 67% when combined with reranking. That is not a small improvement. That is the difference between a RAG system users trust and one they quietly stop\u00a0using.<\/p>\n<p>Related article: <a href=\"https:\/\/medium.com\/gopenai\/rag-complete-tutorial-part-07-5ec9fb426614\">Chunking Strategies<\/a><\/p>\n<h3>The Real Problem With\u00a0Chunking<\/h3>\n<p>Say you are building a RAG system over a company\u2019s financial filings. One chunk\u00a0says:<\/p>\n<p>\u201cThe company\u2019s revenue grew by 3% over the previous quarter.\u201d<\/p>\n<p>Sounds fine until someone asks \u201cWhat was ACME Corp\u2019s Q2 2024 revenue growth?\u201d Your retriever has no idea this chunk is even about ACME Corp, let alone Q2 2024. The company name and the quarter were mentioned two paragraphs earlier, in a chunk that got split away during preprocessing. The embedding for this chunk carries none of that information, so it barely resembles the query, and it gets buried under chunks that are only superficially related.<\/p>\n<p>This is not a rare edge case. It is the default behavior of naive chunking, and it is the single biggest reason RAG systems return \u201cI could not find relevant information\u201d or, worse, confidently hallucinate an\u00a0answer.<\/p>\n<h3>What Contextual Retrieval Actually\u00a0Does<\/h3>\n<p>The fix Anthropic proposed is almost embarrassingly simple: before you embed or index a chunk, prepend a short piece of context that situates it within the full document. Instead of embedding<\/p>\n<p>\u201cThe company\u2019s revenue grew by 3% over the previous quarter.\u201d<\/p>\n<p>you embed<\/p>\n<p>\u201cThis chunk is from an SEC filing on ACME Corp\u2019s Q2 2024 performance. The previous quarter\u2019s revenue was $314 million. The company\u2019s revenue grew by 3% over the previous quarter.\u201d<\/p>\n<p>Now the chunk carries the company name, the time period, and the surrounding numbers, all inside the text that gets embedded and indexed. Both your vector search and your keyword search suddenly have something real to match\u00a0against.<\/p>\n<p>The two components that make this work\u00a0are:<\/p>\n<p><strong>Contextual Embeddings<\/strong> Every chunk gets a short, chunk specific context written by an LLM before embedding. This context is generated using the whole document as reference, so it captures things like the document\u2019s subject, the section it belongs to, and any entities or dates that got separated from the chunk during splitting.<\/p>\n<p><strong>Contextual BM25<\/strong> The same contextualized chunk is also indexed for keyword search using BM25. This matters because embeddings alone still miss exact matches like order numbers, product codes, or specific dates, the same limitation we covered in the keyword search article in this series. Contextual BM25 gives you that exact match capability on top of contextualized text.<\/p>\n<p>Run both together in a hybrid retriever, and you get chunks that are both semantically rich and precisely searchable.<\/p>\n<h3>Why This Works Better Than Alternatives<\/h3>\n<p>You might be thinking, why not just make chunks bigger so they carry more context naturally(That was my first thought too \ud83e\udd14).A few reasons this does not hold\u00a0up:<\/p>\n<p><strong>Bigger chunks dilute relevance.<\/strong> A 2000 token chunk covering five different topics gets a muddy embedding that does not represent any single topic well. Precision drops even as recall might improve slightly.<\/p>\n<p><strong>Bigger chunks blow up your context window.<\/strong> If you retrieve the top 10 chunks and each one is huge, you are burning tokens on irrelevant surrounding text instead of giving the LLM more distinct, relevant pieces of information.<\/p>\n<p><strong>Contextual retrieval keeps chunks small and precise, and adds context as metadata baked into the text itself<\/strong>, rather than solving the problem by brute force. You get the recall benefits of bigger chunks without the\u00a0noise.<\/p>\n<h3>Implementation, Step by\u00a0Step<\/h3>\n<p>Here is a working pipeline you can adapt to your own documents.<\/p>\n<h3>Step 1: Generate Context for Each\u00a0Chunk<\/h3>\n<p>from anthropic import Anthropic<\/p>\n<p>client = Anthropic()<br \/>CONTEXT_PROMPT = &#8220;&#8221;&#8221;&lt;document&gt;<br \/>{full_document}<br \/>&lt;\/document&gt;<br \/>Here is the chunk we want to situate within the whole document:<br \/>&lt;chunk&gt;<br \/>{chunk_content}<br \/>&lt;\/chunk&gt;<br \/>Please give a short, succinct context to situate this chunk within the overall<br \/>document for the purposes of improving search retrieval of the chunk.<br \/>Answer only with the succinct context and nothing else.&#8221;&#8221;&#8221;<br \/>def generate_chunk_context(full_document, chunk_content):<br \/>    response = client.messages.create(<br \/>        model=&#8221;claude-opus-4-8&#8243;,<br \/>        max_tokens=150,<br \/>        messages=[{<br \/>            &#8220;role&#8221;: &#8220;user&#8221;,<br \/>            &#8220;content&#8221;: CONTEXT_PROMPT.format(<br \/>                full_document=full_document,<br \/>                chunk_content=chunk_content<br \/>            )<br \/>        }]<br \/>    )<br \/>    return response.content[0].text<\/p>\n<p>If you are worried about cost, Claude\u2019s prompt caching handles this well. You cache the full document once and reuse that cached version across every chunk in that document, so you are only paying full price for the document tokens a single\u00a0time.<\/p>\n<h3>Step 2: Build the Contextualized Chunks<\/h3>\n<p>def build_contextualized_chunks(document_text, chunks):<br \/>    contextualized_chunks = []<br \/>    for chunk in chunks:<br \/>        context = generate_chunk_context(document_text, chunk)<br \/>        contextualized_chunk = f&#8221;{context}nn{chunk}&#8221;<br \/>        contextualized_chunks.append(contextualized_chunk)<br \/>    return contextualized_chunks<\/p>\n<h3>Step 3: Index for Both Embedding Search and\u00a0BM25<\/h3>\n<p>from rank_bm25 import BM25Okapi<br \/>import numpy as np<\/p>\n<p>def build_hybrid_index(contextualized_chunks, embedding_model):<br \/>    # Embedding index<br \/>    embeddings = embedding_model.encode(contextualized_chunks)<br \/>    # BM25 index<br \/>    tokenized = [chunk.lower().split() for chunk in contextualized_chunks]<br \/>    bm25 = BM25Okapi(tokenized)<br \/>    return embeddings, bm25<\/p>\n<h3>Step 4: Retrieve With Both, Then\u00a0Rerank<\/h3>\n<p>def hybrid_retrieve(query, chunks, embeddings, bm25, embedding_model, top_k=20):<br \/>    # Semantic search<br \/>    query_embedding = embedding_model.encode([query])[0]<br \/>    semantic_scores = np.dot(embeddings, query_embedding)<br \/>    # Keyword search<br \/>    tokenized_query = query.lower().split()<br \/>    bm25_scores = bm25.get_scores(tokenized_query)<br \/>    # Combine, normalize both to 0-1 range first<br \/>    semantic_norm = semantic_scores \/ (semantic_scores.max() + 1e-8)<br \/>    bm25_norm = bm25_scores \/ (bm25_scores.max() + 1e-8)<br \/>    combined = 0.5 * semantic_norm + 0.5 * bm25_norm<br \/>    top_indices = combined.argsort()[-top_k:][::-1]<br \/>    return [chunks[i] for i in top_indices]<\/p>\n<p>For the final accuracy boost, add a reranker (Cohere\u2019s rerank model or a cross encoder) on top of these top 20 results before passing the final 3 to 5 chunks to your LLM. This is the step that took Anthropic\u2019s numbers from 49% failure reduction to\u00a067%.<\/p>\n<h3>Best Practices for Contextual Retrieval<\/h3>\n<p>Use a cheap, fast model for context generation. You do not need your most expensive model to write a two sentence summary of where a chunk sits in a document.Cache the full document when generating context for multiple chunks from the same source. This is where prompt caching saves real money at\u00a0scale.Keep the generated context short, 50 to 100 tokens is usually enough. Long context defeats the purpose of keeping chunks\u00a0small.Always pair contextual embeddings with contextual BM25. Using only one leaves accuracy on the\u00a0table.Add a reranking step if your use case can tolerate the extra latency. The accuracy gain is significant.Test on your own domain before trusting published numbers. Anthropic\u2019s benchmarks used codebases, fiction, ArXiv papers, and science papers, your documents may behave differently.Monitor your actual retrieval failure rate before and after, do not assume the improvement transfers without measuring it.<\/p>\n<h3>Conclusion<\/h3>\n<p>Contextual retrieval is one of those techniques that feels obvious once you see it, and that is usually a sign it is worth adopting. It does not require a new vector database, a new architecture, or months of engineering work. It requires rethinking one step in your pipeline: what actually gets embedded and\u00a0indexed.<\/p>\n<p>If your RAG system has been giving vague or wrong answers and you have already tuned your chunk size, tried different embedding models, and added reranking, this is very likely the missing\u00a0piece.<\/p>\n<p><strong>Thank you for following this <\/strong><a href=\"https:\/\/medium.com\/gopenai\/rag-complete-tutorial-part-05-20cbdfddc930\"><strong>RAG tutorial series<\/strong><\/a><strong>!<\/strong> If you found this helpful, please give it a clap \ud83d\udc4f and share with others building RAG\u00a0systems.<\/p>\n<p><a href=\"https:\/\/medium.com\/coinmonks\/contextual-retrieval-anthropics-method-for-cutting-rag-failures-b28d98d57c48\">Contextual Retrieval: Anthropic\u2019s Method for Cutting RAG Failures<\/a> was originally published in <a href=\"https:\/\/medium.com\/coinmonks\">Coinmonks<\/a> on Medium, where people are continuing the conversation by highlighting and responding to this story.<\/p>","protected":false},"excerpt":{"rendered":"<p>Introduction Welcome back!!! If you have been following my RAG series, you already know that chunking is where most retrieval pipelines quietly break. You split a document into neat little pieces, embed each one, store it in a vector database, and assume the system now understands your content. It does not. Each chunk sits alone, [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":197241,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-197240","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-interesting"],"_links":{"self":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts\/197240"}],"collection":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=197240"}],"version-history":[{"count":0,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts\/197240\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/media\/197241"}],"wp:attachment":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=197240"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=197240"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=197240"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}