RAG with Multiple Documents
Learn how to build a RAG system that can search across an entire collection of documents while preserving document identity, metadata, permissions, and source context.
By the end of this lesson, you will be able to:
- Understand how multi-document RAG differs from single-document RAG.
- Design an ingestion pipeline for many files.
- Preserve document-level and chunk-level metadata.
- Index chunks from different documents in one searchable collection.
- Retrieve and rank evidence across multiple documents.
- Build grounded context without mixing unrelated sources.
- Handle versions, duplicates, freshness, and access control.
Why Multiple Documents?
Real knowledge assistants rarely depend on one file. A company may have hundreds of policies, product manuals, FAQs, contracts, technical guides, and reports. The user's question may require information from one document or several documents.
Single-Document vs Multi-Document RAG
One knowledge source
The pipeline can focus on one file, such as a single handbook or report.
Shared knowledge collection
Retrieval must choose the right evidence across many sources and preserve their identities.
The retrieval problem becomes more interesting as the collection grows: irrelevant documents, duplicate content, outdated versions, and permission boundaries can all affect the answer.
The Multi-Document RAG Pipeline
Separate the system into ingestion time and query time. Ingestion happens when documents enter or change in the knowledge base. Query time happens for every user question.
1. Discover files
Find PDFs, text files, web exports, manuals, and other supported sources.
2. Identify documents
Assign a stable document ID and capture source metadata.
3. Extract + clean
Convert each file into reliable machine-readable content.
4. Chunk + index
Create chunks, embeddings, and searchable records.
5. Process question
Normalize the query and apply relevant user or tenant constraints.
6. Retrieve candidates
Search across the authorized document collection.
7. Rerank + select
Improve ordering and choose evidence that fits the context budget.
8. Generate
Answer using the selected evidence and cite its sources.
Document Identity Is Essential
When thousands of chunks share one vector collection, you still need to know exactly where every chunk came from.
document = {
"document_id": "travel-policy-2026",
"title": "Domestic Travel Policy",
"source": "policies/travel-policy-2026.pdf",
"version": "2026.1",
"department": "finance",
}
chunk = {
"text": "Domestic travel reimbursement is limited to ...",
"document_id": document["document_id"],
"title": document["title"],
"version": document["version"],
"department": document["department"],
"page": 7,
}
print(chunk["document_id"], chunk["page"])Choose a Metadata Schema
Metadata lets you filter, group, debug, cite, and reason about documents before and after vector search.
A stable identifier for the source document.
Human-readable source information.
Helps prevent stale information from winning retrieval.
Useful for citations and debugging.
Supports scoped retrieval in multi-team systems.
Helps enforce which evidence a user may retrieve.
Process Many Documents
A simple ingestion loop can process a collection one document at a time. Production systems usually add queues, retries, change detection, and batch processing.
documents = [
{"id": "policy-01", "path": "policies/leave.txt"},
{"id": "policy-02", "path": "policies/travel.txt"},
{"id": "manual-01", "path": "manuals/product.txt"},
]
records = []
for document in documents:
with open(document["path"], "r", encoding="utf-8") as file:
text = file.read()
words = text.split()
for start in range(0, len(words), 300):
chunk = " ".join(words[start:start + 300])
if chunk:
records.append({
"document_id": document["id"],
"text": chunk,
})
print("Documents:", len(documents))
print("Chunks:", len(records))This example demonstrates the data flow. A production ingestion service would also create embeddings and persist the records in a vector database.
One Collection or Separate Collections?
- Simple global search
- Easy to search across related documents
- Metadata filters become important
- Good for many related sources
- Useful for strong isolation
- Can simplify tenant boundaries
- Requires choosing which collection to search
- More infrastructure to manage
There is no universal rule. Choose the design based on tenant isolation, document relationships, scale, operational complexity, and retrieval requirements.
Embed Chunks from Every Document
All chunks that need to be compared in the same vector search should use a compatible embedding representation.
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("all-MiniLM-L6-v2")
texts = [record["text"] for record in records]
vectors = model.encode(texts, normalize_embeddings=True)
print("Chunks:", len(texts))
print("Vector dimensions:", vectors.shape[1])The model name is a teaching example. In a real application, select an embedding model appropriate for your language, quality, latency, and infrastructure requirements.
Retrieve Across the Whole Collection
A user's question should be compared against the indexed chunks from all documents the user is allowed to access.
import numpy as np
question = "What is the domestic travel reimbursement limit?"
query_vector = model.encode(question, normalize_embeddings=True)
scores = vectors @ query_vector
best_indices = np.argsort(scores)[::-1][:5]
for index in best_indices:
record = records[index]
print(round(float(scores[index]), 3), record["document_id"])
print(record["text"][:180], "\n")Use Metadata Filtering Before Retrieval
Do not search every document if the application already knows a useful constraint. Filtering can reduce noise and prevent unauthorized sources from entering the candidate set.
allowed = [
record for record in records
if record.get("department") in {"finance", "hr"}
]
print("Authorized candidates:", len(allowed))In a real vector database, apply supported metadata filters as part of the search request rather than loading the entire collection into application memory.
Top-K Can Return Duplicate Sources
Suppose the top five chunks all come from the same document. That may be correct, but it can also leave useful evidence from another relevant document out of the context.
The answer may require the current policy and the previous version.
Retrieval can over-represent one document.
Group or diversify candidates when the task benefits from multiple sources.
Optimize for answer quality, not simply the largest raw Top-K.
Rerank the Candidates
For larger collections, a first-stage vector search can retrieve a candidate set and a reranker can reorder those candidates using the full query and passage together.
This two-stage approach balances broad recall with more precise final ranking.
Build Context Without Losing Sources
Before sending evidence to an LLM, format each selected chunk with enough metadata to keep its origin visible.
selected = [
{
"title": "Domestic Travel Policy",
"document_id": "travel-policy-2026",
"page": 7,
"text": "Domestic travel reimbursement is limited to ...",
},
{
"title": "Expense FAQ",
"document_id": "expense-faq",
"page": 3,
"text": "Receipts are required for claims above ...",
},
]
context = "\n\n".join(
f"Source: {item['title']} | page {item['page']}\n{item['text']}"
for item in selected
)
print(context)When One Question Needs Multiple Documents
Some questions are naturally cross-document questions. The system should allow evidence from multiple sources when the task requires it.
Old vs new policy
Retrieve both versions and explain the change.
Manual + FAQ
Combine technical instructions with support guidance.
Multiple reports
Compare findings across several reports.
Policy + procedure
Connect the rule with the steps needed to follow it.
Agreement + amendment
Retrieve related clauses while preserving document versions.
Several manuals
Find evidence across product generations or components.
Handle Versions and Duplicates
Multiple documents often contain the same information. Old versions can be especially dangerous because they may look highly relevant to the query.
The old policy ranks above the current policy.
The same document is indexed more than once.
Some chunks are updated while old chunks remain searchable.
The system cannot distinguish current and stale records.
Access Control in Multi-Document RAG
A retrieval system must not expose content merely because it is semantically relevant. Authorization is a separate requirement from relevance.
Search everything β filter later
Sensitive chunks can enter logs, prompts, caches, or model context before the final filter.
Authorize β retrieve β generate
Apply tenant, role, or document permissions before candidate evidence is returned.
For multi-tenant systems, treat tenant or access scope as a hard retrieval boundary rather than a soft ranking preference.
Document-Level and Chunk-Level Retrieval
Sometimes the first question is not βwhich chunk?β but βwhich documents are relevant?β A system can use a two-step approach: discover useful documents, then retrieve precise chunks inside them.
This can be useful when documents have strong identities, such as product manuals, case files, projects, or policy collections.
Cross-Document Context Assembly
More retrieved chunks do not automatically mean a better answer. Select evidence that is relevant, non-redundant, recent, authorized, and small enough to fit the context budget.
- Many near-duplicate chunks
- Old and new versions mixed
- Unrelated documents
- No source labels
- Relevant evidence only
- Useful source diversity
- Current authorized versions
- Clear document/page labels
Common Multi-Document RAG Problems
A semantically similar source is relevant to the words but not to the user's actual intent.
The model combines conflicting statements without recognizing their origins.
Old versions remain in the candidate set.
Large Top-K values add noise and increase cost.
The application cannot explain which document produced the answer.
Retrieval crosses a tenant or access boundary.
Evaluate Retrieval Across the Collection
Test more than whether the final answer sounds good. Build questions whose correct evidence is known and measure whether the right documents and chunks are retrieved.
Checks whether relevant chunks appear in the top K.
Rewards systems that place a relevant result near the top.
Useful when multiple results have different relevance levels.
Check document and page attribution separately.
A Simple End-to-End Mental Model
def multi_document_rag(question, documents):
# 1. Keep only documents the user may access.
authorized = [
doc for doc in documents
if doc["access"] == "allowed"
]
# 2. Retrieve relevant chunks across the collection.
candidates = retrieve(question, authorized)
# 3. Improve ranking and remove redundant evidence.
selected = rerank_and_select(question, candidates)
# 4. Build source-aware context.
context = "\n\n".join(
f"[{item['title']} | page {item['page']}] {item['text']}"
for item in selected
)
# 5. Generate only from the supplied evidence.
return generate_answer(question, context)When Multi-Document RAG Works Well
Internal assistant
Search policies, guides, FAQs, and procedures.
Product assistant
Search manuals and troubleshooting documents across product lines.
Research assistant
Compare evidence from many papers or reports.
Contract search
Retrieve clauses across authorized agreements and amendments.
Policy assistant
Search current financial procedures and controlled documents.
Course assistant
Answer questions from a collection of course materials.
Production Checklist
Stable IDs
Every document and chunk has a traceable identity.
Validate extraction
Check text, tables, OCR, and document structure.
Preserve context
Keep version, page, source, tenant, and access information.
Authorize retrieval
Enforce permissions before protected evidence reaches context.
Measure ranking
Evaluate Recall@K, MRR, NDCG, and source accuracy.
Manage updates
Re-index changed files and retire obsolete versions.
Practice: Design a Multi-Document RAG System
You have 500 company documents across HR, Finance, Engineering, and Sales. A user asks: βWhat is the current remote-work allowance, and who is eligible?β
Design the retrieval flow
Write down: 1) what metadata you would store, 2) how you would restrict the search to authorized documents, 3) how you would handle old policy versions, 4) how you would retrieve and rerank candidates, and 5) how you would show the source documents in the final answer.
Quick Quiz
1. What makes multi-document RAG different from single-document RAG?
The system must retrieve useful evidence across a collection while preserving source identity, versions, metadata, and access boundaries.
2. Why is document_id important?
It keeps every chunk traceable to its source and supports citations, debugging, updates, and filtering.
3. Why can a large Top-K hurt?
It can add irrelevant or duplicate evidence, increase context size, and make the model's job harder.
4. Why should access control happen before retrieval results enter model context?
Unauthorized evidence should never become available to downstream prompts, logs, caches, or model generation.
5. Why can old versions be dangerous?
They may be semantically relevant and rank highly even though the current policy has changed.
6. When might document-level retrieval help?
When documents have strong identities and the application benefits from first selecting relevant sources before finding precise chunks.
30-Second Recap
Gather many sources.
Assign document IDs.
Create retrieval units.
Store searchable vectors.
Apply authorization.
Search the collection.
Select best evidence.
Answer with sources.
Key Takeaways
- Multi-document RAG searches a collection rather than a single source.
- Every chunk should retain document identity and useful metadata.
- Metadata supports filtering, version control, citations, debugging, and access control.
- Top-K retrieval may need reranking, source diversity, or document-level selection.
- Old versions and duplicate documents can create misleading retrieval results.
- Authorization should be enforced before protected evidence reaches model context.
- Evaluate both retrieval quality and source attribution, not only final answer quality.
- Good multi-document RAG is a controlled evidence-selection system, not simply a larger prompt.