IMEN891K / IDSC501 / AXGS-501 · Data Engineering · Fall 2026

16. RAG

Sangwoo Mo · POSTECH
Industrial & Management Engineering

Where We Are

Collection and PreparationPart I · Lec 2–5StoragePart II · Lec 6–13ML / LLM SystemsPart III · Lec 12–13RAGPart III · Lec 14AgentsPart III · Lec 15
Part III connects stored data to models, retrieval, and actions

Outline: RAG

01How RAG Answers Questionsfind supporting text beforegenerating an answer02Preparing Documents forSearchturn source documents intosearchable pieces03Finding Evidence andChecking Answersfind relevant text and verify theanswer

01 · How RAG Answers Questions

01How RAG Answers Questionsfind supporting text beforegenerating an answer02Preparing Documents forSearchturn source documents intosearchable pieces03Finding Evidence andChecking Answersfind relevant text and verify theanswer

§ Embeddings and Storage

How RAG Answers QuestionsEmbeddings and StorageRepresent and find documentsLoadingSimilarity / indexesAnswer ConstructionGenerate from supplied evidenceGenerationContext composition

Recap: LLM WorkflowA retrieval step supplies external evidence to the model

QuestionrequestRetrievalrelevant recordsPromptquestion + evidenceLLManswer
Question → Retrieval → Prompt → LLM

Retrieval-Augmented GenerationRAG conditions generation on evidence retrieved at query time

Retrieval-Augmented Generation
Indexing prepares documents; retrieval supplies answer context

Why RetrievalPrivate and changing facts need evidence outside model parameters

QuestionWhat torque doesmanual v4 specify?Model parameterswhat training text said,possibly an older versionRetriever + manual v4the approved document,read at question timeRecollectionno source to checkAnswer with evidencequotes manual v4
Retrieval reads the approved manual at question time

Document LoaderA loader supplies text and source metadata to the indexing pipeline

Sourceapproved manualLoadertext / metadataIndex inputdocument records
The source reference travels with the loaded content

Text EmbeddingsAn embedding maps text into a vector used for similarity search

Text Embeddings
Similar wording or meaning can produce nearby vectors

Embedding SimilaritySemantic similarity can differ from exact word overlap

Embedding Similarity
The two animal sentences are closer than the car sentence

Cosine SimilarityCosine similarity compares vector direction

cos(q, d) = (q · d) / (‖q‖ ‖d‖)qd₁ · cos 0.991.6·d₁ · cos 0.99d₂ · cos 0.55Direction, not lengthd₁ and a longer vector along d₁get the same cosineTwo cautionsnormalize if the index scores dot producta score of 0.99 is not a probability
Same direction, same score, whatever the length

Embedding InterfaceUse one compatible embedding space for documents and queries

import { OllamaEmbeddings } from '@langchain/ollama';

const embeddings = new OllamaEmbeddings({
  model: '<installed-embedding-model>',
  baseUrl: 'http://127.0.0.1:11434'
});
const documents = await embeddings.embedDocuments([
  'P-17 pressure alarm threshold is 5 bar.'
]);
const query = await embeddings.embedQuery('P-17 pressure threshold');
// Use one embedding model and compatible preprocessing for both.

Vector StorageStore vectors together with the original chunks and metadata

Vector Storage
A vector is a search key; the text remains answer evidence

Vector SearchA query vector retrieves nearby chunk vectors

Vector Search
The query selects candidate documents through the vector index

Vector Index ChoicesSelect the index using collection size, latency, and recall

Exact scanqueryscores every vector: work grows with sizeHNSWqueryhops a neighbour graph: memory vs effortIVFqueryprobes a few clusters: recall depends on probes
Index recall and embedding relevance are separate questions

Caveat: Embedding VersionA model change requires a compatible query and document index

Index built with model AQueries embedded with model Btorquepressurealarmflange"torque limit"Same 768 dimensions, different coordinate systems: the nearest neighbour is meaninglessRe-embed every document, or keep both indexes until the switch
Mixing models compares points from two different spaces

