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:

bash
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

python
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:

text
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 fragmentationlangchain imports change across versions. Most integrations moved from langchain.chat_models to langchain_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 with chain.input_schema and chain.output_schema.

LLM calls in loops are slow — running chain.invoke() inside a Python loop makes sequential API calls. Use chain.batch(inputs) for parallel invocation across a list.

Enable LangSmith tracing with two environment variables for free observability: LANGCHAIN_TRACING_V2=true and LANGCHAIN_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 over invoke() for any user-facing interface.

Richer example — RAG pipeline

python
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.

python
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:

text
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.

python
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.

python
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:

text
{'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.

python
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:

text
[{'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.

python
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:

text
> 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.

python
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:

python
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.

python
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:

text
Nice to meet you, Alice! How can I help you today?
Your name is Alice.

batch — parallel invocation

python
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

TaskCode
LCEL chainprompt | model | parser
Invokechain.invoke({"key": "value"})
Streamfor chunk in chain.stream(input):
Async streamasync for chunk in chain.astream(input):
Batch parallelchain.batch([input1, input2])
Chat modelChatAnthropic(model="...", api_key=...)
Prompt templateChatPromptTemplate.from_messages([("system", "..."), ("human", "{q}")])
String parserStrOutputParser()
JSON parserJsonOutputParser(pydantic_object=MyModel)
Tool@tool decorator on a typed function
Bind toolsmodel.bind_tools([tool1, tool2])
Agentcreate_tool_calling_agent(model, tools, prompt)
Execute agentAgentExecutor(agent, tools).invoke({"input": "..."})
Memory chainRunnableWithMessageHistory(chain, get_session_history, ...)
Retrievervectorstore.as_retriever(search_kwargs={"k": 4})
Enable tracingLANGCHAIN_TRACING_V2=true LANGCHAIN_API_KEY=<key>