[Ch 1] Introduction to AI Agents — Why Do We Need Them?

Apr 7, 2026 · 6 min read
blog AI Agent

Before you write a single line of LangGraph, you need a clear mental model of what an AI agent actually is — and more importantly, what it isn’t. This chapter builds that foundation.


What Is an AI Agent?

An AI agent is a system that uses a language model to decide which actions to take, executes those actions (via tools), observes the results, and then decides what to do next — repeating this cycle until a goal is reached.

The key word is decide. Unlike a simple LLM call that takes input and produces output in one shot, an agent has a control loop. It can take multiple steps, use external tools, and adapt based on intermediate results.


The Spectrum: Chatbot → Pipeline → Agent

Most AI systems fall somewhere on this spectrum:

TypeControlStepsToolsExample
ChatbotStatic1NoneFAQ bot, direct LLM reply
LLM PipelineFixedN (predetermined)OptionalSummarize → Translate → Format
AI AgentDynamicN (decided at runtime)YesResearch → Plan → Write → Verify
Fig 1: The spectrum from stateless chatbots to dynamic agents

A chatbot is a single LLM call: user sends a message, LLM replies. It has no memory between sessions and no ability to use external tools.

An LLM pipeline chains multiple LLM calls in a predetermined order. The steps are fixed: you always summarize, then translate, then format. There’s no branching — the flow doesn’t change based on what the model produces.

An AI agent decides its own next step based on the current state. It might call a search tool, decide the result is insufficient, call it again with a refined query, then write an answer. The number and order of steps are not known in advance.


The Agent Loop (ReAct)

The dominant pattern for AI agents is ReAct (Reason + Act), introduced by Yao et al. (2022). The agent alternates between two phases:

  1. Reason — the LLM thinks about what to do given the current context
  2. Act — the agent executes an action (tool call) and receives an observation

This cycle repeats until the LLM decides it has enough information to produce a final answer.

graph TD A([User Input]) --> B[LLM: Think\nWhat should I do next?] B --> C{Tool call\nneeded?} C -- Yes --> D[Execute Tool] D --> E[Observe Result] E --> B C -- No --> F([Final Answer\nto User]) style A fill:#4CAF50,color:#fff,stroke:none style F fill:#2196F3,color:#fff,stroke:none style D fill:#FF9800,color:#fff,stroke:none style B fill:#9C27B0,color:#fff,stroke:none
Fig 2: The ReAct agent loop — Reason and Act until a final answer is reached

The loop terminates when the LLM produces a regular message (no tool call). In practice this means the LLM’s output is inspected after each step: if it contains a tool call, the system runs it and feeds the result back; if not, the loop ends.


Real-World Use Cases

Agents shine when a task requires dynamic decision-making with external information or actions:

Use CaseWhy It Needs an Agent
Automated report generationMust retrieve data, validate it, format by section — each step depends on previous
Code review assistantReads a PR, calls static analysis tools, queries docs, writes structured feedback
Presentation builderParses user intent, generates slide content, creates charts, assembles file
Test case generatorUnderstands spec, identifies scenarios, generates cases, checks for coverage
Customer supportLooks up account info, checks policy docs, escalates if needed

In every case, the number of steps is not fixed in advance. The agent decides what to do based on what it finds.


When NOT to Use an Agent

This is just as important.

Use a simple LLM pipeline when:

  • The task has a fixed, known structure (always: extract → classify → output)
  • Latency is critical — each agent step adds 1–5 seconds
  • The task is well-defined enough that a deterministic pipeline won’t miss edge cases

Use a basic LLM call when:

  • You just need to summarize, translate, or classify a single piece of text
  • There’s nothing to look up externally
  • You want 100% predictable, testable behavior

Agents introduce non-determinism — the same input can take different paths. They are also slower and more expensive per task. Don’t reach for an agent when a two-step pipeline does the job.

⚠️ A common mistake: wrapping every feature in an agent “for flexibility.” The result is a slow, expensive, hard-to-test system. Start with the simplest thing that works.


The Agent Loop in Plain Python

Before introducing any framework, here’s what the agent loop looks like as pure Python pseudocode. This will help you understand exactly what LangGraph is doing for you later.

# agent_loop.py — Plain Python pseudo-code (no framework)
import os
import json
from openai import OpenAI

client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))

# --- Tool registry ---
def search_web(query: str) -> str:
    """Simulate a web search (replace with real implementation)."""
    return f"[Search results for '{query}': ...]"

def calculate(expression: str) -> str:
    """Evaluate a math expression safely."""
    try:
        result = eval(expression, {"__builtins__": {}})
        return str(result)
    except Exception as e:
        return f"Error: {e}"

TOOLS = {
    "search_web": search_web,
    "calculate": calculate,
}

# OpenAI-format tool schemas
TOOL_SCHEMAS = [
    {
        "type": "function",
        "function": {
            "name": "search_web",
            "description": "Search the web for current information.",
            "parameters": {
                "type": "object",
                "properties": {"query": {"type": "string"}},
                "required": ["query"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "calculate",
            "description": "Evaluate a mathematical expression.",
            "parameters": {
                "type": "object",
                "properties": {"expression": {"type": "string"}},
                "required": ["expression"],
            },
        },
    },
]

# --- The agent loop ---
def run_agent(user_input: str, max_steps: int = 10) -> str:
    messages = [
        {"role": "system", "content": "You are a helpful assistant. Use tools when needed."},
        {"role": "user", "content": user_input},
    ]

    for step in range(max_steps):
        print(f"\n[Step {step + 1}] Calling LLM...")
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=messages,
            tools=TOOL_SCHEMAS,
            tool_choice="auto",
        )

        message = response.choices[0].message

        # No tool call → agent is done, return final answer
        if not message.tool_calls:
            print("[Step] Final answer reached.")
            return message.content

        # Execute each requested tool call
        messages.append(message)  # add assistant message with tool_calls
        for tool_call in message.tool_calls:
            fn_name = tool_call.function.name
            fn_args = json.loads(tool_call.function.arguments)

            print(f"[Step] Calling tool: {fn_name}({fn_args})")
            result = TOOLS[fn_name](**fn_args)
            print(f"[Step] Tool result: {result[:100]}...")

            # Add the observation back into the conversation
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": result,
            })

    return "Max steps reached without a final answer."


if __name__ == "__main__":
    answer = run_agent("What is the square root of the number of days in a leap year?")
    print(f"\nFinal Answer: {answer}")

What this shows:

  1. The conversation history (messages) is the agent’s working memory
  2. The loop continues as long as the model keeps making tool calls
  3. Each tool result is appended as a "role": "tool" message before the next LLM call
  4. max_steps is a safety guard against infinite loops

This is essentially what LangGraph automates — the graph manages the loop, the state replaces the raw messages list, and nodes replace the explicit tool execution code.


.env.example

# .env.example
OPENAI_API_KEY=your-api-key-here

💡 Using Ollama instead? Replace the OpenAI client with:

from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
# Use model="llama3.2" or any model you have pulled

Make sure Ollama is running (ollama serve) and the model supports tool use.


Summary

ConceptKey Takeaway
AI AgentAn LLM in a control loop that decides actions dynamically
ReActAlternate between Reasoning (LLM) and Acting (tools)
When to useDynamic tasks requiring external info or multi-step decisions
When NOT to useFixed pipelines, latency-sensitive tasks, simple transformations
The loopLLM call → tool call → observation → repeat until final answer

In the next chapter, we’ll break down the four core components of an agent system and introduce one of the most important (and most overlooked) concepts in production AI: Context Engineering.


← Ch 0: Series Overview | Ch 2: Components & Context Engineering →