How to Become an AI Engineer in 2026: The Complete Training Guide

How to Become an AI Engineer in 2026: The Complete Training Guide – SQLYARD

How to Become an AI Engineer in 2026: The Complete Training Guide


AI Engineer is one of the fastest-growing and most in-demand roles in enterprise technology in 2026. Demand for professionals who can design, build, and deploy production AI systems is running well ahead of available talent, and the gap is large enough that companies are actively hiring from adjacent technical backgrounds including data engineering, SQL development, and software development. The role is not reserved for machine learning researchers. It is an engineering discipline built on skills that many data professionals already partially have.

This guide explains what the AI Engineer role actually involves, maps out every skill area from the most current enterprise job specifications, describes how to build each skill with specific resources and hands-on exercises, and provides a complete workshop covering the tools that appear most in production AI engineering requirements right now: Python, RAG, LangGraph, Strands, AWS Bedrock, and Bedrock AgentCore.

All certification information, framework details, and technology descriptions are verified against current official documentation as of July 2026.

Written for data professionals making the transition. SQL Server DBAs, data engineers, and Azure/AWS data platform professionals already have significant transferable skills for this role. Data pipelines, SQL, cloud service familiarity, security awareness, and production operations experience all map directly to AI engineering requirements. The gap is primarily in Python, LLM tooling, and agentic frameworks. This guide builds those bridges explicitly.

1 What an AI Engineer Actually Does Beginner

An AI Engineer designs, develops, deploys, and maintains AI-powered applications and agentic systems that support business and operational objectives. The role is distinct from a data scientist, who focuses on research and model development, and from a traditional software engineer, who builds applications without AI-specific knowledge. The AI Engineer sits at the intersection: production engineering discipline applied to AI systems.

In practice the role involves building the systems through which AI models deliver business value. That means writing the code that connects foundation models to enterprise data, building the pipelines that prepare that data for model consumption, designing the agent architectures that allow models to take multi-step actions, deploying those agents into production environments with proper monitoring and security, and maintaining them as models and requirements evolve.

Enterprise AI engineering in regulated industries adds a further layer: every AI system must meet security standards, governance requirements, and regulatory compliance expectations. A financial services AI engineer is not just building a chatbot. They are building a system that touches customer data, must not hallucinate facts about accounts or regulations, and must operate within defined audit and compliance boundaries.

The SQL Server DBA parallel: A production SQL Server DBA does not just write queries. They design data architecture, tune performance, manage security and access control, maintain availability, monitor for issues, and operate within strict change control processes. An AI engineer does all of that for AI systems. The operational discipline required is the same. The technical surface is different.

2 How AI Engineering Differs from Data Science and ML Engineering Beginner

RolePrimary FocusTypical DeliverableModel Relationship
Data Scientist Research, experimentation, model development Model, notebook, analysis Creates and trains models
ML Engineer Operationalizing and scaling trained models ML pipeline, inference infrastructure, CI/CD Deploys and maintains trained models
AI Engineer Building applications and agents on top of foundation models Production AI application, agent system, API, workflow Integrates and orchestrates pre-trained foundation models

The key distinction for 2026 is that AI engineering primarily uses foundation models that already exist (GPT-4o, Claude 3.7, Llama 3, Amazon Nova, Titan) rather than training models from scratch. The engineer’s job is to wire these models into enterprise systems effectively, safely, and reliably. Fine-tuning is occasionally required but consuming and orchestrating pre-trained models is the core day-to-day activity.

3 Skills Map: What Enterprise AI Engineering Requires Beginner

The following table maps every skill from current enterprise AI engineering requirements into five proficiency tiers, and indicates which skills data professionals typically already have and which represent genuine gaps to close.

Skill AreaEnterprise RequirementData Professional Starting Point
PythonCore language for all AI tooling. 3+ years in production recommended.Partial. SQL-first professionals usually have some Python but not at production application level.
Foundation Models and LLMsUnderstand capabilities, limitations, context windows, tokenization, temperature, sampling.Gap. This is new knowledge for most data professionals.
Prompt EngineeringSystem prompts, few-shot prompting, chain-of-thought, structured output prompting.Gap. Requires direct hands-on practice.
RAG SystemsDesign and implement retrieval-augmented generation pipelines.Partial. Data pipeline experience transfers. Vector-specific knowledge is new.
Vector DatabasesEmbeddings, similarity search, indexing strategies, integration patterns.Gap for most. Conceptually similar to indexed searches but technically different.
Agentic AILangGraph, Strands, multi-agent patterns, agent-to-agent communication, tool use.Gap. This is the frontier skill with the highest demand and lowest supply.
AWS Bedrock and AgentCoreFoundation model access, agent deployment, memory management, observability.Partial. AWS familiarity helps but Bedrock-specific services need targeted study.
MCP (Model Context Protocol)Tool discovery, agent-to-agent communication, external service integration.Gap. Very new. Rapidly becoming essential for enterprise agentic systems.
Data Pipelines for AIPipelines for model training, inference, feature extraction, embedding generation.Strong match. SQL Server and Azure data professionals already do this.
API DevelopmentREST API design and integration for AI services and enterprise applications.Partial. Data professionals may have consumed APIs but less experience building them.
MLOps and DeploymentCI/CD for AI, model versioning, A/B testing, rollback, monitoring.Partial. Production operations experience transfers. ML-specific tooling is new.
Security and GovernanceResponsible AI principles, access controls, audit logging, compliance.Strong match. Regulated environment experience is directly applicable.

4 Python: The Foundation Beginner

Every AI engineering framework, library, and cloud SDK uses Python as its primary interface. There is no path through this role without production-level Python ability. For data professionals who have only used Python for scripting or data analysis, the gap to close is writing modular, maintainable, testable Python applications rather than ad-hoc scripts.

What to focus on specifically for AI engineering

  • Virtual environments and dependency management. venv, pip, and increasingly uv for fast dependency resolution. Know how to pin versions in requirements.txt and understand why it matters in production.
  • Async programming. Many AI APIs are asynchronous. Understanding async/await, asyncio, and how to structure async pipelines is essential for building responsive agent systems.
  • Typing and type hints. Production AI code uses type annotations extensively. typing, Pydantic for data validation, and dataclasses appear throughout every major AI framework.
  • Environment variables and configuration. API keys, model endpoints, and deployment configuration are always passed through environment variables or secrets managers, never hardcoded.
  • HTTP clients and REST APIs. httpx and requests for calling external services. Building FastAPI applications to expose AI capabilities.
# Example: async call to Amazon Bedrock using boto3 and the Bedrock Runtime client
# This is the fundamental pattern for all foundation model calls on AWS

import asyncio
import boto3
import json

bedrock = boto3.client(
    service_name   = 'bedrock-runtime',
    region_name    = 'us-east-1'
)

def invoke_model(prompt: str, model_id: str = 'amazon.nova-pro-v1:0') -> str:
    """Invoke a foundation model on Amazon Bedrock and return the response text."""
    body = json.dumps({
        "messages": [
            {"role": "user", "content": prompt}
        ],
        "inferenceConfig": {
            "maxTokens": 1000,
            "temperature": 0.1
        }
    })

    response = bedrock.invoke_model(
        modelId      = model_id,
        body         = body,
        contentType  = 'application/json',
        accept       = 'application/json'
    )

    response_body = json.loads(response['body'].read())
    return response_body['output']['message']['content'][0]['text']


if __name__ == '__main__':
    result = invoke_model("Explain what a vector database is in two sentences.")
    print(result)

5 Foundation Models, LLMs, and Prompt Engineering Beginner

A foundation model is a large neural network trained on broad data and capable of performing many tasks. Large Language Models (LLMs) are a category of foundation models trained primarily on text. Understanding how they work at a conceptual level, not a mathematical research level, is necessary for building effective AI applications.

