arrow_forwardGuides / AI Agents
Updated April 2026 18 min read Advanced

AI Agents
The complete guide

From the ReAct Loop to Multi-Agent Systems — everything you need to know to build autonomous digital workers with Python, CrewAI and LangGraph.

2026
Current
Python
Code language
Multi-Agent
Architecture

What is an AI Agent?

When you use ChatGPT and ask a question, you get an answer — and that's it. The model takes input, produces output, and it's done. That's a basic language model (LLM). An AI Agent is fundamentally different: it doesn't just answer questions — it plans, decides, invokes external tools, and reacts to the results in an autonomous loop until a goal is reached.

The best way to picture an Agent is as an"autonomous digital worker". Say you ask an Agent to research a market and prepare a report. It will plan the task, search for information online, read files, analyze data, write results to a file, and return an organized report — all without you having to manage each step.

The heart of every Agent is the ReAct loop (Reasoning + Acting): the model thinks about what to do (Thought), performs an action (Action), sees the result (Observation), and then thinks again — until the task is complete. This loop is what distinguishes an Agent from a simple LLM.

lightbulb
ReAct in brief

Thought: "I need to look up Apple's stock price" → Action: web_search("AAPL stock price") → Observation: "185.20$" → Thought: "Now I can calculate the return" → ...

How does an Agent work? — the components

A modern Agent is made of four main components that work together. Each is essential for true autonomous operation.

1. LLM — the brain

The model itself (GPT-4o, Claude Sonnet, Gemini Pro, etc.) is the decision engine. It reads the current context — goal, memory, previous tool results — and decides the next action. The LLM doesn't "know" how to use tools on its own; what enables that is the Function Calling (or Tool Use) mechanism, which adds tool descriptions to the System Prompt and prompts the model to produce structured output.

2. Tools — the hands

Without Tools, an Agent is just an LLM. Tools are the functions the Agent can call: internet search, running Python code, reading and writing files, sending HTTP requests, SQL queries, sending emails and more. Each Tool is defined with a name, a description, and the parameters it accepts — and the model automatically chooses when and how to call it.

3. Memory

Short-term memory is the Conversation History kept in the Context Window. Long-term memory is information stored outside the Context — usually in a Vector Store like Pinecone or Chroma — and retrieved by relevance when needed. An Agent that wants to "remember" information across different conversations must use Long-term memory.

4. Planning

Complex tasks require breaking down into subtasks. Planning mechanisms like Task Decomposition let the Agent take a broad goal ("write a competitive analysis of the cybersecurity market") and break it into actionable steps. Frameworks like LangGraph and CrewAI add a structured planning layer on top of the LLM.

Component Role Technology examples
LLM (brain)Decision-making, reasoningGPT-4o, Claude, Gemini
Tools (hands)Interaction with the worldSearch, Code, APIs, Files
MemoryKeeping context and informationConversation, Vector Store
PlanningBreaking down complex tasksCrewAI, LangGraph, ToT

Types of Agents — when to use each

Not every Agent is built the same way. There are four main archetypes, each suited to a different kind of task.

Agent type When to use Examples
ReAct Agent General tasks with varied tools, search, calculations Research assistant, Q&A bot
Tool Calling Agent When there is a well-defined tool set and structured output is required API integrations, CRM bots
Plan-and-Execute Long, complex tasks requiring upfront planning Writing reports, market analysis
Multi-Agent When a task is too big for a single Agent, or expertise is needed CrewAI pipelines, AutoGen

ReAct Agent is the most flexible type and suits most cases. It runs in a Thought-Action-Observation loop and fits when the task isn't known in advance. Tool Calling Agent is more defined — the model chooses from a known list of Tools and returns structured JSON. Plan-and-Execute fits when work can be planned in advance: one Agent creates a plan, a separate Agent executes each step. Multi-Agent is an architecture where several Agents work together — each with a defined role, with a Crew Manager coordinating them.

CrewAI — a Framework for Multi-Agent Systems

CrewAI is the most popular Framework for building Multi-Agent systems in Python. The idea is simple: you define a "Crew" of Agents, each with a role and goal, and define tasks (Tasks). The Framework manages the communication between them.

