From a wall of requirements to a sensible architecture
Every AIP-C01 scenario question reads like a client brief: a company, a goal, and three or four constraints tucked into the text, such as "must respond within 2 seconds", "data must stay in the EU" or "with the least operational overhead". Skill 1.1.1 of the exam guide asks you to create comprehensive architectural designs that align with specific business needs and technical constraints, for example by using appropriate FMs, integration patterns, deployment strategies. In this level you'll learn to read a brief the way an architect does, and then pick the simplest design that meets every constraint.
(As a reminder from Level 1: the exam guide belongs to AWS and is free on the AWS Certification site. This course quotes it but doesn't reproduce it. Check AWS's exam page for the current length, price and languages. This course is independent of AWS, isn't endorsed by AWS, and can't promise you a pass.)
Step 1: Break the scenario into five buckets
Before you think about any services, sort what the scenario tells you into five buckets. I keep a worksheet like this for real projects as well as for exam questions:
# Requirements worksheet for: "A bank wants an assistant that answers staff questions
# about internal lending policies. Policies change weekly. Answers must cite the source
# policy. Staff in Frankfurt and Paris. Data must not leave the EU. ~500 users."
requirements = {
"functional": ["answer questions from internal policy docs", "cite sources"],
"latency": "interactive chat, a few seconds is fine",
"cost": "moderate, steady weekday usage",
"sensitivity": "internal, EU data residency required",
"scale": "~500 users, business hours",
}
# Words that should jump out: 'change weekly' + 'cite' -> retrieval, not training.
# 'must not leave the EU' -> a hard constraint that removes some options.
Gotcha: the exam often gives a constraint once, in half a sentence. If you skip "least operational overhead" or "without managing infrastructure", a self-hosted answer can look attractive when it's actually wrong.
Step 2: Climb the complexity ladder, one rung at a time
Start at the bottom rung. Only go up a rung when a requirement forces you to.
| Rung | Pattern | Choose it when... |
|---|---|---|
| 1 | Direct invocation of an FM on Amazon Bedrock | The model's general knowledge plus a good prompt is enough: summarising, classifying, drafting |
| 2 | RAG (for example, Amazon Bedrock Knowledge Bases) | Answers depend on private or frequently changing data, or need citations |
| 3 | Agents (Amazon Bedrock Agents, Amazon Bedrock AgentCore) | The system must take actions or plan multi-step work by calling tools and APIs |
| 4 | Customised model (fine-tuning and similar) | Prompting and retrieval can't produce the behaviour you need, such as a specialised style, format or domain language |
Rung 1 in code is a single Converse API call. Converse gives you one request format across the supported models, so you can swap models later without rewriting the request:
import boto3
bedrock = boto3.client("bedrock-runtime", region_name="eu-central-1")
response = bedrock.converse(
modelId=MODEL_ID, # a model ID or inference profile ID (choosing one comes in Level 4)
system=[{"text": "Summarise support tickets in 3 bullet points."}],
messages=[{"role": "user", "content": [{"text": ticket_text}]}],
inferenceConfig={"maxTokens": 300, "temperature": 0.2},
)
print(response["output"]["message"]["content"][0]["text"])
Our bank needs rung 2, because the policies change weekly and answers must cite them. A Bedrock knowledge base does the retrieval and returns citations for you:
agent_rt = boto3.client("bedrock-agent-runtime", region_name="eu-central-1")
result = agent_rt.retrieve_and_generate(
input={"text": "What is the max loan-to-value for buy-to-let?"},
retrieveAndGenerateConfiguration={
"type": "KNOWLEDGE_BASE",
"knowledgeBaseConfiguration": {
"knowledgeBaseId": "KB12345678", # your knowledge base
"modelArn": MODEL_ARN, # FM or inference profile that writes the answer
},
},
)
print(result["output"]["text"]) # the answer
print(result["citations"]) # which retrieved chunks support it
When a policy changes, you re-sync the data source and the next answer reflects it. You don't retrain anything.
The key trade-off: fine-tuning changes how a model behaves. It's a poor way to give a model facts that change. If a distractor says "fine-tune the model weekly on the new policies", it's wrong: retraining that often is slow and expensive, and it still gives you no citations.
Step 3: Pick the integration pattern and deployment strategy
The constraints you collected also decide how requests arrive and how the model is served:
- Integration pattern: an interactive chat suits a synchronous API (API Gateway in front of Lambda calling Bedrock), with streaming if users should see text as it's generated. A job that runs over 10,000 documents overnight suits an asynchronous, event-driven design (Amazon SQS or Amazon EventBridge feeding workers), so a slow model never blocks a user.
- Deployment strategy: on-demand Bedrock inference means pay per token with no capacity to manage, which is the default for spiky or modest traffic. Provisioned Throughput buys dedicated capacity for consistent, high-volume workloads. A custom model on Bedrock can be served through Provisioned Throughput or, where supported, a custom model deployment for on-demand inference. A SageMaker AI endpoint makes sense when you need to host a model yourself, but you then run that infrastructure.
- Data sensitivity: for EU residency, a geographic cross-Region inference profile for the EU only routes requests to EU Regions. A global profile can route anywhere, so it fails our bank's constraint.
The finished design for the bank: API Gateway → Lambda → Bedrock Knowledge Bases (retrieve_and_generate) using an EU geographic inference profile, on-demand. That's rung 2 and nothing more.
Spotting over-engineered distractors
Wrong answers on this skill usually fail in one of three ways:
- Over-engineering: agents, multi-model ensembles or fine-tuning when one prompt or a knowledge base would do. If no requirement calls for actions, you don't need an agent.
- Ignoring a stated constraint: a global routing option when data must stay in a region, or a self-managed GPU fleet when the brief says "least operational overhead".
- Wrong tool for changing data: retraining to keep facts current.
A quick test I use: for each component in an answer, name the requirement that needs it. If you can't name one, that answer is probably a distractor.
What the exam asks about this
Expect scenario questions with three or four plausible architectures. The best answer meets every stated constraint with the least complexity. Underline the qualifiers ("most cost-effective", "least operational overhead", "must stay in"), sort the scenario into the five buckets, climb the ladder only as far as you have to, and then rule out any option that breaks a constraint.
Key takeaways
- Sort every scenario into functional, latency, cost, sensitivity and scale requirements before you pick services.
- Climb the ladder in order: direct invocation → RAG → agents → customised model, and only climb when a requirement forces it.
- Data that changes or needs citations calls for RAG (Bedrock Knowledge Bases), not fine-tuning.
- Actions and multi-step tool use call for agents.
- Interactive traffic suits synchronous APIs (with streaming if needed). Bulk jobs suit async or event-driven designs.
- Residency constraints rule options out. An EU geographic inference profile keeps routing inside the EU; a global one doesn't.
- Every component in an answer should map to a stated requirement.