Key concepts to understand

  • Context window. The maximum amount of text (measured in tokens) a model can process in a single request. Ranges from 8,000 tokens for smaller models to over 1 million for frontier models. Context window size determines how much data can be fed to the model in a single call.
  • Temperature. A parameter controlling output randomness. Temperature 0 produces deterministic, consistent output. Temperature 1 produces more varied and creative output. For business applications requiring accuracy and consistency, use 0 to 0.2.
  • System prompt. An instruction block that establishes the model’s role, constraints, output format, and behavior before any user message. In enterprise AI applications the system prompt is where business rules, persona, and guardrails are defined.
  • Tokens. The units models use to process text. A token is approximately 3 to 4 characters in English. API pricing and context limits are always measured in tokens, not words or characters.
  • Hallucination. The tendency of LLMs to generate plausible-sounding but factually incorrect information. The primary mitigation in enterprise applications is grounding model responses in retrieved facts (RAG) rather than relying on model memory.

Prompt engineering fundamentals

# System prompt pattern for enterprise AI applications
# The system prompt establishes role, constraints, and output format

SYSTEM_PROMPT = """
You are a data analysis assistant for a regulated financial institution.

Rules you must follow:
- Only answer questions using information explicitly provided in the context below.
- If the answer is not in the provided context, respond exactly: 
  "I cannot answer this from the available data."
- Never invent or estimate figures that are not in the context.
- Do not provide investment advice, legal advice, or regulatory interpretation.
- All monetary values must include the currency unit.

Output format:
- Keep responses concise and factual.
- Use bullet points for lists of three or more items.
- Always state the data source when citing a specific figure.
"""

USER_PROMPT_TEMPLATE = """
Context:
{retrieved_context}

Question: {user_question}
"""

6 RAG: Retrieval-Augmented Generation Intermediate

Retrieval-Augmented Generation (RAG) is the most important architectural pattern in enterprise AI engineering. It solves the hallucination problem by providing the model with relevant, current facts at query time rather than relying on what the model learned during training. Understanding and implementing RAG is a core requirement in every enterprise AI engineering role.

How RAG works

  1. A document corpus (policies, manuals, records, knowledge bases) is split into chunks and converted into vector embeddings.
  2. The embeddings are stored in a vector database with references back to the source documents.
  3. When a user asks a question, the question is converted to a vector embedding using the same model.
  4. The vector database performs similarity search to find the most semantically relevant document chunks.
  5. Those chunks are injected into the system prompt as context before the LLM is called.
  6. The LLM generates a response grounded in the retrieved content rather than from training memory.
# Simple RAG pattern with AWS Bedrock Knowledge Bases
# AWS Bedrock Knowledge Bases handles embedding, indexing, and retrieval automatically
# This is the managed RAG approach - no vector DB management required

import boto3
import json

bedrock_agent_runtime = boto3.client(
    service_name = 'bedrock-agent-runtime',
    region_name  = 'us-east-1'
)

def rag_query(
    question         : str,
    knowledge_base_id: str,
    model_arn        : str = 'arn:aws:bedrock:us-east-1::foundation-model/amazon.nova-pro-v1:0',
    max_results      : int = 5
) -> dict:
    """
    Query an AWS Bedrock Knowledge Base using managed RAG.
    Returns the model response and the source documents used.
    """
    response = bedrock_agent_runtime.retrieve_and_generate(
        input = {'text': question},
        retrieveAndGenerateConfiguration = {
            'type'                     : 'KNOWLEDGE_BASE',
            'knowledgeBaseConfiguration': {
                'knowledgeBaseId'         : knowledge_base_id,
                'modelArn'                : model_arn,
                'retrievalConfiguration' : {
                    'vectorSearchConfiguration': {
                        'numberOfResults': max_results
                    }
                }
            }
        }
    )

    return {
        'answer'  : response['output']['text'],
        'sources' : [
            cite['retrievedReferences']
            for cite in response.get('citations', [])
        ]
    }


# Example usage
result = rag_query(
    question          = 'What is the maximum loan amount for a small business application?',
    knowledge_base_id = 'YOUR_KNOWLEDGE_BASE_ID'
)
print('Answer:', result['answer'])
print('Sources used:', len(result['sources']))

7 Vector Databases Intermediate

A vector database stores high-dimensional numerical representations of data (embeddings) and enables similarity search: finding records that are semantically similar to a query even when no exact keywords match. Vector databases are the storage layer that makes RAG systems possible.

Key vector database options in the enterprise landscape:

DatabaseTypeBest For
Amazon OpenSearch ServerlessManaged AWS serviceAWS-native deployments. Integrates directly with Bedrock Knowledge Bases.
pgvectorPostgreSQL extensionTeams already on PostgreSQL. Simple adoption without new infrastructure.
PineconeManaged cloud servicePure vector search at scale. Simple API. No infrastructure management.
ChromaOpen sourceLocal development and prototyping. Not recommended for production at scale.
Azure AI SearchManaged Azure serviceAzure-native deployments. Hybrid keyword and vector search.

The key concept: embeddings

An embedding is a vector of floating-point numbers that represents the semantic meaning of a piece of text. Two texts with similar meaning will have vectors that are close to each other in high-dimensional space. This allows the database to find semantically relevant content without keyword matching. Embedding models convert text to vectors. The quality of the embedding model directly affects RAG system accuracy.

8 Agentic AI: LangGraph, Strands, and Agent Architecture Advanced

An AI agent is a system where a language model takes actions: it calls tools, reads results, decides what to do next, and continues until it has completed a task or determined it cannot. This is fundamentally different from a single-call LLM interaction where the model responds once and stops. Agentic AI is the frontier skill with the highest demand and lowest supply in enterprise AI engineering as of 2026.

LangChain is partially deprecated as a primary agent runtime. LangChain’s latest versions actually run on LangGraph under the hood. The current meaningful choices for enterprise agent development are LangGraph (explicit graph control flow) and Strands Agents (model-first autonomy). Both are production-ready and both run on AWS Bedrock and Bedrock AgentCore. Do not invest significant time in older LangChain agent patterns.

LangGraph: graph-based explicit control flow

LangGraph treats an agent workflow as a directed graph where nodes are functions and edges are transitions. The developer explicitly designs the graph. The model executes it. This gives maximum control over agent behavior, making it well-suited to regulated environments where the possible actions of an agent must be auditable and constrained. The core mental model: LangGraph says the developer designs the graph and the model executes it.

# LangGraph minimal example: a two-node research agent
# Node 1: retrieve relevant context
# Node 2: generate response from retrieved context
# Explicit graph control means the developer controls every transition

from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated
import operator
import boto3
import json

# Define the agent state
class AgentState(TypedDict):
    question      : str
    retrieved_docs: list
    final_answer  : str

# Node 1: retrieve context (calls a vector DB or knowledge base)
def retrieve_context(state: AgentState) -> AgentState:
    # In production, this calls a vector database or Bedrock Knowledge Base
    # Simplified here for illustration
    docs = [f"Retrieved document relevant to: {state['question']}"]
    return {"retrieved_docs": docs}

# Node 2: generate answer grounded in retrieved context
def generate_answer(state: AgentState) -> AgentState:
    bedrock = boto3.client('bedrock-runtime', region_name='us-east-1')
    context = "\n".join(state['retrieved_docs'])

    prompt = f"""
    Context: {context}

    Question: {state['question']}

    Answer using only the provided context. If the context does not contain
    the answer, state that clearly.
    """

    response = bedrock.invoke_model(
        modelId    = 'amazon.nova-pro-v1:0',
        body       = json.dumps({
            "messages"        : [{"role": "user", "content": prompt}],
            "inferenceConfig" : {"maxTokens": 500, "temperature": 0.1}
        }),
        contentType = 'application/json',
        accept      = 'application/json'
    )
    body = json.loads(response['body'].read())
    return {"final_answer": body['output']['message']['content'][0]['text']}

