LangChain Quickstart: Install, Configure, and Build Your First Chain in Python
You have already called the OpenAI API directly. It worked. But the moment you wanted to swap to Claude, add a prompt template, or chain two steps together, you ended up rewriting half your code. LangChain solves that problem — it gives every LLM the same interface, so switching providers is one line, not one afternoon.
What Is LangChain and Why Should You Use It?
LangChain is a Python framework that wraps LLM providers behind a single, consistent interface. Instead of learning OpenAI's client, Anthropic's client, and Google's client separately, you learn one pattern — model.invoke(messages) — and LangChain handles the rest.
I started using LangChain when a project required testing the same prompt across three providers. Without it, I had three completely different code paths. With LangChain, the only thing that changed was the model class name. That alone justified the dependency.
The framework is split into several packages. Here is the mental model: langchain-core provides the base abstractions (message types, the Runnable interface, prompt templates). Provider packages like langchain-openai, langchain-anthropic, and langchain-community implement those abstractions for specific APIs. The main langchain package ties everything together with higher-level chains and utilities.
The key benefit is the Runnable interface — every LLM, prompt template, and output parser implements the same three methods: .invoke(), .stream(), and .batch(). Once you learn this pattern, you know how to call anything in LangChain.
Installation and API Key Configuration
Let's get LangChain installed and configured with at least one provider. I recommend starting with OpenAI since it is the most widely used, but the setup pattern is identical for all providers.
Every LLM provider needs an API key. LangChain reads these from environment variables by default, so you set them once and every model instance picks them up automatically.
With the key set, making your first LangChain call takes exactly three lines.
The model returns an AIMessage object, not a raw string. The .content attribute holds the text. This message object also carries metadata — token counts, model name, and response headers — which matters when you start tracking costs.
The Runnable Interface: invoke, stream, and batch
This is the single most important concept in LangChain. Every component — models, prompt templates, output parsers, chains — implements the Runnable interface. That means every component supports the same three methods.
The batch() method sends all requests concurrently — it does not wait for one to finish before starting the next. For three questions, this is roughly three times faster than calling invoke() in a loop.
Streaming is where LangChain really shines for user-facing applications. Instead of waiting for the full response, .stream() yields tokens as they arrive.
Each chunk is an AIMessageChunk with a .content attribute. Most chunks contain one or a few tokens. The first chunk usually arrives within 200-400ms, which makes your application feel responsive even when the full response takes several seconds.
Messages: How LangChain Structures LLM Input
When you passed a plain string to model.invoke() above, LangChain silently wrapped it in a HumanMessage. In practice, you will almost always build message lists explicitly — especially when you need system instructions or multi-turn conversations.
LangChain provides four message types: SystemMessage (sets behavior), HumanMessage (user input), AIMessage (model response), and ToolMessage (function call results). If you have used the OpenAI API directly, these map exactly to the role field in chat completions — but as typed Python objects instead of raw dictionaries.
For multi-turn conversations, you build the full history as a list.
The Model Abstraction: Swap Providers in One Line
This code block opens with the punchline — three different model classes, all called with .invoke(), all returning the same AIMessage type. That is the core value proposition of LangChain's model abstraction. Your application logic never touches provider-specific code.
In my projects, I keep the model class in a configuration file. When a client asks to switch from GPT-4o to Claude, I change one config value. The rest of the application — prompts, chains, output parsing — stays untouched.
Ollama deserves a special mention because it runs locally. No API key, no network calls, no usage costs. During development, I often prototype with Ollama to iterate quickly, then switch to a cloud provider for production. The code stays identical.
LangChain uses message objects to structure LLM input. Write a pure Python function format_messages(system_prompt, user_message) that returns a list of dictionaries in the OpenAI chat format. Each dictionary must have "role" and "content" keys.
The function should return a list with exactly two dictionaries:
1. A system message with role set to "system"
2. A user message with role set to "user"
Prompt Templates: Reusable, Dynamic Prompts
The template does not call an LLM. It returns a ChatPromptValue — the formatted messages ready to be passed to a model. Notice the .invoke() method: prompt templates are Runnables too. That uniformity is what makes chaining possible.
You can also create templates from a single string when you do not need a system message.
Build Your First Chain with the Pipe Operator
A chain connects multiple Runnables together. In LangChain, you compose chains using the pipe operator | — pronounced "pipe into." The output of the left side becomes the input of the right side, exactly like Unix pipes.
That prompt | model | parser line is LCEL — the LangChain Expression Language. Data flows left to right: the dictionary {"concept": "list comprehensions"} enters the prompt template, which produces formatted messages. Those messages enter the model, which returns an AIMessage. The StrOutputParser extracts the .content string. The final result is a plain Python string.
The beautiful part: because every component is a Runnable, the chain itself is also a Runnable. It supports .invoke(), .stream(), and .batch() — automatically.
Real-World Example: A Translation Chain That Works with Any Provider
Here is a practical scenario: you are building a translation feature for an app. You want a clean function that takes text, a source language, and a target language — and returns the translation. The function should work with any LLM provider without changing the translation logic.
To switch this to Claude, you change exactly one line — the model assignment. The prompt, parser, and chain stay identical.
This pattern — define the chain once, swap the model as needed — is how production LangChain applications are structured. The business logic lives in the chain definition. The model choice lives in configuration.
Response Metadata and Token Tracking
When you skip the StrOutputParser and work with the raw AIMessage, you get access to metadata that matters for production — token counts, model information, and timing.
Token counts are essential once you move past prototyping. Every token costs money. I have seen projects blow through API budgets because nobody tracked token usage until the bill arrived. Build tracking in from the start.
LangChain chains pass data through a sequence of steps. Simulate this pattern in pure Python.
Write a function run_chain(steps, initial_input) that takes a list of functions and an initial input value. It should pass the input through each function in order, where each function's output becomes the next function's input. Return the final result.
For example, if steps = [step_a, step_b] and initial_input = "hello", the function should compute step_b(step_a("hello")).
LangChain prompt templates fill placeholder variables into strings at runtime. Write a pure Python function render_template(template_str, variables) that replaces {variable_name} placeholders in a template string with values from a dictionary.
The function should:
1. Take a template string containing {placeholder} markers
2. Take a dictionary mapping placeholder names to values
3. Return the filled-in string
4. Raise a KeyError if a placeholder in the template has no matching key in the dictionary
Common Mistakes and How to Fix Them
After helping several teams adopt LangChain, I keep seeing the same mistakes. Here are the ones that waste the most time.
Mistake 1: Forgetting the API Key Environment Variable
from langchain_openai import ChatOpenAI
# Forgot to set OPENAI_API_KEY
model = ChatOpenAI(model="gpt-4o-mini")
response = model.invoke("Hello")
# Error: AuthenticationError: No API key providedimport os
os.environ["OPENAI_API_KEY"] = "sk-your-key"
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o-mini")
response = model.invoke("Hello")
print(response.content) # WorksMistake 2: Printing the AIMessage Instead of .content
response = model.invoke("What is Python?")
print(response)
# content='Python is a...' additional_kwargs={} ...
# Messy output with metadata you don't wantresponse = model.invoke("What is Python?")
print(response.content)
# Python is a high-level programming language...Mistake 3: Using the Wrong Import Path
LangChain restructured its packages in late 2023. Older tutorials and Stack Overflow answers use import paths that are either deprecated or broken. Here is the current correct mapping.
Mistake 4: Missing Template Variables in invoke()
template = ChatPromptTemplate.from_template(
"Translate {text} to {language}"
)
# Forgot to include "language"
result = template.invoke({"text": "Hello"})
# KeyError: 'language'template = ChatPromptTemplate.from_template(
"Translate {text} to {language}"
)
result = template.invoke({
"text": "Hello",
"language": "Spanish",
})
print(result) # WorksQuick Error Reference
These four error messages cover most first-week frustrations. Keep this list handy.
Frequently Asked Questions
Do I need LangChain to build LLM applications?
No. You can call any LLM API directly with the provider's SDK. LangChain adds value when you need to switch between providers, chain multiple steps together, or use its ecosystem of document loaders, retrievers, and agents. For a single-provider, single-call application, the raw SDK is simpler.
What is the difference between langchain and langchain-core?
langchain-core contains the base abstractions — the Runnable interface, message types, prompt templates, and output parsers. It has minimal dependencies. langchain builds on top of langchain-core and adds higher-level constructs like chains, agents, and retrieval utilities. For simple chains, langchain-core plus a provider package is enough.
Can I use async/await with LangChain?
Yes. Every Runnable provides async versions of all methods: ainvoke(), astream(), and abatch(). Use them in async frameworks like FastAPI or when you need concurrent LLM calls.
How do I handle rate limits and retries?
LangChain's ChatOpenAI has built-in retry logic with exponential backoff for rate limit errors. You can configure it with max_retries (default is 2). For more control, use the .with_retry() method available on any Runnable.
Complete Code
Here is a single script combining all the core concepts from this tutorial — installation check, model creation, message formatting, prompt templates, and chain composition.