1. Introduction
The most powerful AI applications of today are not simple chatbots that answer questions — they are agents that can reason, plan, and take action. An AI agent perceives its environment, decides which tool to use, executes actions, observes the results, and iterates until it has solved the problem.
LangChain is the leading open-source framework for building such agents in Python. It provides a composable set of building blocks — LLMs, tools, memory, chains, and agent executors — that let you assemble production-grade AI agents without writing a reasoning loop from scratch.
This guide takes you from zero to a fully functional LangChain agent. You will set up your environment, understand the core concepts, build tools, wire up memory, and run a ReAct agent that reasons step by step. Every section includes working code you can run immediately.
2. What Is an AI Agent?
A traditional LLM call is stateless: you send a prompt and receive a response. An AI agent is fundamentally different — it operates in a loop where the LLM acts as a reasoning engine that decides what to do next, not just what to say.
| Concept | Description |
| LLM (Brain) | The language model that reasons, plans, and decides which action to take next |
| Tools | Functions the agent can call — web search, calculators, databases, APIs, file readers |
| Memory | Storage of past conversation turns or facts so the agent can reason across multiple steps |
| Agent Executor | The runtime loop: calls the LLM, parses its output, runs tools, feeds results back |
| ReAct Pattern | Reasoning + Acting: the agent alternates between Thought, Action, and Observation steps |
The ReAct (Reasoning + Acting) pattern is the most widely used agent strategy. At each step the LLM emits a Thought (what it is trying to do), an Action (which tool to call and with what input), and then receives an Observation (the tool’s output). It repeats this loop until it can produce a Final Answer.
3. Environment Setup
Install LangChain and its dependencies. LangChain has split its packages by integration, so you install only what you need.
# Install core LangChain packagespip install langchain langchain-core langchain-community# Install OpenAI integration (or use any other LLM provider)pip install langchain-openai# Install additional tools used in this guidepip install duckduckgo-search wikipedia numexpr# Optional: LangSmith for tracing and debuggingpip install langsmith
Configure Your API Keys
import os# Set your OpenAI API keyos.environ['OPENAI_API_KEY'] = 'your-openai-api-key-here'# Optional: LangSmith tracing (highly recommended for debugging)os.environ['LANGCHAIN_TRACING_V2'] = 'true'os.environ['LANGCHAIN_API_KEY'] = 'your-langsmith-api-key'os.environ['LANGCHAIN_PROJECT'] = 'langchain-agent-demo'
| LangSmith is LangChain’s observability platform. Even in development, enabling tracing gives you a visual trace of every LLM call, tool invocation, and token count — invaluable for debugging agent loops that go wrong. |
4. Core LangChain Concepts
Before building an agent, you need to understand LangChain’s four fundamental building blocks: LLMs, Prompts, Chains, and Tools.
4.1 — LLMs and Chat Models
LangChain wraps any LLM behind a standard interface. ChatOpenAI is the most common — it wraps OpenAI’s GPT models with a message-based interface. You can swap in Anthropic, Google, Mistral, or any local model with a single line change.
from langchain_openai import ChatOpenAI# Initialise the LLMllm = ChatOpenAI( model = 'gpt-4o', # or 'gpt-3.5-turbo', 'gpt-4-turbo' temperature = 0, # 0 = deterministic; higher = more creative max_tokens = 1000)# Simple invocationresponse = llm.invoke('What is LangChain?')print(response.content)
OUTPUT: LLM response
| Field | Value |
| response.content | LangChain is a framework for developing applications powered by language models… |
| response.usage_metadata | {‘input_tokens’: 12, ‘output_tokens’: 87, ‘total_tokens’: 99} |
4.2 — Prompt Templates
Prompt templates let you parameterise your prompts with dynamic values. ChatPromptTemplate is the standard for chat models — it separates the system instruction from the human message.
from langchain_core.prompts import ChatPromptTemplateprompt = ChatPromptTemplate.from_messages([ ('system', 'You are a helpful assistant specialising in {domain}.'), ('human', 'Answer this question: {question}'),])# Format the prompt with valuesformatted = prompt.format_messages( domain = 'data engineering', question = 'What is Apache Spark?')print(formatted)
4.3 — Chains (LCEL)
LangChain Expression Language (LCEL) lets you compose components using the | pipe operator — similar to Unix pipes. Each component receives the output of the previous one. This is the modern way to build LangChain pipelines.
from langchain_core.output_parsers import StrOutputParser# Build a chain: prompt | llm | output parserchain = prompt | llm | StrOutputParser()# Invoke the chainresult = chain.invoke({ 'domain': 'data engineering', 'question': 'What is Apache Spark?'})print(result) # Returns plain string, not a Message object
| LCEL chains are lazy — they don’t execute until .invoke(), .stream(), or .batch() is called. They also support async execution with .ainvoke() and parallel branches with RunnableParallel, making them production-ready from day one. |
5. Building Tools for Your Agent
Tools are functions the agent can call to interact with the world. LangChain tools are standard Python functions decorated with @tool — the decorator extracts the function name, docstring, and type hints to generate the tool’s schema automatically. The docstring is critical: the LLM reads it to decide when and how to use the tool.
5.1 — Creating a Custom Tool
from langchain_core.tools import tool# The docstring is what the LLM reads to decide when to use this tooltooldef calculate_compound_interest( principal: float, rate: float, years: int) -> str: ''' Calculate compound interest. Use this tool when the user asks about investment growth, compound interest, or future value of money. Args: principal: Initial investment amount in dollars rate: Annual interest rate as a decimal (e.g., 0.08 for 8%) years: Number of years to compound ''' amount = principal * (1 + rate) ** years interest = amount - principal return f'Future value: ${amount:,.2f} | Interest earned: ${interest:,.2f}'# Test the tool directlyprint(calculate_compound_interest.invoke({ 'principal': 10000, 'rate': 0.08, 'years': 10}))
OUTPUT: Tool output
| Input | Output |
| principal=10000, rate=0.08, years=10 | Future value: $21,589.25 | Interest earned: $11,589.25 |
5.2 — Using Built-in LangChain Tools
LangChain Community provides dozens of pre-built tools. Here are the most commonly used ones in agent workflows.
from langchain_community.tools import DuckDuckGoSearchRun, WikipediaQueryRunfrom langchain_community.utilities import WikipediaAPIWrapperfrom langchain.tools import tool# Web search toolsearch_tool = DuckDuckGoSearchRun()# Wikipedia toolwiki_tool = WikipediaQueryRun( api_wrapper=WikipediaAPIWrapper(top_k_results=2, doc_content_chars_max=500))# Test built-in toolsprint(search_tool.invoke('LangChain latest version 2026'))print(wiki_tool.invoke('Large Language Model'))
5.3 — Tool Schema
Every tool has a schema that LangChain uses to tell the LLM what tools are available and how to call them. You can inspect it at any time.
# Inspect a tool's schemaprint(calculate_compound_interest.name)# 'calculate_compound_interest'print(calculate_compound_interest.description)# 'Calculate compound interest. Use this tool when...'print(calculate_compound_interest.args_schema.schema())# {'properties': {'principal': {'type': 'number'}, 'rate': ..., 'years': ...}}
| Tool | Package | What It Does |
| DuckDuckGoSearchRun | langchain-community | Web search using DuckDuckGo — no API key required |
| WikipediaQueryRun | langchain-community | Query Wikipedia articles by topic |
| PythonREPLTool | langchain-community | Execute Python code in a sandboxed REPL |
| SQLDatabaseToolkit | langchain-community | Query SQL databases with natural language |
| FileManagementToolkit | langchain-community | Read, write, and manage local files |
| RequestsGetTool | langchain-community | Make HTTP GET requests to any URL |
| @tool (custom) | langchain-core | Decorate any Python function to make it a tool |
6. Adding Memory to Your Agent
By default, each agent invocation is stateless — the agent has no memory of previous interactions. For conversational agents, you need to persist the conversation history across turns. LangChain provides several memory options.
6.1 — In-Memory Conversation History
from langchain_core.chat_history import InMemoryChatMessageHistoryfrom langchain_core.runnables.history import RunnableWithMessageHistory# Store for conversation histories, keyed by session IDstore = {}def get_session_history(session_id: str): if session_id not in store: store[session_id] = InMemoryChatMessageHistory() return store[session_id]# Wrap your chain with message historychain_with_history = RunnableWithMessageHistory( chain, get_session_history, input_messages_key = 'input', history_messages_key= 'chat_history',)# Invoke with a session IDconfig = {'configurable': {'session_id': 'user_abc'}}response1 = chain_with_history.invoke({'input': 'My name is Alice.'}, config=config)response2 = chain_with_history.invoke({'input': 'What is my name?'}, config=config)print(response2) # Agent remembers: 'Your name is Alice.'
6.2 — Persistent Memory with Redis
# pip install langchain-redisfrom langchain_redis import RedisChatMessageHistorydef get_redis_history(session_id: str): return RedisChatMessageHistory( session_id = session_id, url = 'redis://localhost:6379' )chain_with_redis = RunnableWithMessageHistory( chain, get_redis_history, input_messages_key = 'input', history_messages_key= 'chat_history',)# Now conversation history survives server restarts
| For production agents, always use a persistent store like Redis or a database. In-memory history is lost when your server restarts. Use different session IDs for different users to keep their conversations isolated. |
7. Building a ReAct Agent
With tools and memory in place, you are ready to build the agent itself. LangChain’s create_react_agent function wires together the LLM, tools, and a ReAct prompt into a runnable agent.
7.1 — Define Tools and LLM
from langchain_openai import ChatOpenAIfrom langchain_community.tools import DuckDuckGoSearchRun, WikipediaQueryRunfrom langchain_community.utilities import WikipediaAPIWrapperfrom langchain_core.tools import tool# LLMllm = ChatOpenAI(model='gpt-4o', temperature=0)# Built-in toolssearch = DuckDuckGoSearchRun()wiki = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper(top_k_results=1))# Custom tooltooldef calculate(expression: str) -> str: '''Evaluate a mathematical expression. Use for any arithmetic or calculation. Input must be a valid Python math expression, e.g. '2 ** 10' or '100 * 0.15'.''' try: return str(eval(expression, {'__builtins__': {}}, {})) except Exception as e: return f'Error: {e}'# Collect all toolstools = [search, wiki, calculate]
7.2 — Create the Agent
from langchain.agents import create_react_agent, AgentExecutorfrom langchain import hub# Pull the standard ReAct prompt from LangChain Hub# This prompt instructs the LLM to follow Thought/Action/Observation formatreact_prompt = hub.pull('hwchase17/react')# Create the agent (LLM + tools + prompt wired together)agent = create_react_agent( llm = llm, tools = tools, prompt = react_prompt)# Wrap in AgentExecutor — this is the runtime loopagent_executor = AgentExecutor( agent = agent, tools = tools, verbose = True, # print each Thought/Action/Observation max_iterations = 10, # prevent infinite loops handle_parsing_errors = True)
7.3 — Run the Agent
| # Ask the agent a question that requires tool use result = agent_executor.invoke({ ‘input’: ‘What is the current population of India and what is 1.4 billion times 1.08?’ }) print(result[‘output’]) |
OUTPUT: Agent ReAct trace — Thought / Action / Observation loop
| Step | Type | Content |
| 1 | Thought | I need to find the current population of India. I’ll use the search tool. |
| 2 | Action | duckduckgo_search(‘current population of India 2026’) |
| 3 | Observation | India’s population is approximately 1.44 billion as of 2026… |
| 4 | Thought | Now I need to calculate 1.4 billion times 1.08. |
| 5 | Action | calculate(‘1_400_000_000 * 1.08’) |
| 6 | Observation | 1512000000.0 |
| 7 | Final Answer | India’s population is approximately 1.44 billion. 1.4 billion x 1.08 = 1,512,000,000. |
8. Tool Calling Agent (Modern Approach)
For models that support function/tool calling natively (GPT-4, Claude, Gemini), LangChain’s create_tool_calling_agent is more reliable than ReAct. The model returns structured JSON tool calls instead of parsing free-form text — eliminating parsing errors entirely.
from langchain.agents import create_tool_calling_agent, AgentExecutorfrom langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder# Prompt with placeholders for chat history and agent scratchpadprompt = ChatPromptTemplate.from_messages([ ('system', 'You are a helpful AI assistant with access to tools. ' 'Use tools whenever they would help answer the question accurately.'), MessagesPlaceholder(variable_name='chat_history', optional=True), ('human', '{input}'), MessagesPlaceholder(variable_name='agent_scratchpad'),])# Create the tool-calling agentagent = create_tool_calling_agent( llm = llm, tools = tools, prompt = prompt)agent_executor = AgentExecutor( agent = agent, tools = tools, verbose = True, max_iterations = 10)# Invokeresult = agent_executor.invoke({'input': 'Search for LangChain 2026 updates'})print(result['output'])
| Prefer create_tool_calling_agent over create_react_agent when your LLM supports it. Tool calling is faster (fewer tokens), more reliable (structured JSON output, no parsing failures), and produces cleaner traces in LangSmith. |
9. Conversational Agent with Memory
Combining the tool-calling agent with conversation memory gives you a stateful agent that remembers past interactions within a session.
from langchain_core.chat_history import InMemoryChatMessageHistoryfrom langchain_core.runnables.history import RunnableWithMessageHistory# Session storesession_store = {}def get_history(session_id: str): if session_id not in session_store: session_store[session_id] = InMemoryChatMessageHistory() return session_store[session_id]# Wrap the executor with message historyagent_with_memory = RunnableWithMessageHistory( agent_executor, get_history, input_messages_key = 'input', history_messages_key = 'chat_history',)config = {'configurable': {'session_id': 'session_001'}}# Turn 1r1 = agent_with_memory.invoke( {'input': 'My name is Alice and I am interested in investing $50,000.'}, config=config)print(r1['output'])# Turn 2 — agent remembers context from Turn 1r2 = agent_with_memory.invoke( {'input': 'What would my investment be worth in 10 years at 8% interest?'}, config=config)print(r2['output'])# Agent uses calculate tool with principal=50000, rate=0.08, years=10# And addresses the user as Alice
10. Customising Agent Behaviour with System Prompts
The system prompt is how you give your agent a personality, scope, and set of rules. A well-crafted system prompt transforms a general-purpose agent into a domain expert.
SYSTEM_PROMPT = '''You are FinBot, an expert financial analysis assistant.Your capabilities:- Search for current financial news and market data- Perform precise financial calculations- Explain complex financial concepts in simple termsYour rules:- Always use tools to verify current data before answering- Never fabricate financial figures — if unsure, search first- Present numbers in a clear, formatted way- Always include a disclaimer that this is not financial adviceRespond concisely and professionally.'''prompt = ChatPromptTemplate.from_messages([ ('system', SYSTEM_PROMPT), MessagesPlaceholder(variable_name='chat_history', optional=True), ('human', '{input}'), MessagesPlaceholder(variable_name='agent_scratchpad'),])finbot = create_tool_calling_agent(llm=llm, tools=tools, prompt=prompt)finbot_executor = AgentExecutor(agent=finbot, tools=tools, verbose=True)
11. Error Handling and Guardrails
Production agents need robust error handling. Tools can fail, the LLM can produce unparseable output, and loops can run indefinitely. LangChain provides several mechanisms to handle these gracefully.
# 1. Handle tool errors gracefullytooldef safe_search(query: str) -> str: '''Search the web for current information.''' try: result = DuckDuckGoSearchRun().run(query) return result if result else 'No results found for this query.' except Exception as e: return f'Search failed: {str(e)}. Try rephrasing the query.'# 2. Limit iterations and handle errors in AgentExecutoragent_executor = AgentExecutor( agent = agent, tools = tools, max_iterations = 8, # stop after 8 steps max_execution_time = 60, # stop after 60 seconds handle_parsing_errors = True, # recover from LLM output parse errors early_stopping_method = 'generate', # generate a final answer if limit hit)# 3. Catch executor-level errorstry: result = agent_executor.invoke({'input': user_query})except Exception as e: result = {'output': f'Agent encountered an error: {str(e)}'}
| Parameter | Default | Purpose |
| max_iterations | 15 | Stop the agent loop after N steps — prevents infinite loops |
| max_execution_time | None | Stop after N seconds — important for production timeouts |
| handle_parsing_errors | False | If True, retry when the LLM output can’t be parsed |
| early_stopping_method | ‘force’ | ‘generate’ asks the LLM for a final answer; ‘force’ stops abruptly |
| return_intermediate_steps | False | If True, returns each Thought/Action/Observation in the output |
12. Complete Working Agent — End to End
Here is a complete, self-contained agent you can run immediately. It combines all the concepts: tool-calling agent, multiple tools, conversation memory, system prompt, and error handling.
import osfrom langchain_openai import ChatOpenAIfrom langchain_community.tools import DuckDuckGoSearchRun, WikipediaQueryRunfrom langchain_community.utilities import WikipediaAPIWrapperfrom langchain_core.tools import toolfrom langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholderfrom langchain_core.chat_history import InMemoryChatMessageHistoryfrom langchain_core.runnables.history import RunnableWithMessageHistoryfrom langchain.agents import create_tool_calling_agent, AgentExecutoros.environ['OPENAI_API_KEY'] = 'your-api-key'# ── LLM ──llm = ChatOpenAI(model='gpt-4o', temperature=0)# ── TOOLS ──search = DuckDuckGoSearchRun()wiki = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper(top_k_results=1))tooldef calculate(expression: str) -> str: '''Evaluate a Python math expression. Input: valid Python expression string.''' try: return str(eval(expression, {'__builtins__': {}}, {})) except Exception as e: return f'Calculation error: {e}'tools = [search, wiki, calculate]# ── PROMPT ──prompt = ChatPromptTemplate.from_messages([ ('system', 'You are a helpful AI assistant. Use tools when needed.'), MessagesPlaceholder('chat_history', optional=True), ('human', '{input}'), MessagesPlaceholder('agent_scratchpad'),])# ── AGENT ──agent = create_tool_calling_agent(llm=llm, tools=tools, prompt=prompt)executor = AgentExecutor( agent=agent, tools=tools, verbose=True, max_iterations=10, handle_parsing_errors=True)# ── MEMORY ──store = {}def get_history(sid): store.setdefault(sid, InMemoryChatMessageHistory()) return store[sid]agent = RunnableWithMessageHistory( executor, get_history, input_messages_key='input', history_messages_key='chat_history')# ── RUN ──cfg = {'configurable': {'session_id': 'demo'}}while True: user_input = input('You: ') if user_input.lower() in ['quit', 'exit']: break response = agent.invoke({'input': user_input}, config=cfg) print(f'Agent: {response["output"]}')
13. Agent Strategies at a Glance
| Strategy | Function | Best For | Requires |
| ReAct | create_react_agent | Any LLM; text-based reasoning loop | hwchase17/react prompt |
| Tool Calling | create_tool_calling_agent | GPT-4, Claude, Gemini; structured JSON calls | MessagesPlaceholder |
| OpenAI Funcs | create_openai_functions_agent | OpenAI models only; legacy approach | OpenAI functions API |
| Structured | create_structured_chat_agent | Complex multi-input tools | Structured chat prompt |
| Common Mistake | What Goes Wrong | Fix |
| Vague tool docstrings | LLM doesn’t know when to use the tool | Write clear, specific docstrings describing exactly when to use it |
| No max_iterations limit | Agent loops forever on hard questions | Always set max_iterations (8-15 is usually sufficient) |
| Using in-memory history in prod | History lost on restart | Use Redis or a database-backed history store |
| Trusting LLM math | LLM arithmetic is unreliable | Always provide a calculate tool; never let the LLM do arithmetic alone |
| Temperature > 0 for agents | Non-deterministic tool selection | Set temperature=0 for agents that need consistent, predictable behaviour |
| No error handling on tools | One tool failure crashes the agent | Wrap tool logic in try/except and return descriptive error strings |
14. Conclusion
LangChain makes building AI agents accessible without sacrificing control. The framework’s composable design — LLMs, tools, memory, prompts, and executor all as independent, swappable components — means you can start simple and scale to production without rewriting your architecture.
The key principles to carry forward: write clear tool docstrings so the LLM knows when to use each tool; always set max_iterations to prevent runaway loops; use create_tool_calling_agent over ReAct for models that support it; enable LangSmith tracing from day one; and use persistent memory for any agent that needs to remember across sessions.
AI agents are not just a technical pattern — they represent a new way of building software where language models drive logic, tools extend capability, and memory provides continuity. LangChain gives you all the building blocks. The rest is up to what you build.
Happy Building!
Discover more from DataSangyan
Subscribe to get the latest posts sent to your email.