Guardrails, batch, and Knowledge Bases
Three Bedrock surfaces sit just outside the model that an agent platform reaches for constantly: Guardrails for content policy, batch inference for offline fleets, and Knowledge Bases for managed retrieval. Chapters 13, 14, and 34 decoded what each is for; this chapter shows the config shapes and walks a scenario through each, so the decoded concept becomes a deployable resource. As before, follow-along is illustrative, this box has no AWS account, but the shapes are faithful.
Guardrails: the config, and the agent-boundary call
A guardrail is a versioned, reusable resource you create once and apply per call. Its config composes several policy types, and seeing the shape makes the control ladder's "filter" rung concrete:
# illustrative CreateGuardrail config
bedrock.create_guardrail(
name="hive-intake",
contentPolicyConfig={"filtersConfig": [
{"type": "PROMPT_ATTACK", "inputStrength": "HIGH", "outputStrength": "NONE"},
{"type": "HATE", "inputStrength": "MEDIUM", "outputStrength": "MEDIUM"},
]},
topicPolicyConfig={"topicsConfig": [
{"name": "investment-advice", "type": "DENY",
"definition": "Recommendations to buy or sell specific securities.",
"examples": ["Should I buy NVDA?"]},
]},
sensitiveInformationPolicyConfig={"piiEntitiesConfig": [
{"type": "EMAIL", "action": "ANONYMIZE"},
{"type": "CREDIT_DEBIT_CARD_NUMBER", "action": "BLOCK"},
]},
contextualGroundingPolicyConfig={"filtersConfig": [
{"type": "GROUNDING", "threshold": 0.75},
{"type": "RELEVANCE", "threshold": 0.5},
]},
blockedInputMessaging="This request was blocked by policy.",
blockedOutputsMessaging="This response was withheld by policy.",
)
Each policy is a different job: content filters catch harm categories
(note PROMPT_ATTACK, the injection-shaped-input classifier); topic
policies deny subjects by natural-language definition; the sensitive-info
policy either ANONYMIZEs (masks and passes) or BLOCKs PII, and the
ANONYMIZE action is the one an intake pipeline wants, the document
flows on, the card number does not; contextual grounding scores a
response against supplied source text to flag ungrounded claims.
The wire-level insight the steering chapter argued
for: you can apply a guardrail two ways, and for agents the second
matters more. Attached to a Converse call via guardrailConfig, it
wraps that call's input and output automatically. But an agent's most
dangerous text, an injected instruction in a fetched page, arrives in a
tool result mid-loop, not at the call's edges, so you call
ApplyGuardrail yourself at the tool boundary:
# illustrative: check a fetched page before it enters the agent's context
verdict = bedrock.apply_guardrail(
guardrailIdentifier="gr-abc", guardrailVersion="DRAFT",
source="INPUT",
content=[{"text": {"text": fetched_page,
"qualifiers": ["guard_content"]}}])
if verdict["action"] == "GUARDRAIL_INTERVENED":
fetched_page = "[withheld by content policy]" # a tool-error result
This is Chapter 37's "check at the tool boundary" made into one API call, and a newer detect-only variant streamlines exactly this per-step use inside agent loops, so the check does not require attaching a guardrail to every model call. The guardrail is still a sieve, not a wall, run it as the cheap first layer, keep the structural defenses behind it.
Batch inference: the JSONL job
The batch tier is half price for offline work, and
its wire format is a file, not an API loop. You write a JSONL file to S3
where each line is one request, keyed by a recordId:
{"recordId": "shard-0001", "modelInput": {"anthropic_version": "bedrock-2023-05-31", "max_tokens": 1024, "messages": [{"role": "user", "content": "Audit this file group for bugs..."}]}}
{"recordId": "shard-0002", "modelInput": {"anthropic_version": "bedrock-2023-05-31", "max_tokens": 1024, "messages": [{"role": "user", "content": "..."}]}}
The modelInput is exactly the raw InvokeModel (Messages) body from
Chapter 57. You submit a job pointing at the
input and output S3 locations:
# illustrative
bedrock.create_model_invocation_job(
jobName="capstone-a-finders-2026-07-26",
modelId="anthropic.claude-opus-4-8",
roleArn=BATCH_ROLE,
inputDataConfig={"s3InputDataConfig": {"s3Uri": "s3://hive/batch/in/"}},
outputDataConfig={"s3OutputDataConfig": {"s3Uri": "s3://hive/batch/out/"}},
)
The output is a JSONL file whose lines carry each recordId and its
modelOutput, in any order, so you key results by recordId, never by
position, the same key-by-id, not position
discipline the queue contract taught. This is precisely how
Capstone A's finder passes run: the
overnight, latency-tolerant half of the fleet routes to batch at half
price, and the interactive verifier panel stays on-demand. One
constraint to design around: batch has a minimum record count and its
own turnaround (typically within a day, not an SLA), so it is for work
that genuinely can wait, which the use-case gallery
warned is a smaller set than teams assume.
Knowledge Bases: managed retrieval
Chapter 34 framed agentic retrieval as an
iterating tool; Bedrock Knowledge Bases is the managed pipeline that
tool calls into. You point it at documents in S3, it ingests, chunks,
embeds, and stores (increasingly into S3 Vectors),
and exposes two operations. Retrieve returns raw chunks for the agent
to reason over:
# illustrative: the retrieval an agent's "search_docs" tool wraps
kb.retrieve(
knowledgeBaseId="KB123",
retrievalQuery={"text": "payments-db connection pool sizing"},
retrievalConfiguration={"vectorSearchConfiguration": {
"numberOfResults": 8,
"overrideSearchType": "HYBRID"}}) # SEMANTIC | HYBRID
RetrieveAndGenerate does the whole chat-RAG loop in one call (retrieve,
stuff, answer with citations), which is the wrong shape for an agent
that can iterate but the right shape for a one-shot lookup, so an agent
uses Retrieve as a tool and reserves RetrieveAndGenerate for a
sub-question it wants answered in one hop. The response's citations link
each generated claim to its source chunk, the provenance
the verification mesh checks against.
The one config decision that dominates retrieval quality is the
chunking strategy, set on the data source: FIXED_SIZE (simple,
severs context at boundaries), HIERARCHICAL (parent-child, good for an
agent that drills down), SEMANTIC (splits on meaning), or NONE (you
pre-chunked). The agentic-retrieval lesson
applies: because an agent can iterate, prefer coarser, context-preserving
chunks (HIERARCHICAL) over the tiny fragments chat-RAG favors.
Ingestion is a job you trigger (StartIngestionJob) when the source
changes, and GraphRAG (an auto-built entity graph) is the variant for
multi-hop questions the flat vector store answers poorly.
Don't be confused: a Knowledge Base vs your memory plane. Both do vector retrieval and blur in conversation, but they are the knowledge-vs-memory split as two AWS resources. A Knowledge Base holds authored, external documents the agent does not write, ingested and retrieved, a rented chat-RAG pipeline you use as one tool. The memory plane (DynamoDB plus S3 Vectors, your own consolidation) holds earned, internal facts the agent distills from experience and must police for staleness and provenance. Use a Knowledge Base for the docs; do not pour agent memory into it, because it has no notion of the supersede-and-decay policies earned memory needs.
👉 Next: AgentCore Runtime, deployed,
where the container contract from Lab 12.2 becomes a real deployment:
the SDK app, the control-plane APIs, the agentcore CLI, the IAM
execution role, and the Part 1 agent running in a managed microVM.