Skip to main content

LangChain Quickstart: Install, Configure, and Build Your First Chain in Python

Intermediate60 min3 exercises50 XP
0/3 exercises

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.

LangChain package structure
Loading editor...

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.

Installation commands
Loading editor...

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.

API key configuration
Loading editor...

With the key set, making your first LangChain call takes exactly three lines.

Your first LangChain call
Loading editor...

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.

invoke() and batch() in action
Loading editor...

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.

Streaming tokens with stream()
Loading editor...

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.

Building message lists
Loading editor...

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.

Multi-turn conversation
Loading editor...

The Model Abstraction: Swap Providers in One Line

Same code, three different LLM providers
Loading editor...

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.

Running with Ollama locally
Loading editor...
Exercise 1: Build a Message Formatter
Write Code

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"

Loading editor...

Prompt Templates: Reusable, Dynamic Prompts

Hardcoding prompts as strings works for experiments. For real applications, you need templates — prompts with placeholder variables that get filled at runtime. This is where f-strings stop being practical and ChatPromptTemplate takes over.

Creating a prompt template
Loading editor...

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.

Single-string template shorthand
Loading editor...

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.

A three-step chain: prompt → model → parser
Loading editor...

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.

Streaming and batching work on the full chain
Loading editor...

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.

A reusable translation chain
Loading editor...

To switch this to Claude, you change exactly one line — the model assignment. The prompt, parser, and chain stay identical.

Same chain, different provider — one line changed
Loading editor...

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.

Accessing response metadata
Loading editor...

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.

Exercise 2: Simulate a Chain Pipeline
Write Code

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")).

Loading editor...
Exercise 3: Build a Prompt Template Renderer
Write Code

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

Loading editor...

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

Missing API key — cryptic error
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 provided
Set the key before creating the model
import 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)  # Works

Mistake 2: Printing the AIMessage Instead of .content

Printing the full message object
response = model.invoke("What is Python?")
print(response)
# content='Python is a...' additional_kwargs={} ...
# Messy output with metadata you don't want
Extract the text content
response = 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.

Current vs deprecated import paths
Loading editor...

Mistake 4: Missing Template Variables in invoke()

Missing a required variable
template = ChatPromptTemplate.from_template(
    "Translate {text} to {language}"
)
# Forgot to include "language"
result = template.invoke({"text": "Hello"})
# KeyError: 'language'
Provide all template variables
template = ChatPromptTemplate.from_template(
    "Translate {text} to {language}"
)
result = template.invoke({
    "text": "Hello",
    "language": "Spanish",
})
print(result)  # Works

Quick Error Reference

These four error messages cover most first-week frustrations. Keep this list handy.

Common error messages and fixes
Loading editor...

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.

Async usage with ainvoke()
Loading editor...

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.

Configuring retries
Loading editor...

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.

Complete runnable script
Loading editor...

References

  • LangChain Documentation — Get Started. Link
  • LangChain Core API Reference — Runnable Interface. Link
  • LangChain Documentation — Chat Models. Link
  • LangChain Documentation — LCEL (LangChain Expression Language). Link
  • OpenAI API Documentation — Chat Completions. Link
  • Anthropic API Documentation — Messages API. Link
  • LangChain Migration Guide — v0.1 to v0.2. Link
  • Ollama Documentation — Getting Started. Link
  • Related Tutorials