Follow AiTechWorlds on LinkedIn for professional AI content!Follow Now →

LangChain Tutorial 2025: Build AI Applications with Chains and Agents

LangChain tutorial 2025 — learn chains, agents, memory, and RAG with practical Python examples for building production AI applications from scratch.

A
AiTechWorlds Team
May 27, 2026 7 min read
📱

Get more content like this on Telegram!

Daily AI tips, notes & resources — free

Join Free →

LangChain Tutorial 2025: Build AI Applications with Chains and Agents

When I first encountered LangChain, I was skeptical. Why abstract over an API that's already simple? After building a few real applications, the answer became clear: the complexity isn't the API call — it's everything around it. Managing conversation history, connecting to vector databases, chaining multiple LLM calls, building agents with tools.

LangChain handles this scaffolding. By 2025, it's the most widely used framework for AI application development, with a much cleaner architecture than earlier versions thanks to LangChain Expression Language (LCEL).

This tutorial covers the components you'll actually use in production, with working code for each.


Installation and Setup

# Core LangChain packages (modular in v0.2+)
pip install langchain langchain-openai langchain-anthropic langchain-community
pip install langchain-chroma  # Vector store
pip install langgraph          # For agents
pip install langsmith           # Observability (optional)
import os
os.environ["OPENAI_API_KEY"] = "your-key"
# Or use python-dotenv: from dotenv import load_dotenv; load_dotenv()

Core Concept: LangChain Expression Language (LCEL)

LCEL composes chains with the pipe operator:

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

# Components
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
output_parser = StrOutputParser()

# Simple chain: prompt | llm | parser
prompt = ChatPromptTemplate.from_messages([
    ("system", "You are an expert in {domain}."),
    ("human", "{question}")
])

chain = prompt | llm | output_parser

# Invoke
result = chain.invoke({
    "domain": "Python",
    "question": "What are the best practices for error handling?"
})
print(result)

# Stream
for chunk in chain.stream({"domain": "machine learning", "question": "Explain overfitting"}):
    print(chunk, end="", flush=True)

# Batch (parallel execution)
results = chain.batch([
    {"domain": "Python", "question": "What is a generator?"},
    {"domain": "SQL", "question": "When should I use indexes?"},
    {"domain": "Docker", "question": "What is a multi-stage build?"},
])

Prompts and Templates

from langchain_core.prompts import (
    ChatPromptTemplate,
    FewShotChatMessagePromptTemplate,
    MessagesPlaceholder
)

# Few-shot prompting
examples = [
    {"input": "happy", "output": "sad"},
    {"input": "tall", "output": "short"},
    {"input": "energetic", "output": "lethargic"},
]

example_prompt = ChatPromptTemplate.from_messages([
    ("human", "{input}"),
    ("ai", "{output}"),
])

few_shot_prompt = FewShotChatMessagePromptTemplate(
    example_prompt=example_prompt,
    examples=examples,
)

final_prompt = ChatPromptTemplate.from_messages([
    ("system", "You are an antonyms generator."),
    few_shot_prompt,
    ("human", "{input}"),
])

chain = final_prompt | llm | output_parser
print(chain.invoke({"input": "big"}))  # "small"

# Dynamic prompts with message history
with_history_prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant."),
    MessagesPlaceholder(variable_name="history"),  # Inject message history
    ("human", "{input}"),
])

Conversation Memory with Message History

from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory

# Session-based chat history
store = {}

def get_session_history(session_id: str) -> InMemoryChatMessageHistory:
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant."),
    MessagesPlaceholder(variable_name="history"),
    ("human", "{input}"),
])

chain = prompt | llm | output_parser

# Wrap with message history
chain_with_history = RunnableWithMessageHistory(
    chain,
    get_session_history,
    input_messages_key="input",
    history_messages_key="history",
)

# Invoke with session tracking
config = {"configurable": {"session_id": "user-123"}}

print(chain_with_history.invoke({"input": "Hi, my name is Alice."}, config=config))
print(chain_with_history.invoke({"input": "What's my name?"}, config=config))
# Output: "Your name is Alice, as you told me!"

RAG Chain

from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_community.document_loaders import WebBaseLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_core.runnables import RunnablePassthrough

# 1. Load and index documents
loader = WebBaseLoader("https://example.com/docs")
docs = loader.load()

splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = splitter.split_documents(docs)

vectorstore = Chroma.from_documents(splits, OpenAIEmbeddings())
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})

# 2. RAG prompt
rag_prompt = ChatPromptTemplate.from_messages([
    ("system", """Answer the question using only the provided context.
If the answer isn't in the context, say "I don't have that information."

Context: {context}"""),
    ("human", "{question}")
])

def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

# 3. RAG chain with LCEL
rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | rag_prompt
    | llm
    | output_parser
)

answer = rag_chain.invoke("What are the main features?")
print(answer)

# With sources
from langchain_core.runnables import RunnableParallel