§ Answer Construction

How RAG Answers QuestionsEmbeddings and StorageRepresent and find documentsLoadingSimilarity / indexesAnswer ConstructionGenerate from supplied evidenceGenerationContext composition

Answer GenerationThe model receives the question and selected evidence

Answer Generation
The answer prompt receives retrieved chunks, not the whole corpus

Context RecordKeep the selected text and its source reference together

context = [
  {'source_id': 'manual_v4:p12',
   'text': 'Steel flange: tighten to 12 N·m.'},
  {'source_id': 'manual_v4:p13',
   'text': 'Polymer flange: tighten to 8 N·m.'}
]

Grounded Answer PromptAsk for evidence references and an explicit unsupported outcome

# Prompt template
Use only the evidence below for factual claims.
If the evidence is insufficient, say what is missing.
Attach source IDs to the claims they support.

Question: {question}
Evidence: {context}

Context Composition ChoicesChoose how retrieved evidence enters generation

Stuffevidence fits in contextLLManswerone generation callMap–reducemany independent chunksLLM per chunkcombineanswercan lose cross-chunk linksRefineordered evidenceLLMLLMLLManswersequential; order-sensitiveMap–rerankone candidate answer per chunk.6.9.4maxanswercompare answer scores
Four ways evidence enters generation

02 · Preparing Documents for Search

01How RAG Answers Questionsfind supporting text beforegenerating an answer02Preparing Documents forSearchturn source documents intosearchable pieces03Finding Evidence andChecking Answersfind relevant text and verify theanswer

§ Loading

Preparing Documents for SearchLoadingRecover source recordsSource recordsParser validationSplittingCreate searchable chunksChunk boundariesMetadata

Document LoadingA loader reads a source and returns content with metadata

Document Loading
Loaders normalize heterogeneous sources into documents

Document RecordPreserve identity and provenance alongside the text

document = {
  'doc_id': 'pump_manual_v4',
  'text': extracted_text,
  'metadata': {
    'source': 'manuals/pump_v4.pdf',
    'page': 12, 'version': 4,
    'access_group': 'maintenance'
  }
}

Loader ValidationLoading success does not imply faithful content extraction

PDFcolumns, tables, page numbersloaderextracted textreading order kept? table cells paired?Web pagemenu, footer, articleloaderextracted textnavigation removed? article boundary right?Transcripttwo speakers over timeloaderextracted textspeaker turns and timestamps kept?Databaserows that changeloaderextracted textstable keys and update markers kept?
Inspect extracted content before it enters the index

Caveat: PDF Reading OrderFlattening a table can detach a number from its meaning

Table in the PDFPumpTorquePump A12 N·mPump B18 N·mflattenText the index receivesPump A Pump B 12 18Which torque belongs to Pump A?Keep the layout, or store the table as rows: pump → torque
Flattening detaches 12 from Pump A

§ Splitting

Preparing Documents for SearchLoadingRecover source recordsSource recordsParser validationSplittingCreate searchable chunksChunk boundariesMetadata

Document SplittingSplitting creates the retrieval units from loaded documents

Document Splitting
Chunk boundaries determine which evidence travels together

Splitting StrategiesChoose boundaries that match the document structure

The documentHeadingParagraph 1Paragraph 2Torque tableParagraph 3Fixed sizeevery 210 characterscan cut a table in halfRecursiveparagraph, then sentencesplits at paragraph endsStructure-awaresection and table edgeskeeps the table whole; needs a parser
The chunk must keep the evidence an answer needs

Chunk Size and OverlapOverlap keeps some boundary context at the cost of duplication

Chunk Size and Overlap
Neighboring chunks share a controlled overlap

Splitting the ManualRun a real text splitter and inspect the retained metadata

const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 85, chunkOverlap: 15
});
const chunks = await splitter.splitDocuments(manuals);
return chunks.map(d => ({id:d.metadata.id,
  text:d.pageContent, length:d.pageContent.length}));

