{
  "source_file": "0930",
  "source_platform": "Hashnode",
  "author": "Aman Jain",
  "title_listed": "Beyond Basic Retrieval: Improving Precision and Context in RAG",
  "keywords": "",
  "domain": "大模型算法",
  "content_type": "深度技术解析",
  "is_paid_flag": "否",
  "url": "https://curious-pm.hashnode.dev/beyond-basic-retrieval-improving-precision-and-context-in-rag",
  "publish_date": "2026-08-02",
  "corpus_stage": "",
  "notes": "",
  "id": "f5e359f54cfe",
  "collected_at": "2026-10-02T14:11:42",
  "title": "Advanced RAG: Parent-Child, Sentence Windows, and LLM Rerank",
  "body_markdown": "Beyond Basic Retrieval: Improving Precision and Context in RAG\nExploring small-to-large, sentence-window, summary-to-detail retrieval, and LLM reranking\nUpdated\nAugust 2, 2026\n•\n9\nmin read\n•\nView as Markdown\nA\nAman Jain\nProduct manager learning to build AI from the inside out — from LLM architecture to product applications.\nPart of series\nRAG From First Principles\nIn\nPart 2 of this series\n, I explored the basic retrieval mechanisms used in RAG: dense retrieval, BM25, and hybrid search. I also examined evidence grading and Corrective RAG as ways to prevent factually contradictory chunks from moving further through the pipeline.\nThis article focuses on a different problem:\nOnce a retriever finds relevant chunks, how do we ensure they contain enough context to be genuinely useful?\nA small chunk may match the query precisely but omit the surrounding explanation. A large chunk may contain the complete answer but introduce unrelated information that weakens retrieval precision.\nIn this article, I explore four advanced strategies designed to improve retrieval relevance and context quality:\nsmall-to-large retrieval,\nsentence-window retrieval,\nsummary-to-detail retrieval,\nLLM reranking.\nThe goal is not to determine whether a retrieved claim is factually true. Instead, it is to improve which passages are retrieved, how they are ordered, and how much context is ultimately provided.\nTL;DR\nFixed-size chunks force one representation to support both precise retrieval and complete context.\nSmall-to-large retrieval searches small child chunks and returns their larger parent sections.\nSentence-window retrieval matches individual sentences and expands them with nearby sentences.\nSummary-to-detail retrieval uses compact summaries to route queries toward relevant sections before searching their detailed chunks.\nLLM reranking reorders an initial candidate set based on how directly and completely each candidate answers the query.\nThese techniques improve retrieval precision and context quality, but they do not verify factual correctness.\nNo single strategy is best for every query. Each introduces trade-offs in context size, noise, cost, latency, and complexity.\nThe Limitation of Fixed-Size Retrieval\nA basic dense retriever usually splits every document into chunks of one fixed size, embeds each chunk independently, and returns the chunks most similar to the query.\nThis is a useful baseline, but the selected chunk size must perform two conflicting jobs:\nremain small enough for precise matching,\nremain large enough to provide complete context.\nIn my baseline, documents were divided into chunks of approximately 250 words with some overlap.\nThis worked reasonably well, but the retrieved chunks sometimes began or ended in the middle of an explanation. Increasing the chunk size would provide more context, but it could also dilute the embedding with unrelated information.\nThe following strategies try to separate the unit used for\nsearch\nfrom the unit returned as\ncontext\n.\n1. Small-to-Large Retrieval\nSmall-to-large retrieval creates two linked levels of chunks:\nsmall\nchild chunks\nused for precise matching,\nlarger\nparent chunks\nreturned after a child is selected.\nIn my implementation, parent sections contained approximately 900 words, while their child chunks contained around 150 words.\nThe system embeds and searches only the child chunks. When a child matches the query, the system maps it back to its parent and returns the larger context.\nQuery\n→ search small child chunks\n→ identify matched children\n→ map children to parents\n→ return larger parent context\ndef small_to_large_retrieve(\n    query,\n    child_top_k=10,\n    parent_top_k=3,\n):\n    child_results = retrieve_dense(\n        query,\n        child_df,\n        top_k=child_top_k,\n    )\n\n    best_parent_scores = (\n        child_results\n        .groupby(\n            \"parent_id\",\n            as_index=False,\n        )[\"retrieval_score\"]\n        .max()\n        .sort_values(\n            \"retrieval_score\",\n            ascending=False,\n        )\n        .head(parent_top_k)\n    )\n\n    expanded = best_parent_scores.merge(\n        parent_df,\n        on=\"parent_id\",\n        how=\"left\",\n    )\n\n    return child_results, expanded\nOne retrieved child looked like this:\nRank: 1\nSource: theory of relativity.txt\nScore: 0.5520\n\nChild:\n\"...whether in the actual case the two events took place\nsimultaneously or not.\"\nThe child is highly focused, while its parent restores the broader explanation around simultaneity.\nThis strategy worked well when the answer required context surrounding the exact matching passage. However, the larger parent could also introduce unrelated material.\nMore context is not always better context.\n2. Sentence-Window Retrieval\nSentence-window retrieval uses individual sentences as the searchable units.\nEach sentence receives an embedding. When a sentence matches the query, the system adds nearby sentences from the original document to create a context window.\nQuery\n→ retrieve the most similar sentence\n→ add neighboring sentences\n→ return the expanded window\nWINDOW_SIZE = 2\nsentence_rows = []\n\nfor document in documents:\n    sentences = split_sentences(\n        document[\"text\"]\n    )\n\n    for i, sentence in enumerate(sentences):\n        start = max(\n            0,\n            i - WINDOW_SIZE,\n        )\n\n        end = min(\n            len(sentences),\n            i + WINDOW_SIZE + 1,\n        )\n\n        sentence_rows.append({\n            \"sentence_id\": (\n                f\"{document['source']}\"\n                f\"::sentence::{i}\"\n            ),\n            \"source\": document[\"source\"],\n            \"sentence_text\": sentence,\n            \"window_text\": \" \".join(\n                sentences[start:end]\n            ),\n            \"window_start\": start,\n            \"window_end\": end - 1,\n        })\n\nsentence_df = pd.DataFrame(\n    sentence_rows\n)\n\nsentence_df[\"embedding\"] = get_embeddings(\n    sentence_df[\n        \"sentence_text\"\n    ].tolist(),\n    \"sentences\",\n)\nSentence windows were compact and readable when the relevant explanation was concentrated within a few nearby sentences.\nThey work particularly well when:\nthe answer appears in one clear statement,\nneighboring sentences provide sufficient explanation,\nthe source follows a coherent local structure.\nThe main limitation is that nearby text is not always the most useful text. An explanation may depend on another paragraph or section rather than the immediately adjacent sentences.\nSentence-window retrieval therefore assumes that useful context is locally concentrated.\n3. Summary-to-Detail Retrieval\nSummary-to-detail retrieval introduces a hierarchical routing step.\nFirst, larger parent sections are summarized using an LLM. These summaries form a lightweight index representing the major ideas in each section.\nAt query time, the system:\nretrieves the most relevant summaries,\nidentifies their corresponding parent sections,\nsearches detailed child chunks only within those sections.\nQuery\n→ search section summaries\n→ select relevant sections\n→ search detailed chunks within those sections\n→ return the strongest details\ndef summarize_text(text: str) -> str:\n    response = client.responses.create(\n        model=SUMMARY_MODEL,\n        input=[\n            {\n                \"role\": \"system\",\n                \"content\": (\n                    \"Summarize this passage for retrieval \"\n                    \"in 2-3 concise sentences. Preserve \"\n                    \"important concepts, terms, examples, \"\n                    \"and claims. Add no new facts.\"\n                ),\n            },\n            {\n                \"role\": \"user\",\n                \"content\": text,\n            },\n        ],\n    )\n\n    return response.output_text.strip()\nThis reduces the second-stage search space and adds document-level structure to retrieval.\nIn my experiment, parent sections averaged approximately\n883 words\n, while their summaries averaged around\n102 words\n—a reduction of more than\n88%\n.\nAdd notebook visual:\nParent-section length versus summary length.\nThe summaries provide a compact representation for routing, while the child chunks preserve the original detail.\nHowever, the approach depends heavily on summary quality. If an important concept is omitted from a summary, the corresponding section may never enter the detailed search stage.\nThe summary does not merely compress the source. It influences which sections remain discoverable.\n4. LLM Reranking\nThe previous techniques change how the retrieval index is structured. LLM reranking instead changes the order of the retrieved candidates.\nThe first-stage retriever returns a wider candidate set to maximize recall. An LLM then compares those candidates with the query and reorders them according to how directly and completely they answer it.\nQuery\n→ retrieve a wider candidate set\n→ ask the LLM to compare candidates\n→ reorder candidates\n→ retain the strongest results\ndef llm_rerank(\n    query,\n    candidate_df,\n    text_column=\"child_text\",\n    top_k=3,\n):\n    candidates = [\n        {\n            \"candidate_id\": i,\n            \"text\": row[text_column],\n        }\n        for i, (_, row) in enumerate(\n            candidate_df.iterrows(),\n            start=1,\n        )\n    ]\n\n    payload = {\n        \"query\": query,\n        \"instruction\": (\n            \"Rank candidates by how directly \"\n            \"and completely they help answer \"\n            f\"the query. Return the best {top_k} IDs.\"\n        ),\n        \"candidates\": candidates,\n    }\n\n    response = client.responses.create(\n        model=RERANK_MODEL,\n        input=[\n            {\n                \"role\": \"system\",\n                \"content\": (\n                    'Return valid JSON only: '\n                    '{\"ranked_candidate_ids\":[1,2,3]}'\n                ),\n            },\n            {\n                \"role\": \"user\",\n                \"content\": json.dumps(payload),\n            },\n        ],\n    )\n\n    ranked_ids = json.loads(\n        response.output_text\n    )[\"ranked_candidate_ids\"][:top_k]\n\n    rows = []\n\n    for new_rank, candidate_id in enumerate(\n        ranked_ids,\n        start=1,\n    ):\n        if 1 <= candidate_id <= len(candidate_df):\n            row = candidate_df.iloc[\n                candidate_id - 1\n            ].copy()\n\n            row[\"initial_rank\"] = candidate_id\n            row[\"reranked_rank\"] = new_rank\n            rows.append(row)\n\n    return pd.DataFrame(rows)\nAdd notebook output:\nBefore-and-after candidate rankings.\nIn one experiment, a chunk initially ranked third contained the clearest explanation of why observers disagree about simultaneity. After reranking, the LLM moved it to the first position.\nVector similarity identifies broadly related candidates. The reranker performs a second-stage comparison to determine which candidate best answers the actual question.\nHowever, reranking improves\nordering\n, not\nrecall\n.\nIf the correct passage is absent from the original candidate set, the reranker cannot recover it. The first-stage retriever must therefore return a sufficiently broad set.\nComparing the Strategies\nStrategy\nSearch unit\nReturned context\nPrimary benefit\nMain trade-off\nFixed-size\nMedium chunk\nSame chunk\nSimple baseline\nOne size must balance precision and context\nSmall-to-large\nSmall child\nLarge parent\nPrecise matching with fuller context\nParent expansion may add noise\nSentence-window\nSentence\nNearby sentences\nFocused and readable context\nAssumes useful context is nearby\nSummary-to-detail\nSummary, then child\nDetailed child\nHierarchical routing and smaller search space\nSummary omissions affect discoverability\nLLM reranking\nExisting candidates\nReordered candidates\nBetter candidate ordering\nAdds cost and cannot improve recall\nThese strategies can also be combined.\nFor example, a system could retrieve small child chunks, expand them to parents, and then rerank the expanded candidates.\nThe right architecture depends on document structure, query type, latency constraints, context limits, and acceptable cost.\nWhat the Experiments Showed\nFive lessons stood out.\nFirst, fixed-size retrieval remains a useful baseline, but one chunk size rarely optimizes both precise search and complete context.\nSecond, small-to-large retrieval separates retrieval granularity from generation context, although larger parents can introduce noise.\nThird, sentence windows work well when explanations are locally concentrated and become less effective when the required context is distributed across the document.\nFourth, summary-to-detail retrieval provides useful hierarchical routing, but summaries become part of the retrieval infrastructure. Missing information in a summary can hide the original details.\nFinally, reranking can improve the ordering of retrieved candidates but cannot recover evidence that the initial retriever missed.\nNone of these techniques establishes factual truth. They optimize relevance, ranking, and context quality. Evidence verification, contradiction handling, and Corrective RAG remain separate responsibilities.\nIn the final article of this series, I will bring these components together and evaluate the complete RAG pipeline using retrieval metrics, evidence grading, answer evaluation, and an end-to-end scorecard.\nThe implementation is available in the\nRAG From First Principles GitHub repository\n.\nReferences and Further Reading\nLewis, P. et al. (2020).\nRetrieval-Augmented Generation for Knowledge-Intensive NLP Tasks\n.\nKarpukhin, V. et al. (2020).\nDense Passage Retrieval for Open-Domain Question Answering\n.\nHuang, J.\nRAG from First Principles: Mastering Retrieval-Augmented Generation\n.\nLangChain document-loader documentation\n.\nLangChain text-splitter documentation\n.\nLlamaIndex SemanticSplitterNodeParser documentation\n.\nBAAI/bge-small-en-v1.5 model card\n.\nProject code and notebooks\n.\n#\nretrieval-augmented-generation\n#\nrag\n11\nviews",
  "metadata": {
    "title": "Advanced RAG: Parent-Child, Sentence Windows, and LLM Rerank",
    "description": "A practical comparison of small-to-large retrieval, sentence windows, summary-to-detail search, and LLM reranking for improving RAG context quality.",
    "keywords": null,
    "author": null,
    "og:title": "Advanced RAG: Parent-Child, Sentence Windows, and LLM Rerank",
    "og:description": "A practical comparison of small-to-large retrieval, sentence windows, summary-to-detail search, and LLM reranking for improving RAG context quality.",
    "og:url": "https://curious-pm.hashnode.dev/beyond-basic-retrieval-improving-precision-and-context-in-rag",
    "og:site_name": "Curious PM - Product to Parameters",
    "og:image": "https://curious-pm.hashnode.dev/api/og/post?og=eyJhdXRob3IiOiJBbWFuJTIwSmFpbiIsImlzRGVmYXVsdE1vZGVEYXJrIjpmYWxzZSwicmVhZFRpbWUiOjksImNvbW1lbnRzIjowLCJyZWFjdGlvbnMiOjAsImRvbWFpbiI6ImN1cmlvdXMtcG0uaGFzaG5vZGUuZGV2IiwidGl0bGUiOiJCZXlvbmQlMjBCYXNpYyUyMFJldHJpZXZhbCUzQSUyMEltcHJvdmluZyUyMFByZWNpc2lvbiUyMGFuZCUyMENvbnRleHQlMjBpbiUyMFJBRyIsInBob3RvIjoiaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tL2EvQUNnOG9jSWtTZTl3MmJlYjV6SU8zSzctdjN6b2FfNDRSNFlYeGNjUGxybFJSZmR2Vkl2UVhBPXM5Ni1jIiwibG9nbyI6bnVsbCwiZmF2aWNvbiI6bnVsbCwicHVibGlzaGVkQXQiOiIyMDI2LTA4LTAyVDIxOjMyOjI1LjA5M1oiLCJ1cGRhdGVkQXQiOiIyMDI2LTA4LTAyVDIxOjMyOjI1LjA5M1oifQ==",
    "og:image:width": "1200",
    "og:image:height": "630",
    "og:type": "article",
    "twitter:card": "summary_large_image",
    "twitter:title": "Advanced RAG: Parent-Child, Sentence Windows, and LLM Rerank",
    "twitter:description": "A practical comparison of small-to-large retrieval, sentence windows, summary-to-detail search, and LLM reranking for improving RAG context quality.",
    "twitter:image": "https://curious-pm.hashnode.dev/api/og/post?og=eyJhdXRob3IiOiJBbWFuJTIwSmFpbiIsImlzRGVmYXVsdE1vZGVEYXJrIjpmYWxzZSwicmVhZFRpbWUiOjksImNvbW1lbnRzIjowLCJyZWFjdGlvbnMiOjAsImRvbWFpbiI6ImN1cmlvdXMtcG0uaGFzaG5vZGUuZGV2IiwidGl0bGUiOiJCZXlvbmQlMjBCYXNpYyUyMFJldHJpZXZhbCUzQSUyMEltcHJvdmluZyUyMFByZWNpc2lvbiUyMGFuZCUyMENvbnRleHQlMjBpbiUyMFJBRyIsInBob3RvIjoiaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tL2EvQUNnOG9jSWtTZTl3MmJlYjV6SU8zSzctdjN6b2FfNDRSNFlYeGNjUGxybFJSZmR2Vkl2UVhBPXM5Ni1jIiwibG9nbyI6bnVsbCwiZmF2aWNvbiI6bnVsbCwicHVibGlzaGVkQXQiOiIyMDI2LTA4LTAyVDIxOjMyOjI1LjA5M1oiLCJ1cGRhdGVkQXQiOiIyMDI2LTA4LTAyVDIxOjMyOjI1LjA5M1oifQ==",
    "article:published_time": "2026-08-02T21:32:25.093Z",
    "article:modified_time": "2026-08-02T21:32:25.093Z",
    "article:author": "https://curious-pm.hashnode.dev/@aman454"
  },
  "paywall": {
    "listed_paid": false,
    "detected": false,
    "marker": "",
    "public_only": false,
    "policy": "付费/会员内容只保留页面公开可见部分，不绕过付费墙"
  },
  "crawl": {
    "success": true,
    "status_code": 200,
    "error": ""
  },
  "code_blocks": [
    {
      "file": "attachments/f5e359f54cfe/code/body-000.txt",
      "chars": 119,
      "language": "text"
    },
    {
      "file": "attachments/f5e359f54cfe/code/body-001.txt",
      "chars": 623,
      "language": "text"
    },
    {
      "file": "attachments/f5e359f54cfe/code/body-002.txt",
      "chars": 144,
      "language": "text"
    },
    {
      "file": "attachments/f5e359f54cfe/code/body-003.txt",
      "chars": 100,
      "language": "text"
    },
    {
      "file": "attachments/f5e359f54cfe/code/body-004.txt",
      "chars": 910,
      "language": "text"
    },
    {
      "file": "attachments/f5e359f54cfe/code/body-005.txt",
      "chars": 138,
      "language": "text"
    },
    {
      "file": "attachments/f5e359f54cfe/code/body-006.txt",
      "chars": 616,
      "language": "text"
    },
    {
      "file": "attachments/f5e359f54cfe/code/body-007.txt",
      "chars": 127,
      "language": "text"
    },
    {
      "file": "attachments/f5e359f54cfe/code/body-008.txt",
      "chars": 1497,
      "language": "text"
    }
  ],
  "images": [
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/uploads/covers/6a4f47d631bb27227f782535/1627ba3e-a159-414e-874b-c4856630719f.png",
      "bytes": 20668,
      "reused": true,
      "local_path": "attachments/f5e359f54cfe/images/000.png",
      "src": "https://cdn.hashnode.com/uploads/covers/6a4f47d631bb27227f782535/1627ba3e-a159-414e-874b-c4856630719f.png",
      "alt": "",
      "downloaded": true
    }
  ],
  "audio": [],
  "videos": [],
  "links": [
    {
      "href": "https://curious-pm.hashnode.dev/beyond-basic-retrieval-improving-precision-and-context-in-rag.md",
      "label": "View as Markdown",
      "type": "a"
    },
    {
      "href": "https://hashnode.com/@aman454",
      "label": "A",
      "type": "a"
    },
    {
      "href": "https://www.linkedin.com/in/aman-jain-1049188/",
      "label": "",
      "type": "a"
    },
    {
      "href": "https://curious-pm.hashnode.dev/series/building-rag-from-scratch",
      "label": "Part of series RAG From First Principles",
      "type": "a"
    },
    {
      "href": "https://curious-pm.hashnode.dev/ADD_ARTICLE_2_LINK",
      "label": "Part 2 of this series",
      "type": "a"
    },
    {
      "href": "https://github.com/amanjain454/rag-from-scratch-work",
      "label": "RAG From First Principles GitHub repository",
      "type": "a"
    },
    {
      "href": "https://arxiv.org/abs/2005.11401",
      "label": "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks",
      "type": "a"
    },
    {
      "href": "https://arxiv.org/abs/2004.04906",
      "label": "Dense Passage Retrieval for Open-Domain Question Answering",
      "type": "a"
    },
    {
      "href": "https://docs.langchain.com/oss/python/integrations/document_loaders",
      "label": "LangChain document-loader documentation",
      "type": "a"
    },
    {
      "href": "https://docs.langchain.com/oss/python/integrations/splitters/index",
      "label": "LangChain text-splitter documentation",
      "type": "a"
    },
    {
      "href": "https://docs.llamaindex.ai/en/stable/api_reference/node_parsers/semantic_splitter/",
      "label": "LlamaIndex SemanticSplitterNodeParser documentation",
      "type": "a"
    },
    {
      "href": "https://huggingface.co/BAAI/bge-small-en-v1.5",
      "label": "BAAI/bge-small-en-v1.5 model card",
      "type": "a"
    },
    {
      "href": "https://curious-pm.hashnode.dev/tag/retrieval-augmented-generation",
      "label": "# retrieval-augmented-generation",
      "type": "a"
    },
    {
      "href": "https://curious-pm.hashnode.dev/tag/rag",
      "label": "# rag",
      "type": "a"
    }
  ],
  "retry_at": "2026-10-03T08:08:45",
  "repair_version": 2,
  "repaired_at": "2026-10-14T10:50:53Z",
  "source_url": "https://curious-pm.hashnode.dev/beyond-basic-retrieval-improving-precision-and-context-in-rag",
  "papers": [
    {
      "status": "ok",
      "url": "https://arxiv.org/pdf/2005.11401",
      "bytes": 885323,
      "reused": true,
      "local_path": "papers/repair/fc8d32f196e0f0c27ffb.pdf"
    },
    {
      "status": "ok",
      "url": "https://arxiv.org/pdf/2004.04906",
      "bytes": 383508,
      "reused": true,
      "local_path": "papers/repair/8b76986dbe24871ab2c9.pdf"
    }
  ],
  "actual_video_files": [],
  "body_chars": 13837,
  "expected_body_images": 1,
  "discovery_status": "ok",
  "delivery_status": "ok",
  "missing_reason": "",
  "raw_html": "attachments/f5e359f54cfe/pages/source.html",
  "local_html": "attachments/f5e359f54cfe/pages/article.html",
  "body_base_path": "attachments/f5e359f54cfe/pages/",
  "body_markdown_path": "attachments/f5e359f54cfe/pages/article.md"
}