Showing posts with label #AIAgents #Python #GenerativeAI #LLM #AgenticAI #ClaudeAI. Show all posts
Showing posts with label #AIAgents #Python #GenerativeAI #LLM #AgenticAI #ClaudeAI. Show all posts

Wednesday, August 19, 2026

Multi-Head Multi-Layer Self-Attention: How LLMs Understand Context

 

Multi-Head Multi-Layer Self-Attention: How LLMs Understand Context

If you are learning how Large Language Models (LLMs) such as GPT work, you will quickly encounter terms like self-attention, multi-head attention, Transformer layers, and multi-layer attention.

At first, these concepts can feel intimidating.

But the underlying idea is surprisingly simple:

An LLM repeatedly looks at the relationships between tokens, from multiple perspectives, and builds a progressively richer understanding of the input.

This article breaks down what multi-head multi-layer self-attention means and how it fits into the architecture of modern LLMs.


The One-Sentence Definition

Multi-head multi-layer self-attention is the repeated use of multiple parallel attention mechanisms across stacked Transformer layers, allowing an LLM to progressively build richer representations of relationships and context between tokens.

And the hierarchy is:

Self-Attention
      ↓
Multi-Head Self-Attention
      ↓
Transformer Block
      ↓
Multiple Transformer Blocks
      ↓
Transformer Architecture
      ↓
Large Language Model

Once this hierarchy becomes clear, the architecture of GPT becomes much easier to understand.



1. Start with Self-Attention

Let's start with a simple sentence:

"The engineer fixed the server because it was down."

When the model processes the word "it", it needs to understand what "it" refers to.

Is it:

  • the engineer?

  • the server?

  • something else?

Self-attention allows the model to examine the other tokens in the sentence and determine which ones are relevant.

Conceptually:

The engineer fixed the server because it was down.
                    ↑                  ↑
                    │                  │
                 context           important

The model assigns different attention weights to different tokens.

The important idea is:

Self-attention allows every token to consider other tokens in the sequence when building its representation.

This is one of the fundamental ideas behind the Transformer architecture.


2. Why Do We Need Multiple Attention Heads?

A sentence can contain many different types of relationships.

Consider:

"The customer contacted the bank because she needed a loan."

There are several relationships here.

The model needs to understand:

  • Who contacted whom?

  • Who does "she" refer to?

  • What is the relationship between "needed" and "loan"?

  • Why did the customer contact the bank?

One attention mechanism may not be sufficient to capture all these relationships.

This is where multi-head attention comes in.

Instead of having one attention mechanism, the Transformer uses multiple attention heads.

Conceptually:

                    Sentence
                       │
       ┌───────────────┼───────────────┐
       ↓               ↓               ↓
     Head 1          Head 2          Head 3
       │               │               │
   Relationship      Grammar        Semantic
    patterns         patterns        patterns
       │               │               │
       └───────────────┼───────────────┘
                       ↓
                Combined result

Each head performs its own attention calculation.

The outputs of the heads are then combined.


3. Think of Attention Heads as Different Perspectives

A useful analogy is to imagine that you give the same document to several experts.

One expert focuses on:

Grammar

Which words are connected grammatically?

Another focuses on:

Relationships

Which entity is related to which?

Another focuses on:

Meaning

What concepts are connected?

Another might focus on:

Context

What information elsewhere in the sentence changes the meaning of this word?

The experts aren't explicitly programmed to perform these roles. The model learns useful attention patterns during training.

That's an important distinction.

We shouldn't assume:

"Head 1 is always the grammar head."

Instead:

Different heads can learn different useful patterns and relationships.


4. Every Attention Head Has Query, Key and Value

Remember the basic self-attention mechanism?

It uses three components:

  • Query (Q) — What information am I looking for?

  • Key (K) — What information do I contain?

  • Value (V) — What information should I provide?

For a single attention mechanism, we can think of it as:

Query
   │
   ↓
Compare with Keys
   │
   ↓
Calculate attention scores
   │
   ↓
Retrieve weighted Values
   │
   ↓
Attention output

With multiple heads, we have multiple sets of learned transformations:

Head 1 → Q₁, K₁, V₁
Head 2 → Q₂, K₂, V₂
Head 3 → Q₃, K₃, V₃
...
Head N → Qₙ, Kₙ, Vₙ

Each head can therefore learn a different representation of relationships between tokens.


5. What Happens After the Heads Finish?

The outputs of the individual attention heads are combined.

Conceptually:

Head 1 ──┐
Head 2 ──┤
Head 3 ──┤
Head 4 ──┤
Head 5 ──┤
          ↓
      Concatenate
          ↓
   Linear projection
          ↓
     Final output

This gives the Transformer a combined representation containing information from all the attention heads.

So the basic process is:

Split → Attend independently → Combine


6. Now Add Multiple Layers

We have now understood multi-head attention.

But modern LLMs don't perform this operation only once.

They stack many Transformer layers.

For example:

Input
  ↓
Transformer Layer 1
  ↓
Transformer Layer 2
  ↓
Transformer Layer 3
  ↓
...
  ↓
Transformer Layer N
  ↓
Output

Each layer receives the representation produced by the previous layer.

This is where the term multi-layer comes from.


7. Why Do We Need Multiple Layers?

Because language understanding is hierarchical and complex.

Consider:

"The bank approved the loan because the customer's credit history was excellent."

A simplified intuition might be:

Earlier layers

The model begins learning relatively local relationships:

bank → approved
customer → credit
credit → history

Middle layers

It can build more complex relationships:

customer
    ↓
credit history
    ↓
excellent
    ↓
loan approval

Deeper layers

The representation can capture the broader relationship:

The customer's strong credit history contributed to the bank approving the loan.

Again, this is a conceptual illustration rather than a strict rule that every early layer performs grammar and every later layer performs semantics.

The important idea is:

Each layer transforms the representation and passes a richer representation to the next layer.


8. Putting Multi-Head and Multi-Layer Together

Now we can combine the two ideas.

Imagine a Transformer with four layers and four attention heads per layer:

                         INPUT
                           │
                           ↓
              ┌───────────────────────┐
              │       LAYER 1         │
              │                       │
              │ Head 1 ──┐            │
              │ Head 2 ──┤            │
              │ Head 3 ──┤ Attention   │
              │ Head 4 ──┘            │
              └───────────┬───────────┘
                          ↓
              ┌───────────────────────┐
              │       LAYER 2         │
              │                       │
              │ Head 1 ──┐            │
              │ Head 2 ──┤            │
              │ Head 3 ──┤ Attention   │
              │ Head 4 ──┘            │
              └───────────┬───────────┘
                          ↓
              ┌───────────────────────┐
              │       LAYER 3         │
              │                       │
              │ Head 1 ──┐            │
              │ Head 2 ──┤            │
              │ Head 3 ──┤ Attention   │
              │ Head 4 ──┘            │
              └───────────┬───────────┘
                          ↓
              ┌───────────────────────┐
              │       LAYER 4         │
              │                       │
              │ Head 1 ──┐            │
              │ Head 2 ──┤            │
              │ Head 3 ──┤ Attention   │
              │ Head 4 ──┘            │
              └───────────┬───────────┘
                          ↓
                        OUTPUT

This is the basic intuition behind multi-head, multi-layer attention in a Transformer.


9. But a Transformer Layer Is More Than Attention

There is an important technical detail.

A Transformer layer is not simply:

Multi-head attention → next layer

A typical Transformer block also contains a feed-forward network and normalization/residual connections.

Conceptually:

                 Input
                   │
                   ↓
          Multi-Head Attention
                   │
                   ↓
          Residual + Normalization
                   │
                   ↓
         Feed-Forward Network
                   │
                   ↓
          Residual + Normalization
                   │
                   ↓
                Output

This entire block is then repeated many times.

Therefore, when people casually say:

"This LLM has many layers of attention"

they usually mean that the model contains many Transformer blocks, each containing an attention mechanism.


10. The Complete Picture

We can now connect everything together:

Text
  ↓
Tokens
  ↓
Token Embeddings
  ↓
Positional Information
  ↓