Demo: Manual ChunksChange chunk size and overlap; inspect text and source IDs

  1. Set chunkSize to 35 and inspect split conditions.
  2. Compare chunkOverlap 0 and 15. Keep overlap smaller than chunk size.
Run code to inspect its output.
Ready
LangChain.js · JavaScript · Ctrl/Cmd+Enter · Real library execution with local data

Answer: Manual ChunksThe baseline output comes from executing the shown code

chunkSize 85 · chunkOverlap 15 · each bar is one chunk; shaded text appears in bothp17-v4P-17 pressure alarm threshold is 5 bar. Stop if pressure exceeds 5 bar. Use CelsiusUse Celsius for temperature.p17-v2P-17 pressure alarm threshold is 7 bar. This obsolete version must not guide thenot guide the current alarm.p18-v3P-18 pressure alarm threshold is 3 bar. This rule applies to P-18 only.p17-vibrationP-17 vibration inspection: record vibration and temperature before changing thechanging the operating speed.
Overlap repeats a few words; split conditions still separate

Chunk IdentityAssign each chunk a stable source reference

chunk = {
  'chunk_id': 'pump_manual_v4:p12:c2',
  'doc_id': 'pump_manual_v4',
  'text': 'Tighten the flange bolts to 12 N·m.',
  'metadata': {'page': 12, 'section': 'Flange assembly'}
}

Caveat: Broken EvidenceSplitting a condition from an instruction can reverse the answer

Manual sentenceIf the pump is running, do notopen the inspection cover.chunk boundaryQuestion: May I open the cover?retrieves chunk 2 onlyAnswer: open the inspection coverthe condition stayed in chunk 1Fix: split at sentence ends,or attach the parent section
A chunk boundary can drop the condition

03 · Finding Evidence and Checking Answers

01How RAG Answers Questionsfind supporting text beforegenerating an answer02Preparing Documents forSearchturn source documents intosearchable pieces03Finding Evidence andChecking Answersfind relevant text and verify theanswer

§ Retrieval

Finding Evidence and Checking AnswersRetrievalSelect evidence for a questionSearch / filtersRanking / contextEvaluation and UpdatesCheck quality and freshnessConversational QAEvaluation / refresh

Retriever InterfaceA retriever returns evidence records for a query

QuestiontextCandidate searchscores + filtersSelected chunkstext + metadata
Question → Candidate search → Selected chunks

Search MethodsLexical and dense search fail on different queries

Query: "P-17 torque limit"Lexical / BM251P-17 torque limit table2P-17 alarm thresholdsnot found: "tightening limit"exact IDs; misses rewordingDense1Tightening limit for P-172P-71 torque limit3P-17 torque limit tableparaphrase; similar wrong IDsHybrid1P-17 torque limit table2Tightening limit for P-173P-71 torque limitmerges lists; still needs tuning
Exact IDs need lexical matching; rewording needs dense search

Caveat: Similar but WrongA high similarity score can retrieve the wrong entity

Embedding spacequery: P-17 torque limitP-71 torque limit · 0.94P-17 torque limit · 0.91Top hit is the wrong pumptwo IDs differ by one transposed digit,the sentences are almost identicalExact constraintfilter equipment_id = P-17before ranking by similarity
The closest vector belongs to the wrong pump

Retrieval RequestCarry constraints separately from the natural-language question

query = {
  'text': 'What is the flange torque?',
  'filters': {'equipment_id': 'P-17', 'approved': True},
  'top_k': 5
}
# Conceptual interface; actual filter syntax depends on the store.

Metadata FiltersRestrict candidate records using source properties

Candidate chunksP-17v4 approvedenstaffP-71v4 approvedenstaffP-17v3 retiredenstaffP-17v4 approvedkostaffP-17v4 approvedenadmin onlyequipment_id = P-17right entityapproved, current versionno obsolete draftslanguage = enrequested evidencecaller may readaccess policyEvidenceP-17 · v4 · en · staff
Each mismatched tag removes a candidate

Searching the ManualRetrieve documents with a real vector store and an exact filter