Installation

pip install crewai crewai-tools

CrewAI's four core components

A full example — a Crew for writing an article

Here is a Crew of three Agents working together to produce a professional article: a Researcher that gathers information, a Writer that writes, and an Editor that improves it.

from crewai import Agent, Task, Crew, Process
from crewai_tools import SerperDevTool

# search tool (requires SERPER_API_KEY)
search_tool = SerperDevTool()

# --- defining Agents ---

researcher = Agent(
    role="Senior Research Analyst",
    goal="Search for up-to-date, reliable information on the topic you are given",
    backstory="""You are a senior research analyst with 10 years of experience.
    You know how to find reliable sources and filter for relevant information.""",
    tools=[search_tool],
    verbose=True,
    llm="gpt-4o"
)

writer = Agent(
    role="Content Writer",
    goal="Write a deep, engaging and accurate article based on the research",
    backstory="""You are an experienced content writer who specializes in making
    technical topics accessible to a broad audience. Your style is clear and persuasive.""",
    verbose=True,
    llm="gpt-4o"
)

editor = Agent(
    role="Chief Editor",
    goal="Edit the article, improve the flow and clarity, fix errors",
    backstory="""You are a chief editor with a sharp eye for detail.
    You make sure every article meets the highest standards.""",
    verbose=True,
    llm="gpt-4o"
)

# --- defining Tasks ---

research_task = Task(
    description="Research the topic in depth: {topic}. Gather up-to-date data, statistics and examples.",
    expected_output="A detailed research report with at least 5 reliable data points",
    agent=researcher
)

write_task = Task(
    description="Write an 800-1000 word article based on the research. Include a title, introduction, body, and conclusion.",
    expected_output="A complete, formatted article in Markdown",
    agent=writer,
    context=[research_task]  # receives the output of research_task
)

edit_task = Task(
    description="Edit the article: improve the wording, verify facts, add subheadings if needed.",
    expected_output="A final article ready for publication",
    agent=editor,
    context=[write_task]
)

# --- running the Crew ---

crew = Crew(
    agents=[researcher, writer, editor],
    tasks=[research_task, write_task, edit_task],
    process=Process.sequential,
    verbose=True
)

result = crew.kickoff(inputs={"topic": "The impact of AI Agents on the job market in 2026"})
print(result)
star
Tip: Hierarchical Process

You can replace Process.sequential with Process.hierarchical and define manager_llm="gpt-4o". The Manager will distribute the work to the Agents dynamically as needed — useful when the number of tasks isn't known in advance.

LangGraph — Stateful Agents with a Graph Architecture

LangGraph is a LangChain library that lets you build Agents as aGraph of State. Unlike CrewAI, which is team-oriented, LangGraph fits complex agentic flows that require State management, loops, and Human-in-the-Loop.

The basic idea: you define a StateGraph — a graph where each Node is a function that receives and returns State. Edges are the transitions between nodes, and you can define Conditional Edges that decide the path based on the results.

Code example — a Chatbot with Human-in-the-Loop

from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from typing import TypedDict, Annotated
import operator

# defining the State
class AgentState(TypedDict):
    messages: Annotated[list, operator.add]
    human_approved: bool

# defining a Tool
@tool
def calculate(expression: str) -> str:
    """Evaluates a math expression. Takes a string of a Python expression."""
    try:
        return str(eval(expression))
    except Exception as e:
        return f"Error: {e}"

tools = [calculate]
llm = ChatOpenAI(model="gpt-4o").bind_tools(tools)
tool_node = ToolNode(tools)

# node functions
def agent_node(state: AgentState):
    response = llm.invoke(state["messages"])
    return {"messages": [response]}

def human_review_node(state: AgentState):
    # a stop point — waits for human approval
    last_msg = state["messages"][-1]
    print(f"\n[Human Review] Agent wants: {last_msg.content}")
    approval = input("Approve? (y/n): ")
    return {"human_approved": approval.lower() == "y"}

