100% Free with Unlimited Retries

AI Agents Core Concepts Assessment

Finished the content? Take the assessment for free. Retry as many times as you need. It is timed, properly invigilated, and actually means something.

Sign in to track progress and get your certificate

You can take the assessment without signing in, but your progress will not be tracked and you will not receive a certificate of completion. If you complete the course without signing in, you will need to sign in and complete it again to get your certificate. Sign in now

Unlimited retries

Take the assessment as many times as you need

Free certificate

Get a detailed certificate when you pass

Donation supported

We run on donations to keep everything free

Everything is free – If you find this useful and can afford to, please consider making a donation to help us keep courses free, update content regularly, and support learners who cannot pay.

Timed assessment
Detailed feedback
No credit card required

CPD timing for this level

Core Concepts time breakdown

This is the first pass of a defensible timing model for this level, based on what is actually on the page: reading, labs, checkpoints, and reflection.

Reading
26m
3,828 words · base 20m × 1.3
Labs
0m
0 activities × 15m
Checkpoints
10m
2 blocks × 5m
Reflection
40m
5 modules × 8m
Estimated guided time
1h 16m
Based on page content and disclosed assumptions.
Claimed level hours
25h
Claim includes reattempts, deeper practice, and capstone work.
The claimed hours are higher than the current on-page estimate by about 24h. That gap is where I will add more guided practice and assessment-grade work so the hours are earned, not declared.

What changes at this level

Level expectations

I want each level to feel independent, but also clearly deeper than the last. This panel makes the jump explicit so the value is obvious.

Anchor standards (course wide)
OWASP Top 10 for LLM Applications 2025OWASP Top 10 for Agentic Applications 2026NIST AI Risk Management Framework (AI RMF 1.0)ISO/IEC 42001
Assessment intent
Core concepts

Reasoning loops, tool contracts, and memory design that stand up under pressure.

Assessment style
Format: mixed
Pass standard
Coming next

Not endorsed by a certification body. This is my marking standard for consistency and CPD evidence.

Level progress0%

CPD tracking

Fixed hours for this level: not specified. Timed assessment time is included once on pass.

View in My CPD
Progress minutes
0.0 hours

Stage 2: Core Concepts

Now that you understand what AI agents are and have the tools set up, let us dive into how they actually work. This stage covers the fundamental patterns and concepts that power every AI agent, from simple chatbots to complex autonomous systems.

Why this matters

Understanding these core concepts is like understanding how an engine works before you drive a car. You could drive without this knowledge, but when something goes wrong (and it will), you would be helpless. With this understanding, you can diagnose problems, optimise performance, and build systems that actually work in the real world.


Module 2.1: How AI Agents Think (5 hours)

Learning Objectives

By the end of this module, you will be able to:

  1. Explain the ReAct pattern (Reasoning + Acting)
  2. Trace an agent's decision-making process step by step
  3. Identify when agents succeed and when they fail
  4. Design prompts that guide agent reasoning

2.1.1 The ReAct Pattern

ReAct stands for Reasoning and Acting. It is the most influential pattern in AI agent design, introduced by researchers at Princeton and Google in 2022. The insight is simple but powerful: interleaving reasoning with action produces better results than either alone.

The ReAct Pattern

The Three Steps:

  1. Thought: The agent reasons about what to do next. This is explicit reasoning that we can see and debug.

  2. Action: The agent chooses and executes a tool. This is how agents interact with the world.

  3. Observation: The agent sees the result of its action. This new information feeds back into the next thought.

ReAct in Action

Question: "What is the population of London divided by the area of Wales?"

Thought 1: I need to find two pieces of information: the population of London and the area of Wales. Let me search for the population first.

Action 1: search("London population 2024")

Observation 1: "London has a population of approximately 9 million people."

Thought 2: Now I need the area of Wales.

Action 2: search("Wales area square kilometres")

Observation 2: "Wales has an area of 20,779 square kilometres."

Thought 3: I have both pieces of information. 9,000,000 divided by 20,779 equals approximately 433.

Final Answer: The population of London (approximately 9 million) divided by the area of Wales (20,779 square kilometres) is approximately 433 people per square kilometre.


2.1.2 Why ReAct Works

Traditional LLMs generate responses in one go. They cannot stop, check something, and continue. ReAct allows the agent to:

  1. Break complex problems into steps: Instead of trying to solve everything at once, tackle one piece at a time

  2. Ground responses in facts: Each action brings real information into the reasoning process

  3. Self-correct: If an action reveals unexpected information, the agent can adjust its approach

  4. Show its work: The explicit thought process makes debugging much easier

