Prompt Engineering in Production: Templates, Structured Output, and Prompt Management (AWS GenAI Developer Pro, Module 2)
This is Module 2 of AWS GenAI Pro Mastery, a free 16-module course that takes you from your first Amazon Bedrock call to AWS-certified.
The triage prompt worked in the demo. The model read a CloudCart ticket, returned {"intent": "billing", "priority": "high", "sentiment": "negative"}, everyone clapped, and the feature shipped. Three weeks later, ticket number 47 comes in at 2 a.m. The model replies Sure! Here's the JSON you asked for: followed by a tidy markdown code fence — and json.loads blows up on the S of “Sure.” The pager fires. Worse, when you go to fix the prompt, nobody can tell you which version is running. Someone “improved” it on Friday by editing an f-string, and the change is buried in a commit titled “tweaks.”
That is the real cost of a prompt that works in a demo: it’s an invisible production dependency with no version, no validation, and no test. This module fixes all three. By the end, Relay’s triage emits validated JSON 100% of the time — or fails loudly instead of silently — and every prompt change is a pinned, governed version you can roll back.
In this module
- You’ll learn to engineer production prompts (roles, format constraints, few-shot, chain-of-thought), implement reliable structured output with schema validation and retry, tune inference parameters per task, govern prompts in Amazon Bedrock Prompt Management, and test prompts like code.
- You’ll build Relay’s triage: a raw CloudCart ticket in, a validated
Triageobject out — with the prompt versioned in Prompt Management and a 10-ticket regression suite that scores 10/10. - Exam domains covered: D1 — Foundation Model Integration, Data Management, and Compliance — 31% (this module owns Task 1.6, the prompt-engineering task) and D4 — Operational Efficiency and Optimization for GenAI Applications — 12% (skill 4.2.4, inference parameters, shared with Module 12).
- Prerequisites: Module 1 — a secured AWS account with
AWS_PROFILE, a working Converse call, and the basics of inference parameters. Region is us-east-1 throughout.
Where you are in the build
You are at Module 2 of 16. The destination is the AWS Certified Generative AI Developer - Professional (AIP-C01) exam, and the vehicle is Relay, CloudCart’s support agent.
- ✅ M1 — secured account, budget alarm, first Converse call.
- 👉 M2 — triage: structured output + Prompt Management + a prompt test suite.
- ⬜ M3–M16 — model routing, RAG, agents, safety, deployment, cost, evals, observability, capstone.
Relay before this module is an account that can call a model. After it, Relay is a relay/ package with its first two frozen schemas (Ticket, Triage) and a triage step that turns a messy ticket into clean, validated JSON, every time — the spine the rest of the course hangs on.
Prompting that survives contact with production
A prompt that “works on my machine” and a production prompt are different artifacts: the demo prompt is one sentence you got lucky with; the production prompt is engineered against the cases that break the demo. Per the AIP-C01 exam guide, skill 1.6.5 is about refining prompts “beyond basic prompting techniques” using “structured input components, output format specifications, chain-of-thought instruction patterns, feedback loops.” That list is the anatomy of a real prompt.
A production prompt has four load-bearing parts. First, a role — who the model is and, just as important, what it must not do. Relay’s triage role ends with “you do not greet, apologize, explain, or answer the ticket — you only classify it,” which kills the “Sure! Here’s the JSON” preamble at the source. Second, a format constraint: the exact output shape, stated as a rule (“Return a SINGLE JSON object… the first character of your reply MUST be {”). Third, the allowed values for each field, so the model can’t invent a category. Fourth, few-shot examples.
Few-shot prompting means showing the model a handful of worked input/output pairs inside the prompt so it pattern-matches the task instead of guessing. A few examples that span the tricky boundaries are worth more than a paragraph of instructions: for triage, they pin the rule that tone is not priority — an ALL-CAPS complaint about one broken feature is still high, not urgent.
The last technique on the blueprint’s list is chain-of-thought instruction patterns — prompting the model to reason step by step before answering, which helps on genuinely ambiguous calls (is a “charged twice and never refunded” ticket high or urgent?). It is not free: every reasoning token costs money and latency. For a high-volume classifier like triage, I keep the reasoning in the instructions (precise priority rules the model applies) rather than asking for an explicit reasoning trace on every call. Reserve emitted chain-of-thought for hard, low-volume decisions where the accuracy gain pays for the tokens.
You iterate a production prompt against real tickets. Relay’s went through three visible versions:
- v1 (naive): “Classify this ticket’s intent, priority, and sentiment.” On ticket-001 the model writes a paragraph — “This looks like a billing issue, and the customer seems upset…” — and validation explodes. No structure, no JSON.
- v2 (constrained): add the role, the format constraint, and the allowed values per field. Now most tickets parse. But the edge cases drift: the empty ticket gets a hallucinated
technical, and an all-caps complaint getsurgentwhen it’s reallyhigh. - v3 (few-shot): add three worked examples spanning a calm how-to, an angry billing charge, and a store-down emergency. Better — but run against the suite this three-example snapshot lands at 7/10, over-escalating an angry-but-not-on-fire ticket and still mis-handling the empty one.
That 7/10 is the whole point of the next three sections: “better” is not a number, the suite is. (Closing the gap grew the prompt from three examples to the seven in the shipped prompts/triage_prompt.md — more on that in the lab.)
Structured output: from “Here’s the JSON!” to a validated object
Structured output is the discipline of getting a foundation model to emit data your program can parse reliably — here, a JSON object that maps onto a typed schema. The path is deliberately simple: constrain the format in the prompt, parse the reply, and validate it against a Pydantic schema, the contract. Module 2 freezes the first two:
class Ticket(BaseModel): # exactly 4 fields in Module 2
ticket_id: str
channel: Literal["email", "chat"]
customer_message: str
created_at: str
class Triage(BaseModel): # complete and frozen — never extended
intent: Literal["billing", "technical", "account", "shipping", "other"]
priority: Literal["low", "normal", "high", "urgent"]
sentiment: Literal["negative", "neutral", "positive"]
Those Literal enums are the contract: 5 intents, 4 priorities, 3 sentiments, no refund intent, no severity rename — every later module reproduces them identically. Ticket gains fields later (an attachment list, a redaction flag), but only by addition, and not yet.
The load-bearing line is this: you do not trust the model’s output, you validate it. Triage.model_validate_json(text) either returns a typed Triage or raises. If it raises, you retry exactly once — and you feed the validation error back into the prompt so the model can correct its own mistake. If the second attempt still fails, you raise an error carrying the raw text. Never a silent try/except that swallows the failure and ships a wrong label downstream.
You can also get structured output via tool calling, where the model fills a typed schema as if calling a function — Relay adopts that when it becomes an agent in Module 7. The Module 2 path is prompt-constrained plus model_validate_json plus one retry, which is enough for a classifier.
Inference parameters: why triage runs at temperature 0
Skill 4.2.4 on the exam is explicit about “appropriate temperature and top-k/top-p selection based on requirements.” The settings that control randomness are temperature, top-k, top-p (plus maxTokens for length), and the right values depend entirely on the task.
| Parameter | What it controls | Triage (classify) | Drafting (a reply) | Classic trap |
|---|---|---|---|---|
temperature | Randomness of the next-token distribution | 0 | ~0.7 | Thinking 0 = strict determinism (it isn’t) |
topP (nucleus) | Smallest token set whose cumulative probability ≥ p | 1.0 (moot at temp 0) | ~0.9 | Tuning topP and temperature hard at once and fighting yourself |
topK | Cap on the number of candidate tokens considered | unused for triage | small for tighter style | Assuming every model exposes it — Nova via Converse leans on temperature/topP |
maxTokens | Hard cap on output length | small (~100, one JSON line) | larger for prose | Setting it so low the JSON truncates mid-object |
Triage runs at temperature 0. Creativity in a classifier is a bug, not a feature. A reply you draft for a customer wants some warmth (~0.7); a label you assign wants the single most-likely answer every time. At temperature 0 the model already takes the top token, so the other knobs largely stop mattering.
⚠️ Common misconception: “Temperature 0 makes the model deterministic, so I don’t need output validation.” Wrong twice. Temperature 0 reduces variance but guarantees neither strict determinism (floating-point math and cross-Region routing still wobble) nor schema compliance — the model can still emit
"refund", a stray code fence, or a brace it forgot to close. Only programmatic validation guarantees the format, which is exactly whytriage()validates and retries even at temperature 0.
Bedrock Prompt Management: prompts are production artifacts
Here is the distinction the exam cares most about, and the one that bit our 2 a.m. on-call: versioning a prompt in your application code is not the same as governing it. An f-string in git has a history, but anyone can edit it, nothing forces a review, and the running code carries the literal text — so a rollback is a redeploy, not a config change.
Amazon Bedrock Prompt Management is the AWS service that treats prompts as first-class artifacts. Per skill 1.6.3, the exam describes it as using “Amazon Bedrock Prompt Management to create parameterized templates and approval workflows,” explicitly above the alternative of “Amazon S3 to store template repositories.” A prompt template here is a stored prompt with named variables — Relay’s is relay-triage with a single {{ticket}} variable, bound to Nova Micro at temperature 0.
Three mechanics matter for the exam:
- Parameterized template — the text lives in Prompt Management with
{{ticket}}as a typed input variable, and the model binding and inference config (temperature 0, maxTokens) travel with it. - Immutable versions — you publish version 1 as a frozen snapshot (the draft stays editable, published versions never change), and your code pins an identifier and a version number, never the prompt text.
- Governance — a reviewer approves version 2 before any code consumes it, and CloudTrail records who created and read each version, an audit trail git blame cannot give you across teams and accounts.
This is the lifecycle the lab implements, and the shape the exam expects:
flowchart LR
DRAFT["Draft<br/>(editable)"] -->|create_prompt_version| V1["Version 1<br/>(immutable)"]
V1 -->|approve| APP["relay/triage.py<br/>pins id + version"]
APP --> SUITE["10-ticket<br/>regression suite"]
SUITE -->|green 10/10| SHIP["Ship version 1"]
SUITE -.->|edit + re-test| DRAFT2["Draft v2"]
DRAFT2 -->|create_prompt_version| V2["Version 2<br/>(immutable)"]
The
prompts/triage_prompt.mdfile in the repo is the git mirror of the published version — byte-synced for humans and diffs — but the running code never reads it at inference time: it fetches the governed template from Prompt Management by id and version.
The exam tests this directly: three teams editing prompts in three repos with a demand for centralized audit and approval means Prompt Management — immutable versions, an approval gate, an audit trail — not “add a CHANGELOG.”
Testing prompts like code
What turned Relay’s 7/10 v3 into a shippable prompt was not a clever rewrite — it was the suite that told us we weren’t done. Regression testing for prompts means a fixed set of reference inputs with objective expected outputs, re-run on every change to catch quality drops before they reach production. Skill 1.6.4 names exactly this: “Step Functions to test edge cases, CloudWatch to test prompt regression.” The lab’s version is a local script, but the discipline is identical.
Relay’s suite is 10 reference CloudCart tickets covering all 5 intents plus the edge cases that break naive prompts: an empty ticket (008), a bilingual French/English furious billing rant (009), and an all-caps shouting technical complaint (010). The pass criteria are deliberately objective — valid JSON, expected intent, expected priority. Sentiment is the softest field, so the suite doesn’t assert it.
The flow each ticket runs through, with the validation-retry loop:
flowchart TD
T["Ticket JSON<br/>(customer_message)"] --> R["Render template<br/>{{ticket}} from Prompt Mgmt"]
R --> C["Converse<br/>Nova Micro, temperature 0"]
C --> J["Triage.model_validate_json"]
J -->|valid| OK["Triage object<br/>{intent, priority, sentiment}"]
J -->|ValidationError| RETRY{"attempt 1?"}
RETRY -->|yes: append error to prompt| C
RETRY -->|no| RAISE["raise TriageError<br/>(carry raw text)"]
When you change the prompt, the model, or a parameter, you re-run the suite. A green 10/10 is the gate for shipping a new prompt version. This is the code-first path Relay takes — and there’s a no-code sibling on the blueprint worth knowing by mechanics, not just by name.
Amazon Bedrock Prompt Flows (skill 1.6.6) — the no-code orchestrator this course deliberately skips. Prompt Flows is a visual builder: you drag nodes onto a canvas — prompt nodes (a stored Prompt Management template), condition nodes, Lambda nodes, Knowledge Base nodes — and wire their outputs to inputs to define the data flow. That gives you the three things the exam names under skill 1.6.6: sequential prompt chains (the output of one prompt feeds the next), conditional branching (a condition node routes on the model’s response — say, escalate vs auto-answer), and reusable components (each node references a versioned Prompt Management prompt, so the same governed template drops into many flows). The related clarification-workflow pattern (skill 1.6.2) is the multi-turn cousin: keep conversation context across turns with Step Functions plus stored history. When to reach for it: a multi-step pipeline that a non-developer must own and edit in a console, where a visual graph beats reading code. Why Relay stays code-first on purpose: triage is a single code-owned step, and a Python suite with
model_validate_jsongives tighter control, real unit tests, and a git-native diff a canvas can’t. (The AWS console now files Prompt Flows under “Amazon Bedrock Flows”; the AIP-C01 exam guide still says Prompt Flows.) Module 15 returns to Flows from the decision angle — when a whole solution should be visual orchestration versus code.
Build it: Relay’s triage
Goal: build Relay’s triage, versioned in Bedrock Prompt Management, with a 10-ticket regression suite. One command triages a ticket and prints validated JSON plus a cost line; another prints a 10-row scoreboard ending in score: 10/10. This lab cost me $0.0032 on June 2026 prices (about 40 Nova Micro calls — prompt iteration plus several suite passes). Full code lives in module-02/; excerpts below.
Step 1 — Publish the governed prompt
setup.py is idempotent and verbose: it reads prompts/triage_prompt.md (git stays authoritative), creates the parameterized prompt relay-triage with a {{ticket}} variable bound to Nova Micro at temperature 0, and publishes version 1. The API names are create_prompt and create_prompt_version on the bedrock-agent client:
PROMPT_NAME = "relay-triage"
MODEL_ID = "us.amazon.nova-micro-v1:0" # inference profile, NOT a bare regional ID
variant = {
"name": "triage-v3-fewshot",
"templateType": "TEXT",
"templateConfiguration": {
"text": {"text": template_text, "inputVariables": [{"name": "ticket"}]}
},
"modelId": MODEL_ID,
"inferenceConfiguration": {"text": {"temperature": 0.0, "maxTokens": 100}},
}
created = client.create_prompt(name=PROMPT_NAME, defaultVariant=variant["name"],
description="Relay triage classifier: ticket -> {intent, priority, sentiment} JSON.",
variants=[variant])
client.create_prompt_version(promptIdentifier=created["id"]) # immutable version 1
Prompt Management does not bill for storing prompts or versions, so setup.py costs $0 — you only pay per token at inference time. Re-running it reconciles the editable draft and leaves the published version 1 untouched.
Step 2 — Triage a ticket
relay/triage.py loads the template by id and version, renders {{ticket}}, calls Converse, and validates. The model ID is a single top-of-file constant here — provisional: from Module 3, relay/config.py is the sole home of every model ID in the course.
_MODEL_ID = "us.amazon.nova-micro-v1:0" # provisional; config.py owns this at M3
_TEMPERATURE = 0.0 # classification, not creativity
_PROMPT_VERSION = "1" # code pins a number, never the text
response = client.converse(
modelId=_MODEL_ID,
messages=[{"role": "user", "content": [{"text": prompt_text}]}],
inferenceConfig={"maxTokens": _MAX_TOKENS, "temperature": _TEMPERATURE},
)
text = response["output"]["message"]["content"][0]["text"]
The validation-and-retry core — the load-bearing part — is one loop. Try the schema; on failure, append the error and retry once; after that, raise rather than ship a bad label (excerpt simplified; the committed triage.py echoes the rejected reply and names the three keys in the retry message):
for attempt in (1, 2): # one call + one validation retry
raw, usage = _converse_once(prompt_text, client=runtime_client)
try:
result = Triage.model_validate_json(_extract_json(raw))
except ValueError as err:
if attempt == 2:
break # second failure -> raise below
prompt_text += f"\n\nYour previous reply was rejected: {err}\nReturn ONLY corrected JSON."
continue
return result, usage
raise TriageError("Triage produced invalid output even after one retry.", raw_output=raw)
This retry is a validation retry. Network retries, throttling backoff, and cross-Region fallback belong to the FM integration layer — that’s Module 3. Run it:
uv run python -m relay.triage data/tickets/ticket-001.json
Real output:
{"intent":"billing","priority":"high","sentiment":"negative"}
tokens: in=1305 out=19 | est. cost: $0.000048
One ticket, validated, for five-thousandths of a cent — a large share of those 1,305 input tokens (~700) being the few-shot examples riding along on every call.
Step 3 — Run the regression suite
uv run python run_prompt_tests.py
Real output:
ticket expected got result
-----------------------------------------------------------------------
ticket-001 billing/high billing/high PASS
ticket-002 technical/urgent technical/urgent PASS
ticket-005 other/low other/low PASS
ticket-007 shipping/high shipping/high PASS
ticket-008 other/low other/low PASS
ticket-009 billing/urgent billing/urgent PASS
ticket-010 technical/high technical/high PASS
-----------------------------------------------------------------------
score: 10/10
tokens: in=13092 out=220 | est. cost: $0.000489
(Truncated; the full run shows all ten rows.) Getting from 7/10 to 10/10 was not a vibe edit. The suite pointed at three specific failures: ticket-007 and ticket-010 came back urgent when they should be high (tone is not priority), and the empty ticket-008 hallucinated an intent. The fix tightened the urgent-vs-high boundary in prose, lifted the empty-ticket rule to a FIRST RULE at the top of the prompt, and added few-shot examples for exactly those cases — growing the prompt from three examples to the seven now in prompts/triage_prompt.md. Then 10/10 — stable across three consecutive runs at temperature 0, identical token counts each time.
This is a deterministic, assertion-based suite — no LLM-as-a-judge, no semantic metric (those are Module 13).
One honest caveat, because we’re teaching you to test prompts like code: seven of the ten suite tickets deliberately mirror the few-shot examples in the prompt. That overlap is on purpose — it pins the exact boundaries the examples teach (e.g. an unauthorized charge is high, not urgent — anger is sentiment, not priority). But a 10/10 on those seven mostly proves the model copies from context, not that it generalizes. The real generalization signal is the three tickets that are not examples in the prompt — 005, 006, and 009 — plus every new ticket you add. So read 10/10 as “the taught boundaries hold,” and grow the suite with held-out tickets (scenarios absent from the prompt) to measure true generalization. That is exactly how you’d guard a classifier prompt in production.
Try it yourself. (1) Publish a version 2: add an eighth few-shot example to prompts/triage_prompt.md (say, a shipping delay that’s only normal priority), re-run setup.py, point _PROMPT_VERSION at "2", and re-run the suite. Did 10/10 hold? Did token counts move? That before/after comparison is how you roll out a prompt change safely. (2) Set _TEMPERATURE = 0.9 and run the suite a few times; the strong few-shot examples anchor the model, so the score may hold — but you’ll see phrasing vary and the occasional edge case wobble, which is exactly why a classifier wants temperature 0 and why validation is there to catch the fallout when it doesn’t. Then uv run python teardown.py to delete the prompt; the M1 budget stays.
In production
At one ticket a second this lab grows three sharp edges. First, prompts multiply. A real CloudCart runs separate triage prompts per locale and per brand, each its own governed version — which is precisely why immutable versioning and an approval gate stop being bureaucracy. A new version rolls out with a measured comparison, not a Friday f-string edit (tooled A/B testing arrives in Module 13).
Second, few-shot is not free, and the bill is recurring. Those ~700 tokens of examples ride along on every triage call. At a million tickets a month that’s ~700M input tokens of examples alone — roughly $25/month at Nova Micro’s $0.035 per million input tokens (as of June 2026), paid forever. Each example you add to fix one edge case taxes every call. (Module 12’s prompt caching makes those repeated prefix tokens nearly free.)
Third, validation becomes a metric. You track the validation-retry rate as a health signal: a rising rate means the prompt, the model, or the traffic mix has drifted, and you want the alarm before malformed output reaches a customer. The 2 a.m. incident that opened this module is a monitored, recoverable event when triage validates and retries — and a silent data-corruption bug when it doesn’t.
Exam corner
What the exam tests here. This module sits in D1 (31%, Task 1.6 — the whole prompt-engineering task) and D4 (12%, skill 4.2.4 — inference parameters). The blueprint wants you to design instruction frameworks that control FM behavior (1.6.1); govern prompts with parameterized templates, approval workflows, and audit (1.6.3); QA prompts with expected outputs, edge cases, and regression (1.6.4); refine prompts with format specifications and chain-of-thought (1.6.5); situate Prompt Flows for complex chains (1.6.6); and select temperature/top-k/top-p per requirement (4.2.4).
Five scenario questions in the exam’s style; answers follow.
1. Three teams maintain copies of the same triage prompt as hardcoded strings in three separate repos. Compliance now requires central approval before any prompt change ships and a record of who edited and used each version. What do you recommend? A. Store all prompts in a shared Amazon S3 bucket with versioning enabled. B. Use Amazon Bedrock Prompt Management: parameterized templates, immutable versions, approval workflow, with CloudTrail for audit. C. Add a CODEOWNERS file and require pull-request reviews in each git repo. D. Concatenate all prompts into one Lambda environment variable.
2. A service parses a foundation model’s free-form output and crashes intermittently in production when the model wraps its JSON in prose or a code fence. What combination fixes this most reliably? A. Raise the temperature so the model is more creative about formatting. B. Constrain the output format in the prompt, validate the reply against a schema, and retry once with the validation error fed back in. C. Wrap the parse in a try/except that ignores the error and returns a default object. D. Switch to a larger frontier model and trust its output.
3. You run two workloads: classifying support tickets into fixed intents (must be reproducible) and generating marketing copy variants (must be diverse). Which parameter settings fit each? A. Both at temperature 0.9 for consistency. B. Classification at temperature 0; copy generation at a higher temperature (~0.7) with top-p around 0.9. C. Classification at temperature 1.0; copy generation at temperature 0. D. Both at temperature 0, varying only maxTokens.
4. After a developer “improved” the triage prompt, classification accuracy quietly dropped in production with no alert. What practice would have caught the regression before deployment? A. A larger maxTokens so outputs are never truncated. B. A reference suite of representative tickets with objective expected outputs (including edge cases), re-run on every prompt change as a gate. C. Manually spot-checking three tickets after each change. D. Raising the temperature to add robustness.
5. A non-developer operations team needs a multi-step prompt workflow with conditional branching based on the model’s response, built without writing code. Which AWS option fits best? A. Amazon Bedrock Prompt Flows. B. A hand-written Python script orchestrating multiple Converse calls. C. A single very long prompt that does everything at once. D. AWS Step Functions state machine authored in JSON by the team.
Answers. 1 → B. The requirement is governance — approval and audit, not just storage. Prompt Management gives parameterized templates, immutable versions, an approval workflow, and CloudTrail audit; S3 versioning (A) and PR review (C) are storage and code-review, not prompt governance. 2 → B. Reliable structured output is format constraint + schema validation + a bounded retry; A makes it worse, C is the silent-swallow anti-pattern that ships bad data, D still can’t guarantee the schema. 3 → B. Reproducible classification wants temperature 0; diverse generation wants a higher temperature with nucleus sampling (top-p) — that’s the textbook 4.2.4 split. 4 → B. Skill 1.6.4: a regression suite with objective criteria and edge cases, run on every change, is the gate; manual spot-checks (C) miss exactly the edge cases that drift. 5 → A. Prompt Flows is the no-code, branch-able prompt-chaining tool for non-developers; B and D require code, and C is unmaintainable.
Traps to avoid.
- Believing temperature 0 guarantees determinism and schema compliance. It reduces variance only; programmatic validation is the sole guarantee of the format.
- Confusing versioning a prompt (a git history of an editable string) with governing it (immutable versions + approval + audit). The exam means Prompt Management by the second.
- Stacking few-shot examples without pricing their recurring per-call cost. Every example is paid on every invocation, forever.
Key takeaways
- A production prompt is role + format constraint + allowed values + few-shot examples — engineered against the cases that break the demo, not a lucky one-liner.
- Unvalidated JSON is a deferred incident. Constrain the format in the prompt, validate against a schema, retry once with the error fed back, and raise — never swallow — on failure.
- Tune inference parameters per task. Triage runs at temperature 0; creativity in a classifier is a bug. Drafting a reply wants ~0.7 and nucleus sampling.
- Prompts are versioned, approved, audited artifacts in Amazon Bedrock Prompt Management — not f-strings in git. Code pins an id and version number, never the prompt text.
- Ten reference tickets beat a thousand vibes. A deterministic regression suite with objective criteria is the gate for shipping a prompt change (skill 1.6.4).
- Few-shot is a recurring cost. Every example rides along on every call — price it before you stack it.
- Prompt Flows is the no-code chaining path for non-developers; Relay’s code-first triage doesn’t need it here.
What’s next
Relay can triage — but it calls one hardcoded model with no retries, no streaming, and no way to swap models without a code change. Module 3 builds the FM integration layer: model selection and routing across a fast/smart tier map, ConverseStream, network resilience with backoff and cross-Region fallback, and the converse() contract every later module relies on. The provisional _MODEL_ID constant you wrote today is replaced by a tier→profile map in relay/config.py (triage drops the constant and names only a tier), and triage gets refactored to call converse(tier="fast") — its 10/10 suite re-passing, untouched.
Want the full AIP-C01 question bank and the next module in your inbox? Subscribe here — it’s free, like everything in this series.
Links: Module 2 lab · ← Module 1 · Course index · Module 3 →
References
- AWS Certified Generative AI Developer - Professional (AIP-C01) exam guide — Task 1.6 (skills 1.6.1–1.6.6) and skill 4.2.4, the vocabulary this module reuses.
- Amazon Bedrock Prompt Management — parameterized templates, variables, and versions.
- Inference request parameters and response fields — temperature, top-p, top-k, and maxTokens.
- Use the Converse API — the single message shape used for every generation call in this course.
- Amazon Bedrock Flows (Prompt Flows) — no-code sequential prompt chaining and conditional branching.
- Amazon Bedrock pricing — re-verify the Nova Micro per-token figures used in the few-shot cost math.