# conditional routing
def should_continue(state: AgentState):
    last = state["messages"][-1]
    if hasattr(last, "tool_calls") and last.tool_calls:
        return "human_review"  # requires approval before using tools
    return END

def after_review(state: AgentState):
    return "tools" if state["human_approved"] else END

# building the graph
workflow = StateGraph(AgentState)
workflow.add_node("agent", agent_node)
workflow.add_node("human_review", human_review_node)
workflow.add_node("tools", tool_node)

workflow.set_entry_point("agent")
workflow.add_conditional_edges("agent", should_continue)
workflow.add_conditional_edges("human_review", after_review)
workflow.add_edge("tools", "agent")  # back to the agent after a Tool

app = workflow.compile()

# run
result = app.invoke({
    "messages": [{"role": "user", "content": "What is 847 * 293?"}],
    "human_approved": False
})
warning
LangGraph vs CrewAI — when to use which?

Choose CrewAI when you have a defined business process with clear roles — it's the faster solution. Choose LangGraph when you need precise control over the State, conditional loops, Human-in-the-Loop, or flows a Crew can't represent.

Build your first Agent — 5 minutes with OpenAI Function Calling

Before using Frameworks, it's important to understand how an Agent works at the basic level. Here is a minimal Agent built directly on the OpenAI API with Function Calling — without external Frameworks.

Step 1 — installation

pip install openai
export OPENAI_API_KEY="your-key-here"

Step 2 — defining Tools and running the Agent

from openai import OpenAI
import json
import math

client = OpenAI()

# --- defining Tools ---
tools = [
    {
        "type": "function",
        "function": {
            "name": "web_search",
            "description": "Searches for current information online. Use when you need information not in memory.",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "the search query"
                    }
                },
                "required": ["query"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "calculate",
            "description": "Performs a math calculation. Takes a valid Python expression.",
            "parameters": {
                "type": "object",
                "properties": {
                    "expression": {
                        "type": "string",
                        "description": "a math expression, e.g.: '2 ** 10' or 'math.sqrt(144)'"
                    }
                },
                "required": ["expression"]
            }
        }
    }
]

# --- implementing Tools ---
def web_search(query: str) -> str:
    # in Production: connect to Serper, Tavily, or Brave Search API
    return f"[search results for '{query}']: sample simulated information"

def calculate(expression: str) -> str:
    try:
        result = eval(expression, {"math": math, "__builtins__": {}})
        return str(result)
    except Exception as e:
        return f"Calculation error: {e}"

# --- the Agent loop ---
def run_agent(user_message: str, max_steps: int = 10) -> str:
    messages = [
        {
            "role": "system",
            "content": "You are a helpful AI assistant that can search for information and perform calculations. "
                       "Use the tools when needed. Always answer in English."
        },
        {"role": "user", "content": user_message}
    ]

    for step in range(max_steps):
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=tools,
            tool_choice="auto"
        )

        message = response.choices[0].message
        messages.append(message)

        # if there are no Tool Calls — the Agent is done
        if not message.tool_calls:
            return message.content

        # execute each Tool Call
        for tool_call in message.tool_calls:
            func_name = tool_call.function.name
            func_args = json.loads(tool_call.function.arguments)

            print(f"[Agent] calling: {func_name}({func_args})")

            if func_name == "web_search":
                result = web_search(**func_args)
            elif func_name == "calculate":
                result = calculate(**func_args)
            else:
                result = f"Unknown tool: {func_name}"

            # add the Tool result to the conversation
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": result
            })

    return "The Agent could not complete the task within the allowed number of steps"

# --- running examples ---
print(run_agent("What is the square root of 2025?"))
print(run_agent("Search for information about ChatGPT-5 and summarize in bullets"))
rocket_launch
Adding real search

Replace the web_search simulated one with a connection to the Tavily API (free up to 1,000 searches/month): pip install tavily-python, and then from tavily import TavilyClient; client = TavilyClient(api_key="..."); results = client.search(query).

5 real use cases for AI Agents

The theory is clear — but where do Agents actually change lives? Here are five real Use Cases you can build today with the tools we described.

1. Research Agent — automatic market research