Expecting perfect reasoning

Agents do not reason like humans. They predict plausible next tokens. Sometimes the predicted reasoning is wrong. Always verify agent outputs, especially for important decisions.


2.1.3 When Agents Fail

Understanding failure modes helps you build more robust systems.

Common Agent Failure Modes

What goes wrong and why

🔄 Infinite Loops

Agent keeps repeating the same action, never reaching an answer. Usually caused by ambiguous stopping conditions.

❓ Wrong Tool Selection

Agent chooses a tool that cannot help. Often happens when tool descriptions are unclear or overlapping.

🎭 Hallucinated Actions

Agent tries to use tools that do not exist, or calls tools with impossible parameters.

📉 Reasoning Drift

Agent starts on track but gradually drifts away from the original goal over multiple steps.


Module 2.2: Tools and Actions (5 hours)

Learning Objectives

By the end of this module, you will be able to:

  1. Define what tools are in the context of AI agents
  2. Design tool schemas with clear descriptions and parameters
  3. Implement input validation for tools
  4. Handle tool errors gracefully

2.2.1 What Are Tools?

Tools are functions that agents can call to interact with the world. They are the bridge between thinking and doing.

Tool

A function with a name, description, and defined parameters that an AI agent can choose to execute. Tools extend the agent's capabilities beyond text generation to real-world actions.

Common Tool Categories:

Types of Agent Tools

Extending agent capabilities

🔍 Information Retrieval

  • • Web search
  • • Database queries
  • • File reading
  • • API calls

✏️ Content Creation

  • • File writing
  • • Email composition
  • • Code generation
  • • Image creation

⚙️ System Operations

  • • Code execution
  • • Process management
  • • Scheduling
  • • Notifications

2.2.2 Designing Good Tool Schemas

The schema tells the agent what a tool does and how to use it. A well-designed schema reduces errors and improves tool selection.

"""
Tool Schema Design
==================
How to define tools that agents can use effectively.
"""

from dataclasses import dataclass
from typing import Any, Dict, Callable


@dataclass
class Tool:
    """
    A tool that an AI agent can use.
    
    Attributes:
        name: Unique identifier (use snake_case)
        description: What the tool does (be specific!)
        function: The Python function to execute
        parameters: JSON Schema defining expected inputs
    """
    name: str
    description: str
    function: Callable
    parameters: Dict[str, Any]
    
    def to_prompt_format(self) -> str:
        """Format for inclusion in agent prompts."""
        params = ", ".join(
            f"{k}: {v.get('description', 'no description')}"
            for k, v in self.parameters.get("properties", {}).items()
        )
        return f"- {self.name}({params}): {self.description}"


# Example: Well-designed tool

def search_web(query: str, num_results: int = 5) -> dict:
    """Search the web and return results."""
    # Implementation would go here
    return {"results": []}


search_tool = Tool(
    name="search_web",
    description="Search the internet for current information. Use for facts, news, or data you do not know. Returns titles, snippets, and URLs.",
    function=search_web,
    parameters={
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "The search query. Be specific and include relevant keywords."
            },
            "num_results": {
                "type": "integer",
                "description": "Number of results to return (1-10)",
                "default": 5
            }
        },
        "required": ["query"]
    }
)

Tool Description Best Practices:

  1. Be specific: "Search the internet for current information" is better than "Search"

  2. Explain when to use it: "Use for facts, news, or data you do not know"

  3. Describe outputs: "Returns titles, snippets, and URLs"

  4. Mention limitations: If the tool only works for certain inputs, say so


2.2.3 Tool Input Validation

Never trust agent-generated inputs. Always validate.

"""
Tool Input Validation
=====================
Validate inputs before executing tools.
"""

from typing import Any, Optional, Tuple
import re