# Build the graph
graph = StateGraph(AgentState)
graph.add_node("retrieve", retrieve_context)
graph.add_node("generate", generate_answer)
graph.set_entry_point("retrieve")
graph.add_edge("retrieve", "generate")
graph.add_edge("generate", END)
agent = graph.compile()

# Run the agent
result = agent.invoke({"question": "What are the current interest rate offerings?",
                       "retrieved_docs": [], "final_answer": ""})
print(result["final_answer"])

Strands Agents: model-first autonomous agents

Strands is an AWS-released open-source SDK that takes a model-first approach: define a prompt and a list of tools, and the model itself decides how to plan and sequence tool calls. Strands is lighter-weight than LangGraph for agents that do not need explicit graph control flow. The core mental model: Strands says the developer gives the model tools and lets the model figure out the workflow. Strands has built-in support for A2A (Agent-to-Agent) protocol and native integration with Bedrock AgentCore for production deployment.

# Strands Agents minimal example: a SQL analysis agent
# The model decides when to call each tool and in what order
# pip install strands-agents strands-agents-tools

from strands import Agent, tool
from strands.models import BedrockModel

# Define tools the agent can use
@tool
def run_sql_query(query: str) -> str:
    """Execute a SQL query and return the results as a string.
    Use this when you need to retrieve data from the database.
    """
    # In production, this connects to your database
    # Simplified return for illustration
    return f"Query results for: {query}\n[Row 1] [Row 2] [Row 3]"

@tool
def get_table_schema(table_name: str) -> str:
    """Get the schema for a database table.
    Use this to understand the structure before writing a query.
    """
    return f"Schema for {table_name}: id INT, name VARCHAR(255), created_at DATETIME"

# Create the agent with Bedrock model and tools
model = BedrockModel(
    model_id    = "amazon.nova-pro-v1:0",
    region_name = "us-east-1"
)

agent = Agent(
    model  = model,
    tools  = [run_sql_query, get_table_schema],
    system_prompt = """
    You are a SQL data analysis assistant.
    Always check the table schema before writing a query.
    Only run queries that retrieve data. Never run INSERT, UPDATE, or DELETE.
    Explain your findings in plain English after retrieving data.
    """
)

# The model plans and executes tool calls autonomously
response = agent("How many customers signed up last month, and what is their average loan size?")
print(response)

9 AWS Bedrock and Bedrock AgentCore Intermediate

Amazon Bedrock is the AWS managed service providing API access to foundation models from Amazon (Nova, Titan), Anthropic (Claude), Meta (Llama), Mistral, Cohere, and others without managing the underlying infrastructure. Bedrock is where model invocations happen. It also provides Knowledge Bases (managed RAG), Guardrails (content safety and compliance filters), and model evaluation tools.

Amazon Bedrock AgentCore reached general availability in October 2025. It is the production deployment platform for AI agents on AWS. AgentCore is not a framework for writing agent logic and it does not replace LangGraph, Strands, or CrewAI. It is the platform those frameworks run on when deployed to AWS production infrastructure.

What AgentCore provides

ComponentWhat It Does
RuntimeSecure Firecracker microVM environment for agent execution on EC2 bare metal. Handles containerization, scaling, and session isolation without developer-managed infrastructure.
MemoryBuilt-in short-term and long-term memory for agents. Enables multi-turn conversations and cross-session context persistence without building custom memory infrastructure.
Gateway (MCP)Tool discovery and management via Model Context Protocol. Register REST APIs, Lambda functions, or OpenAPI specs. AgentCore converts them to MCP-compatible tool definitions any framework can invoke.
ObservabilityBuilt-in distributed tracing and monitoring using OpenTelemetry. Tracks every LLM invocation, tool call, memory operation, and multi-step reasoning chain with Foundry Observability integration.
Identity and AuthCognito integration for user authentication. Role-based access control for tool and memory scoping. Authentication pass-through for downstream services.
# Deploy a Strands agent to AgentCore using the AgentCore CLI
# The AgentCore CLI handles containerization, ECR push, and deployment automatically

# Install the AgentCore CLI
# pip install agentcore

# In your agent Python file (agent.py):
from strands import Agent, tool
from strands.models import BedrockModel

@tool
def get_account_summary(account_id: str) -> str:
    """Retrieve account summary for a given account ID."""
    # Your business logic here
    return f"Account {account_id}: Balance $10,000, Status: Active"

model = BedrockModel(model_id="amazon.nova-pro-v1:0", region_name="us-east-1")
agent = Agent(
    model         = model,
    tools         = [get_account_summary],
    system_prompt = "You are a secure account inquiry assistant. Never share PII."
)

def handler(event, context):
    """AgentCore entrypoint function."""
    response = agent(event.get('message', ''))
    return {"response": str(response)}

# From the terminal - AgentCore CLI commands:
# agentcore init             # initialize the project
# agentcore dev              # run locally with hot reload and agent inspector
# agentcore deploy           # deploy to AWS - handles all containerization automatically

10 MCP: Model Context Protocol Intermediate

The Model Context Protocol is an open standard for how AI agents discover and invoke external tools and services. It is to AI agents what REST is to web APIs: a shared communication standard that allows any MCP-compatible agent to use any MCP-compatible tool without custom integration work for each combination. The SQLYARD MCP guide covers this topic in depth. This section summarizes the AI engineering context specifically.

In enterprise AI engineering, MCP matters because it allows agents to call internal enterprise tools (databases, APIs, document systems, ERPs) through a standardized protocol with authentication and rate limiting handled by AgentCore Gateway. The agent developer registers the enterprise tool as an MCP server once. Every agent that has been granted access can then invoke it without each agent implementing its own integration.

Agent-to-agent communication (A2A) builds on MCP to allow specialist agents to hand off tasks to each other: an orchestrator agent delegates a SQL analysis task to a database specialist agent which returns structured results the orchestrator incorporates into a broader response. This multi-agent pattern is now a stated requirement in enterprise AI engineering job specifications.

11 Data Pipelines for AI Intermediate

Data professionals transitioning to AI engineering have the strongest existing skills in this area. Data pipelines for AI are fundamentally the same discipline as data engineering pipelines with an additional set of AI-specific outputs: embedding generation, training dataset preparation, inference result storage, and model performance telemetry.

Where AI pipelines differ from standard ETL

  • Chunking and embedding pipelines. Documents must be split into semantically coherent chunks and converted to vector embeddings before storage in a vector database. The chunk size and overlap strategy significantly affects RAG accuracy.
  • Feature stores for ML. Training machine learning models requires consistent, versioned feature extraction. Feature stores (AWS SageMaker Feature Store, Feast) ensure training and inference use the same feature computation logic.
  • Model input/output logging. Every model invocation in production should log the input, output, model version, latency, and any error. This data feeds monitoring, evaluation, and fine-tuning pipelines.
  • Inference result pipelines. AI model outputs often need downstream processing: structured data extraction from free-text responses, confidence scoring, result routing to different systems based on content classification.

12 MLOps and Production Deployment Intermediate

MLOps applies DevOps principles to machine learning and AI systems. For an AI engineer working with foundation models rather than training custom models, MLOps primarily concerns the deployment, versioning, monitoring, and rollback of AI applications and their model configurations rather than model training pipelines.

