cheat sheet
LangChain
Build LLM-powered pipelines with LangChain. Covers LCEL chains, chat models, prompts, output parsers, tools, agents, retrievers, memory, and streaming.
LangChain — LLM Application Framework
What it is
LangChain is a Python (and JavaScript) framework for composing large language model calls into pipelines — chains of prompts, models, parsers, retrievers, memory, and tools. Its core abstraction is the LangChain Expression Language (LCEL), which connects components with the | pipe operator, enabling streaming, async, parallelism, and tracing with a uniform interface. LangChain is the most widely-used framework for building RAG (retrieval-augmented generation) systems, conversational agents, and structured-output pipelines.
Install
LangChain is split into provider packages. Install the core plus the model provider you need:
pip install langchain
pip install langchain-openai # OpenAI / Azure OpenAI
pip install langchain-anthropic # Anthropic Claude
pip install langchain-google-genai # Google Gemini
pip install langchain-community # 200+ third-party integrations
pip install langchain-chroma # ChromaDB vector store
Output: (none — exits 0 on success)
Quick example
from langchain_anthropic import ChatAnthropic
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
import os
model = ChatAnthropic(
model="claude-sonnet-4-6",
api_key=os.environ["ANTHROPIC_API_KEY"],
)
prompt = ChatPromptTemplate.from_messages([
("system", "You are a concise technical writer."),
("human", "{topic}"),
])
chain = prompt | model | StrOutputParser()
print(chain.invoke({"topic": "Explain LCEL in one sentence."}))
Output:
LCEL (LangChain Expression Language) is a declarative pipe syntax that chains
LangChain components—prompts, models, parsers, retrievers—into composable, streamable pipelines.
When / why to use it
- Building RAG pipelines that combine document retrieval with LLM generation.
- Connecting LLMs to external tools (web search, databases, APIs) via agents.
- Standardising across multiple LLM providers behind a single interface.
- Streaming responses token-by-token in chat applications.
- Adding memory (conversation history) to multi-turn interactions.
- Extracting structured data from unstructured text with output parsers.
Common pitfalls
Package fragmentation —
langchainimports change across versions. Most integrations moved fromlangchain.chat_modelstolangchain_openai,langchain_anthropic, etc. Always import from the provider-specific package.
LCEL runnable contracts — every LCEL component must receive and return compatible types. If a parser expects a string but the previous step returns an
AIMessage, chain composition silently breaks. Check input/output types withchain.input_schemaandchain.output_schema.
LLM calls in loops are slow — running
chain.invoke()inside a Python loop makes sequential API calls. Usechain.batch(inputs)for parallel invocation across a list.
Enable LangSmith tracing with two environment variables for free observability:
LANGCHAIN_TRACING_V2=trueandLANGCHAIN_API_KEY=<key>. Every chain call appears in the LangSmith dashboard with latency, tokens, and full prompt/response logs.
chain.stream(input)yields tokens as they arrive. Prefer this overinvoke()for any user-facing interface.
Richer example — RAG pipeline
from langchain_anthropic import ChatAnthropic
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
import os
# 1. Vector store with pre-indexed documents
embeddings = OpenAIEmbeddings(api_key=os.environ["OPENAI_API_KEY"])
vectorstore = Chroma(
collection_name="docs",
embedding_function=embeddings,
persist_directory="./chroma_db",
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
# 2. Prompt
prompt = ChatPromptTemplate.from_messages([
("system", "Answer using only the provided context.\n\nContext:\n{context}"),
("human", "{question}"),
])
# 3. Model
model = ChatAnthropic(model="claude-sonnet-4-6", api_key=os.environ["ANTHROPIC_API_KEY"])
# 4. Chain
def format_docs(docs):
return "\n\n".join(d.page_content for d in docs)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| model
| StrOutputParser()
)
answer = rag_chain.invoke("What is retrieval-augmented generation?")
print(answer)
Chat models
LangChain wraps all major LLM providers with a uniform ChatModel interface. Each invoke() call returns an AIMessage. Provider-specific parameters (temperature, max tokens) are set at construction.
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.messages import SystemMessage, HumanMessage
import os
# OpenAI
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0, api_key=os.environ["OPENAI_API_KEY"])
# Anthropic
llm = ChatAnthropic(model="claude-sonnet-4-6", api_key=os.environ["ANTHROPIC_API_KEY"])
# Google Gemini
llm = ChatGoogleGenerativeAI(model="gemini-1.5-flash", google_api_key=os.environ["GEMINI_API_KEY"])
# Direct invocation with message objects
response = llm.invoke([
SystemMessage(content="You are a Python expert."),
HumanMessage(content="What is a context manager?"),
])
print(response.content[:120])
Output:
A context manager is a Python object that implements `__enter__` and `__exit__` methods,
enabling the `with` statement to manage setup and teardown of resources automatically.
Prompts and ChatPromptTemplate
ChatPromptTemplate builds prompt messages from a template string with named placeholders. It supports system, human, assistant, and placeholder (for injecting full message lists) message types.
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# Basic template
prompt = ChatPromptTemplate.from_messages([
("system", "You are an expert in {domain}. Be concise."),
("human", "{question}"),
])
formatted = prompt.invoke({"domain": "databases", "question": "What is a B-tree?"})
print(formatted.messages)
# With history placeholder (for memory chains)
prompt_with_history = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant."),
MessagesPlaceholder(variable_name="history"),
("human", "{input}"),
])
Output parsers
Output parsers convert raw model output into structured Python objects. StrOutputParser extracts the string content from an AIMessage. JsonOutputParser expects JSON in the model's response. PydanticOutputParser validates against a Pydantic model.
from langchain_anthropic import ChatAnthropic
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.pydantic_v1 import BaseModel, Field
import os
class MovieReview(BaseModel):
title: str = Field(description="Movie title")
score: int = Field(description="Score from 1 to 10")
summary: str = Field(description="One sentence summary")
parser = JsonOutputParser(pydantic_object=MovieReview)
prompt = ChatPromptTemplate.from_messages([
("system", "Extract movie review data. Return valid JSON only.\n{format_instructions}"),
("human", "{text}"),
]).partial(format_instructions=parser.get_format_instructions())
model = ChatAnthropic(model="claude-sonnet-4-6", api_key=os.environ["ANTHROPIC_API_KEY"])
chain = prompt | model | parser
result = chain.invoke({
"text": "Oppenheimer was a stunning 9/10 — a cerebral, visually brilliant masterpiece."
})
print(result)
print(type(result))
Output:
{'title': 'Oppenheimer', 'score': 9, 'summary': 'A cerebral, visually brilliant masterpiece.'}
<class 'dict'>
Tools and bind_tools
Tools are Python functions the model can call to retrieve information or perform actions. bind_tools attaches them to the model so it can choose when to invoke them.
from langchain_anthropic import ChatAnthropic
from langchain_core.tools import tool
import os
@tool
def get_stock_price(ticker: str) -> str:
"""Return the current stock price for the given ticker symbol."""
prices = {"AAPL": "182.50", "GOOGL": "174.20", "MSFT": "420.00"}
return prices.get(ticker.upper(), "Unknown ticker")
@tool
def get_company_info(ticker: str) -> str:
"""Return basic company information for the given ticker symbol."""
info = {"AAPL": "Apple Inc., consumer electronics", "GOOGL": "Alphabet Inc., internet services"}
return info.get(ticker.upper(), "Unknown")
model = ChatAnthropic(
model="claude-sonnet-4-6",
api_key=os.environ["ANTHROPIC_API_KEY"],
).bind_tools([get_stock_price, get_company_info])
response = model.invoke("What is the current price of Apple stock?")
print(response.tool_calls)
Output:
[{'name': 'get_stock_price', 'args': {'ticker': 'AAPL'}, 'id': 'toolu_01...'}]
Agents — create_tool_calling_agent
An agent combines a model with tools into a loop: the model decides what to do, calls tools, observes results, and continues until it has a final answer. create_tool_calling_agent wraps this loop for models that support tool/function calling.
from langchain_anthropic import ChatAnthropic
from langchain_core.tools import tool
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import os
@tool
def calculator(expression: str) -> str:
"""Evaluate a mathematical expression and return the result."""
try:
return str(eval(expression, {"__builtins__": {}}, {}))
except Exception as e:
return f"Error: {e}"
@tool
def reverse_string(text: str) -> str:
"""Reverse a string and return the result."""
return text[::-1]
model = ChatAnthropic(model="claude-sonnet-4-6", api_key=os.environ["ANTHROPIC_API_KEY"])
tools = [calculator, reverse_string]
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant with access to tools."),
MessagesPlaceholder(variable_name="agent_scratchpad"),
("human", "{input}"),
])
agent = create_tool_calling_agent(model, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
result = executor.invoke({"input": "What is 17 * 23, and what is 'hello' reversed?"})
print(result["output"])
Output:
> Entering new AgentExecutor chain...
[tool: calculator → 391]
[tool: reverse_string → olleh]
17 × 23 = 391, and "hello" reversed is "olleh".
> Finished chain.
17 × 23 = 391, and "hello" reversed is "olleh".
Streaming
All LCEL chains support .stream() which yields chunks as they arrive from the model.
from langchain_anthropic import ChatAnthropic
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
import os
model = ChatAnthropic(model="claude-sonnet-4-6", api_key=os.environ["ANTHROPIC_API_KEY"])
chain = ChatPromptTemplate.from_template("{topic}") | model | StrOutputParser()
for chunk in chain.stream({"topic": "Explain async/await in Python briefly."}):
print(chunk, end="", flush=True)
print()
For async streaming in FastAPI or asyncio contexts:
async for chunk in chain.astream({"topic": "Explain recursion."}):
print(chunk, end="", flush=True)
Memory — RunnableWithMessageHistory
RunnableWithMessageHistory wraps any chain with a session-aware message store, automatically injecting and updating conversation history on each call.
from langchain_anthropic import ChatAnthropic
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
import os
store = {}
def get_history(session_id: str) -> InMemoryChatMessageHistory:
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]
model = ChatAnthropic(model="claude-sonnet-4-6", api_key=os.environ["ANTHROPIC_API_KEY"])
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant."),
MessagesPlaceholder(variable_name="history"),
("human", "{input}"),
])
chain = prompt | model | StrOutputParser()
chain_with_memory = RunnableWithMessageHistory(
chain,
get_session_history=get_history,
input_messages_key="input",
history_messages_key="history",
)
cfg = {"configurable": {"session_id": "user-42"}}
print(chain_with_memory.invoke({"input": "My name is Alice."}, config=cfg))
print(chain_with_memory.invoke({"input": "What is my name?"}, config=cfg))
Output:
Nice to meet you, Alice! How can I help you today?
Your name is Alice.
batch — parallel invocation
inputs = [
{"topic": "What is a hash table?"},
{"topic": "What is a binary tree?"},
{"topic": "What is a heap?"},
]
results = chain.batch(inputs)
for r in results:
print(r[:80])
Quick reference
| Task | Code |
|---|---|
| LCEL chain | prompt | model | parser |
| Invoke | chain.invoke({"key": "value"}) |
| Stream | for chunk in chain.stream(input): |
| Async stream | async for chunk in chain.astream(input): |
| Batch parallel | chain.batch([input1, input2]) |
| Chat model | ChatAnthropic(model="...", api_key=...) |
| Prompt template | ChatPromptTemplate.from_messages([("system", "..."), ("human", "{q}")]) |
| String parser | StrOutputParser() |
| JSON parser | JsonOutputParser(pydantic_object=MyModel) |
| Tool | @tool decorator on a typed function |
| Bind tools | model.bind_tools([tool1, tool2]) |
| Agent | create_tool_calling_agent(model, tools, prompt) |
| Execute agent | AgentExecutor(agent, tools).invoke({"input": "..."}) |
| Memory chain | RunnableWithMessageHistory(chain, get_session_history, ...) |
| Retriever | vectorstore.as_retriever(search_kwargs={"k": 4}) |
| Enable tracing | LANGCHAIN_TRACING_V2=true LANGCHAIN_API_KEY=<key> |