class ToolInputValidator:
    """Validates tool inputs before execution."""
    
    @staticmethod
    def validate_search_query(query: Any) -> Tuple[bool, Optional[str]]:
        """
        Validate a search query.
        
        Args:
            query: The query to validate
            
        Returns:
            Tuple of (is_valid, error_message)
        """
        # Must be a string
        if not isinstance(query, str):
            return False, "Query must be a string"
        
        # Must not be empty
        if not query.strip():
            return False, "Query cannot be empty"
        
        # Length limits
        if len(query) > 500:
            return False, "Query too long (max 500 characters)"
        
        # No potential injection patterns
        suspicious = [
            r"<script",
            r"javascript:",
            r"data:",
            r"vbscript:",
        ]
        for pattern in suspicious:
            if re.search(pattern, query, re.IGNORECASE):
                return False, "Query contains suspicious content"
        
        return True, None
    
    @staticmethod
    def validate_file_path(path: Any, allowed_dirs: list) -> Tuple[bool, Optional[str]]:
        """
        Validate a file path is within allowed directories.
        
        Args:
            path: The file path to validate
            allowed_dirs: List of allowed directory prefixes
            
        Returns:
            Tuple of (is_valid, error_message)
        """
        import os
        
        if not isinstance(path, str):
            return False, "Path must be a string"
        
        # Normalise path to prevent directory traversal
        normalised = os.path.normpath(os.path.abspath(path))
        
        # Check if within allowed directories
        for allowed in allowed_dirs:
            if normalised.startswith(os.path.abspath(allowed)):
                return True, None
        
        return False, f"Path not in allowed directories: {allowed_dirs}"

Module 2.3: Memory and Context (5 hours)

Learning Objectives

By the end of this module, you will be able to:

  1. Distinguish between short-term and long-term memory
  2. Implement conversation history management
  3. Understand context window limitations
  4. Use vector databases for semantic memory

2.3.1 Types of Agent Memory

Agent Memory Architecture

Short-Term Memory

Information held during a single conversation. Includes the chat history, current task details, and temporary working state. Lost when the session ends.

Long-Term Memory

Persistent information that survives across sessions. User preferences, learned facts, and historical interactions. Stored in databases.

Context Window

The maximum amount of text an LLM can process at once. For a 128K context window, roughly 96,000 words. Anything beyond this limit is simply not seen by the model.

🎯 Interactive: Memory Architecture Explorer

Explore the different types of agent memory through this interactive tool. Test your understanding of when to use short-term, long-term, and external memory for different queries.

Which memory type is needed?

Score: 0/6

User: "What did I ask you about 5 minutes ago?"

Agent Memory Architecture

Short-Term

Session only

Long-Term

Persists across sessions

External

Knowledge bases

Agent Processing

Combines all memory sources


2.3.2 Managing Conversation History

As conversations grow, they eventually exceed the context window. You need strategies to handle this.

"""
Conversation Memory Management
==============================
Strategies for maintaining conversation context.
"""

from dataclasses import dataclass, field
from typing import List, Optional
from datetime import datetime


@dataclass
class Message:
    """A single message in conversation history."""
    role: str  # "user", "assistant", or "system"
    content: str
    timestamp: datetime = field(default_factory=datetime.now)
    token_count: int = 0


class ConversationMemory:
    """
    Manages conversation history with context window awareness.
    """
    
    def __init__(self, max_tokens: int = 4000, preserve_system: bool = True):
        """
        Initialise conversation memory.
        
        Args:
            max_tokens: Maximum tokens to keep in history
            preserve_system: Always keep the system message
        """
        self.max_tokens = max_tokens
        self.preserve_system = preserve_system
        self.messages: List[Message] = []
        self.system_message: Optional[Message] = None
    
    def add_message(self, role: str, content: str) -> None:
        """Add a message to history."""
        # Estimate token count (roughly 4 chars per token)
        token_count = len(content) // 4
        
        message = Message(
            role=role,
            content=content,
            token_count=token_count
        )
        
        if role == "system":
            self.system_message = message
        else:
            self.messages.append(message)
        
        # Prune if needed
        self._prune_to_fit()
    
    def _prune_to_fit(self) -> None:
        """Remove old messages to fit within token limit."""
        current_tokens = sum(m.token_count for m in self.messages)
        
        if self.system_message:
            current_tokens += self.system_message.token_count
        
        # Remove oldest messages until we fit
        while current_tokens > self.max_tokens and len(self.messages) > 2:
            removed = self.messages.pop(0)
            current_tokens -= removed.token_count
    
    def get_messages(self) -> List[dict]:
        """Get messages in format suitable for LLM."""
        result = []
        
        if self.system_message:
            result.append({
                "role": "system",
                "content": self.system_message.content
            })
        
        for msg in self.messages:
            result.append({
                "role": msg.role,
                "content": msg.content
            })
        
        return result
    
    def summarise_and_compress(self, summariser_fn) -> None:
        """
        Summarise older messages to save tokens.
        
        Args:
            summariser_fn: Function that takes messages and returns summary
        """
        if len(self.messages) < 10:
            return
        
        # Take oldest 70% of messages
        split_point = int(len(self.messages) * 0.7)
        old_messages = self.messages[:split_point]
        recent_messages = self.messages[split_point:]
        
        # Summarise old messages
        old_content = "\n".join(
            f"{m.role}: {m.content}" 
            for m in old_messages
        )
        summary = summariser_fn(old_content)
        
        # Replace with summary
        summary_message = Message(
            role="system",
            content=f"[Summary of earlier conversation: {summary}]",
            token_count=len(summary) // 4
        )
        
        self.messages = [summary_message] + recent_messages