rag_chain_with_sources = RunnableParallel(
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
).assign(answer=rag_prompt | llm | output_parser)

result = rag_chain_with_sources.invoke("What are the main features?")
print(f"Answer: {result['answer']}")

Tool Use and Agents

from langchain.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent

# Define tools
@tool
def search_web(query: str) -> str:
    """Search the web for current information."""
    # In practice: integrate with SerpAPI, Tavily, etc.
    return f"Search results for: {query}"

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

@tool
def get_weather(city: str) -> str:
    """Get current weather for a city."""
    # In practice: call a weather API
    return f"Weather in {city}: 72°F, partly cloudy"

tools = [search_web, calculate, get_weather]

# Create agent with LangGraph (modern approach)
model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
agent = create_react_agent(model, tools)

# Run the agent
response = agent.invoke({
    "messages": [("human", "What is the square root of 144, and what's the weather like in Paris?")]
})

for message in response["messages"]:
    print(f"{message.type}: {message.content}")

Structured Output

from pydantic import BaseModel, Field
from typing import List

class MovieReview(BaseModel):
    title: str = Field(description="Movie title")
    rating: float = Field(description="Rating from 1-10")
    pros: List[str] = Field(description="Positive aspects")
    cons: List[str] = Field(description="Negative aspects")
    summary: str = Field(description="One-sentence summary")

# Structured output with Pydantic
structured_llm = llm.with_structured_output(MovieReview)

review = structured_llm.invoke(
    "Review the movie 'Inception' by Christopher Nolan"
)

print(f"Title: {review.title}")
print(f"Rating: {review.rating}/10")
print(f"Pros: {review.pros}")
print(f"Summary: {review.summary}")

LangSmith Observability

import os
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "your-langsmith-key"
os.environ["LANGCHAIN_PROJECT"] = "my-chatbot"

# All LangChain calls are now traced to LangSmith dashboard
# View: latency, token usage, inputs/outputs, errors
# No code changes needed beyond setting environment variables

chain = prompt | llm | output_parser
result = chain.invoke({"question": "What is machine learning?"})
# This call appears in your LangSmith project dashboard

Conclusion

LangChain's LCEL architecture makes AI application composition genuinely elegant — the pipe operator creates readable, composable pipelines. For production systems, the most valuable parts are the retriever integrations, memory management, and LangSmith observability.

Start simple: use LCEL chains for most tasks, add agents only when you genuinely need dynamic tool selection. Most AI applications don't need agents — they need well-designed chains.

For the RAG component that makes chatbots know your data, see our RAG system tutorial. For deploying LangChain applications, see our production deployment guide.


Frequently Asked Questions

What is LangChain and what is it used for?

LangChain is a Python framework for building LLM applications. It provides abstractions for chaining LLM calls, conversation memory, connecting to vector databases, and building AI agents. Key use cases: RAG systems, conversational AI, multi-step workflows, AI agents with tools.

What is LCEL?

LangChain Expression Language — compose chains with the pipe operator (|). prompt | llm | output_parser creates a chain where each step's output becomes the next step's input. Built-in streaming, batch execution, and parallel processing. The modern recommended approach for building LangChain applications.

How do LangChain agents work?

Agents use an LLM to decide which tools to call and when. The LLM sees available tools, the user's request, and decides what to do. The framework executes tool calls, returns results to the LLM, and repeats until the LLM produces a final answer. Modern LangChain uses LangGraph for complex agent workflows.

What is the difference between LangChain and LlamaIndex?

LangChain is broader: covers agents, chains, tools, and RAG. LlamaIndex focuses on data ingestion and complex retrieval. Many production systems use both: LlamaIndex for retrieval, LangChain for the application layer.

Is LangChain good for production?

Yes, with caveats: pin to specific versions (frequent breaking changes), don't use abstractions where plain SDK calls are simpler, use LangSmith for observability. The retrieval integrations and agent framework genuinely save production engineering time.

Share this article:

Frequently Asked Questions

LangChain is an open-source Python (and JavaScript) framework for building applications powered by large language models. It provides abstractions for common AI application patterns: chaining LLM calls together, adding memory to conversations, connecting to external data sources, and building AI agents that use tools. Key use cases: RAG systems (question-answering over documents), conversational AI with memory, multi-step workflows, AI agents that browse the web or query databases. LangChain has evolved significantly — LangChain v0.2+ uses a more modular architecture with LangChain Expression Language (LCEL) for composable pipelines.
A

AiTechWorlds Team

✓ Verified Writer

The AiTechWorlds team is passionate about AI, technology, and education. We create high-quality, research-backed content to help you learn, grow, and succeed in the modern digital world.

Related Articles

10K+ Members Growing Daily

Get Free AI Notes Daily

Join AiTechWorlds on Telegram and get daily AI tips, prompt engineering templates, coding resources, and exclusive content — 100% free!

📚 Free Study Notes🤖 AI Tips Daily⚡ Prompt Templates💻 Coding Resources
Join Free Channel

No spam. Leave anytime.

!