Lab 03 — Bedrock Knowledge Bases: Managed RAG-as-a-Service
Phase 24 · Lab 03 · Phase README · Warmup
The problem
You already built a hybrid retriever from scratch (Phase 05). A Bedrock Knowledge Base is the
managed version of that same pipeline: point it at a data source, and Bedrock runs
ingestion (chunk → embed → index into a vector store), retrieval (Retrieve), and
grounded generation with citations (RetrieveAndGenerate) for you. Two things still matter a
lot and are still your decisions: the chunking strategy, and the vector store you bring —
Bedrock KB does not ship its own vector database, it manages a pipeline around one you
provision (OpenSearch Serverless, Aurora PostgreSQL with pgvector, Pinecone, Redis Enterprise
Cloud, or MongoDB Atlas). That "BYO-vector-store, managed pipeline" model is the whole idea, and
you build a faithful miniature of every piece of it: two chunking strategies, a deterministic
embedder + vector index, the sync job lifecycle, and both retrieval APIs.
What you build
| Piece | What it does | The lesson |
|---|---|---|
fixed_size_chunk | fixed-size, overlapping chunks | the default, predictable chunking strategy |
hierarchical_chunk | small CHILD chunks (embedded/matched) under large PARENT chunks (returned for context) | small chunks match better, big chunks generate better — hierarchical buys both |
embed / cosine | deterministic hashing bag-of-words embedding | the Phase 05 convention, reused: a transparent stand-in for Titan/Cohere embeddings |
VectorIndex.search | cosine-ranked search with metadata filtering | the BYO-vector-store contract (OpenSearch/pgvector/Pinecone/...) in miniature |
KnowledgeBase.run_ingestion_job | STARTING → IN_PROGRESS → COMPLETE/FAILED | the real StartIngestionJob status lifecycle |
KnowledgeBase.retrieve | ranked chunks + scores + metadata | the Retrieve API — you supply the query, you get sources |
KnowledgeBase.retrieve_and_generate | retrieve → grounded prompt → injected generator → answer + citations | the RetrieveAndGenerate API — RAG as one call, with provenance |
File map
| File | Role |
|---|---|
lab.py | your implementation (fill the # TODOs) |
solution.py | reference implementation + worked example (python solution.py) |
test_lab.py | 29 tests: chunking, embedding, sync lifecycle, retrieve, metadata filters, hierarchical parent-return, citations |
requirements.txt | pytest only |
Run it
pytest test_lab.py -v # your lab.py (red until you implement)
LAB_MODULE=solution pytest test_lab.py -v # the reference (green)
python solution.py # worked example
Success criteria
-
fixed_size_chunkproduces overlapping spans that cover every token;overlap >= chunk_sizeraisesChunkingError. -
hierarchical_chunkreturns children whoseparent_idrefers to a real parent, and whose token offsets fall within that parent's range. -
embedis deterministic and L2-normalized;cosineof a vector with itself is 1.0. -
A sync job moves
STARTING → IN_PROGRESS → COMPLETE, orFAILEDon a bad configuration — never silently succeeds with a broken strategy. -
retrieveranks by cosine score, respectstop_k, and metadatafiltersexclude non-matching chunks. -
For a hierarchical knowledge base,
retrievereturns the matched child's parent text ascontent.text(wider context) whilelocation.chunkIdstill names the child. -
retrieve_and_generatereturns an answer with citations that reference the exact retrieved chunks, and the prompt passed to the injected generator visibly contains the retrieved source text (proving it's grounded, not a bare question). -
All 29 tests pass under both
labandsolution.
How this maps to the real stack
- Chunking strategies are real, named Bedrock KB options. Fixed-size-with-overlap is the
default; hierarchical (parent-child) chunking is a real, selectable strategy specifically
because embedding similarity works best over small, focused chunks while the generator wants
as much surrounding context as it can get — hierarchical chunking is the one config knob that
buys both without a tradeoff, and
retrieve's parent-lookup-on-child-match models exactly how Bedrock KB resolves it. (Bedrock also offers a semantic chunking strategy that splits on embedding-distance breakpoints rather than a fixed token count; this lab implements the two strategies named in the brief.) - The BYO-vector-store model is real and important: Bedrock Knowledge Bases do not include a
proprietary vector database. You provision OpenSearch Serverless (the common default), Aurora
PostgreSQL with the pgvector extension, Pinecone, Redis Enterprise Cloud, or MongoDB Atlas, and
Bedrock manages ingestion and querying against it.
VectorIndexhere plays that role — a pluggable store behind a stableadd/searchcontract. RetrievevsRetrieveAndGenerateare the two real Bedrock Knowledge Base Runtime APIs.Retrievereturns ranked source chunks and lets YOU build the prompt (useful when the KB is one of several context sources feeding a larger agent).RetrieveAndGeneratedoes retrieval AND generation AND citation assembly as one managed call — the real response shape includesoutput.textplus acitationsarray where each entry ties a piece of the generated text back to theretrievedReferencesthat grounded it, whichKnowledgeBase.retrieve_and_generatemirrors closely enough to reason about a real citation UI.- Metadata filtering on
retrievematches a real KB feature: you tag ingested documents with metadata (category, source, ACL tags, date) and filter retrieval by it — the same mechanism that makes a Knowledge Base usable for coarse multi-tenant or access-scoped RAG. - The sync job lifecycle (
STARTING → IN_PROGRESS → COMPLETE/FAILED) is the realStartIngestionJobstatus vocabulary: ingestion is asynchronous, and a production caller pollsGetIngestionJob(or reacts to an EventBridge event) rather than blocking —run_ingestion_jobbeing a single synchronous call here is the deterministic, testable stand-in for that async process. - The embedder is injected exactly like every "model" in this track: real Knowledge Bases call a Titan Embeddings or Cohere Embed model (via Bedrock, naturally) to vectorize both documents and queries; the deterministic hashing bag-of-words embedder (identical convention to Phase 05's hybrid retriever) is the honest, offline stand-in that keeps ranking and citation correctness testable without a real embedding call.
Limits of the miniature. Real vector stores use approximate nearest-neighbor indexes (HNSW,
IVF) for sub-linear search at scale; VectorIndex.search is brute-force cosine, the same
honest-but-unscalable tradeoff Phase 05 makes for the same pedagogical reason. Real chunking also
offers a semantic strategy (breakpoints by embedding-distance) this lab doesn't implement. Real
ingestion jobs read from S3/Confluence/SharePoint/web-crawler data sources with format-specific
parsers (PDF, HTML, Markdown); ours takes pre-extracted text.
Extensions (your own machine)
- Add a semantic chunking strategy: split at sentence boundaries where consecutive-sentence embedding similarity drops below a threshold, instead of a fixed token count.
- Swap
VectorIndexfor a version backed by an HNSW-style approximate index (Phase 05's dense index) and confirmretrieve's interface is unchanged — proving the vector store is pluggable. - Add a reranking step (Phase 05's cross-encoder stand-in) between
retrieveandretrieve_and_generateto see whether it changes which chunks get cited. - Wire
retrieve_and_generate'sgenerate_fnto Lab 01'sBedrockRuntime.converse, so a single call goes through the unified invocation layer AND the KB retrieval layer together.
Interview / resume signal
"Built a miniature Bedrock Knowledge Base: fixed-size and hierarchical parent-child chunking, a deterministic embedding + cosine vector index with metadata filtering standing in for a BYO-vector-store (OpenSearch/pgvector/Pinecone), an async data-source sync job lifecycle, and both the
RetrieveandRetrieveAndGenerateAPIs — proving citations trace back to the exact source chunks and that hierarchical chunking returns wider parent context on a precise child match, without a single real embedding call in the test suite."