The problem: Market competitor analysis takes days of manual work. The solution: An Agent that takes a domain name, searches for information about competitors, gathers pricing and feature data, and produces a comparative report. With CrewAI: a Researcher finds competitors, an Analyst analyzes data, a Writer writes the report — the whole crew runs automatically.

Implementation time: 2-3 hours. Savings: 8-16 hours of manual work per report.

2. Code Review Agent — code review on GitHub

The problem: Code Reviews take time and leave bugs behind. The solution: An Agent that takes a GitHub Pull Request webhook, reads the diff, runs static analysis, checks for security vulnerabilities, and adds detailed comments directly to the PR. It uses the GitHub API as a Tool, and Claude Sonnet as the LLM to focus on logical issues.

Implementation time: 4-6 hours. Benefit: Finds about 70% of common issues before Human Review.

3. Customer Support Agent — automatic ticket replies

The problem: Repetitive support consumes engineering time. The solution: An Agent connected to Zendesk/Intercom that reads each new ticket, searches the knowledge base (RAG over Documentation), and writes a tailored reply. If the issue is complex — it flags "human_needed" and hands it off to a rep with a summary.

Implementation time: 1 day. Benefit: Resolves 60-80% of tickets automatically.

4. Content Agent — creating content from a Brief

The problem: Creating high-quality, consistent marketing content takes a lot of time. The solution: A Crew of Agents: an SEO Researcher (finds keywords), a Content Strategist (defines the structure), a Writer (writes), and a Social Media Specialist (adapts for each platform). Input: a short 2-3 line Brief. Output: an article, 3 LinkedIn posts, 5 Tweets.

Implementation time: 3-4 hours. Savings: 3-4 hours of manual work per Brief.

5. Data Analysis Agent — analyzing CSV and generating Insights

The problem: Raw CSV data requires analysis that takes time and expertise. The solution: An Agent that takes a CSV, writes and runs Python code (Pandas, Matplotlib), generates charts, and returns a natural-language report with Insights and Recommendations. It uses a Code Interpreter Tool or an isolated Python environment.

Implementation time: 2-3 hours. Benefit: Makes data analysis accessible to people who aren't Data Scientists.

The ReAct loop — full code from scratch

Before using a Framework like CrewAI or LangGraph, it's important to understand how to build a ReAct Agent from scratch. This gives a deep understanding that will help diagnose issues later.

terminal — python agent.py
[Agent] Task: "What is 17 * 23 and then look up what a prime number is?"
─────────────────────────────────────────
Thought: I first need to calculate 17 * 23
Action: calculate("17 * 23")
Observation: 391
Thought: 391 is not prime (371 = 7 × 53). Now to look up what a prime number is
Action: web_search("what is a prime number")
Observation: a prime number is a natural number greater than 1 that is divisible only by 1 and itself
Thought: I have all the information I need. I can answer.
Final Answer: 17 × 23 = 391. 391 is not a prime number (divisible by 17 and 23). A prime number is a number divisible only by 1 and itself.

ReAct Agent with LangChain

from langchain.agents import AgentExecutor, create_react_agent
from langchain_anthropic import ChatAnthropic
from langchain.tools import tool
from langchain_core.prompts import PromptTemplate

@tool
def search_web(query: str) -> str:
    """חפש מידע Current באינטרנט. קבל שאילתת חיפוש בעברית."""
    # in Production: connect to a Tavily / Serper API
    return f"search results for: {query} — [sample simulated results]"

@tool
def calculate(expression: str) -> str:
    """Evaluate a math expression. Takes a valid Python expression."""
    try:
        return str(eval(expression, {"__builtins__": {}}))
    except Exception as e:
        return f"Error: {e}"

@tool
def read_file(filename: str) -> str:
    """Read the content of a local text file."""
    try:
        with open(filename, 'r', encoding='utf-8') as f:
            return f.read()
    except Exception as e:
        return f"Error reading file: {e}"

# defining the LLM
llm = ChatAnthropic(model="claude-3-5-sonnet-20241022", temperature=0)
tools = [search_web, calculate, read_file]

