In this article
AI agents are the most exciting development in applied AI right now. Unlike a standard LLM call that takes an input and returns an output, an agent can reason, plan, and take actions — calling tools, browsing the web, writing and running code, or interacting with external APIs to complete complex, multi-step tasks.
LangChain has become the go-to framework for building agents in Python. In this guide, I'll walk you through everything you need to know — from the core concepts to shipping a production-grade agent.
What Are AI Agents?
Think of a traditional LLM call as a calculator: you give it an expression and it returns an answer. An AI agent is more like a human employee: you give it a goal, and it figures out the steps, uses available tools, and iterates until the task is done.
A minimal agent has three components:
- An LLM — the "brain" that plans and reasons (GPT-4o, Claude 3.5, Gemini 1.5 Pro)
- Tools — functions the agent can call (web search, calculator, database query, email sender)
- A loop — the agent thinks, acts, observes the result, then thinks again until it's done
The ReAct Framework
The most widely used agent pattern is ReAct (Reasoning + Acting). At each step, the agent produces a structured output:
Thought: I need to find the current price of Bitcoin.
Action: web_search
Action Input: "Bitcoin price today"
Observation: Bitcoin is currently trading at $67,420.
Thought: Now I have the price. I can answer the question.
Final Answer: Bitcoin is currently $67,420 USD.
LangChain implements this loop for you — all you need to do is define the tools and connect an LLM.
Building Your First Agent
Let's build a simple agent with web search and a calculator. First, install the dependencies:
pip install langchain langchain-openai duckduckgo-search
Then set up the agent:
from langchain.agents import create_react_agent, AgentExecutor
from langchain_openai import ChatOpenAI
from langchain_community.tools import DuckDuckGoSearchRun
from langchain.tools import tool
from langchain import hub
# Define tools
search = DuckDuckGoSearchRun()
@tool
def calculator(expression: str) -> str:
"""Evaluate a mathematical expression."""
return str(eval(expression))
tools = [search, calculator]
# Create agent
llm = ChatOpenAI(model="gpt-4o", temperature=0)
prompt = hub.pull("hwchase17/react")
agent = create_react_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# Run it
result = agent_executor.invoke({
"input": "What is the current market cap of Apple? How many times bigger is it than Google?"
})
Run this and watch the agent reason through the problem in real time — it will search for both values, then use the calculator to compute the ratio.
Adding Custom Tools
The real power comes from custom tools that connect your agent to your specific systems. Here's how to create a tool that queries a database:
@tool
def query_customer_database(customer_id: str) -> str:
"""Look up a customer's order history by their customer ID."""
# Replace with your actual database query
conn = get_db_connection()
orders = conn.execute(
"SELECT * FROM orders WHERE customer_id = ?", [customer_id]
).fetchall()
return str(orders)
Good tool design matters a lot. Make sure your docstring is crystal clear — the LLM reads it to decide when and how to use the tool. Be specific about what parameters are expected and what gets returned.
Giving Your Agent Memory
By default, agents have no memory of previous conversations. For a customer support bot or personal assistant, you want the agent to remember what was discussed earlier. Add memory like this:
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True
)
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
memory=memory,
verbose=True
)
For production, swap ConversationBufferMemory for a persistent store like Redis or PostgreSQL so conversations survive restarts.
Multi-Agent Systems with LangGraph
Single agents work well for focused tasks. For complex workflows — like a research pipeline that searches, writes, reviews, and publishes — you need multiple agents working together. LangGraph is the best tool for this.
from langgraph.graph import StateGraph, END
# Define your agent graph
workflow = StateGraph(AgentState)
workflow.add_node("researcher", researcher_agent)
workflow.add_node("writer", writer_agent)
workflow.add_node("reviewer", reviewer_agent)
workflow.add_edge("researcher", "writer")
workflow.add_edge("writer", "reviewer")
workflow.add_conditional_edges("reviewer", should_revise)
LangGraph handles the state passing between agents, conditional routing (e.g. "send back for revision if quality score is below 8"), and even parallel execution.
Production Tips
- Always add a max_iterations limit — agents can loop forever if something goes wrong. Set
max_iterations=10as a safety net. - Log every step — use LangSmith or your own logging to trace what the agent does. This is essential for debugging.
- Handle tool failures gracefully — wrap tool calls in try/except and return informative error messages so the agent can recover.
- Rate limit tool calls — especially for external APIs. Add delays or caching to avoid hitting rate limits or unexpected bills.
- Test with cheap models first — use GPT-4o-mini during development. Switch to GPT-4o or Claude 3.5 for production when you're happy with the behaviour.
Want to Build AI Agents Yourself?
Our 12-week Generative AI coaching track takes you from zero to building and deploying production AI agents — with 1-on-1 sessions, code reviews, and a portfolio project.
View the Generative AI Course →