Key MLOps concepts for AI engineers

  • CI/CD for AI applications. Every change to an agent’s system prompt, tool definitions, model version, or RAG configuration should go through automated testing and staged deployment. A bad prompt change in production is a bug just as a bad code change is.
  • Model versioning. Track which foundation model version each application is using. Model providers update models, which changes behavior. Pin model versions in production and test before upgrading.
  • Evaluation pipelines. Automated evaluation of AI response quality is essential. Define ground-truth answer sets and run them against every deployment to catch regressions before users see them.
  • Monitoring and alerting. Track latency, error rates, token consumption, and response quality metrics. AgentCore Observability provides this through OpenTelemetry for Bedrock-deployed agents. SageMaker Model Monitor provides similar for SageMaker-deployed models.
  • Rollback procedures. Production AI incidents often require fast rollback to a previous model version, system prompt, or RAG configuration. Define and test rollback procedures before deploying to production.

13 Responsible AI, Governance, and Regulated Environments Intermediate

Enterprise AI engineering in regulated industries has requirements that do not appear in consumer AI tutorials but are explicitly required by employers in financial services, healthcare, insurance, and government. Data professionals with production database experience in regulated environments already understand the mindset. The specific tools and frameworks are different.

The six AWS Responsible AI principles

AWS frames responsible AI around six principles that align closely with what regulated employers require from AI engineers: fairness (no discriminatory bias in outputs), explainability (decisions must be traceable and understandable), privacy and security (data handling meets compliance requirements), robustness (systems perform reliably under adversarial conditions), governance (oversight policies and controls are defined and enforced), and safety (systems cannot be used to cause harm).

Practical implementation in enterprise AI systems

  • Amazon Bedrock Guardrails. Content filtering, PII detection and redaction, topic restrictions, grounding checks (hallucination detection), and word filters applied at the model invocation layer. Guardrails are the primary technical control for AI output compliance.
  • Audit logging. Every model invocation, tool call, and data access must be logged to CloudTrail or equivalent. Who called what, with what input, and what was returned must be recoverable for compliance review.
  • Data residency. In regulated industries, data cannot leave defined geographic boundaries. AWS region selection and VPC endpoint configuration for Bedrock are required to enforce this.
  • Human review gates. High-consequence AI decisions (loan decisioning, fraud flags, medical information) require a human review step before action. AI engineers build the workflow that routes cases to human review when confidence thresholds are not met.

14 The AWS AI Certification Roadmap Beginner

Two certifications appear most consistently in enterprise AI engineering job requirements on AWS. Both are verified current as of July 2026.

Certification 1: AWS Certified Machine Learning Engineer Associate (MLA-C01)

Exam update in progress. MLA-C01 is being updated to MLA-C02. Beta registration opens September 1, 2026. The last day to take MLA-C01 in English is September 28, 2026. MLA-C02 will add generative AI, agentic AI, and foundation model workloads to the existing traditional ML engineering content. Take MLA-C01 now if prepared, or wait for MLA-C02 for a credential that validates both traditional ML and GenAI skills. The certification credential from MLA-C01 remains active through its original expiration date.

DetailMLA-C01 (Current)
Exam codeMLA-C01
Questions65 questions
Duration170 minutes
Score range100 to 1,000
English availabilityUntil September 28, 2026
Recommended experience1+ year with Amazon SageMaker and AWS ML services, 1+ year in a relevant ML role
Key domainsData ingestion and preparation, model training and tuning, deployment infrastructure, CI/CD for ML, monitoring and security

Certification 2: AWS Certified Generative AI Developer Professional (AIP-C01)

Now generally available since March 2026. The standard version of this exam opened for registration on March 17, 2026 after beta ended. This is one of the freshest AWS certifications available and among the least saturated in the market, meaning it stands out significantly on a resume right now.

DetailAIP-C01
Exam codeAIP-C01
LevelProfessional (AWS’s highest tier)
Questions65 scored + 10 unscored
Passing score750 out of 1,000
Cost$300
Validity3 years
No mandatory prerequisitesRecommended to have Developer Associate or MLA-C01 first
Key domainsFoundation model selection, RAG architectures, vector databases, agentic workflows and orchestration, Bedrock Guardrails, cost and performance optimization, monitoring and reliability
Exam realityCannot be passed by reading documentation alone. Hands-on Bedrock and agentic workflow experience is essential. Bedrock Agents and multi-agent architectures appear frequently.

Recommended certification sequence

  1. AWS Certified AI Practitioner (AIF-C01) if starting with no prior AWS AI knowledge. Free digital training available on AWS Skill Builder. This is the entry-level AI credential.
  2. AWS Certified Machine Learning Engineer Associate (MLA-C01 or MLA-C02). Take MLA-C01 before September 28, 2026 if already prepared. Wait for MLA-C02 if starting study now.
  3. AWS Certified Generative AI Developer Professional (AIP-C01). Take this after completing hands-on projects with Bedrock, RAG, and agentic workflows. This is the primary professional differentiation credential for the AI engineer role.

15 Workshop: Building an AI Engineer Skill Stack from Scratch Beginner

This workshop walks through every step from zero to a working AI agent deployed on AWS. Every code block is complete and executable. Every stage explains what is happening and why, not just what to type. Complete each stage in order as each one builds on the previous.

What is needed before starting: A computer running Windows, Mac, or Linux. Python 3.11 or higher installed (verify with python --version in a terminal). An AWS account (create a free one at aws.amazon.com if needed). A credit card on the AWS account. Estimated AWS cost for the full workshop is $5 to $20 depending on usage. All AWS resources should be cleaned up after completing the workshop to avoid ongoing charges.

Stage 1: Set up the Python environment (Day 1)

A virtual environment keeps the packages for this workshop separate from anything else on the computer. This is standard practice for every Python AI project.

# Open a terminal (Command Prompt or PowerShell on Windows, Terminal on Mac/Linux)
# Create a new folder for the workshop
mkdir ai-engineer-workshop
cd ai-engineer-workshop

# Create a virtual environment named .venv
python -m venv .venv

# Activate the virtual environment
# On Windows:
.venv\Scripts\activate
# On Mac or Linux:
source .venv/bin/activate

# The terminal prompt should now show (.venv) at the start
# This confirms the virtual environment is active

# Install the packages needed for this workshop
pip install boto3 strands-agents langgraph pydantic

# Verify the installations worked
python -c "import boto3; import strands; import langgraph; print('All packages installed correctly')"
# Expected output: All packages installed correctly

Stage 2: Configure AWS access (Day 1)

The code needs permission to call AWS services. AWS uses access keys to authenticate programmatic access. Follow these steps exactly.

Step 2.1: Create an IAM user

  1. Go to the AWS Console at console.aws.amazon.com and sign in.
  2. In the search bar at the top, type IAM and click the IAM service.
  3. In the left sidebar, click Users, then click Create user.
  4. Enter a username such as ai-workshop-user. Click Next.
  5. Select Attach policies directly. Search for and select AmazonBedrockFullAccess. Click Next, then Create user.
  6. Click on the user you just created. Click the Security credentials tab. Scroll to Access keys and click Create access key.
  7. Select Local code as the use case. Click Next, then Create access key.
  8. Copy the Access Key ID and Secret Access Key. This is the only time the secret key is shown. Save both values somewhere safe.

Step 2.2: Configure AWS credentials on the computer

# Run this in the terminal
aws configure

# It will prompt for four values:
# AWS Access Key ID: paste the Access Key ID from step 2.1
# AWS Secret Access Key: paste the Secret Access Key from step 2.1
# Default region name: us-east-1
# Default output format: json

# Verify the configuration works
aws sts get-caller-identity
# Expected output: a JSON object showing your AWS account ID and user ARN
# If you see an error, double-check the access key values

Step 2.3: Enable Bedrock model access

  1. In the AWS Console search bar, type Bedrock and click Amazon Bedrock.
  2. In the left sidebar, click Model access (under Bedrock configurations).
  3. Click Modify model access.
  4. Find and check the boxes for: Amazon Nova Pro and Anthropic Claude Sonnet 4.
  5. Click Next, then Submit. Access is usually granted within a few seconds. The status column should change to “Access granted.”

