AI on AWS Best Practices
Picking an AWS AI service well is a repeatable decision, not a guess. This checklist makes it repeatable.
Search across all documentation pages
Picking an AWS AI service well is a repeatable decision, not a guess. This checklist makes it repeatable.
Because AWS AI spans three layers - purpose-built services, Amazon Bedrock, and Amazon SageMaker AI - the risk is choosing by brand recognition instead of by fit. Each rule below states a habit positively, with a one-line rationale, so you can run it as an audit whenever you start a new AI feature.
Walk it once end to end, then keep it as a review checklist per use case.
Prefer the tuned task service when one names your job.
# --- Python (boto3) ---
import boto3
# Fixed task ("read sentiment") -> purpose-built service, no prompt.
comprehend = boto3.client("comprehend", region_name="us-east-1")
comprehend.detect_sentiment(Text="Fast and friendly.", LanguageCode="en")// --- TypeScript (AWS SDK v3) ---
import { ComprehendClient, DetectSentimentCommand } from "@aws-sdk/client-comprehend";
// Fixed task -> purpose-built service.
const comprehend = new ComprehendClient({ region: "us-east-1" });
await comprehend.send(new DetectSentimentCommand({ Text: "Fast and friendly.", LanguageCode: "en" }));Converse/ConverseStream so one request shape works across model providers and switching models is a modelId change.ConverseStream for interactive UX so tokens arrive incrementally instead of all at once.Keep model calls portable across providers.
# --- Python (boto3) ---
import boto3
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
bedrock.converse(
modelId="anthropic.claude-3-5-sonnet-20240620-v1:0",
messages=[{"role": "user", "content": [{"text": "One-line tip."}]}],
inferenceConfig={"maxTokens": 200}, # cap output to bound cost
)// --- TypeScript (AWS SDK v3) ---
import { BedrockRuntimeClient, ConverseCommand } from "@aws-sdk/client-bedrock-runtime";
const bedrock = new BedrockRuntimeClient({ region: "us-east-1" });
await bedrock.send(new ConverseCommand({
modelId: "anthropic.claude-3-5-sonnet-20240620-v1:0",
messages: [{ role: "user", content: [{ text: "One-line tip." }] }],
inferenceConfig: { maxTokens: 200 }, // cap output to bound cost
}));usage field, tag AI resources, and set AWS Budgets alerts so spend growth surfaces early.modelId on your task rather than defaulting to the newest one.bedrock:InvokeModel, textract:AnalyzeDocument), not broad wildcards.AccessDeniedException often means model access is not enabled; back off on throttling.Does a purpose-built service name my exact task? If yes and volume is high, use it. Otherwise consider Bedrock for open-ended work, and SageMaker AI only when you need a custom model.
No. For fixed, high-volume tasks a tuned purpose-built service is usually cheaper, faster, and more consistent. Reserve foundation models for open-ended or custom-format work.
Converse keeps one request shape across model providers, so switching models is a modelId change rather than a rewrite. It also standardizes usage reporting for cost tracking.
Cap output with a max-tokens setting, keep prompts short, choose an appropriately sized model, read the response usage field, and set AWS Budgets alerts on tagged AI resources.
Idle capacity. SageMaker real-time endpoints and Bedrock provisioned-throughput reservations bill continuously regardless of traffic. Tear down what you are not actively using.
Prompt for a specific short format, then validate it in code. Do not assume the model matches your schema every run; foundation-model output varies between calls.
Least privilege - only the exact actions you call, such as bedrock:InvokeModel or rekognition:DetectLabels. Avoid broad wildcards, and resolve credentials from IAM roles, not static keys.
They enforce content and safety policy on prompts and responses centrally, so open-ended generation stays within bounds you define. Apply them on user-facing generative features.
Model and service availability, and sometimes pricing, vary by region. Confirm your target region supports the model or service you call before you build against it.
It usually means model access is not enabled yet. Request access to the model in the Bedrock console, and confirm your IAM policy allows the invoke action for that model.
Whenever a new task type appears, a new model ships, or volume climbs. A layer or pricing mode that fit at low volume may not at scale, so revisit the decision.
Yes, and it is the common pattern. Use a task service for the mechanical part (extraction, transcription) and a foundation model for reasoning over the result. Each bills on its own model.
Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Reviewed by Chris St. John·Last updated Jul 24, 2026