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: truewhen 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.
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
- 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.
- Grounding is retrieve-the-right-context plus instruct-the-model-to-use-only-it and to refuse otherwise; both halves, every time.
- Safety is a separate, versioned control applied on every call, not a line in the prompt you hope holds.
- Cite the sources and report when the guardrail acted, so every answer is auditable.
- An acceptance test turns “it seems to work” into a number you can defend, which is what lets you change anything with confidence.
- Build each piece so you understand it, then reach for the managed service that runs it for you at scale.