If model access is not available in your region: Some models require the region to be set to us-east-1 (US East, N. Virginia). If model access is not showing the models listed above, confirm the AWS Console is set to the us-east-1 region using the region selector in the top-right corner.

Stage 3: First foundation model call (Day 1)

This is the most important milestone in the workshop. Once this works, every other stage is just building on top of this foundation.

Create a new file called stage3_first_call.py in the workshop folder and paste the complete code below into it.

# stage3_first_call.py
# First complete call to a foundation model via Amazon Bedrock
# Run with: python stage3_first_call.py

import boto3   # AWS SDK for Python - connects to all AWS services
import json    # Built-in Python library for reading and writing JSON
import sys     # Built-in Python library for reading command-line arguments

def call_bedrock(prompt: str, temperature: float = 0.1) -> str:
    """
    Send a prompt to Amazon Nova Pro via Amazon Bedrock and return the response text.

    Parameters:
        prompt      : The question or instruction to send to the model.
        temperature : Controls how creative/random the response is.
                      0.0 = very consistent, same answer every time.
                      1.0 = more varied and creative answers.
                      For business applications, keep this at 0.1 to 0.3.

    Returns:
        The model's response as a plain text string.
    """

    # Create a Bedrock Runtime client
    # This is the service that actually runs the model inference
    bedrock = boto3.client(
        service_name = 'bedrock-runtime',
        region_name  = 'us-east-1'  # must match where model access was enabled
    )

    # Build the request body
    # The 'messages' format is used by most modern foundation models
    # Each message has a 'role' (user or assistant) and 'content'
    request_body = {
        "messages": [
            {
                "role"   : "user",
                "content": prompt
            }
        ],
        "inferenceConfig": {
            "maxTokens"  : 500,         # maximum length of the response
            "temperature": temperature  # randomness setting
        }
    }

    # Call the model
    # modelId tells Bedrock which specific model to use
    response = bedrock.invoke_model(
        modelId     = 'amazon.nova-pro-v1:0',
        body        = json.dumps(request_body),  # convert dict to JSON string
        contentType = 'application/json',
        accept      = 'application/json'
    )

    # Parse the response
    # response['body'] is a streaming object - read() gets the bytes, decode() makes it a string
    response_body = json.loads(response['body'].read().decode('utf-8'))

    # Navigate the response structure to get the text
    # The path is: output -> message -> content -> first item -> text
    answer = response_body['output']['message']['content'][0]['text']

    return answer


# Main program - runs when the script is executed directly
if __name__ == '__main__':

    # Check if a question was provided as a command-line argument
    # Usage: python stage3_first_call.py "What is a vector database?"
    if len(sys.argv) > 1:
        question = ' '.join(sys.argv[1:])  # join all arguments in case of spaces
    else:
        question = "Explain what Amazon Bedrock is in two sentences."

    print(f"\nQuestion: {question}")
    print("-" * 50)

    answer = call_bedrock(question)

    print(f"Answer: {answer}")
    print("-" * 50)

    # Experiment: call the same question with different temperatures
    print("\nSame question with temperature 0.0 (most consistent):")
    answer_low_temp = call_bedrock(question, temperature=0.0)
    print(answer_low_temp)

    print("\nSame question with temperature 0.9 (most creative):")
    answer_high_temp = call_bedrock(question, temperature=0.9)
    print(answer_high_temp)

Run the script from the terminal:

python stage3_first_call.py
# Expected output:
# Question: Explain what Amazon Bedrock is in two sentences.
# --------------------------------------------------
# Answer: Amazon Bedrock is a fully managed AWS service that provides access to
# high-performing foundation models from leading AI companies through a single API.
# It enables developers to build and scale generative AI applications without managing
# any underlying infrastructure.
# --------------------------------------------------

# Try with your own question:
python stage3_first_call.py "What is the difference between SQL Server and a vector database?"

If the script runs and returns an answer, Stage 3 is complete. The core Bedrock connection is working. Every AI engineering project on AWS builds on exactly this pattern: create a boto3 client, build a request body with messages, call invoke_model, and parse the response.

Common errors at this stage and what they mean:

  • NoCredentialsError: The AWS credentials are not configured. Re-run aws configure and verify the access key values.
  • AccessDeniedException: Model access has not been enabled for this model in the Bedrock console, or the IAM user does not have BedrockFullAccess. Check both.
  • ValidationException: The provided model identifier is invalid: The model ID is incorrect or the model is not available in the selected region. Confirm us-east-1 is the region.

Stage 4: System prompts and structured output (Day 2)

A system prompt is an instruction block that defines how the model should behave before any user question arrives. This is where business rules, output format requirements, and topic restrictions go in production AI applications.

Create stage4_prompts.py:

# stage4_prompts.py
# Demonstrates system prompts and structured JSON output
# Run with: python stage4_prompts.py

import boto3
import json

bedrock = boto3.client(service_name='bedrock-runtime', region_name='us-east-1')

def call_with_system_prompt(system_prompt: str, user_question: str) -> str:
    """
    Call Bedrock with a separate system prompt and user question.
    The system prompt defines the model's behavior.
    The user question is what the user actually asks.
    """
    request_body = {
        "system": [
            {"text": system_prompt}  # system prompt goes in a separate 'system' key
        ],
        "messages": [
            {"role": "user", "content": user_question}
        ],
        "inferenceConfig": {
            "maxTokens"  : 800,
            "temperature": 0.1
        }
    }

    response = bedrock.invoke_model(
        modelId     = 'amazon.nova-pro-v1:0',
        body        = json.dumps(request_body),
        contentType = 'application/json',
        accept      = 'application/json'
    )

    response_body = json.loads(response['body'].read().decode('utf-8'))
    return response_body['output']['message']['content'][0]['text']


# ---------------------------------------------------------------
# Experiment 1: Topic restriction
# The system prompt limits what the model will discuss
# ---------------------------------------------------------------
TOPIC_RESTRICTED_PROMPT = """
You are a SQL Server database assistant.
You only answer questions about SQL Server, database performance, indexing, and query tuning.
If asked about anything else, respond exactly with:
"I can only answer SQL Server and database questions."
Do not apologize. Do not explain further.
"""

print("=== Experiment 1: Topic restriction ===")
print("\nIn-scope question:")
answer = call_with_system_prompt(TOPIC_RESTRICTED_PROMPT, "How do I rebuild an index in SQL Server?")
print(answer)

print("\nOut-of-scope question:")
answer = call_with_system_prompt(TOPIC_RESTRICTED_PROMPT, "What is the best recipe for chocolate cake?")
print(answer)


# ---------------------------------------------------------------
# Experiment 2: Structured JSON output
# Force the model to return JSON that Python can parse
# ---------------------------------------------------------------
JSON_OUTPUT_PROMPT = """
You are a database health assessment tool.
When given a description of a database problem, you must respond ONLY with a JSON object.
No explanation, no markdown, no code fences. Only the raw JSON object.
The JSON must follow exactly this structure:
{
  "severity": "CRITICAL|HIGH|MEDIUM|LOW",
  "category": "Performance|Availability|Security|Capacity",
  "finding": "one sentence description of the problem",
  "recommended_action": "one sentence describing what to do",
  "estimated_fix_time_hours": a number
}
"""

print("\n=== Experiment 2: Structured JSON output ===")
problem_description = "Index fragmentation is at 87% on the main orders table and queries are taking 45 seconds."
raw_response = call_with_system_prompt(JSON_OUTPUT_PROMPT, problem_description)

print("Raw response from model:")
print(raw_response)

# Parse the JSON and use it programmatically
try:
    finding = json.loads(raw_response)
    print(f"\nParsed successfully!")
    print(f"Severity: {finding['severity']}")
    print(f"Category: {finding['category']}")
    print(f"Finding: {finding['finding']}")
    print(f"Action: {finding['recommended_action']}")
    print(f"Estimated fix: {finding['estimated_fix_time_hours']} hours")
