LangChain Tutorial: The Most Popular Framework for Building LLM Applications
LangChain is an open-source framework designed to simplify the development of applications powered by Large Language Models (LLMs). With a mature ecosystem and a large community, LangChain has become the go-to choice for developers building chatbots, RAG systems, AI agents, and various other AI applications. This tutorial covers LangChain comprehensively, from installation to advanced usage patterns.
Why LangChain?
Building LLM applications from scratch requires significant boilerplate code: managing prompts, connecting to various model providers, implementing memory, orchestrating chains of thought, and much more. LangChain simplifies all of this by providing consistent and modular abstractions.
Key advantages of LangChain:
- Consistent Model Abstraction: A single interface for OpenAI, Anthropic, Google, Ollama, and dozens of other providers
- Composable Chains: Build complex pipelines by combining simple components
- Complete Ecosystem: Integrations with vector databases, document loaders, tools, and external services
- LangChain Expression Language (LCEL): Declarative syntax for building chains that natively support streaming and async
- Large Community: Thousands of third-party integrations and comprehensive documentation
Installation and Setup
Basic Installation
pip install langchain langchain-core langchain-community
Provider-Specific Installation
# For OpenAI
pip install langchain-openai
For Anthropic (Claude)
pip install langchain-anthropic
For Google (Gemini)
pip install langchain-google-genai
For local models via Ollama
pip install langchain-ollama
For vector stores
pip install langchain-chroma # ChromaDB
pip install langchain-pinecone # Pinecone
pip install faiss-cpu # FAISS
API Key Configuration
import os
Set API keys as environment variables
os.environ["OPENAIAPIKEY"] = "sk-..."
os.environ["ANTHROPICAPIKEY"] = "sk-ant-..."
Or use .env file with python-dotenv
from dotenv import loaddotenv
loaddotenv()
Core Concepts
1. Chat Models
Chat models are the core component of LangChain. Here's how to use various providers:
from langchainopenai import ChatOpenAI
from langchain
anthropic import ChatAnthropic
from langchainollama import ChatOllama
OpenAI GPT-4
llmopenai = ChatOpenAI(model="gpt-4o", temperature=0.7)
Anthropic Claude
llmclaude = ChatAnthropic(model="claude-sonnet-4-20250514", temperature=0.7)
Ollama (local models)
llmollama = ChatOllama(model="llama3.1")
Call the model
response = llmopenai.invoke("Explain what machine learning is in 2 sentences")
print(response.content)
2. Prompt Templates
Prompt templates allow you to create dynamic and reusable prompts:
from langchaincore.prompts import ChatPromptTemplate, MessagesPlaceholder
Simple prompt
prompt = ChatPromptTemplate.frommessages([
("system", "You are an AI assistant specialized in {domain}."),
("human", "{question}")
])
Use the prompt
formatted = prompt.invoke({
"domain": "data science",
"question": "What is the difference between supervised and unsupervised learning?"
})
print(formatted)
3. Output Parsers
Output parsers help transform LLM outputs into structured formats:
from langchaincore.outputparsers import JsonOutputParser, StrOutputParser
from langchain
core.pydanticv1 import BaseModel, Field
String output parser (simplest)
parser = StrOutputParser()
JSON output parser with schema
class BookReview(BaseModel):
title: str = Field(description="Book title")
rating: int = Field(description="Rating 1-5")
summary: str = Field(description="Review summary")
json
parser = JsonOutputParser(pydanticobject=BookReview)
prompt = ChatPromptTemplate.from
messages([
("system", "Provide a book review in JSON format.\n{formatinstructions}"),
("human", "Review the book: {booktitle}")
])
chain = prompt | llmopenai | jsonparser
result = chain.invoke({
"booktitle": "Clean Code",
"formatinstructions": jsonparser.getformatinstructions()
})
print(result) # {'title': 'Clean Code', 'rating': 5, 'summary': '...'}
LangChain Expression Language (LCEL)
LCEL is the modern way to build chains in LangChain. Using the pipe operator (|), you can combine components declaratively:
from langchainopenai import ChatOpenAI
from langchaincore.prompts import ChatPromptTemplate
from langchaincore.outputparsers import StrOutputParser
Define a chain with LCEL
chain = (
ChatPromptTemplate.frommessages([
("system", "You are a professional translator."),
("human", "Translate the following text to {language}: {text}")
])
| ChatOpenAI(model="gpt-4o-mini")
| StrOutputParser()
)
Invoke (synchronous)
result = chain.invoke({
"language": "Japanese",
"text": "Good morning, how are you?"
})
print(result)
Streaming
for chunk in chain.stream({
"language": "Japanese",
"text": "Good morning, how are you?"
}):
print(chunk, end="", flush=True)
Batch processing
results = chain.batch([
{"language": "Japanese", "text": "Hello"},
{"language": "Korean", "text": "Thank you"},
{"language": "Mandarin", "text": "Goodbye"},
])
RunnablePassthrough and RunnableParallel
from langchaincore.runnables import RunnablePassthrough, RunnableParallel
RunnableParallel - run multiple chains in parallel
analysis
chain = RunnableParallel(
sentiment=ChatPromptTemplate.fromtemplate(
"Analyze the sentiment of this text (positive/negative/neutral): {text}"
) | llmopenai | StrOutputParser(),
summary=ChatPromptTemplate.fromtemplate(
"Summarize this text in one sentence: {text}"
) | llmopenai | StrOutputParser(),
language=ChatPromptTemplate.fromtemplate(
"Detect the language of this text: {text}"
) | llmopenai | StrOutputParser(),
original=RunnablePassthrough()
)
result = analysischain.invoke({
"text": "This product is amazing, I'm very satisfied with the quality!"
})
print(result)
{
'sentiment': 'positive',
'summary': 'Customer is very satisfied with product quality.',
'language': 'English',
'original': {'text': 'This product is amazing...'}
}
Retrieval-Augmented Generation (RAG)
RAG is one of the most popular use cases in LangChain. Here's a complete implementation:
Setting Up the Vector Store
from langchaincommunity.documentloaders import PyPDFLoader, TextLoader
from langchain.text
splitter import RecursiveCharacterTextSplitter
from langchainopenai import OpenAIEmbeddings
from langchainchroma import Chroma
1. Load documents
loader = PyPDFLoader("companyreport.pdf")
documents = loader.load()
2. Split documents into chunks
textsplitter = RecursiveCharacterTextSplitter(
chunksize=1000,
chunkoverlap=200,
separators=["\n\n", "\n", ".", " "]
)
chunks = textsplitter.splitdocuments(documents)
3. Create embeddings and store in vector store
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.fromdocuments(
documents=chunks,
embedding=embeddings,
persistdirectory="./chromadb"
)
4. Create retriever
retriever = vectorstore.asretriever(
searchtype="similarity",
searchkwargs={"k": 5}
)
RAG Chain
from langchaincore.prompts import ChatPromptTemplate
from langchain
core.runnables import RunnablePassthrough
from langchaincore.outputparsers import StrOutputParser
RAG template
ragprompt = ChatPromptTemplate.frommessages([
("system", """You are an assistant that answers questions based on the provided context.
Answer ONLY based on the context. If the information is not in the context, say "Information not found."
Context:
{context}"""),
("human", "{question}")
])
def formatdocs(docs):
return "\n\n".join(doc.pagecontent for doc in docs)
RAG Chain with LCEL
ragchain = (
{
"context": retriever | formatdocs,
"question": RunnablePassthrough()
}
| ragprompt
| llmopenai
| StrOutputParser()
)
Ask questions
answer = ragchain.invoke("What was the company's total revenue last year?")
print(answer)
Memory and Conversation History
LangChain provides several ways to manage conversation history:
from langchaincore.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchaincore.messages import HumanMessage, AIMessage
from langchaincommunity.chatmessagehistories import ChatMessageHistory
from langchaincore.runnables.history import RunnableWithMessageHistory
Setup prompt with history
prompt = ChatPromptTemplate.frommessages([
("system", "You are a friendly and helpful AI assistant."),
MessagesPlaceholder(variablename="history"),
("human", "{input}")
])
chain = prompt | llmopenai | StrOutputParser()
Store for managing per-session history
store = {}
def getsessionhistory(sessionid: str):
if sessionid not in store:
store[sessionid] = ChatMessageHistory()
return store[sessionid]
Chain with memory
chainwithhistory = RunnableWithMessageHistory(
chain,
getsessionhistory,
inputmessageskey="input",
historymessageskey="history"
)
Multi-turn conversation
config = {"configurable": {"sessionid": "user123"}}
response1 = chainwithhistory.invoke(
{"input": "My name is Alex, I'm a data engineer"},
config=config
)
print(response1)
response2 = chainwithhistory.invoke(
{"input": "What's my name and what do I do?"},
config=config
)
print(response2) # The model will remember the name and occupation
Tool Calling and AI Agents
Defining Tools
from langchaincore.tools import tool
from langchainopenai import ChatOpenAI
@tool
def getweather(city: str) -> str:
"""Get weather information for a specific city."""
return f"Weather in {city}: Sunny, 28°C, Humidity 65%"
@tool
def calculate(expression: str) -> str:
"""Calculate a mathematical expression."""
try:
result = eval(expression)
return f"Result: {result}"
except Exception as e:
return f"Error: {str(e)}"
@tool
def searchdatabase(query: str) -> str:
"""Search for information in the company database."""
data = {
"revenue": "Total revenue Q1 2026: $3.2 Million",
"employees": "Number of employees: 150",
"products": "3 main products: AI Platform, Data Consulting, Training"
}
for key, value in data.items():
if key in query.lower():
return value
return "Data not found"
Bind tools to model
llmwithtools = ChatOpenAI(model="gpt-4o").bindtools(
[getweather, calculate, searchdatabase]
)
Building an Agent with LangGraph
from langgraph.prebuilt import createreactagent
Create a ReAct agent
agent = createreactagent(
model=ChatOpenAI(model="gpt-4o"),
tools=[getweather, calculate, searchdatabase]
)
Run the agent
result = agent.invoke({
"messages": [("human", "What's the weather in Tokyo and calculate 15% of the company revenue")]
})
for message in result["messages"]:
print(f"{message.type}: {message.content}")
Advanced Usage
Custom Chain with Branching Logic
from langchaincore.runnables import RunnableBranch
Different chains based on input language
classification
chain = (
ChatPromptTemplate.fromtemplate(
"Classify the language of this text (english/spanish/other): {text}\nAnswer in one word only."
)
| llmopenai
| StrOutputParser()
)
englishchain = (
ChatPromptTemplate.fromtemplate("Summarize this English text: {text}")
| llmopenai
| StrOutputParser()
)
spanishchain = (
ChatPromptTemplate.fromtemplate("Resume este texto en español: {text}")
| llmopenai
| StrOutputParser()
)
branch = RunnableBranch(
(lambda x: "english" in x["language"].lower(), englishchain),
(lambda x: "spanish" in x["language"].lower(), spanishchain),
englishchain # default
)
fullchain = (
RunnableParallel(
text=RunnablePassthrough(),
language=lambda x: classificationchain.invoke(x)
)
| branch
)
Structured Output with Pydantic
from pydantic import BaseModel, Field
from typing import List, Optional
class CompanyAnalysis(BaseModel):
companyname: str = Field(description="Company name")
industry: str = Field(description="Company industry")
strengths: List[str] = Field(description="Key strengths")
weaknesses: List[str] = Field(description="Key weaknesses")
recommendation: str = Field(description="Recommendation")
confidencescore: float = Field(description="Confidence score 0-1")
Use withstructuredoutput
structuredllm = ChatOpenAI(model="gpt-4o").withstructuredoutput(CompanyAnalysis)
result = structuredllm.invoke(
"Analyze Tesla based on current market conditions"
)
print(f"Company: {result.companyname}")
print(f"Industry: {result.industry}")
print(f"Strengths: {result.strengths}")
print(f"Score: {result.confidencescore}")
Callbacks and Observability
from langchaincore.callbacks import BaseCallbackHandler
from langchaincore.outputs import LLMResult
class CustomCallback(BaseCallbackHandler):
def onllmstart(self, serialized, prompts, kwargs):
print(f"[LLM Start] Model: {serialized.get('name', 'unknown')}")
def onllmend(self, response: LLMResult, kwargs):
tokenusage = response.llmoutput.get("tokenusage", {})
print(f"[LLM End] Tokens: {tokenusage}")
def ontoolstart(self, serialized, inputstr, kwargs):
print(f"[Tool Start] {serialized.get('name')}: {inputstr}")
def ontoolend(self, output, kwargs):
print(f"[Tool End] Output: {output}")
Use the callback
llm = ChatOpenAI(model="gpt-4o", callbacks=[CustomCallback()])
response = llm.invoke("Hello, how are you?")
Best Practices
1. Use the Right Model for the Right Task
# Small model for simple classification
classifier = ChatOpenAI(model="gpt-4o-mini", temperature=0)
Large model for complex reasoning
reasoner = ChatOpenAI(model="gpt-4o", temperature=0.2)
Local model for prototyping
localllm = ChatOllama(model="llama3.1")
2. Implement Error Handling
from tenacity import retry, stopafterattempt, waitexponential
@retry(stop=stop
afterattempt(3), wait=waitexponential(min=1, max=10))
def invokewithretry(chain, inputdata):
return chain.invoke(inputdata)
3. Optimize Token Usage
import tiktoken
def counttokens(text: str, model: str = "gpt-4o") -> int:
encoding = tiktoken.encodingformodel(model)
return len(encoding.encode(text))
def trimcontext(docs, maxtokens=3000):
total = 0
trimmed = []
for doc in docs:
tokens = counttokens(doc.pagecontent)
if total + tokens > maxtokens:
break
trimmed.append(doc)
total += tokens
return trimmed
4. Use Caching
from langchaincore.globals import setllmcache
from langchain
community.cache import InMemoryCache, SQLiteCache
In-memory cache (for development)
setllmcache(InMemoryCache())
SQLite cache (for production)
setllmcache(SQLiteCache(databasepath=".langchaincache.db"))
Now identical responses will be cached
llm = ChatOpenAI(model="gpt-4o")
response1 = llm.invoke("What is AI?") # Hits API
response2 = llm.invoke("What is AI?") # From cache (instant)
5. Async for High Performance
import asyncio
async def processbatchasync(texts: list[str]):
chain = (
ChatPromptTemplate.fromtemplate("Summarize: {text}")
| ChatOpenAI(model="gpt-4o-mini")
| StrOutputParser()
)
tasks = [chain.ainvoke({"text": t}) for t in texts]
results = await asyncio.gather(*tasks)
return results
Run
texts = ["Long text 1...", "Long text 2...", "Long text 3..."]
results = asyncio.run(processbatch_async(texts))
Conclusion
LangChain is a powerful framework for building LLM-powered applications. Key takeaways:
LangChain evolves rapidly. Always check the official documentation at python.langchain.com for the latest features. For more complex agent orchestration, consider exploring LangGraph, which is part of the LangChain ecosystem.