# ReAct Prompt Template
react_prompt = PromptTemplate.from_template("""
You are a helpful AI Agent that answers in English. Follow the ReAct Loop:
Thought → Action → Observation → ... → Final Answer

You have access to the following tools:
{tools}

Tool names: {tool_names}

Question: {input}
{agent_scratchpad}
""")

# creating the Agent
agent = create_react_agent(llm, tools, react_prompt)
executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
    max_iterations=10,
    handle_parsing_errors=True
)

# run
result = executor.invoke({"input": "What is 17 * 23? And what is the name for a number divisible only by 1 and itself?"})
print(result["output"])
play_circle
AI Agents with LangChain & CrewAI — Full Tutorial
YouTube • search for tutorials
open_in_new

LangGraph — State Machine Agents

LangGraph is a library that adds full State Management to LangChain Agents. Instead of a simple ReAct loop, LangGraph lets you build complex Agents with states, Conditional Routing and Human-in-the-Loop.

When is LangGraph better than plain ReAct?

A basic Graph Agent — Research + Write

from langgraph.graph import StateGraph, END
from langchain_anthropic import ChatAnthropic
from typing import TypedDict, List

# defining the State — the data passed between the Nodes
class AgentState(TypedDict):
    topic: str
    research: str
    draft: str
    feedback: str
    final_article: str

llm = ChatAnthropic(model="claude-3-5-sonnet-20241022")

# Node 1: research
def research_node(state: AgentState) -> AgentState:
    prompt = f"Research the topic: {state['topic']}. Provide 5 key facts."
    result = llm.invoke(prompt)
    return {"research": result.content}

# Node 2: writing
def write_node(state: AgentState) -> AgentState:
    prompt = f"""כתוב מאמר בעברית על: {state['topic']}
    based on the following research: {state['research']}
    The article should be 300-500 words."""
    result = llm.invoke(prompt)
    return {"draft": result.content}

# Node 3: review and improve
def review_node(state: AgentState) -> AgentState:
    prompt = f"""Review the following draft and improve it:
    {state['draft']}

    The output: the improved draft only."""
    result = llm.invoke(prompt)
    return {"final_article": result.content}

# building the Graph
workflow = StateGraph(AgentState)
workflow.add_node("research", research_node)
workflow.add_node("write", write_node)
workflow.add_node("review", review_node)

# Edges — defining the flow
workflow.set_entry_point("research")
workflow.add_edge("research", "write")
workflow.add_edge("write", "review")
workflow.add_edge("review", END)

# Compile and run
app = workflow.compile()
result = app.invoke({"topic": "Artificial intelligence and medicine in Israel"})
print(result["final_article"])
fork_right
Conditional Routing — dynamic paths

In LangGraph, Conditional Edges let you choose a Node based on the result of a previous Node. workflow.add_conditional_edges("review", lambda s: "approved" if len(s["draft"]) > 300 else "rewrite", {"approved": END, "rewrite": "write"})

Memory — memory for Agents

An Agent without memory "forgets" everything between conversations. Memory is the component that lets it remember context, preferences, and organizational information over time.

Short-term Memory — Conversation History

The LLM's Context Window is the short-term memory — everything that happened in the current conversation. LangChain manages this automatically inConversationBufferMemory. Problem: Context Windows are limited (even GPT-4 Turbo is limited to 128K). For long conversations — useConversationSummaryMemory which automatically summarizes old conversations.

from langchain.memory import ConversationSummaryBufferMemory
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_react_agent

llm = ChatOpenAI(model="gpt-4o")

# memory that automatically summarizes old conversations (keeps up to 500 tokens in live memory)
memory = ConversationSummaryBufferMemory(
    llm=llm,
    max_token_limit=500,
    memory_key="chat_history",
    return_messages=True
)

# integration with an Agent
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    memory=memory,
    verbose=True
)

# the Agent remembers everything from the conversation — even after 50+ messages
r1 = agent_executor.invoke({"input": "My name is Alon and I work at an AI Startup"})
r2 = agent_executor.invoke({"input": "What is my name and where do I work?"})
# r2 will answer "Your name is Alon and you work at an AI Startup"