except json.JSONDecodeError as e:
    print(f"JSON parsing failed: {e}")
    print("The model did not return valid JSON. This is a common issue.")
    print("Fix: add more explicit instructions like 'Return ONLY the JSON, no other text'")


# ---------------------------------------------------------------
# Experiment 3: Few-shot prompting
# Provide examples so the model learns the expected pattern
# ---------------------------------------------------------------
FEW_SHOT_PROMPT = """
You are a severity classifier for database alerts.
Classify each alert as CRITICAL, HIGH, MEDIUM, or LOW.

Examples:
Alert: "Database is offline"  -> CRITICAL
Alert: "Backup failed last night" -> HIGH
Alert: "Index fragmentation at 45%" -> MEDIUM
Alert: "Statistics not updated in 5 days" -> LOW

Respond with only the severity word. Nothing else.
"""

print("\n=== Experiment 3: Few-shot classification ===")
alerts = [
    "Transaction log is 99% full",
    "A query is running slightly slower than usual",
    "Always On AG replica is disconnected",
    "Auto-shrink ran on a database"
]

for alert in alerts:
    severity = call_with_system_prompt(FEW_SHOT_PROMPT, alert)
    print(f"{severity.strip():10} | {alert}")
# Expected output for Experiment 3:
# CRITICAL   | Transaction log is 99% full
# LOW        | A query is running slightly slower than usual
# CRITICAL   | Always On AG replica is disconnected
# MEDIUM     | Auto-shrink ran on a database

Stage 5: Build a Strands AI agent with tools (Day 3)

An agent is a model that can call tools and act on the results. The model decides which tool to call, calls it, reads the result, and decides what to do next. This is fundamentally different from a single prompt-response interaction.

Create stage5_strands_agent.py:

# stage5_strands_agent.py
# A complete working AI agent using Strands Agents and Amazon Bedrock
# The agent can look up server information and check index fragmentation
# Run with: python stage5_strands_agent.py
#
# Install first: pip install strands-agents

from strands import Agent, tool
from strands.models import BedrockModel

# ---------------------------------------------------------------
# Step 1: Define the tools the agent can use
# Each tool has a @tool decorator and a docstring that tells the model
# what the tool does and when to use it.
# In production these would call real databases - here we return mock data.
# ---------------------------------------------------------------

@tool
def get_server_stats(server_name: str) -> str:
    """
    Get current performance statistics for a SQL Server instance.
    Use this tool when the user asks about server performance, CPU usage,
    memory, or current activity on a specific server.

    Parameters:
        server_name: The name of the SQL Server instance to check.

    Returns:
        A text summary of current server statistics.
    """
    # In production: connect to the server and run DMV queries
    # Here we return realistic mock data to demonstrate the pattern
    mock_data = {
        "SQLPROD01": {
            "cpu_percent"         : 78,
            "memory_used_gb"      : 14.2,
            "memory_total_gb"     : 16.0,
            "active_connections"  : 145,
            "blocked_sessions"    : 3,
            "status"              : "Online"
        },
        "SQLPROD02": {
            "cpu_percent"         : 12,
            "memory_used_gb"      : 6.1,
            "memory_total_gb"     : 16.0,
            "active_connections"  : 34,
            "blocked_sessions"    : 0,
            "status"              : "Online"
        }
    }

    if server_name.upper() in mock_data:
        stats = mock_data[server_name.upper()]
        return (
            f"Server: {server_name}\n"
            f"Status: {stats['status']}\n"
            f"CPU: {stats['cpu_percent']}%\n"
            f"Memory: {stats['memory_used_gb']}GB used of {stats['memory_total_gb']}GB "
            f"({stats['memory_used_gb']/stats['memory_total_gb']*100:.0f}%)\n"
            f"Active connections: {stats['active_connections']}\n"
            f"Blocked sessions: {stats['blocked_sessions']}"
        )
    else:
        return f"Server '{server_name}' not found. Available servers: SQLPROD01, SQLPROD02"


@tool
def check_index_fragmentation(server_name: str, database_name: str) -> str:
    """
    Check index fragmentation levels across all user tables in a database.
    Use this tool when the user asks about index health, fragmentation,
    or whether indexes need maintenance.

    Parameters:
        server_name   : The SQL Server instance name.
        database_name : The database to check fragmentation in.

    Returns:
        A summary of index fragmentation with maintenance recommendations.
    """
    # Mock fragmentation data - in production this runs sys.dm_db_index_physical_stats
    mock_fragmentation = {
        "OrdersDB": [
            {"table": "dbo.Orders",         "index": "PK_Orders",       "fragmentation": 4.2,  "pages": 1250},
            {"table": "dbo.Orders",         "index": "IX_Orders_Date",  "fragmentation": 67.8, "pages": 890},
            {"table": "dbo.OrderDetails",   "index": "PK_OrderDetails", "fragmentation": 12.1, "pages": 4500},
            {"table": "dbo.Customers",      "index": "PK_Customers",    "fragmentation": 2.1,  "pages": 340},
        ]
    }

    if database_name not in mock_fragmentation:
        return f"Database '{database_name}' not found. Available: {list(mock_fragmentation.keys())}"

    indexes = mock_fragmentation[database_name]
    needs_rebuild     = [i for i in indexes if i['fragmentation'] > 30]
    needs_reorganize  = [i for i in indexes if 10 < i['fragmentation'] <= 30]
    healthy           = [i for i in indexes if i['fragmentation'] <= 10]

    result = f"Index fragmentation report for {server_name}.{database_name}:\n\n"

    if needs_rebuild:
        result += "REBUILD REQUIRED (>30% fragmentation):\n"
        for idx in needs_rebuild:
            result += f"  {idx['table']}.{idx['index']}: {idx['fragmentation']}% ({idx['pages']} pages)\n"

    if needs_reorganize:
        result += "\nREORGANIZE RECOMMENDED (10-30% fragmentation):\n"
        for idx in needs_reorganize:
            result += f"  {idx['table']}.{idx['index']}: {idx['fragmentation']}% ({idx['pages']} pages)\n"

    if healthy:
        result += "\nHEALTHY (<10% fragmentation):\n"
        for idx in healthy:
            result += f"  {idx['table']}.{idx['index']}: {idx['fragmentation']}%\n"

    return result


# ---------------------------------------------------------------
# Step 2: Create the agent
# The model is Amazon Nova Pro via Bedrock.
# The system_prompt defines the agent's role and constraints.
# ---------------------------------------------------------------
model = BedrockModel(
    model_id    = "amazon.nova-pro-v1:0",
    region_name = "us-east-1"
)

agent = Agent(
    model         = model,
    tools         = [get_server_stats, check_index_fragmentation],
    system_prompt = """
    You are a SQL Server database monitoring assistant.

    You have access to tools that check server performance and index fragmentation.
    Use the tools to get real data before answering any question about server health.

    Rules:
    - Always use a tool to get data before making any assessment.
    - If the user does not specify a server name, ask for it before calling tools.
    - Report all findings factually without exaggerating severity.
    - For blocked sessions: 1-2 is normal, 3+ warrants investigation, 10+ is critical.
    - For CPU: below 60% is healthy, 60-80% needs watching, above 80% is concerning.
    - For index fragmentation: below 10% is healthy, 10-30% reorganize, above 30% rebuild.
    - Always end your response with a clear recommendation.
    """
)


# ---------------------------------------------------------------
# Step 3: Test the agent with different questions
# Watch how the agent decides which tools to call and in what order
# ---------------------------------------------------------------
print("=== AI Agent Demo ===\n")