2.3.3 Vector Databases for Semantic Memory

When you need to remember things across sessions or search through large amounts of information, vector databases are essential.

Vector Database

A database that stores information as numerical vectors (lists of numbers). Similar items have similar vectors. This allows semantic search: finding things by meaning, not just keywords.

How Vector Search Works

Module 2.4: Design Patterns (5 hours)

Learning Objectives

By the end of this module, you will be able to:

  1. Apply the ReAct pattern to complex problems
  2. Implement Plan-and-Execute patterns
  3. Choose appropriate patterns for different use cases
  4. Combine patterns for sophisticated behaviour

2.4.1 Pattern Overview

Agent Design Patterns

When to use each pattern

PatternBest ForComplexity
ReActSimple reasoning with tool useLow
Plan-and-ExecuteMulti-step tasks with clear goalsMedium
ReflectionQuality-critical outputsMedium
Multi-AgentComplex tasks with specialisationHigh

2.4.2 Plan-and-Execute Pattern

For complex tasks, planning before acting often works better than interleaved reasoning.

Plan-and-Execute Pattern

2.4.3 Reflection Pattern

The agent reviews and improves its own output before presenting it.

"""
Reflection Pattern Implementation
==================================
Agent reviews and improves its output.
"""


def generate_with_reflection(
    query: str,
    generator_fn,
    critic_fn,
    max_iterations: int = 3
) -> str:
    """
    Generate content with self-reflection loop.
    
    Args:
        query: The user's request
        generator_fn: Function that generates initial content
        critic_fn: Function that critiques and suggests improvements
        max_iterations: Maximum improvement iterations
        
    Returns:
        Final improved content
    """
    # Generate initial response
    content = generator_fn(query)
    
    for i in range(max_iterations):
        # Get critique
        critique = critic_fn(
            query=query,
            content=content
        )
        
        # Check if good enough
        if critique.get("is_satisfactory", False):
            break
        
        # Improve based on feedback
        improvements = critique.get("suggestions", [])
        content = generator_fn(
            f"{query}\n\n"
            f"Previous attempt: {content}\n\n"
            f"Improvements needed: {improvements}\n\n"
            f"Please provide an improved version."
        )
    
    return content

Module 2.5: Architecture Fundamentals (5 hours)

Learning Objectives

By the end of this module, you will be able to:

  1. Design agent state management
  2. Implement control flows and routing
  3. Handle errors and edge cases
  4. Build robust agent architectures

2.5.1 Agent State Management

State is everything your agent needs to remember to complete its task.

Agent State Structure
"""
Agent State Management
======================
Immutable state for reliable agent operation.
"""

from dataclasses import dataclass, field
from typing import List, Dict, Any, Optional
from datetime import datetime
from enum import Enum


class AgentStatus(Enum):
    """Current state of the agent."""
    IDLE = "idle"
    THINKING = "thinking"
    ACTING = "acting"
    WAITING = "waiting"
    COMPLETE = "complete"
    ERROR = "error"