┌───────────────────────────────────┐
│        Transformer Layer 1        │
│                                   │
│  Multi-Head Self-Attention        │
│              ↓                    │
│     Feed-Forward Network          │
└─────────────────┬─────────────────┘
                  ↓
┌───────────────────────────────────┐
│        Transformer Layer 2        │
│                                   │
│  Multi-Head Self-Attention        │
│              ↓                    │
│     Feed-Forward Network          │
└─────────────────┬─────────────────┘
                  ↓
                 ...
                  ↓
┌───────────────────────────────────┐
│        Transformer Layer N        │
│                                   │
│  Multi-Head Self-Attention        │
│              ↓                    │
│     Feed-Forward Network          │
└─────────────────┬─────────────────┘
                  ↓
          Final Representation
                  ↓
        Next-Token Prediction

This repeated transformation is what allows the model to build increasingly sophisticated representations of the input.


11. Where Does GPT Fit In?

GPT-style models use causal self-attention.

That means the model is not allowed to look at future tokens when predicting the next token.

Suppose the model has:

"The cat sat on the"

The model needs to predict what comes next.

It can use:

The
 ↓
cat
 ↓
sat
 ↓
on
 ↓
the
 ↓
?

But it cannot peek at the answer.

The attention mechanism is therefore masked so that each position can only attend to the appropriate previous context.

The model might produce something conceptually like:

mat       42%
floor     18%
chair      9%
bed        6%
...

It then selects or samples a token and continues generating.


12. One More Important Distinction

It is useful to keep these terms separate:

Self-Attention

Tokens attend to other tokens in the same sequence.

Multi-Head Self-Attention

Multiple attention mechanisms examine those relationships in parallel.

Multi-Layer Transformer

Multiple Transformer blocks are stacked so that representations are repeatedly transformed.

Causal Self-Attention

Attention is restricted so that a token cannot use future tokens when generating text.

LLM

A large neural network built using many such Transformer components and trained to model language.


13. A Simple Mental Model

If you remember only one analogy, remember this:

Imagine a large team of analysts working in multiple rounds.

Round 1

Several analysts examine the raw information from different perspectives.

             Input
               ↓
       ┌───────┼───────┐
       ↓       ↓       ↓
   Analyst   Analyst  Analyst
       └───────┼───────┘
               ↓
        Combined view

Round 2

Another group receives that combined view and analyzes it again.

       Combined view
              ↓
      Multiple analysts
              ↓
       Better representation

Round 3

The process continues.

     Better representation
              ↓
      Multiple analysts
              ↓
      Even richer representation

That is a useful mental model for multi-head multi-layer self-attention.



Saturday, April 25, 2026

Building Your First AI Agent from Scratch

 Building Your First AI Agent from Scratch 

                                                                                              AI agents are one of the most exciting developments in software right now. But if you cut t  through the hype, the core idea is surprisingly simple. In this post, I'll explain what agents actually are, how they work, and walk you through building one from scratch in Python.                                                                                                  

What Is an AI Agent?             

An agent is an AI system that can autonomously plan, reason, and take actions to accomplish a goal — going beyond simple Q&A to actually do things on your behalf.


The key difference from a chatbot: agents take actions and iterate. A chatbot answers your question once and stops. An agent keeps working — using the result of one action to decide what to do   next — until the task is complete.                                                                  

Every agent, no matter how complex, is built on four core concepts:


  - Perceive — take in inputs: user messages, tool results, environment state                 - Reason — decide what to do next

  - Act — call a tool, run code, hit an API                                                   - Loop — repeat until the task is done