# Question 1: Agent should call get_server_stats
print("Question 1: How is SQLPROD01 performing right now?")
print("-" * 50)
response = agent("How is SQLPROD01 performing right now?")
print(response)
print()

# Question 2: Agent should call check_index_fragmentation
print("Question 2: Check index health in the OrdersDB database on SQLPROD01")
print("-" * 50)
response = agent("Check index health in the OrdersDB database on SQLPROD01")
print(response)
print()

# Question 3: Multi-tool - agent should call both tools
print("Question 3: Give me a complete health check of SQLPROD01 including server stats and OrdersDB index fragmentation")
print("-" * 50)
response = agent("Give me a complete health check of SQLPROD01 including server stats and OrdersDB index fragmentation")
print(response)

What to observe when running Stage 5. The terminal output shows Strands calling each tool in sequence. The agent does not get all the data at once and then reason about it. It calls a tool, reads the result, decides whether it needs more information, calls another tool if needed, and then composes the final response. This is what makes it an agent rather than a simple prompt. The model is making decisions autonomously about what to do next.

Stage 6: LangGraph explicit control flow (Day 4)

Strands lets the model decide the workflow. LangGraph lets the developer define the workflow as a graph. LangGraph is the right choice when the sequence of steps must be auditable, constrained, or guaranteed regardless of what the model might prefer to do.

Create stage6_langgraph.py:

# stage6_langgraph.py
# A LangGraph agent with explicit control flow and conditional routing
# The developer controls every step. The model cannot deviate from the graph.
# Run with: python stage6_langgraph.py
#
# Install first: pip install langgraph

from langgraph.graph import StateGraph, END
from typing import TypedDict
import boto3
import json

bedrock = boto3.client(service_name='bedrock-runtime', region_name='us-east-1')

# ---------------------------------------------------------------
# Step 1: Define the state
# State is a dictionary that persists across all nodes in the graph.
# Each node reads from state and writes back to state.
# This is the key difference from Strands: the full context is explicit and auditable.
# ---------------------------------------------------------------
class AssessmentState(TypedDict):
    question          : str   # the user's original question
    server_data       : str   # raw data retrieved about the server
    analysis          : str   # the model's analysis of the data
    final_report      : str   # the final formatted report
    data_available    : bool  # flag: did we actually get data to analyze?


# ---------------------------------------------------------------
# Step 2: Define the nodes
# Each node is a function that receives the current state
# and returns a dictionary of state updates.
# ---------------------------------------------------------------

def retrieve_server_data(state: AssessmentState) -> dict:
    """
    Node 1: Retrieve server data.
    In production this connects to a real server.
    Here it returns mock data based on keywords in the question.
    """
    print("  [Node 1: retrieve_server_data] Retrieving data...")

    question = state['question'].lower()

    # Mock data retrieval - in production this calls DMVs or monitoring APIs
    if 'sqlprod01' in question or 'prod01' in question:
        data = """
        Server: SQLPROD01
        CPU: 78% (High - investigate)
        Memory: 14.2GB / 16GB (88% used - concerning)
        Active Sessions: 145
        Blocked Sessions: 3
        Longest blocking chain: 12 seconds
        Recent errors: None in error log
        Disk space (C:): 45% free
        Disk space (D: Data): 22% free - WARNING
        """
        return {"server_data": data, "data_available": True}
    else:
        return {
            "server_data"   : "",
            "data_available": False
        }


def analyze_data(state: AssessmentState) -> dict:
    """
    Node 2: Analyze the retrieved data using the foundation model.
    Only runs if data_available is True.
    """
    print("  [Node 2: analyze_data] Running model analysis...")

    prompt = f"""
    Analyze this SQL Server health data and identify all issues.
    For each issue, state: what it is, why it matters, and the severity (CRITICAL/HIGH/MEDIUM/LOW).

    Data:
    {state['server_data']}

    List each issue on a separate line starting with its severity in brackets.
    """

    request_body = {
        "messages": [{"role": "user", "content": prompt}],
        "inferenceConfig": {"maxTokens": 600, "temperature": 0.1}
    }

    response = bedrock.invoke_model(
        modelId     = 'amazon.nova-pro-v1:0',
        body        = json.dumps(request_body),
        contentType = 'application/json',
        accept      = 'application/json'
    )
    body = json.loads(response['body'].read().decode('utf-8'))
    analysis = body['output']['message']['content'][0]['text']
    return {"analysis": analysis}


def format_report(state: AssessmentState) -> dict:
    """
    Node 3: Format the analysis into a structured report.
    Combines the raw data and analysis into a final output.
    """
    print("  [Node 3: format_report] Formatting final report...")

    report = f"""
SQL SERVER HEALTH ASSESSMENT REPORT
====================================
Generated by: AI Assessment Agent
Question: {state['question']}

RAW DATA COLLECTED:
{state['server_data']}

ANALYSIS AND FINDINGS:
{state['analysis']}

REPORT COMPLETE
====================================
"""
    return {"final_report": report}


def no_data_response(state: AssessmentState) -> dict:
    """
    Node for when no data could be retrieved.
    Returns a specific message instead of letting the model hallucinate.
    This is the key safety node that a Strands agent cannot guarantee.
    """
    print("  [No-data node] Returning data-unavailable response...")
    report = f"""
SQL SERVER HEALTH ASSESSMENT REPORT
====================================
Question: {state['question']}

STATUS: Unable to retrieve server data.

The server name could not be identified from the question.
Please specify the exact server name (e.g., SQLPROD01) and resubmit.

No assessment has been performed. No assumptions have been made.
====================================
"""
    return {"final_report": report}


# ---------------------------------------------------------------
# Step 3: Build the graph
# Add nodes, set the entry point, add edges including conditional routing
# ---------------------------------------------------------------
graph = StateGraph(AssessmentState)

# Add all nodes to the graph
graph.add_node("retrieve",       retrieve_server_data)
graph.add_node("analyze",        analyze_data)
graph.add_node("format",         format_report)
graph.add_node("no_data",        no_data_response)

# Set where the graph starts
graph.set_entry_point("retrieve")

# Add conditional edge from retrieve node
# If data is available -> go to analyze
# If no data -> go directly to no_data node and skip the model call
def route_after_retrieve(state: AssessmentState) -> str:
    """Return the name of the next node based on whether data was retrieved."""
    if state.get('data_available'):
        return "analyze"   # data found, proceed to model analysis
    else:
        return "no_data"   # no data, skip to the safe response node

graph.add_conditional_edges(
    "retrieve",                # the node this routing decision comes after
    route_after_retrieve,      # the function that decides the next node
    {
        "analyze" : "analyze", # if function returns "analyze", go to analyze node
        "no_data" : "no_data"  # if function returns "no_data", go to no_data node
    }
)

# Add remaining edges (unconditional - always go to the next node)
graph.add_edge("analyze",  "format")   # after analysis, always format
graph.add_edge("format",   END)        # after formatting, done
graph.add_edge("no_data",  END)        # no_data node is also a terminal node

# Compile the graph into a runnable agent
assessment_agent = graph.compile()


# ---------------------------------------------------------------
# Step 4: Test the graph with different inputs
# ---------------------------------------------------------------
print("=== LangGraph Assessment Agent Demo ===\n")

# Test 1: Server name recognized - full flow runs
print("Test 1: Valid server request")
print("-" * 50)
initial_state = {
    "question"       : "Run a health assessment on SQLPROD01",
    "server_data"    : "",
    "analysis"       : "",
    "final_report"   : "",
    "data_available" : False
}
result = assessment_agent.invoke(initial_state)
print(result['final_report'])

# Test 2: No server name - routes to no_data node, model never called
print("\nTest 2: No server name specified")
print("-" * 50)
initial_state_2 = {
    "question"       : "Can you check the health of the database?",
    "server_data"    : "",
    "analysis"       : "",
    "final_report"   : "",
    "data_available" : False
}
result_2 = assessment_agent.invoke(initial_state_2)
print(result_2['final_report'])

