{
  "source_file": "0930",
  "source_platform": "Hashnode",
  "author": "Jaival Suthar",
  "title_listed": "Building Knowledge Vault : From RAG Implementation to Retrieval Engineering",
  "keywords": "",
  "domain": "大模型算法",
  "content_type": "深度技术解析",
  "is_paid_flag": "否",
  "url": "https://jaivalsuthar.hashnode.dev/building-knowledge-vault-rag-retrieval-engineering",
  "publish_date": "2026-08-10",
  "corpus_stage": "",
  "notes": "",
  "id": "c40b344fe489",
  "collected_at": "2026-10-02T14:12:15",
  "title": "Building a RAG System: Retrieval Engineering Lessons",
  "body_markdown": "Building Knowledge Vault : From RAG Implementation to Retrieval Engineering\nWhat a controlled retrieval experiments revealed about ranking, evidence, latency, and failure modes.\nUpdated\nAugust 11, 2026\n•\n21\nmin read\n•\nView as Markdown\nJ\nJaival Suthar\nI write technical articles that analyze real world engineering problems through the lens of production systems.\nMy work focuses on architectural tradeoffs, UI layer system design, performance optimization, and reliability patterns derived from shipping and maintaining scalable applications.\nPart of series\nBuilding AI Systems from Scratch\nMost RAG demos are deceptively simple.\nIngest a document. Chunk it. Generate embeddings. Store the vectors. Retrieve a few chunks. Send them to an LLM.\nThe architecture fits neatly on a screen.\nThe difficult part starts when you stop asking, \"Does it work?\" and start asking, \"Why does it work when it does, and why does it fail when it doesn't?\"\nThat's what I wanted to understand with\nKnowledge Vault\n.\nKnowledge Vault is\nMilestone 1 (M1)\nof my local AI stack: a local-first PDF retrieval system that turns books and documents into searchable, source-attributed context for an LLM.\nIt builds on\nInference Lab (M0)\n, my local model-serving layer which is responsible for running the model. Knowledge Vault is responsible for everything that happens before the model sees the prompt: document ingestion, retrieval, reranking, and context construction.\n🔗\nKnowledge Vault (M1) — GitHub\n🔗\nInference Lab (M0) — GitHub\nBut I wasn't trying to build another feature-heavy RAG demo.\nI wanted to make the retrieval system understandable.\nSo I gave Knowledge Vault a deliberately narrow objective:\nBuild the retrieval layer, understand its behavior, and find out where it breaks.\nFor the project, success meant four things:\nBuild a working document retrieval pipeline.\nEstablish a reproducible evaluation set.\nMeasure retrieval and inference behavior separately.\nIdentify concrete failure modes that could guide the next engineering decisions.\nI deliberately left out the usual temptation to keep adding \"advanced RAG\" features.\nFirst, I wanted to know whether the foundations actually worked.\nScope\nText-layer PDFs\nSingle-document evaluation\nDense retrieval\nOptional cross-encoder reranking\nLocal inference\nControlled retrieval experiments\nExplicitly out of scope\nOCR\nHybrid search\nKnowledge graphs\nMulti-agent systems\nLarge-scale distributed retrieval\nProduction-scale evaluation infrastructure\nFirst, I wanted to answer a simpler question:\nDoes the retrieval system I actually built work?\nAnd more importantly:\nCan I prove it?\nThe Architecture: Decoupling Knowledge From Inference\nKnowledge Vault has a deliberate boundary between knowledge retrieval and model inference.\nThe retrieval system is responsible for turning documents into useful context. A separate inference service is responsible for executing the language model.\nThe high-level architecture looks like this:\nThe system attempts to ground generation in retrieved evidence, but Knowledge Vault does not guarantee that the final answer is faithful to that evidence.\nDocument extraction\nThe first stage uses PyMuPDF for page-by-page PDF text extraction.\nThis is intentionally a text-layer PDF pipeline. It preserves source metadata such as document identity and page information so that retrieved chunks can be traced back to where they came from.\nThat traceability matters because a RAG system shouldn't just return an answer. It should be possible to inspect the evidence behind that answer.\nIt also establishes an important boundary for M1: scanned PDFs requiring OCR are not the problem being solved yet.\nChunking\nExtracted text is passed through recursive, token-aware chunking.\nThe goal is not simply to cut the document every N characters.\nA retrieval chunk should be large enough to preserve useful context but small enough to remain a precise retrieval unit.\nThat creates an unavoidable trade-off.\nChunks that are too small can destroy context:\nChunk A: \"The system uses...\"\nChunk B: \"...a two-stage retrieval process.\"\nChunks that are too large can dilute relevance:\nQuestion\n   ↓\nLarge chunk containing five unrelated concepts\n   ↓\nWeak retrieval precision\nSo chunking is not just preprocessing. It is already a retrieval decision.\nEmbeddings\nEach chunk is converted into a vector using:\nBAAI/bge-small-en-v1.5\nThe same embedding model is used for the user's query.\nConceptually:\nDocument chunk          Question\n     ↓                      ↓\nEmbedding model        Embedding model\n     ↓                      ↓\n   Vector               Query vector\nThe purpose is to move the problem from lexical matching toward semantic similarity.\nQdrant\nThe chunk vectors are stored in Qdrant along with metadata such as:\ndocument identity\nchunk identity\npage information\nchunk position\ntoken information\nsource metadata\nThat metadata becomes important later because vector similarity alone doesn't tell us whether a retrieved result is actually useful evidence.\nReranking\nThe retrieval pipeline can optionally add:\nBAAI/bge-reranker-base\nThe architecture is intentionally two-stage:\nThe vector database is responsible for quickly finding candidates.\nThe reranker gets a smaller candidate set and performs a more expensive query-passage relevance calculation.\nThat makes the reranker more than a quality component. It is also a latency-bearing architectural component.\nThis is a classic retrieval architecture, but I didn't want to assume that adding the second stage automatically made the system better.\nThat had to be measured.\nWhy Separate Retrieval From Inference?\nThe other major architectural choice was separating Knowledge Vault from the model-serving layer.\nIt would have been simpler to put everything into one application:\nPDF → RAG → Ollama → Answer\nInstead, the system uses a service boundary:\nThe two systems have different responsibilities.\nKnowledge Vault is primarily a knowledge-processing and retrieval system.\nThe inference service is a model-execution system.\nThis means I can change the retrieval pipeline without redesigning model serving, and I can change the model runtime without rewriting the document pipeline.\nThere is still coupling. It simply becomes explicit API coupling rather than shared implementation coupling.\nThat distinction matters once a system grows beyond a single experiment.\nThe First Problem Isn't the LLM\nOne of the first things Knowledge Vault made clear is that the LLM is actually quite far downstream in the failure chain.\nConsider the pipeline:\nEvery stage can introduce a different failure.\nIf PDF extraction corrupts the text, the embedding model receives bad input.\nIf chunking separates related information, retrieval may fail.\nIf the correct chunk isn't close enough in vector space, Qdrant may never retrieve it.\nIf the correct chunk isn't in the candidate set, the reranker cannot recover it.\nIf the reranker promotes a structurally irrelevant page, the LLM may receive weak evidence.\nAnd if the retrieved evidence is good but the model misinterprets it, generation can still fail.\nThis is why I started thinking about RAG less as a single algorithm and more as a chain of failure boundaries.\nUnderstanding those boundaries became one of the central goals of Knowledge Vault.\nFrom \"It Works\" to \"Prove It\"\nThe first version of Knowledge Vault could answer questions over a PDF.\nThat was encouraging.\nBut:\n\"I asked it a question and got a good answer\"\nisn't an evaluation methodology. It's a demo.\nI wanted a controlled experiment.\nSo I selected one real document:\nThe 10X Rule\n, a 214-page book.\nRather than immediately indexing many different documents, I deliberately constrained the experiment.\nOne document.\nOne question set.\nControlled retrieval configurations.\nThen compare the results.\nThe experiment was run locally using the same document, question set, inference configuration, and final context size across runs. Retrieval configuration was changed only where required by the experiment. Latency was measured per pipeline stage rather than from a single end-to-end timer.\nThe benchmark is intentionally small and controlled; it is intended to compare configurations within this system, not to claim general RAG performance across datasets.\nThis reduces the number of variables and makes it easier to understand why something changed.\nDesigning the 35-Question Evaluation\nI created a set of 35 questions designed to exercise different retrieval behaviors.\nThe questions included:\nfactual questions\nconceptual questions\nparaphrased questions\nquestions targeting specific sections\nmulti-part questions\nquestions sensitive to chunk boundaries\ndeliberately unanswerable questions\nThe last category is important.\nA RAG system should not assume every question has an answer in its source.\nIf the document doesn't contain the required information, the desired behavior is:\nQuestion → Retrieve → No sufficient evidence → No grounded answer\nrather than:\nQuestion → LLM → Plausible-sounding answer\nThe deliberately unanswerable questions were included to test how retrieval and generation behaved when the source did not contain sufficient evidence.\nThree Runs, Not One\nThe most important detail of the experiment is that this was not simply \"reranker off versus reranker on.\"\nThere were three runs.\nRun\nRetrieval\nReranking\nCandidate K\nPurpose\n1\nDense\nOff\n5\nEstablish baseline\n2\nDense\nOn\n5\nTest reranking\n3\nDense\nOn\n20\nTest larger candidate pool\nThe comparison was deliberately controlled:\nSame document\nSame 35 questions\nSame inference model\nSame final context size\nOne retrieval variable changed per comparison\nRun 1 — Baseline vector retrieval\nThe baseline path was:\nQuestion → BGE embedding → Qdrant → Top-K candidates → LLM\nThis establishes what the vector retrieval layer can do on its own.\nRun 2 — Add the reranker\nThe second run added the cross-encoder:\nQuestion → BGE embedding → Qdrant → Candidate chunks → BGE reranker → \nFinal Top-K → LLM\nThe initial candidate pool was small.\nThe hypothesis was straightforward: if vector similarity gives us reasonable candidates but imperfect ordering, a cross-encoder should be able to improve the final ranking.\nRun 3 — Increase rerank_candidate_k to 20\nThe third run changed another important variable.\nInstead of giving the reranker only the initial small candidate set, I changed:\nrerank_candidate_k = 20\nwhile keeping the final context size at the smaller Top-K.\nThe architecture became:\nThis distinction matters.\ncandidate_k\nis how many candidates the reranker is allowed to consider.\ntop_k\nis how many results ultimately become context for the LLM.\nThat means Run 3 tests a real retrieval design question:\nDoes giving the reranker a larger candidate pool allow it to recover better evidence?\nIf the correct passage is ranked 8th by vector similarity, a reranker that sees only five candidates can never select it. Increasing the candidate pool to 20 gives it that opportunity.\nBut more candidates also mean more distractors.\nSo the experiment is not simply \"5 is bad, 20 is good.\" It is a candidate-coverage-versus-ranking trade-off.\nWhat Exactly Am I Evaluating?\nI realized I was actually evaluating several different properties of the system:\nCan retrieval find the evidence?\nCan reranking put that evidence near the top?\nIs the retrieved evidence actually sufficient to answer the question?\nDoes the LLM answer from that evidence?\nHow does the system behave when sufficient evidence is absent?\nThese are related, but they are not the same metric.\nA system can retrieve a relevant passage and still generate the wrong answer. A reranker can change the ranking without improving correctness. And a system can retrieve a highly similar passage that is not actually useful evidence.\nThe First Numbers\nBefore looking at individual failures, I wanted to understand the system at the aggregate level.\nThe reranker changed the Top-1 retrieved result on\n27 of 35 queries\n— that's\n77.1%\n.\nOnly 8 queries retained the same Top-1 result.\nAt first glance, that sounds like a strong result. A component changed the ranking on more than three quarters of the questions.\nIt would be very easy to write:\n\"The reranker improved retrieval by 77.1%.\"\nBut that would be an invalid conclusion.\nThe experiment measured ranking\nchanges\n, not correctness.\nA ranking can change from bad → good. It can also change from good → bad.\nSo I needed to inspect what actually moved.\nRun 2 vs Run 3: Did 20 Candidates Help?\nChanging\nrerank_candidate_k\nfrom the smaller candidate pool to 20 produced Top-1 changes on only 6 of 35 queries.\nQuery\nRun 2 Top-1\nRun 3 Top-1\nAssessment\nQ006\np65\np16\nImprovement\nQ013\np134\np4\nWorse\nQ022\np11\np166\nWorse\nQ023\np58\np197\nWorse\nQ029\np14\np145\nWorse\nQ032\np200\np6\nUnanswerable\nThis is a useful result precisely because it isn't spectacular.\nIncreasing the candidate pool gave the reranker more information. But it did not automatically produce better Top-1 retrieval.\nOnly one of these changes was clearly an improvement. Several moved the system toward less useful evidence. And one was an unanswerable query, so changing its retrieved page isn't inherently a retrieval improvement.\nThe conclusion is therefore:\nA larger reranking candidate pool increases the search space, but it does not guarantee better evidence selection.\nMore candidates can create more opportunities. They can also create more distractions.\nThe Latency Trade-Off\nThe other half of the experiment was performance.\nI instrumented the pipeline stage by stage rather than measuring only end-to-end latency.\nThe results were revealing.\nReranker A/B latency\nStage\nReranker OFF p50\nReranker ON p50\nOFF p95\nON p95\nEmbedding\n13 ms\n12 ms\n26 ms\n22 ms\nQdrant retrieval\n8 ms\n9 ms\n12 ms\n14 ms\nLLM\n13.716 s\n13.486 s\n19.819 s\n18.818 s\nTotal\n13.750 s\n16.162 s\n19.847 s\n21.817 s\nThe reranker itself added approximately:\nReranker metric\nValue\nMean overhead\n2.897 s\nMedian overhead\n2.894 s\nP95 overhead\n2.981 s\nThis produced a useful architectural insight.\nI initially expected vector retrieval to be one of the expensive parts of the system. It wasn't.\nQdrant retrieval was around\n8–9 ms p50\n,\n12–14 ms p95\n.\nThe cross-encoder was around\n~2.9 seconds per query\n.\nThe LLM was still the largest stage overall, at roughly\n13–14 seconds p50\n.\nBut the reranker was no longer a negligible preprocessing step. It became a meaningful latency decision.\nThe Tail Matters Too\nAverages can hide the behavior users actually experience.\nSome queries were much more expensive in the reranking stage. For example:\nQ001 ≈ 20 seconds total, ≈ 5.2 seconds reranking\nQ006 ≈ 20.3 seconds total, ≈ 5.8 seconds reranking\nThis changes the engineering question.\nIt is no longer simply:\n\"Does reranking work?\"\nIt becomes:\n\"Under what conditions is several additional seconds of reranking worth the retrieval benefit?\"\nThat is a much better engineering question because it forces quality and performance into the same decision.\nThe Semantic Blind Spot\nNow came the most interesting part of the experiment.\nConsider a question about the four degrees of action.\nThe book's Chapter 7 contains the actual explanation. The relevant concepts include:\nDo nothing\nRetreat\nNormal action\nMassive action\nThat is the evidence the LLM actually needs.\nBut the book's Table of Contents also contains:\nFour Degrees of Action\nThe First Degree of Action\nThe Second Degree of Action\nThe Third Degree of Action\nThe Fourth Degree: Massive Action\nFrom a semantic matching perspective, the Contents page looks extremely relevant.\nFrom an evidence perspective, it is weak.\nThe reranker sometimes preferred exactly that kind of page.\nFor one query, the baseline retrieved substantive Chapter 7 content around page 45, while reranking moved the Top-1 result to the Contents page.\nFor another, the baseline again found Chapter 7 content while the reranker promoted an Index entry around page 196.\nThis is not random noise. It exposes a structural limitation.\nSemantic Relevance ≠ Evidentiary Usefulness\nThis became one of the central lessons from the experiment.\nA reranker evaluates the relationship between:\nquery ↔ passage\nBut a RAG system needs to optimize something closer to:\nquery ↔ passage ↔ answerability ↔ evidence usefulness\nA Table of Contents can have high semantic relevance because it contains the exact concepts mentioned in the question.\nAn Index can have even more keyword-dense matches.\nBut neither necessarily contains enough information to answer the question.\nSo the system has a subtle failure mode:\nThe problem isn't necessarily that the reranker doesn't understand the query.\nIt is that document structure is not the same thing as semantic relevance.\nA Contents page, Index page, Glossary, Copyright page, and chapter body have very different evidentiary value.\nThe reranker doesn't automatically know that.\nThen Source Verification Caught a Different Problem\nThe retrieval failures were not the only thing the evaluation exposed.\nOne generated answer was also factually wrong when checked against the source.\nThe question asked:\nWhat are the four major mistakes people make when setting goals?\nThe generated answer produced a plausible list involving things such as:\nsetting goals too low\nnot aligning goals with a greater purpose\nunderestimating effort\nlack of consistent tracking\nThe answer sounded reasonable.\nBut the book's actual four mistakes are:\nMistargeting by setting objectives too low.\nSeverely underestimating the actions, resources, money, and energy required.\nSpending too much time competing rather than dominating.\nUnderestimating adversity required to attain the goal.\nThis was a genuine correctness failure.\nAnd it exposed another boundary:\nA fluent answer is not proof that the RAG pipeline retrieved or used the correct evidence.\nThe LLM can produce an answer that sounds consistent with the topic while still contradicting the source.\nThat is why final-answer evaluation alone is insufficient.\nThe Missing Number\nAt this point I had some compelling measurements:\n35 evaluation questions\n27 / 35 Top-1 changes with reranking (77.1%)\n~2.90 seconds mean reranking overhead\n~8–9 ms Qdrant p50\n~13–14 seconds LLM p50\ncandidate_k increased to 20 in Run 3\nBut one number was still missing: retrieval accuracy.\nI could not honestly say:\n\"Reranking improved retrieval accuracy by X%,\"\nbecause I had not yet established machine-checked passage-level ground truth for every query.\nAnd this distinction matters. The experiment proves:\nThe reranker materially changes retrieval ordering.\nThe reranker introduces significant latency.\nIncreasing the candidate pool to 20 changes some rankings, but does not automatically improve them.\nSome ranking changes are clearly worse when checked against the source.\nAt least one final generated answer was factually wrong despite sounding plausible.\nIt does not yet justify a universal claim that reranking improves retrieval quality.\nThat restraint is part of the result.\nRetrieval and Answer Quality Are Different Metrics\nThis experiment changed how I think about RAG evaluation.\nThere are at least three separate questions:\n1. Did retrieval find the evidence?\nQuestion → Candidate set → Is the correct evidence present?\nThis measures candidate-generation quality.\n2. Did reranking put the evidence in a useful position?\nCandidate set → Reranker → Did the correct evidence move upward?\nThis measures ranking quality.\n3. Did the LLM answer from the evidence?\nRetrieved evidence → LLM → Is the answer supported?\nThis measures grounded answer quality.\nAnd there is a fourth question:\n4.\nHow did the system behave when sufficient evidence was absent?\nThat matters for the deliberately unanswerable questions.\nA useful evaluation therefore looks more like:\nQuery\n  ↓\nCandidate Retrieval\n  ↓\nReranker Quality\n  ↓\nEvidence Quality\n  ↓\nAnswer Grounding\n  ↓\nAbstention Quality\nA single \"answer accuracy\" number hides too much.\nWhat I Thought Would Happen vs. What Actually Happened\nAssumption 1: Adding a reranker should improve retrieval\nWhat happened: The reranker changed the Top-1 result on 27/35 queries. What I learned: A ranking change is not an improvement metric. It has to be evaluated against ground truth.\nAssumption 2: The vector database would be expensive\nWhat happened: Qdrant retrieval was around 8–9 ms p50. What I learned: The vector database was not the bottleneck I expected.\nAssumption 3: Better semantic relevance means better context\nWhat happened: The reranker sometimes promoted Contents and Index pages. What I learned: Semantic relevance and evidentiary usefulness are different objectives.\nAssumption 4: A correct-looking final answer means retrieval worked\nWhat happened: At least one answer sounded plausible but was wrong when checked against the source. What I learned: Retrieval and generation must be evaluated independently.\nAssumption 5: More candidates should give the reranker more chances to succeed\nWhat happened:\nrerank_candidate_k = 20\nchanged only 6/35 Top-1 results, and most of those changes were not improvements. What I learned: More candidates increase opportunity and search space at the same time.\nWhat I Would Change Next\nThe next improvement should not be \"use a bigger model.\"\nIt should be a more precise evaluation and a targeted retrieval change.\nFirst, I want passage-level ground truth for the 35 questions:\nQuestion\nExpected answer\nGold page(s)\nGold passage/chunk(s)\nAcceptable alternative evidence\nThen I can calculate meaningful retrieval metrics:\nRecall@1\nRecall@5\nRecall@10\nRecall@20\nMRR\nNDCG\nFor reranking, I want to compare multiple candidate-pool sizes and determine where the correct evidence first appears.\nSeparately, I want to evaluate whether the final answer is actually supported by the retrieved evidence.\nAnd for unanswerable questions, I want to measure whether the system avoids producing unsupported answers.\nOnly then can I make a strong claim about whether a particular retrieval configuration is better.\nThe First Structural Hypothesis\nThe observed Contents and Index failures suggest a concrete hypothesis.\nBefore the reranker sees candidates, Knowledge Vault could incorporate document structure:\nQdrant → Candidate chunks → Structural filtering → Reranker → Final context\nPages classified as:\nTable of Contents\nIndex\nCopyright\nGlossary\ncould be down-ranked or excluded when they are unlikely to provide answer-bearing evidence.\nBut I don't want to call that a solution yet. It is a hypothesis.\nThe correct engineering process is:\nFailure → Hypothesis → Implement → Re-run same benchmark → Compare → \nKeep or reject\nThat keeps the system honest.\nThe Bigger Lesson\nKnowledge Vault started as a RAG implementation exercise.\nIt became an evaluation exercise.\nAnd the most useful thing I learned wasn't a particular embedding model, vector database, or reranker. It was that a RAG system has several different meanings of \"relevance.\"\nA passage can be:\nsemantically similar to the query\nranked highly by the retriever\nranked highly by the reranker\nstructurally related to the requested topic\nuseful as evidence\nsufficient to answer the question\nThose are not the same property.\nThe experiment made that visible.\nA Table of Contents can be highly relevant to a question and still be poor evidence.\nA larger candidate pool can expose the reranker to more relevant chunks and more distractors.\nA reranker can substantially change rankings without necessarily improving correctness.\nAnd an LLM can produce a convincing answer even when the retrieval layer has made a poor choice.\nThat is why I no longer think of RAG as:\nPDF → Vector DB → LLM\nI think of it as:\nDocument → Extraction → Chunking → Representation → Candidate Retrieval →\nRanking → Evidence Selection → Generation → Verification\nEvery stage has its own failure modes.\nWhat M1 Actually Revealed\nI started with a working PDF RAG system.\nThen I instrumented it.\nI created a controlled 35-question evaluation.\nI compared retrieval configurations.\nI measured latency instead of guessing where the bottleneck was.\nI increased\nrerank_candidate_k\nfrom the smaller candidate pool to 20 and observed what actually changed.\nI inspected the retrieved pages against the original source.\nAnd I found cases where a component that looked useful at first glance was actually selecting worse evidence.\nI also found an answer that sounded correct but failed source verification.\nThose are not failures\nof the project. Those are the results of the experiment.\nThe system doesn't need to be perfect for the milestone to be successful.\nIt needs to become understandable.\nAnd after this experiment, I understand a lot more about where the retrieval system succeeds, where it fails, what the reranker actually costs, and which assumptions need to be tested next.\nThat is a much more useful result\nthan a benchmark number that simply says:\n\"RAG works.\"\nBecause the real engineering question was never whether I could make a RAG system answer a question.\nIt was whether I could\nbuild one, measure it\n, challenge it, find where it breaks, and use those failures to make the next decision.\nThat is what Knowledge Vault was really about.\n#\nrag\n#\nai\n#\nllm\n#\ninformation-retrieval\n#\nsoftware-engineering",
  "metadata": {
    "title": "Building a RAG System: Retrieval Engineering Lessons",
    "description": "What I learned building and evaluating a local-first PDF RAG system, from retrieval and reranking to latency and failure analysis.",
    "keywords": null,
    "author": null,
    "og:title": "Building a RAG System: Retrieval Engineering Lessons",
    "og:description": "What I learned building and evaluating a local-first PDF RAG system, from retrieval and reranking to latency and failure analysis.",
    "og:url": "https://jaivalsuthar.hashnode.dev/building-knowledge-vault-rag-retrieval-engineering",
    "og:site_name": "Jaival Suthar | Engineering Portfolio",
    "og:image": "https://cdn.hashnode.com/uploads/og-images/6972fc71207454ddbf7903dd/83ccc695-c57b-4104-ac00-0a2e7b7f8875.png",
    "og:image:width": "1200",
    "og:image:height": "630",
    "og:type": "article",
    "twitter:card": "summary_large_image",
    "twitter:creator": "https://x.com/Jaival_Suthar",
    "twitter:title": "Building a RAG System: Retrieval Engineering Lessons",
    "twitter:description": "What I learned building and evaluating a local-first PDF RAG system, from retrieval and reranking to latency and failure analysis.",
    "twitter:image": "https://cdn.hashnode.com/uploads/og-images/6972fc71207454ddbf7903dd/83ccc695-c57b-4104-ac00-0a2e7b7f8875.png",
    "article:published_time": "2026-08-10T18:08:27.111Z",
    "article:modified_time": "2026-08-11T09:35:11.221Z",
    "article:author": "https://jaivalsuthar.hashnode.dev/@JaivalSuthar"
  },
  "paywall": {
    "listed_paid": false,
    "detected": false,
    "marker": "",
    "public_only": false,
    "policy": "付费/会员内容只保留页面公开可见部分，不绕过付费墙"
  },
  "crawl": {
    "success": true,
    "status_code": 200,
    "error": ""
  },
  "code_blocks": [
    {
      "file": "attachments/c40b344fe489/code/body-000.txt",
      "chars": 75,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-001.txt",
      "chars": 91,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-002.txt",
      "chars": 169,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-003.txt",
      "chars": 28,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-004.txt",
      "chars": 66,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-005.txt",
      "chars": 43,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-006.txt",
      "chars": 59,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-007.txt",
      "chars": 89,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-008.txt",
      "chars": 16,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-009.txt",
      "chars": 54,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-010.txt",
      "chars": 60,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-011.txt",
      "chars": 65,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-012.txt",
      "chars": 52,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-013.txt",
      "chars": 116,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-014.txt",
      "chars": 76,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-015.txt",
      "chars": 85,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-016.txt",
      "chars": 22,
      "language": "text"
    },
    {
      "file": "attachments/c40b344fe489/code/body-017.txt",
      "chars": 131,
      "language": "text"
    }
  ],
  "images": [
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/uploads/covers/6972fc71207454ddbf7903dd/36b0e581-3ec0-4b17-9cbe-a84c6c8ef7cc.png",
      "bytes": 1301203,
      "reused": true,
      "local_path": "attachments/c40b344fe489/images/000.png",
      "src": "https://cdn.hashnode.com/uploads/covers/6972fc71207454ddbf7903dd/36b0e581-3ec0-4b17-9cbe-a84c6c8ef7cc.png",
      "alt": "Building Knowledge Vault : From RAG Implementation to Retrieval Engineering",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/uploads/covers/6972fc71207454ddbf7903dd/14a846c9-61dc-4ccb-bfa2-3ac911c68009.png",
      "bytes": 31459,
      "reused": true,
      "local_path": "attachments/c40b344fe489/images/001.png",
      "src": "https://cdn.hashnode.com/uploads/covers/6972fc71207454ddbf7903dd/14a846c9-61dc-4ccb-bfa2-3ac911c68009.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/uploads/covers/6972fc71207454ddbf7903dd/c6d08070-f14d-43d3-a38d-a084b4b8fe4d.png",
      "bytes": 8340,
      "reused": true,
      "local_path": "attachments/c40b344fe489/images/002.png",
      "src": "https://cdn.hashnode.com/uploads/covers/6972fc71207454ddbf7903dd/c6d08070-f14d-43d3-a38d-a084b4b8fe4d.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/uploads/covers/6972fc71207454ddbf7903dd/840ed1c7-a9b0-4fa8-a8d9-2388d0c30277.png",
      "bytes": 16155,
      "reused": true,
      "local_path": "attachments/c40b344fe489/images/003.png",
      "src": "https://cdn.hashnode.com/uploads/covers/6972fc71207454ddbf7903dd/840ed1c7-a9b0-4fa8-a8d9-2388d0c30277.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/uploads/covers/6972fc71207454ddbf7903dd/5eba6bde-94ce-4acb-8928-a775e9c4920f.png",
      "bytes": 10914,
      "reused": true,
      "local_path": "attachments/c40b344fe489/images/004.png",
      "src": "https://cdn.hashnode.com/uploads/covers/6972fc71207454ddbf7903dd/5eba6bde-94ce-4acb-8928-a775e9c4920f.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/uploads/covers/6972fc71207454ddbf7903dd/d3c817f3-b5b2-4c2a-bd8c-65d11ef26383.png",
      "bytes": 13005,
      "reused": true,
      "local_path": "attachments/c40b344fe489/images/005.png",
      "src": "https://cdn.hashnode.com/uploads/covers/6972fc71207454ddbf7903dd/d3c817f3-b5b2-4c2a-bd8c-65d11ef26383.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/uploads/covers/6972fc71207454ddbf7903dd/7a0e6c76-da6a-42c6-bf28-6f7268b0ad02.png",
      "bytes": 13272,
      "reused": true,
      "local_path": "attachments/c40b344fe489/images/006.png",
      "src": "https://cdn.hashnode.com/uploads/covers/6972fc71207454ddbf7903dd/7a0e6c76-da6a-42c6-bf28-6f7268b0ad02.png",
      "alt": "",
      "downloaded": true
    }
  ],
  "audio": [],
  "videos": [],
  "links": [
    {
      "href": "https://jaivalsuthar.hashnode.dev/building-knowledge-vault-rag-retrieval-engineering.md",
      "label": "View as Markdown",
      "type": "a"
    },
    {
      "href": "https://hashnode.com/@JaivalSuthar",
      "label": "J",
      "type": "a"
    },
    {
      "href": "https://x.com/Jaival_Suthar",
      "label": "",
      "type": "a"
    },
    {
      "href": "https://www.linkedin.com/in/jaival-suthar-57195b257/",
      "label": "",
      "type": "a"
    },
    {
      "href": "https://jaivalsuthar.hashnode.dev/series/building-ai-systems-from-scratch",
      "label": "Part of series Building AI Systems from Scratch",
      "type": "a"
    },
    {
      "href": "https://github.com/Jaival-Suthar/pdf-rag-system",
      "label": "Knowledge Vault (M1) — GitHub",
      "type": "a"
    },
    {
      "href": "https://github.com/Jaival-Suthar/inference-lab",
      "label": "Inference Lab (M0) — GitHub",
      "type": "a"
    },
    {
      "href": "https://jaivalsuthar.hashnode.dev/tag/rag",
      "label": "# rag",
      "type": "a"
    },
    {
      "href": "https://jaivalsuthar.hashnode.dev/tag/ai",
      "label": "# ai",
      "type": "a"
    },
    {
      "href": "https://jaivalsuthar.hashnode.dev/tag/llm",
      "label": "# llm",
      "type": "a"
    },
    {
      "href": "https://jaivalsuthar.hashnode.dev/tag/information-retrieval",
      "label": "# information-retrieval",
      "type": "a"
    },
    {
      "href": "https://jaivalsuthar.hashnode.dev/tag/software-engineering",
      "label": "# software-engineering",
      "type": "a"
    }
  ],
  "retry_at": "2026-10-03T08:10:33",
  "repair_version": 2,
  "repaired_at": "2026-10-14T10:51:03Z",
  "source_url": "https://jaivalsuthar.hashnode.dev/building-knowledge-vault-rag-retrieval-engineering",
  "papers": [],
  "actual_video_files": [],
  "body_chars": 25139,
  "expected_body_images": 7,
  "discovery_status": "ok",
  "delivery_status": "ok",
  "missing_reason": "",
  "raw_html": "attachments/c40b344fe489/pages/source.html",
  "local_html": "attachments/c40b344fe489/pages/article.html",
  "body_base_path": "attachments/c40b344fe489/pages/",
  "body_markdown_path": "attachments/c40b344fe489/pages/article.md"
}