@dataclass(frozen=True)
class AgentState:
    """
    Immutable agent state.
    
    Using frozen=True ensures state cannot be accidentally modified.
    Each operation creates a new state object.
    """
    messages: tuple = field(default_factory=tuple)
    status: AgentStatus = AgentStatus.IDLE
    current_step: int = 0
    max_steps: int = 10
    tool_results: tuple = field(default_factory=tuple)
    error: Optional[str] = None
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    def with_message(self, role: str, content: str) -> "AgentState":
        """Return new state with added message."""
        new_messages = self.messages + ({"role": role, "content": content},)
        return AgentState(
            messages=new_messages,
            status=self.status,
            current_step=self.current_step,
            max_steps=self.max_steps,
            tool_results=self.tool_results,
            error=self.error,
            metadata=self.metadata.copy()
        )
    
    def with_status(self, status: AgentStatus) -> "AgentState":
        """Return new state with updated status."""
        return AgentState(
            messages=self.messages,
            status=status,
            current_step=self.current_step,
            max_steps=self.max_steps,
            tool_results=self.tool_results,
            error=self.error,
            metadata=self.metadata.copy()
        )
    
    def with_tool_result(self, tool: str, result: Any) -> "AgentState":
        """Return new state with added tool result."""
        new_results = self.tool_results + ({"tool": tool, "result": result},)
        return AgentState(
            messages=self.messages,
            status=self.status,
            current_step=self.current_step + 1,
            max_steps=self.max_steps,
            tool_results=new_results,
            error=self.error,
            metadata=self.metadata.copy()
        )
    
    def is_complete(self) -> bool:
        """Check if agent has finished."""
        return self.status in (AgentStatus.COMPLETE, AgentStatus.ERROR)
    
    def should_continue(self) -> bool:
        """Check if agent should keep going."""
        return (
            not self.is_complete() 
            and self.current_step < self.max_steps
        )

2.5.2 Error Handling

Agents will fail. The question is how gracefully.

Agent Error Handling Strategy

Fail gracefully, not catastrophically

🔄 Recoverable Errors

  • • Tool timeout → Retry with backoff
  • • Rate limit → Wait and retry
  • • Invalid input → Ask for clarification
  • • Partial failure → Complete what you can

🛑 Terminal Errors

  • • Authentication failure → Stop and report
  • • Critical tool missing → Cannot proceed
  • • Safety violation → Halt immediately
  • • Max retries exceeded → Give up gracefully

Stage 2 Assessment

Module 2.1-2.2: Thinking and Tools Quiz

What does ReAct stand for?

What is the purpose of the Observation step in ReAct?

What makes a good tool description?

Why is tool input validation important?

What is an infinite loop in agent terms?

Module 2.3-2.5: Memory and Architecture Quiz

What is the difference between short-term and long-term memory?

What is a context window?

What is a vector database used for in AI agents?

When should you use the Plan-and-Execute pattern?

Why should agent state be immutable?


Summary

In this stage, you have learned:

  1. The ReAct pattern: How agents interleave reasoning with action

  2. Tools and actions: How to design, validate, and execute agent tools

  3. Memory management: Short-term, long-term, and semantic memory

  4. Design patterns: When to use ReAct, Plan-and-Execute, and Reflection

  5. Architecture fundamentals: State management and error handling

Ready to build

You now understand the core concepts behind AI agents. In Stage 3, we will put this knowledge into practice by building real, working agents.

Ready to test your knowledge?

AI Agents Core Concepts Assessment

Validate your learning with practice questions and earn a certificate to evidence your CPD. Try three preview questions below, then take the full assessment.

50+

Questions

45

Minutes

PDF

Certificate

Everything is free with unlimited retries

  • Take the full assessment completely free, as many times as you need
  • Detailed feedback on every question explaining why answers are correct or incorrect
  • Free downloadable PDF certificate with details of what you learned and hours completed
  • Personalised recommendations based on topics you found challenging

Sign in to get tracking and your certificate

You can complete this course without signing in, but your progress will not be saved and you will not receive a certificate. If you complete the course without signing in, you will need to sign in and complete it again to get your certificate.

We run on donations. Everything here is free because we believe education should be accessible to everyone. If you have found this useful and can afford to, please consider making a donation to help us keep courses free, update content regularly, and support learners who cannot pay. Your support makes a real difference.

During timed assessments, copy actions are restricted and AI assistance is paused to ensure fair evaluation. Your certificate will include a verification URL that employers can use to confirm authenticity.

Course materials are protected by intellectual property rights.View terms

Quick feedback

Optional. This helps improve accuracy and usefulness. No accounts required.

Rating (optional)

Related Architecture Templates4

Production-ready templates aligned with industry frameworks. Download in multiple formats.

View all

Password and Passphrase Coach

/ai-agents/core-concepts

Foundation

Scores passwords and suggests stronger passphrases.

MFA Method Picker

/ai-agents/core-concepts

Foundation

Chooses MFA methods based on threat fit and device context.

Session and Token Hygiene Checker

/ai-agents/core-concepts

Practitioner

Evaluates session lifetimes, refresh, rotation, and cookie settings.

URL Risk Triage Tool

/ai-agents/core-concepts

Foundation

Checks URLs for risky patterns and produces a quick decision.

Related categories:SecurityIntegrationEmerging