LangChain Tutorial for Beginners: Build Your First LLM App
Learn LangChain from scratch. Install the library, write your first LLMChain, and build a real LLM app with PromptTemplate and ChatOpenAI in Python.
Get more content like this on Telegram!
Daily AI tips, notes & resources — free
I remember the first time I tried to build something with an LLM. I had the OpenAI API key. I had Python. I had a vague idea about chatbots. What I did not have was any idea how to connect everything into something that actually worked reliably. I spent three hours writing string formatting code that LangChain would have handled in five lines.
That's the honest pitch for LangChain. It's not magic. It doesn't make models smarter. What it does is give you a structured way to build apps on top of LLMs so you're not reinventing the wheel every single time.
This tutorial walks you through everything from installation to your first working LLM application. We'll cover PromptTemplates, ChatOpenAI, and LLMChain. By the end, you'll have a working app and a clear mental model of how LangChain fits together.
If you want to go deeper after this, check out the LangChain tutorial 2025 guide we published earlier, or jump straight to building an AI agent with LangChain once you're comfortable with the basics.
What Is LangChain and Why Should You Care
LangChain is an open-source Python (and JavaScript) framework for building applications with large language models. The core idea is that most LLM apps share common patterns — you format a prompt, send it to a model, parse the output, maybe store some context, maybe call a tool. LangChain gives you pre-built components for all of that.
The project started in late 2022, gained massive traction through 2023 and 2024, and has settled into being the de-facto standard framework for production LLM applications. According to the LangChain GitHub repository, the project had over 90,000 stars as of early 2026, making it one of the most starred AI-focused repositories on GitHub.
The framework is organized around a few core abstractions:
- Models — wrappers around LLMs (OpenAI, Anthropic, local models via Ollama, etc.)
- Prompts — PromptTemplates that let you parametrize your instructions
- Chains — sequences that connect models and other components
- Memory — components that persist context across calls
- Agents — systems that can decide which tools to call
- Tools — functions that agents can invoke (search, calculators, APIs)
You don't need all of these to get started. You need three: a model, a prompt, and a chain.
How LangChain Fits Into the Modern AI Stack
The AI developer stack in 2026 has gotten opinionated. You have your LLM provider (OpenAI, Anthropic, Mistral, local), your orchestration layer (LangChain, LlamaIndex, raw SDK), and your infrastructure (vector databases, caches, observability tools). LangChain lives in that middle orchestration layer.
What makes it worth the abstraction overhead is composability. You can swap one model for another without rewriting your prompt logic. You can add memory to a chain with two lines of code. You can plug in a vector store retriever and suddenly your LLM can answer questions about your private documents. None of those things are trivially easy without a framework.
Setting Up Your Environment
Before writing any code, let's get the environment right. I'm going to assume you have Python 3.10 or newer. If you're on 3.9, most things will work but you might hit minor compatibility issues.
Installing LangChain
pip install langchain langchain-openai langchain-community python-dotenv
The split between langchain and langchain-openai is intentional. The core library is model-agnostic. The langchain-openai package adds the OpenAI-specific integrations. langchain-community adds hundreds of third-party integrations. python-dotenv handles your API keys securely.
Setting Up Your API Key
Create a .env file in your project root:
OPENAI_API_KEY=your_actual_key_here
Never hardcode keys. Never commit .env to git. Add .env to your .gitignore right now before you forget.
from dotenv import load_dotenv
import os
load_dotenv()
# Verify it loaded
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
raise ValueError("OPENAI_API_KEY not found. Check your .env file.")
print("API key loaded successfully")
Verifying the Installation
import langchain
import langchain_openai
print(f"LangChain version: {langchain.__version__}")
print(f"LangChain OpenAI version: {langchain_openai.__version__}")
If those print without errors, you're ready.
Your First LLM Call with ChatOpenAI
Let's start with the absolute minimum — just talking to a model.
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
# Initialize the model
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0.7,
max_tokens=500
)
# Make a simple call
response = llm.invoke([HumanMessage(content="What is the capital of France?")])
print(response.content)
# Output: Paris is the capital of France.
This is the raw LangChain equivalent of calling openai.chat.completions.create(). The invoke method takes a list of messages and returns an AIMessage object. The .content attribute gives you the string response.
The temperature parameter controls creativity — 0 is deterministic and consistent, 1.0 is more creative and variable. For factual tasks, keep it low. For creative writing, push it up.
Understanding the Message Types
LangChain uses typed messages rather than raw dictionaries. This is one of those design choices that seems fussy at first but saves you debugging time later.
from langchain_core.messages import (
HumanMessage, # User input
AIMessage, # Model response
SystemMessage, # System prompt / instructions
)
messages = [
SystemMessage(content="You are a helpful assistant who speaks like a pirate."),
HumanMessage(content="Tell me about Python programming."),
]
response = llm.invoke(messages)
print(response.content)
# "Arrr, Python be a fine language for any swashbuckling developer..."
For a deep dive on all message types and when to use each one, see our guide on LangChain message types — it covers HumanMessage, AIMessage, SystemMessage, and the newer ToolMessage type used in function calling.
PromptTemplates: The Right Way to Build Prompts
Hard-coding prompts is how beginner projects start and how production systems break. The moment you need to insert dynamic values — a user's name, a document chunk, a product category — you need a template.
LangChain's ChatPromptTemplate handles this cleanly:
from langchain_core.prompts import ChatPromptTemplate
# Create a template with variables
template = ChatPromptTemplate.from_messages([
("system", "You are an expert in {domain}. Keep responses concise and practical."),
("human", "Explain {concept} to someone who has {experience} experience.")
])
# Format the template with actual values
messages = template.format_messages(
domain="machine learning",
concept="gradient descent",
experience="zero"
)
print(messages)
# [SystemMessage(content='You are an expert in machine learning...'),
# HumanMessage(content='Explain gradient descent to someone who has zero experience.')]
# Now invoke the model
response = llm.invoke(messages)
print(response.content)
PromptTemplate vs ChatPromptTemplate
There are two main template types. PromptTemplate is for older-style text completion models. ChatPromptTemplate is for chat models (which is everything you'll use in 2026). Always use ChatPromptTemplate unless you have a specific reason not to.
from langchain_core.prompts import PromptTemplate
# Simpler template for non-chat use cases
simple_template = PromptTemplate.from_template(
"Write a {length} paragraph summary of: {text}"
)
formatted = simple_template.format(
length="two",
text="The French Revolution began in 1789..."
)
print(formatted)
Few-Shot Prompting with Templates
Sometimes you want to include examples in your prompt. Rather than manually formatting them, use FewShotChatMessagePromptTemplate:
from langchain_core.prompts import FewShotChatMessagePromptTemplate
examples = [
{"input": "2 + 2", "output": "4"},
{"input": "What is 10 * 5?", "output": "50"},
{"input": "100 / 4", "output": "25"},
]
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 a calculator. Answer math problems directly."),
few_shot_prompt,
("human", "{input}"),
])
chain = final_prompt | llm
result = chain.invoke({"input": "15 + 27"})
print(result.content) # 42
Notice the | operator there. That's LangChain Expression Language (LCEL), and it's how you build chains in modern LangChain. We have a full guide on LangChain prompt templates and few-shot examples if you want to go deeper on this topic.
Building Your First Real LLM App
Let's put everything together and build something actually useful: a content summarizer that takes text content and produces a structured summary.
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
# Initialize model
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.3)
# Build the prompt
summary_prompt = ChatPromptTemplate.from_messages([
("system", """You are a content analyst. When given text, produce:
1. A one-sentence summary
2. Three key takeaways (bullet points)
3. The target audience
Format your response clearly with these three sections."""),
("human", "Analyze this content:\n\n{content}")
])
# Output parser
output_parser = StrOutputParser()
# Build the chain using LCEL
summary_chain = summary_prompt | llm | output_parser
# Test it
sample_content = """
Python 3.12 was released in October 2023 and brings several performance
improvements. The release includes a new interpreter that is 15-60% faster
than Python 3.11 for certain workloads. It also improves error messages
significantly, making debugging easier for beginners and experienced developers
alike. The new f-string syntax allows more complex expressions inside braces.
"""
result = summary_chain.invoke({"content": sample_content})
print(result)
This is a complete, working LLM application. It has a structured prompt, a configured model, and an output parser. It's also composable — you could swap gpt-4o-mini for Claude or a local model without changing anything else.
Adding a Second Step: Chaining Outputs
Let's extend the chain. After summarizing, we'll generate a social media post from the summary:
from langchain_core.runnables import RunnablePassthrough
social_prompt = ChatPromptTemplate.from_messages([
("system", "You write engaging Twitter/X posts for tech content. Keep it under 280 characters. No hashtag spam."),
("human", "Turn this summary into a tweet:\n\n{summary}")
])
# Chain summary into social post using proper LCEL composition
full_chain = (
{"summary": summary_prompt | llm | output_parser}
| social_prompt
| llm
| output_parser
)
tweet = full_chain.invoke({"content": sample_content})
print(tweet)
This two-step chain produces a summary first, then feeds it into the social post generator. Each component is independent and swappable. That modularity is the point.
Structured Output with Pydantic
For production apps, you often want your LLM to return structured data, not free-form text:
from pydantic import BaseModel, Field
from langchain_core.output_parsers import PydanticOutputParser
from typing import List
class ContentAnalysis(BaseModel):
one_sentence_summary: str = Field(description="A concise one-sentence summary")
key_takeaways: List[str] = Field(description="3-5 key takeaways as a list")
target_audience: str = Field(description="Who this content is for")
reading_level: str = Field(description="Beginner, Intermediate, or Advanced")
parser = PydanticOutputParser(pydantic_object=ContentAnalysis)
structured_prompt = ChatPromptTemplate.from_messages([
("system", "Analyze the provided content. {format_instructions}"),
("human", "{content}")
]).partial(format_instructions=parser.get_format_instructions())
structured_chain = structured_prompt | llm | parser
analysis = structured_chain.invoke({"content": sample_content})
print(f"Summary: {analysis.one_sentence_summary}")
print(f"Audience: {analysis.target_audience}")
print(f"Takeaways: {analysis.key_takeaways}")
This approach is much better than asking LLMs to "return JSON" and hoping they comply. The Pydantic parser handles validation and retry logic automatically.
LangChain vs Raw OpenAI API vs Hugging Face Pipeline
Before you commit to LangChain, it's fair to look at the alternatives. I've worked with all three approaches, and here's my honest comparison:
| Dimension | LangChain | Raw OpenAI API | Hugging Face Pipeline |
|---|---|---|---|
| Setup Time | 10 minutes | 5 minutes | 15–30 minutes |
| Learning Curve | Medium | Low | High |
| Flexibility | Very High | High | Very High |
| Abstraction Level | High | Low | High |
| Multi-model Support | Excellent (100+ LLMs) | OpenAI only | Open-source models |
| Agent Capabilities | Built-in | Manual | Limited |
| Memory / Context | Built-in | Manual | Manual |
| Production Readiness | High | High | Medium |
| Community Size | Very Large | Large | Very Large |
| Cost | Free (pay for API) | Free (pay for API) | Free (compute costs) |
| Best For | Complex apps, agents | Simple API calls | Local/custom models |
My take: if you're making a single API call, raw OpenAI is fine. The moment you need conversation history, multiple steps, or tools, LangChain starts paying for itself. Hugging Face is the right choice when you need to run models locally or fine-tune on your own data.
For most production LLM applications in 2026, LangChain or a mix of LangChain plus direct SDK calls is the most practical approach.
Handling Errors and Production Considerations
Real applications break. Models return unexpected formats, APIs time out, rate limits hit. Let's talk about making your LangChain code resilient.
Retry Logic
from langchain_openai import ChatOpenAI
# LangChain handles retries natively
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0.7,
max_retries=3, # Retry up to 3 times on failure
request_timeout=30, # 30-second timeout per request
)
Streaming Responses
For user-facing applications, streaming makes a huge difference in perceived speed:
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
prompt = ChatPromptTemplate.from_template("Write a short story about {topic}")
chain = prompt | llm
# Stream the output token by token
for chunk in chain.stream({"topic": "a robot learning to cook"}):
print(chunk.content, end="", flush=True)
Users see text appearing immediately rather than waiting for the full response. For responses that take 5–10 seconds, this is the difference between a good UX and a frustrating one.
Caching API Calls
If you're making the same (or very similar) calls repeatedly, caching can meaningfully cut your API costs:
from langchain_community.cache import InMemoryCache
from langchain_core.globals import set_llm_cache
# Enable in-memory cache (resets when process exits)
set_llm_cache(InMemoryCache())
# For persistent cache across restarts, use SQLiteCache
from langchain_community.cache import SQLiteCache
set_llm_cache(SQLiteCache(database_path=".langchain_cache.db"))
The cache stores exact prompt matches. Identical prompts with the same model parameters return the cached response without an API call. During development, this is genuinely useful when you're iterating on downstream logic.
Async Processing for Throughput
import asyncio
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
async def process_multiple(prompts: list[str]) -> list[str]:
llm = ChatOpenAI(model="gpt-4o-mini")
# Process all prompts concurrently
tasks = [llm.ainvoke([HumanMessage(content=p)]) for p in prompts]
responses = await asyncio.gather(*tasks)
return [r.content for r in responses]
results = asyncio.run(process_multiple([
"What is Python?",
"What is LangChain?",
"What is an LLM?"
]))
for r in results:
print(r)
Async processing can make your application 5–10x faster when handling multiple concurrent requests. For any server-side application, async is worth the extra setup.
Common Beginner Mistakes
I've seen the same patterns trip people up. Let me save you the debugging time.
Mistake 1: Not parsing the output
# Wrong — response is an AIMessage object, not a string
result = llm.invoke(messages)
print(result) # AIMessage(content='Paris', additional_kwargs={}, ...)
# Right — use StrOutputParser or access .content directly
from langchain_core.output_parsers import StrOutputParser
chain = prompt | llm | StrOutputParser()
result = chain.invoke({"city": "France"})
print(result) # "Paris"
Mistake 2: Forgetting to handle context limits
# This may fail silently or get truncated for very long inputs
response = llm.invoke([HumanMessage(content=very_long_document)])
# Better — chunk your content first
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(chunk_size=2000, chunk_overlap=200)
chunks = splitter.split_text(very_long_document)
# Process chunks or use a RAG approach — see /post/rag-system-tutorial
Mistake 3: Using temperature=1.0 for factual tasks
High temperature means more randomness. For factual Q&A, code generation, or structured output, use temperature=0 or at most 0.3. Save the high temperatures for creative writing tasks.
Mistake 4: Not using async for anything serving real users
If your app handles concurrent users, every synchronous LLM call is a bottleneck. Switch to ainvoke, astream, and asyncio.gather from the start. Retrofitting async later is painful.
What to Build Next
You now have a working foundation. Here's what I'd explore next, roughly in order of complexity:
-
Chain types — Learn the different chain types for routing, sequential processing, and retrieval. The LangChain chain types guide covers all of them with working code.
-
RAG — Connect your chains to documents and databases. Start with our RAG system tutorial to understand the retrieval-augmented generation pattern.
-
Agents — Once you're comfortable with chains, agents are the natural next step. We cover the different agent architectures in the AI agent memory and planning guide.
-
Vector Databases — Every serious RAG system needs one. Our vector database guide compares Pinecone, Chroma, FAISS, and other options.
-
OpenAI integration details — For a deep dive on function calling, tool binding, and model selection, see OpenAI API integration.
The official LangChain documentation at python.langchain.com is genuinely good. Their how-to guides are practical and usually up to date.
Conclusion
LangChain gives you a structured way to build on top of LLMs without writing all the plumbing yourself. In this tutorial, we went from a fresh install to a working multi-step LLM application — covering ChatOpenAI initialization, PromptTemplates, LCEL chain composition, streaming, caching, and async patterns.
The framework has a real learning curve, but it's worth it. The patterns you learn here — prompt templates, chain composition, output parsing — apply whether you're building a chatbot, a document analyzer, or a full AI agent system. Start small, build one working thing, then extend it piece by piece.
If you run into issues, the LangChain Discord is active and the community is helpful. Ready to go deeper? Start with building an AI agent with LangChain — it builds directly on everything covered here and introduces tools, memory, and the agent execution loop.
Frequently Asked Questions
Do I need to know machine learning to use LangChain?
No. LangChain is a high-level abstraction layer. You write Python, call APIs, and chain components together. You don't need to understand neural network internals to build useful LLM applications.
Is LangChain still worth learning in 2026?
Yes. Despite competition from newer frameworks, LangChain has the largest community, the most integrations, and the best documentation for beginners. Many production systems still run on it, and new companies continue to choose it for greenfield projects.
What is the difference between LangChain and the OpenAI Python SDK?
The OpenAI SDK gives you direct API access with minimal abstraction. LangChain wraps that API and adds PromptTemplates, memory, chains, agents, and dozens of integrations so you can build complex applications without writing all the glue code yourself.
Frequently Asked Questions
AiTechWorlds Team
✓ Verified WriterThe 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
7 AutoGen Termination Conditions (Max Rounds, Human Approval)
Master all 7 AutoGen termination conditions including is_termination_msg, max_turns, and human approval patterns to stop agent loops reliably and safely.
AutoGen Tutorial: Microsoft's Multi-Agent Framework (2026)
Learn Microsoft AutoGen from scratch in 2026 — install, first agent conversation, GroupChat, and a full comparison of AutoGen 0.2 vs 0.4 features.
AutoGen vs LangChain: Which for Multi-Agent Systems in 2026?
AutoGen vs LangChain for multi-agent systems in 2026 — feature comparison, same use case in both frameworks, and an honest verdict on when each wins.
How to Use AutoGen with Tools (Web Scraper, Calculator, File)
Learn how to equip AutoGen agents with custom tools like web scrapers, calculators, and file handlers using register_for_llm and register_for_execution.