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

PieceWhat it doesThe lesson
fixed_size_chunkfixed-size, overlapping chunksthe default, predictable chunking strategy
hierarchical_chunksmall CHILD chunks (embedded/matched) under large PARENT chunks (returned for context)small chunks match better, big chunks generate better — hierarchical buys both
embed / cosinedeterministic hashing bag-of-words embeddingthe Phase 05 convention, reused: a transparent stand-in for Titan/Cohere embeddings
VectorIndex.searchcosine-ranked search with metadata filteringthe BYO-vector-store contract (OpenSearch/pgvector/Pinecone/...) in miniature
KnowledgeBase.run_ingestion_jobSTARTING → IN_PROGRESS → COMPLETE/FAILEDthe real StartIngestionJob status lifecycle
KnowledgeBase.retrieveranked chunks + scores + metadatathe Retrieve API — you supply the query, you get sources
KnowledgeBase.retrieve_and_generateretrieve → grounded prompt → injected generator → answer + citationsthe RetrieveAndGenerate API — RAG as one call, with provenance

File map

FileRole
lab.pyyour implementation (fill the # TODOs)
solution.pyreference implementation + worked example (python solution.py)
test_lab.py29 tests: chunking, embedding, sync lifecycle, retrieve, metadata filters, hierarchical parent-return, citations
requirements.txtpytest 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_chunk produces overlapping spans that cover every token; overlap >= chunk_size raises ChunkingError.
  • hierarchical_chunk returns children whose parent_id refers to a real parent, and whose token offsets fall within that parent's range.
  • embed is deterministic and L2-normalized; cosine of a vector with itself is 1.0.
  • A sync job moves STARTING → IN_PROGRESS → COMPLETE, or FAILED on a bad configuration — never silently succeeds with a broken strategy.
  • retrieve ranks by cosine score, respects top_k, and metadata filters exclude non-matching chunks.
  • For a hierarchical knowledge base, retrieve returns the matched child's parent text as content.text (wider context) while location.chunkId still names the child.
  • retrieve_and_generate returns 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 lab and solution.

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. VectorIndex here plays that role — a pluggable store behind a stable add/search contract.
  • Retrieve vs RetrieveAndGenerate are the two real Bedrock Knowledge Base Runtime APIs. Retrieve returns ranked source chunks and lets YOU build the prompt (useful when the KB is one of several context sources feeding a larger agent). RetrieveAndGenerate does retrieval AND generation AND citation assembly as one managed call — the real response shape includes output.text plus a citations array where each entry ties a piece of the generated text back to the retrievedReferences that grounded it, which KnowledgeBase.retrieve_and_generate mirrors closely enough to reason about a real citation UI.
  • Metadata filtering on retrieve matches 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 real StartIngestionJob status vocabulary: ingestion is asynchronous, and a production caller polls GetIngestionJob (or reacts to an EventBridge event) rather than blocking — run_ingestion_job being 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 VectorIndex for a version backed by an HNSW-style approximate index (Phase 05's dense index) and confirm retrieve's interface is unchanged — proving the vector store is pluggable.
  • Add a reranking step (Phase 05's cross-encoder stand-in) between retrieve and retrieve_and_generate to see whether it changes which chunks get cited.
  • Wire retrieve_and_generate's generate_fn to Lab 01's BedrockRuntime.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 Retrieve and RetrieveAndGenerate APIs — 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."