Stage 7: Add Bedrock Guardrails for compliance (Day 5)

Guardrails intercept every model call and apply content filters, PII detection, topic restrictions, and grounding checks before the response reaches the user. This is the primary technical control for regulated environment compliance.

Step 7.1: Create a Guardrail in the AWS Console

  1. In the AWS Console, go to Amazon Bedrock.
  2. In the left sidebar, click Guardrails under Safeguards.
  3. Click Create guardrail.
  4. Name it workshop-compliance-guardrail. Click Next.
  5. On the Content filters page, set Hate, Insults, Sexual, Violence, and Misconduct all to High for both Input and Output. Click Next.
  6. On the Denied topics page, click Add denied topic. Name it Investment Advice. Enter the definition: "Any recommendation about buying, selling, or holding financial investments, stocks, bonds, or securities." Click Next.
  7. On the Word filters page, add any words that should always be blocked in responses. Click Next.
  8. On the Sensitive information filters page, enable PII types: Name, Email, Phone, and US Social Security Number. Set the action to Mask. Click Next.
  9. Review and click Create guardrail.
  10. After creation, copy the Guardrail ID shown on the guardrail detail page. It looks like: ab12cd34ef56.

Create stage7_guardrails.py:

# stage7_guardrails.py
# Attaches a Bedrock Guardrail to model invocations for compliance
# Replace GUARDRAIL_ID with the ID copied from the AWS Console
# Run with: python stage7_guardrails.py

import boto3
import json

bedrock = boto3.client(service_name='bedrock-runtime', region_name='us-east-1')

GUARDRAIL_ID      = "YOUR_GUARDRAIL_ID_HERE"   # replace with actual ID from console
GUARDRAIL_VERSION = "DRAFT"                     # use DRAFT for testing, version number for production

def call_with_guardrail(user_question: str) -> dict:
    """
    Call Bedrock with the guardrail attached.
    Returns both the response text and the guardrail assessment.
    """
    request_body = {
        "messages": [
            {"role": "user", "content": user_question}
        ],
        "inferenceConfig": {
            "maxTokens"  : 400,
            "temperature": 0.1
        }
    }

    response = bedrock.invoke_model(
        modelId          = 'amazon.nova-pro-v1:0',
        body             = json.dumps(request_body),
        contentType      = 'application/json',
        accept           = 'application/json',
        guardrailIdentifier = GUARDRAIL_ID,      # attach the guardrail
        guardrailVersion    = GUARDRAIL_VERSION,
        trace               = 'ENABLED'          # ENABLED shows guardrail evaluation details
    )

    response_body = json.loads(response['body'].read().decode('utf-8'))

    # Get the response text
    answer = response_body['output']['message']['content'][0]['text']

    # Check if the guardrail blocked or modified the response
    # 'GUARDRAIL_INTERVENED' means the guardrail took action
    amazon_bedrock_trace = response_body.get('amazon-bedrock-trace', {})
    guardrail_trace      = amazon_bedrock_trace.get('guardrail', {})

    return {
        "answer"             : answer,
        "guardrail_action"   : guardrail_trace.get('modelOutput', [{}])[0].get('action', 'NONE'),
        "pii_findings"       : guardrail_trace.get('sensitiveInformationPolicy', {})
    }


print("=== Guardrail Compliance Demo ===\n")

# Test 1: Normal question - guardrail should pass through
print("Test 1: Normal question (should pass)")
print("-" * 50)
result = call_with_guardrail("What does CPU usage above 80% indicate for a SQL Server?")
print(f"Answer: {result['answer']}")
print(f"Guardrail action: {result['guardrail_action']}")

# Test 2: Denied topic - guardrail should block
print("\nTest 2: Denied topic - investment advice (should be blocked)")
print("-" * 50)
result = call_with_guardrail("Should I buy Microsoft stock right now?")
print(f"Answer: {result['answer']}")
print(f"Guardrail action: {result['guardrail_action']}")

# Test 3: PII in the question - guardrail should mask it
print("\nTest 3: PII in question - SSN (should be masked in response)")
print("-" * 50)
result = call_with_guardrail("The user John Smith with SSN 123-45-6789 cannot log in. What should I check?")
print(f"Answer: {result['answer']}")
print(f"PII findings: {result['pii_findings']}")

Stage 7 completion check: After running this script, the investment advice test should return a blocked response (the model says it cannot help with that topic). The PII test should show the SSN masked or removed from the response. The guardrail trace confirms which policy triggered. This is the core compliance pattern for regulated industry AI applications: guardrails enforce rules that the model cannot override regardless of what is asked.

What to build next

With all seven stages complete, the foundation for enterprise AI engineering is in place. The portfolio now demonstrates: Bedrock foundation model calls, system prompt design and structured output, a working tool-using Strands agent, a LangGraph graph with conditional routing and safe failure handling, and compliance-enforcing Bedrock Guardrails. These are the exact capabilities that appear in enterprise AI engineering requirements. The next step is combining them into a single cohesive application, adding a FastAPI web layer so others can call the agent via REST, and deploying the whole thing to Bedrock AgentCore for production-grade hosting.

16 Free and Paid Learning Resources Beginner

Official free resources

  • AWS Skill Builder: Free digital training for all AWS AI and ML certifications including MLA-C01 exam prep and a dedicated Generative AI learning path. Available at skillbuilder.aws. The Generative AI Essentials course is the right starting point.
  • Amazon Bedrock Workshop: Hands-on labs covering model invocation, RAG with Knowledge Bases, agents, guardrails, and evaluation. Available at github.com/aws-samples/amazon-bedrock-workshop.
  • Strands Agents documentation and examples: The official Strands Agents GitHub repository includes notebooks covering agent basics, multi-agent patterns, memory, and streaming. github.com/strands-agents/sdk-python.
  • LangGraph documentation: The LangGraph conceptual guides at langchain-ai.github.io/langgraph/ cover graph construction, state management, human-in-the-loop patterns, and deployment.
  • DeepLearning.AI short courses: Free short courses on RAG, LangChain, prompt engineering, and building AI applications. All courses are under 5 hours and hands-on. deeplearning.ai/courses.
  • fast.ai Practical Deep Learning: Free course covering neural networks and deep learning fundamentals. Not AWS-specific but essential for understanding why models behave as they do. fast.ai.

Paid resources worth investing in

  • AWS Certification Official Study Guide (MLA-C01): Available through AWS Skill Builder subscription ($29/month) or purchased individually. Includes practice exams.
  • Tutorials Dojo practice exams: Third-party practice question banks with detailed explanations for MLA-C01 and AIP-C01. portal.tutorialsdojo.com. Consistently recommended by exam takers as the most representative practice material available.
  • Udemy courses on AWS Bedrock and AI engineering: Multiple instructors offer project-based courses covering Bedrock, LangChain/LangGraph, and agentic AI patterns. Look for courses updated within the last six months given how rapidly this space moves.

Hands-on practice environment

  • An AWS personal account with Bedrock model access enabled is the minimum requirement. The AWS Free Tier covers S3 and Lambda but Bedrock invocations have usage-based costs. Budget $20 to $50 per month for active learning.
  • AWS provides CloudSandbox environments through Skill Builder for guided labs without using a personal account. These reset after the lab session ends.
  • Build a portfolio of three to five documented projects using the workshop stages above. Public GitHub repositories demonstrating working agents, RAG systems, and deployed Bedrock applications are the strongest interview evidence for this role.

References


Discover more from SQLYARD

Subscribe to get the latest posts sent to your email.

Leave a Reply

Discover more from SQLYARD

Subscribe now to keep reading and get access to the full archive.

Continue reading