Long-term Memory — Vector Store

For memory kept across different Sessions, you use a Vector Store. Every important piece of information is stored as an Embedding, and retrieved by relevance when needed.

from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain.memory import VectorStoreRetrieverMemory

# creating a Vector Store for memory
embeddings = OpenAIEmbeddings()
vectorstore = Chroma(
    collection_name="agent_memory",
    embedding_function=embeddings,
    persist_directory="./agent_memory_db"  # persisted to disk!
)

retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

# memory that searches for the 3 most relevant memories
memory = VectorStoreRetrieverMemory(retriever=retriever)

# saving a memory
memory.save_context(
    {"input": "Alon runs an AI Startup in Tel Aviv"},
    {"output": "Saved: Alon → AI Startup → Tel Aviv"}
)

# retrieval by relevance — even after a restart of the program!
relevant = memory.load_memory_variables({"prompt": "Tell me about Alon"})

Production — Error Handling and costs

An Agent that works in a Demo doesn't always work in Production. Here are the most important practices for turning an Agent into a reliable tool.

Error Handling and Retry Logic

import time
from tenacity import retry, stop_after_attempt, wait_exponential

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=4, max=10)
)
def safe_llm_call(prompt: str) -> str:
    """A safe LLM call with automatic retry"""
    try:
        response = llm.invoke(prompt)
        return response.content
    except RateLimitError:
        print("Rate Limit — waiting and retrying...")
        raise  # tenacity will handle the retry
    except APIError as e:
        print(f"API Error: {e}")
        raise

# Tool with validation
@tool
def safe_calculate(expression: str) -> str:
    """A safe calculator with Sandboxing"""
    ALLOWED = set('0123456789+-*/.(). ')
    if not all(c in ALLOWED for c in expression):
        return "Error: expression contains disallowed characters"
    if len(expression) > 100:
        return "Error: expression too long"
    try:
        result = eval(expression)
        if not isinstance(result, (int, float)):
            return "Error: invalid result"
        return str(round(result, 6))
    except Exception as e:
        return f"Calculation error: {e}"

Tracking costs and Token Usage

Model Input / 1M tokens Output / 1M tokens Notes
Claude 3.5 Sonnet$3$15Recommended for Agents
GPT-4o$5$15Flexible, excellent function calling
GPT-4o mini$0.15$0.60Cheap — for simple roles
Claude 3 Haiku$0.25$1.25Anthropic's cheapest
savings
A cost-saving strategy

Use a strong model (GPT-4o / Claude Sonnet) for Reasoning and a cheap model (Haiku / GPT-4o mini) for mechanical operations like parsing JSON or Classification. A Hybrid Architecture can save 60-80% of the cost.

Cheat sheet — Framework Comparison

Criterion CrewAI LangGraph OpenAI API direct
Ease of getting startedVery easyMediumMedium
Multi-AgentExcellentExcellentManual
State ManagementBasicFullManual
Human-in-LoopLimitedBuilt-inManual
ObservabilityLangSmithLangSmithManual
Best forContent pipelines, ResearchComplex WorkflowsFull control

5 quick-start points

1
A simple ReAct Agent: pip install langchain langchain-openai + one tool (calculate) + a basic loop. Takes 30 minutes.
2
CrewAI Starter: pip install crewai crewai-tools + Researcher + Writer + Task. Takes an hour.
3
LangGraph Graph: pip install langgraph + StateGraph + 3 Nodes. Takes 2 hours.
4
Tool Custom: Every @tool decorator + a precise docstring = the LLM will call the tool correctly.
5
Observability: LANGCHAIN_TRACING_V2=true + LANGCHAIN_API_KEY = every Trace appears in LangSmith automatically.

Summary — tips and best practices

AI Agents are a powerful tool, but with great power comes responsibility. Here are the key tips that will help you build stable, reliable Agents.

Useful links

rocket_launch

The next step

Now that you understand AI Agents — the next step is to combine them with RAG to give them organizational knowledge, or to build visual Workflows with n8n and Make.com.