« Phase 24 README · Warmup · Hitchhiker's · Deep Dive · Principal Deep Dive · Staff Notes

Phase 24 — Core Contributor Notes: How Bedrock Is Actually Built

This is the maintainer's-eye view of the real service, not our miniature. Where the lab injects a tick and processes batches in memory, real Bedrock is a distributed control plane fronting a fleet of heterogeneous model servers across regions. The interesting engineering is in the seams. Where I am describing a pattern rather than a documented internal, I say so — do not quote internal implementation details you cannot verify.

Two clients, two API surfaces, one deliberate split

The clearest window into Bedrock's design is its SDK shape. AWS exposes four service clients: bedrock and bedrock-agent (control plane) and bedrock-runtime and bedrock-agent-runtime (data plane). This is not incidental packaging — AWS services are generated from Smithy service models, and splitting Bedrock into distinct services means distinct IAM action namespaces (bedrock:InvokeModel vs bedrock:CreateGuardrail), distinct throttling, distinct endpoints, and distinct VPC endpoint services. A committer's takeaway: the plane boundary is enforced at the API model layer, so you cannot accidentally grant data-plane invoke by granting control-plane admin. Our lab collapses these into one BedrockRuntime object for teachability and loses that property.

The model-access grant, and why federation needs it

Before the data plane will let you InvokeModel, an account must enable model access per model per region through the control plane. This exists because Bedrock resells third-party providers' models under AWS billing and terms — the grant is where you accept the provider's EULA and AWS records the entitlement. Mechanically it means "the model exists in the catalog" and "you can invoke it" are two different states, and a very common first-day error is a AccessDeniedException on invoke that is not an IAM problem at all — it is an un-granted model. Our miniature has no catalog gating; the real system's catalog is the primary control-plane object.

Converse: a real translation service, with real seams

The pattern behind Converse is exactly what Lab 01 builds — per-provider request/response translators — but at production scale the seams are sharper than a round-trip test shows:

  • Feature negotiation is per-model, not per-API. Whether a modelId supports tool use, streaming, system prompts, document/image content blocks, or guardrail integration is a property of that model's Converse integration, surfaced (in part) through catalog metadata. Calling toolConfig against a model without tool support returns a ValidationException. The maintainer's design constraint is that Converse must never silently degrade — a dropped capability is worse than an error, because it corrupts downstream logic invisibly. Our UnsupportedFeatureError mirrors this contract.
  • additionalModelRequestFields is the escape hatch inside the abstraction. Converse cannot normalize a provider-specific knob (say, a vendor's unique sampling parameter), so it provides a passthrough bag that is not translated. This is the honest admission that a least-common- denominator API needs a bypass, and it is the seam where portability leaks back out.
  • Streaming is a separate wire protocol. ConverseStream returns an event stream (messageStart, contentBlockDelta, contentBlockStop, messageStop, plus metadata with token usage) — reassembling deltas into a coherent message, including partial toolUse JSON, is real work the SDK does for you and a hand-rolled client must get right.

Guardrails: an independent resource with its own lifecycle

Guardrails is not a flag on an invoke; it is a versioned control-plane resource you create, then reference by ID and version on the data plane (either as a parameter to Converse or via the standalone ApplyGuardrail call). This design has consequences a committer cares about:

  • Versioning is immutable-publish. You edit a DRAFT, then publish a numbered version; production pins a version so a policy edit cannot silently change behavior under a running service. This is the same discipline as a Lambda version or an ECS task definition revision.
  • ApplyGuardrail is decoupled from invocation so you can filter text that never touches a Bedrock model — e.g., screen user input before a non-Bedrock model, or screen a tool's output. Our lab wires guardrails inline; the real API also supports this standalone mode.
  • Contextual grounding needs sources supplied at call time. It scores the generation against the grounding source you pass in that request — it is not magic access to your Knowledge Base unless you feed the retrieved passages in. Teams miss this and wonder why grounding never fires.
  • The six content-filter categories and the None/Low/Medium/High strengths are the real knobs, and "prompt attack" is a genuine named category for jailbreak/injection detection. AWS has also previewed Automated Reasoning checks — policy-as-formal-logic validation aimed at hallucination in domains where "probably grounded" is not good enough. Our token-overlap scorer is a deterministic stand-in for a purpose-built model.

Knowledge Bases: managed pipeline, BYO store — and the sync job is the sharp edge

The real design decision people underrate: Bedrock Knowledge Bases ships no vector database. You provision OpenSearch Serverless (the common default), Aurora PostgreSQL + pgvector, Pinecone, Redis Enterprise Cloud, or MongoDB Atlas, and Bedrock owns ingestion and query orchestration on top. This is deliberate — no second proprietary store to migrate off, and data residency stays in your footprint.

The sharp edges a committer learns:

  • Ingestion is an async job (StartIngestionJob) moving through STARTING → IN_PROGRESS → COMPLETE|FAILED, and it is incremental — re-syncing reprocesses changed/added documents, not the whole corpus, which means the idempotency and change-detection semantics of your data source matter. Our lab makes this synchronous and full-reprocess for testability.
  • Chunking strategy is fixed at KB creation for a data source; changing it means re-ingesting. Hierarchical (parent-child) chunking is the strategy that returns parent text on a child match — embed small for precision, return large for context.
  • RetrieveAndGenerate manages a session and assembles the citations array tying spans back to source locations. Retrieve is the lower-level primitive when the KB is one context source among several. Metadata filtering is how one KB does coarse multi-tenant retrieval.

API evolution, and why it moved

The lineage tells you where AWS learned:

  1. InvokeModel first — the honest primitive: provider-native bytes both ways. It works but forces per-provider special-casing on every caller.
  2. Converse next — the normalization layer, added once the multi-provider catalog made the special-casing pain universal. It did not replace InvokeModel; it is built on the same primitive and coexists.
  3. Agents for Bedrock — a bundled, console-configurable single agent where AWS owns the reasoning loop, action groups (Lambda tools), and attached Knowledge Bases. Convenient, low- code, but you adopt AWS's agent shape.
  4. AgentCore — the reversal: composable operational primitives (Runtime session isolation via microVMs, Gateway API-to-MCP, Cedar policy, memory) around a loop you own in any framework, model-agnostic. This is AWS conceding that serious teams will not hand over their agent loop, only the ops substrate around it.

The consistent lesson across all four: AWS keeps offering both a bundled convenience layer and the raw primitive underneath, and lets the market pick. "Bedrock" is the model platform; it has no agent loop at all — the loop lives in Agents-for-Bedrock (AWS's) or AgentCore (yours).

What our miniature deliberately simplifies

  • Injected tick instead of a wall clock (determinism); a real token bucket refills on real time.
  • In-memory, submission-order batch; real batch is S3-in/S3-out with no output-order guarantee.
  • One BedrockRuntime object instead of the four-client plane split.
  • A hashing bag-of-words embedder and token-overlap grounding scorer instead of real embedding and grounding models — the shape is faithful, the quality is a stand-in, on purpose, so everything stays offline and deterministic.

Know the shape and the seams, and the real service's docs read as confirmation rather than surprise.