const chunks = await new RecursiveCharacterTextSplitter({
  chunkSize: 100, chunkOverlap: 15
}).splitDocuments(manuals);
const store = await MemoryVectorStore.fromDocuments(chunks, makeEmbeddings());
const k = 2;
const filter = d => d.metadata.equipment === "P-17" && d.metadata.approved;
const hits = await store.similaritySearch("P-17 pressure alarm threshold", k, filter);
return {embedding:EMBEDDING_MODE, hits:hits.map(d => ({
  source:d.metadata.id, text:d.pageContent}))};

Demo: Filtered RetrievalChange k and the filter; inspect which version becomes evidence

  1. Remove approved from the filter. Does the obsolete 7 bar rule appear?
  2. Search P-18 and update the equipment filter together.
Run code to inspect its output.
Ready
LangChain.js · JavaScript · Ctrl/Cmd+Enter · Offline replay uses fixed responses; local mode calls your model

Answer: Filtered RetrievalThe baseline output comes from executing the shown code

{"embedding": "Local token-presence vectors (not learned)", "hits": [{"source": "p17-v4", "text": "P-17 pressure alarm threshold is 5 bar. Stop if pressure exceeds 5 bar. Use Celsius for temperature."}, {"source": "p17-vibration", "text": "P-17 vibration inspection: record vibration and temperature before changing the operating speed."}]}

Query ReformulationA rewritten query can recover evidence missed by the original wording

OriginalWhy does P-17 shake?RewriteP-17 vibration diagnosisSearchmanual / logsMergededuplicated candidates
Original → Rewrite → Search → Merge

Caveat: Query DriftA rewrite can silently change the question

Original questionalarms before the repairRewritten queryalarms after the repairone word changed the time windowrepairasked: alarms beforeretrieved: alarms afterKeep the original question and the rewrite in the trace
Retrieval succeeded on the wrong question

Evidence Selection MethodsChoose the operation by what the candidate set still lacks

Rerankingchanges the orderCABACBuse whenrelevant evidence ranked too lowMMRchanges the selected setAA′A″DADuse whentop hits repeat the same contentCompressionchanges the text spansneeded spanneeded spanuse whenchunks carry excess context
Choose by what the candidate set still lacks

RerankingA reranker scores the query together with each candidate

QuestionRetrieverfast, one vector eachtop 50 chunksRerankerreads question + chunktogether, scores eachchunk · .92chunk · .88chunk · .71fits the budgetA chunk the retriever missed cannot be recovered here
Reranking reorders only what retrieval found

Maximum Marginal RelevanceMMR balances query relevance with novelty among selected chunks

Maximum Marginal Relevance
Diverse candidates reduce repeated evidence

MMR ObjectivePenalize similarity to evidence that has already been selected

argmax over d in C∖S of λ·sim(q,d) − (1−λ)·max over s in S of sim(d,s)query qnear-duplicates of s₁s₁s₂: relevant and newnovel but irrelevantλ near 1relevance only: picks the duplicatesLower λpenalizes similarity to what is chosenC candidates, S selected set
MMR trades relevance against repetition

Context CompressionCompression removes irrelevant text before generation

Context Compression
Only selected evidence enters the answer prompt

Caveat: Compression LossCompressing away a condition can change a safety instruction

Source chunkTighten to 12 N·monly for the steel flangedropped by compressionCompressedTighten to 12 N·mNow reads as a rule for every flangecheck critical facts against the source span
Compression removed the condition

Complete RAG ApplicationSearch approved records, build context, and invoke the answer chain

const chunks = await new RecursiveCharacterTextSplitter({
  chunkSize: 100, chunkOverlap: 15
}).splitDocuments(manuals);
const store = await MemoryVectorStore.fromDocuments(chunks, makeEmbeddings());
const retriever = RunnableLambda.from(q => store.similaritySearch(q, 2,
  d => d.metadata.equipment === "P-17" && d.metadata.approved));
