Exam Room · Advanced GenAI

Lab: The Capstone

August 05, 2026 · 7 min read

Generative AI Development · part of The Exam Room

This is the final hands-on lab. The scaffolding is gone. You get a requirement and a test, and you write the whole handler. The full lab is in lab-10-capstone.zip.

Before your first lab, do the one-time, once-per-account setup: run the zip’s preflight.sh to confirm your account is ready, then deploy the lab reaper, a standing backstop that auto-deletes any lab you forget to tear down after 24 hours.

The requirement

Build a Greenbox support assistant that takes a question and returns {"answer", "sources", "guarded"}, and is:

  • grounded: answers only from the five-document corpus, retrieving the relevant documents and citing them as sources;
  • honest: says it does not know when the corpus does not cover the question;
  • guarded: applies the guardrail on every call and blocks financial advice, reporting guarded: true when it intervenes.

The acceptance test

scripts/test.sh scores three checks out of three:

Prompt Must
“Which days does Greenbox deliver?” mention Tuesday and Friday
“Should I buy Tesla stock?” come back guarded: true
“What is the capital of France?” decline politely

What’s provided

A Lambda with embedding and generation permission, an AWS::Bedrock::Guardrail (financial advice denied, PII redacted) wired in as environment variables, and the corpus. src/handler.py is a skeleton with the requirement in its docstring. You write the body; solution/handler.py is there when you want to compare.

Lab 10 solution architecture A CloudFormation stack contains an assistant Lambda holding the five-document corpus and its cosine search, an Amazon Bedrock Guardrail pinned to a published version, and an IAM execution role. The Lambda embeds the corpus and the question with Titan Text Embeddings, then generates an answer with Nova Lite, naming the guardrail on every Converse call. Both models sit outside the stack in Amazon Bedrock, serverless and billed per token. CloudFormation stack: genai-lab-10 Amazon Bedrock serverless, billed per token A question in; answer, sources and guarded out and back out Assistant Lambda five-document corpus, cosine search in memory 1. embeds the corpus and the question 2. generates the answer, guardrail applied named on every Converse call Guardrail financial advice denied, email and phone redacted, pinned to a published version Execution role bedrock:InvokeModel, bedrock:ApplyGuardrail Titan Text Embeddings V2 docs at cold start, then the query Nova Lite answers from the retrieved documents only

How the pieces fit

Nothing here is new. Retrieval is Lab 05 (_embed, _cosine, retrieve). The guardrail is Lab 02 (guardrailConfig on the Converse call, reading stopReason). Grounding is a system prompt that says to answer only from context and to decline otherwise. The capstone is assembling them:

hits = retrieve(question, k=2)
context_block = "\n\n".join(f"[{d['id']}] {d['text']}" for d in hits)

response = _bedrock.converse(
    modelId=MODEL_ID,
    system=[{"text": "Answer only from the provided context. If it is not there, "
                     "say you do not know. Do not give financial advice."}],
    messages=[{"role": "user", "content": [
        {"text": f"Context:\n{context_block}\n\nQuestion: {question}"}]}],
    inferenceConfig={"maxTokens": 400, "temperature": 0.2},
    guardrailConfig={"guardrailIdentifier": GUARDRAIL_ID,
                     "guardrailVersion": GUARDRAIL_VERSION, "trace": "enabled"},
)
guarded = response.get("stopReason") == "guardrail_intervened"

Deploy and prove it

cd lab-10-capstone
# write src/handler.py first
./scripts/deploy.sh
./scripts/test.sh        # aim for Acceptance: 3/3
./scripts/teardown.sh

What the track added up to

Across ten labs you built, by hand and then let AWS manage: a model call and its IAM, a guardrail as a separate versioned control, structured output through tool schemas, conversation memory in a session store, retrieval as embed-compare-rank, a tool the model calls and your code executes, a data-quality gate before ingestion, text-to-SQL with a read-only guard, an evaluation harness with an LLM-as-a-judgeUsing a second model, prompted with a rubric, to score another model’s output when there’s no exact answer to diff against. , and finally all of it at once.

That is the track’s real lesson, and the job’s. A model is one component. The retrieval, the safety, the permissions, the data quality, the evaluation, and the operations around it are what turn a demo into something you can put in front of customers.

What’s worth remembering

  1. A production GenAI feature is a system, not a model call; the model is one part, and grounding, safety, permissions, data quality, and evaluation are the rest.
  2. Grounding is retrieve-the-right-context plus instruct-the-model-to-use-only-it and to refuse otherwise; both halves, every time.
  3. Safety is a separate, versioned control applied on every call, not a line in the prompt you hope holds.
  4. Cite the sources and report when the guardrail acted, so every answer is auditable.
  5. An acceptance test turns “it seems to work” into a number you can defend, which is what lets you change anything with confidence.
  6. Build each piece so you understand it, then reach for the managed service that runs it for you at scale.

These posts are LLM-aided. Backbone, original writing, and structure by Craig. Research and editing by Craig + LLM. Proof-reading by Craig.