The Agent Loop


  User Input

     

  ┌─────────────────────────────────┐

    LLM thinks: "What should I do?" │

                                 

    Call a tool? → Get result      

                                 

    Call another tool? → Get result │

                                 

    Done? → Return final answer    

  └─────────────────────────────────┘

     

  Final Response


  This loop is the heartbeat of every agent. Let's build one.                                                                                                                                           

  Building the Agent


  Prerequisites


  You'll need Python installed and an Anthropic API key from https://console.anthropic.com.


  mkdir my-agent && cd my-agent

  python3 -m venv venv

  source venv/bin/activate

  pip install anthropic

  export ANTHROPIC_API_KEY="sk-ant-your-key-here"


 Step 1 Define the Tools


 Tools are regular Python functions — the agent can call them to take actions. Here we'll give our agent two abilities: a calculator and a weather lookup.                                              

   

  def calculator(operation: str, a: float, b: float) -> float:                                                                                                                                           

      ops = {                                                                                                                                                                                            

          "add": a + b,                                                                                                                                                                                  

          "subtract": a - b,                                                                                                                                                                             

          "multiply": a * b,                                                                                                                                                                             

          "divide": a / b if b != 0 else "Error: division by zero",                                                                                                                                      

      }                                                                                                                                                                                                  

      return ops.get(operation, "Unknown operation")                                                                                                                                                     

                                                                                                                                                                                                         

  def get_weather(city: str) -> str:                                                                                                                                                                     

      # In production, call a real weather API here                                                                                                                                                      

      data = {                                                                                                                                                                                           

          "london": "15°C, cloudy",                                                                                                                                                                      

          "new york": "22°C, sunny",                                                                                                                                                                     

          "tokyo": "28°C, humid",                                                                                                                                                                        

      }                                                                                                                                                                                                  

      return data.get(city.lower(), f"No weather data for {city}")                                                                                                                                       

                                                                                                                                                                                                         

Step 2 — Describe the Tools to the LLM                                                                                                                                                                 

The LLM never sees your Python functions directly. You give it a description — a menu of what's available, what each tool does, and what inputs it expects. The LLM reads this to decide when and how  

  to use each tool.

                                                                                                                                                                                                         

  TOOLS = [                                                                                                                                                                                              

      {

          "name": "calculator",                                                                                                                                                                          

          "description": "Perform basic arithmetic. Use this for any math.",                                                                                                                             

          "input_schema": {                                                                                                                                                                              

              "type": "object",                                                                                                                                                                          

              "properties": {                                                                                                                                                                            

                  "operation": {                                                                                                                                                                         

                      "type": "string",                                                                                                                                                                  

                      "enum": ["add", "subtract", "multiply", "divide"],                                                                                                                                 

                  },                                                                                                                                                                                     

                  "a": {"type": "number"},                                                                                                                                                               

                  "b": {"type": "number"},                                                                                                                                                               

              },                                                                                                                                                                                         

              "required": ["operation", "a", "b"],                                                                                                                                                       

          },                                                                                                                                                                                             

      },                                                                                                                                                                                                 

      {                                                                                                                                                                                                  

          "name": "get_weather",                                                                                                                                                                         

          "description": "Get the current weather for a city.",                                                                                                                                          

          "input_schema": {                                                                                                                                                                              

              "type": "object",                                                                                                                                                                          

              "properties": {                                                                                                                                                                            

                  "city": {"type": "string"},                                                                                                                                                            

              },                                                                                                                                                                                         

              "required": ["city"],                                                                                                                                                                      

          },                                                                                                                                                                                             

      },                                                                                                                                                                                                 

  ]                                                                                                                                                                                                      

                                                                                                                                                                                                         

Step 3 — The Tool Dispatcher                                                                               

When the LLM says "call calculator with these inputs", we need something to route that request to the right Python function. That's the dispatcher:                                                    

                  

  def run_tool(name: str, inputs: dict):                                                                                                                                                                 

      if name == "calculator":                                                                                                                                                                           

          return calculator(**inputs)                                                                                                                                                                    

      elif name == "get_weather":                                                                                                                                                                        

          return get_weather(**inputs)                                                                                                                                                                   

      else:                                                                                                                                                                                              

          return f"Unknown tool: {name}"                                                                                                                                                                 

                                                                                                                                                                                                         