const prompt = ChatPromptTemplate.fromMessages([
  ["system", "Use only evidence. Cite source IDs. If missing, return unknown."],
  ["human", "Question: {question}\nEvidence: {context}"]
]);
const model = makeModel(["P-17: 5 bar [p17-v4]"]);
const chain = prompt.pipe(model).pipe(new StringOutputParser());
const question = "What is the P-17 pressure alarm threshold?";
const hits = await retriever.invoke(question);
const context = hits.map(d => `[${d.metadata.id}] ${d.pageContent}`).join("\n");
return {mode:MODEL_MODE, sources:hits.map(d=>d.metadata.id),
  answer:await chain.invoke({question,context})};

§ Evaluation and Updates

Finding Evidence and Checking AnswersRetrievalSelect evidence for a questionSearch / filtersRanking / contextEvaluation and UpdatesCheck quality and freshnessConversational QAEvaluation / refresh

Question Answering PipelineA QA pipeline combines a retriever with an answer generator

Question Answering Pipeline
A question produces evidence before an answer

Conversational RetrievalResolve the current question before retrieving new evidence

Conversational Retrieval
Conversation history helps form the retrieval question

Caveat: Follow-Up AmbiguityHistory can resolve a pronoun to the wrong equipment

User: Compare P-17 and P-71.Assistant: …both limits…User: What is its torque?P-17 ?P-71 ?Clarifyask which pumpbefore retrievingGuessing one pump turns an exact filter into confident error
"Its" points to two pumps

RAG Evaluation LayersDiagnose the first stage that loses the required evidence

Loadingparsed correctly?Retrievalright chunk in context?Generationclaims follow the context?AnswerSystem: latency, cost and access behaviour across the whole pathA wrong answer: find the first stage where the needed evidence disappeared
A wrong answer may start with parsing or retrieval

Retrieval Recall at kRecall measures how many relevant chunks are retrieved

Recall@k = |Relevant ∩ Retrieved_k| / |Relevant|Retrieved, k = 51v4 pressure rule2P-18 rule3v4 stop condition4vibration check5v2 obsolete ruleRelevant (labelled first)v4 pressure rulev4 stop conditionv4 unit note · missedRecall@5 = 2 / 3two of three relevant chunksreached the contextOne required source: recall becomes a hit-or-miss check
Relevance labels come before the score

Answer EvaluationCorrectness and groundedness are different questions

Reference12 N·m for steelContextmanual v4, steel flange: 12Answerclaim: 12 N·m [manual v4]or: "not in the manual"CorrectnessGroundednessCitation accuracythe cited source says itAbstentionno evidence → refuse
A cited answer still needs claim-level checks

Case study: Versioned ManualsA correct fact from an obsolete manual can be the wrong answer

Query: approved manual, steel flangev3 · retiredflange: Steeltorque: 10 N·mwrong versionv4 · approvedflange: Steeltorque: 12 N·mthe answerv4 · approvedflange: Polymertorque: 8 N·mwrong material
Entity, material and version decide the answer

Index RefreshSource changes must reach chunks, vectors, and cached answers

Detectnew / changed / deletedTransformparse / splitIndexupsert / deleteValidatequeries / permissions
Detect → Transform → Index → Validate

Caveat: Stale ChunksDeleting a source file may leave its indexed text searchable

Source folderVector indexmanual v4.pdfmanual v3.pdf · deletedv4 chunk 1v4 chunk 2v4 chunk 3v3 chunk 1 · still searchablev3 chunk 2 · still searchableno delete eventTrack source identity so a deletion removes its chunks
Deleting the file left its chunks searchable

RAG Release BundleVersion the corpus and index together with the answer system

Corpussource versionsIndexsplitter / embeddingsRetrievalfilters / rankingGenerationprompt / model
Corpus → Index → Retrieval → Generation

Takeaway: Retrieve the evidence before trusting the answer

1Index content together with identity, versions, and access metadata
2Evaluate retrieval before changing the answer prompt
3Return supported answers and keep the index current
← → / Space · S notes · O overview · F fullscreen · number + Enter
Speaker notes