Think of it as a switchboard — it takes the LLM's instruction and hands it off to the right function.                                                                                                                                                                                                

  Step 4 — The Agent Loop                                                                                  

                  

  This is where everything comes together. The loop is the agent.                                          

                  

  import anthropic                                                                                         

  client = anthropic.Anthropic()                                                                           

  def run_agent(user_message: str):                                                                                                                                                                      

  print(f"\nUser: {user_message}")

  messages = [{"role": "user", "content": user_message}]                                               

      while True:                                                                                          

          response = client.messages.create(                                                               

              model="claude-sonnet-4-6",                                                                    max_tokens=1024,

              tools=TOOLS,                                                                                  messages=messages,

          )                                                                                                                                                                                                   

          # Agent is done — return the final answer                                                     if response.stop_reason == "end_turn":

              final = next(b.text for b in response.content if hasattr(b, "text"))                         

              print(f"\nAgent: {final}")                                                                                                                                                                  return final                                                                                 

          # Agent wants to use tools                                                                       

          if response.stop_reason == "tool_use":                                                           

              messages.append({"role": "assistant", "content": response.content})                          

              tool_results = []                                                                            

              for block in response.content:                                                               

                  if block.type == "tool_use":                                                             

                      print(f"  [tool call] {block.name}({block.input})")                                  

                      result = run_tool(block.name, block.input)                                           

                      print(f"  [tool result] {result}")                                                   

                      tool_results.append({                                                                

                          "type": "tool_result",                                                           

                          "tool_use_id": block.id,                                                         

                          "content": str(result),                                                          

                      })                                                                                                                                                                                 

              messages.append({"role": "user", "content": tool_results})                                                                                                                                  # Loop continues — agent reasons again with new information                                                                                                                                                                                                                              

  run_agent("What's the weather in London and Tokyo? Also, what's 847 times 23?")                          

  ---             

  What Happens When You Run It

                                                                                                                                                                                                         

  User: What's the weather in London and Tokyo? Also, what's 847 times 23?

                                                                                                                                                                                                         

    [tool call] get_weather({'city': 'London'})                                                                                                                                                          

    [tool result] 15°C, cloudy                                                                                                                                                                           

    [tool call] get_weather({'city': 'Tokyo'})                                                                                                                                                           

    [tool result] 28°C, humid                                                                                                                                                                            

    [tool call] calculator({'operation': 'multiply', 'a': 847, 'b': 23})                                                                                                                                 

    [tool result] 19481.0                                                                                                                                                                                

                                                                                                                                                                                                         

  Agent: Here's what I found:                                                                                                                                                                            

  - London: 15°C, cloudy                                                                                                                                                                                 

  - Tokyo: 28°C, humid                                                                                                                                                                                   

  - 847 × 23 = 19,481                                                                                      

The agent autonomously decided which tools to call, called them in the right order, and synthesized the results into a final answer.                                                                   

How the Four Agent Concepts Map to the Code

                                                                                                           

  Perceive

                                                                                                messages = [{"role": "user", "content": user_message}]

  # ...                                                                                                                                                                                                  

  messages.append({"role": "user", "content": tool_results})                                                                                                                                             

The messages list is everything the agent knows — the user's question, tool results, conversation history. It grows every iteration. The agent perceives again after every action, which is what makes it an agent rather than a chatbot.                                                                                                                                                   

  Reason                                                                                                                                                                                    response = client.messages.create(

      model="claude-sonnet-4-6",                                                                           

      tools=TOOLS,                                                                                         

      messages=messages,                                                                        )                                                                                                                                                                                                      

  Claude looks at the full history and available tools and decides: Do I have enough to answer? (end_turn) or Do I need more information? (tool_use). The TOOLS list is the agent's awareness of its own

  capabilities.                                                                                                                                                                                          

                  

  Act                                                                                                      

                  

  result = run_tool(block.name, block.input)

  

This is the agent doing something in the world. Here it's calling a calculator. In a more powerful agent it could be browsing the web, writing and running code, sending an email, or updating a database. The pattern is identical regardless.                                                   

  Loop                                                                                                                                                                                                        

  while True:

      # reason                                                                                             

      # act                                                                                                                                                                                       # perceive result                                                                                    

      # repeat                                                                                                                                                                                           

The while True is the agent. A chatbot stops after one LLM call. An agent keeps looping until it decides it's done.                                                                                    

                                                                                                           

The One-Line Summary                                                                                                                                                                      The while True loop is the agent. Everything else is just giving it eyes (perceive), a brain (reason), and hands (act).

                                                                                                  

The full code is available above. To run it, save it as agent.py, set your ANTHROPIC_API_KEY, and run python3 agent.py.