Back to cheat sheets

AI & LLMs

LangChain

Building LLM apps: models and prompts, the LCEL Runnable interface, output parsers, retrieval and RAG, memory, tools and agents, and LangGraph — with rapid-fire Q&A.

Now playing: LangChain

LangChain

Two-host episode · 13:56

0:0013:56
 Download

Now playing: LangChain

LangChain

Mock interview · 10:01

0:0010:01
 Download

01What LangChain Is

LangChain is a framework for building applications powered by large language models. It gives you two things: standard interfaces over many providers (so swapping OpenAI for Anthropic is a one-line change) and composition primitives to wire models, prompts, retrievers, and tools into pipelines.

LangChain's value isn't the model — it's the glue: a common abstraction for models, prompts, parsers, and retrievers, plus a way to chain them and observe what happened.

The codebase is split into focused packages:

  • langchain-core — the base abstractions (Runnables, messages, prompts, output parsers). Lightweight, few dependencies.
  • langchain — higher-level chains, agents, and retrieval strategies.
  • integration packageslangchain-openai, langchain-anthropic, etc., one per provider.
  • langchain-community — community-maintained integrations.

02Models, Messages & Prompts

The core object is a chat model — it takes a list of messages and returns an AIMessage. Messages carry a role: SystemMessage, HumanMessage, AIMessage.

Prompt templates turn variables into messages so prompts are reusable and testable:

from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a {role}."),
    ("human", "{question}"),
])
prompt_value = prompt.invoke({"role": "tutor", "question": "What is RAG?"})
messages = prompt_value.to_messages()  # PromptValue -> list of messages
Because every provider's chat model implements the same interface, the rest of your pipeline doesn't care which LLM is behind it — that's what makes models swappable. Distinguish chat models (message in, message out) from the older text-completion LLMs (string in, string out); chat models are the default today.

03LCEL & the Runnable Interface

LCEL (LangChain Expression Language) is how you compose components. Every component — prompt, model, parser, retriever — implements the Runnable interface, so they share the same methods and snap together with the pipe operator |.

from langchain_core.output_parsers import StrOutputParser

chain = prompt | model | StrOutputParser()
chain.invoke({"role": "tutor", "question": "What is RAG?"})

The Runnable interface gives every chain the same execution methods for free:

  • invoke() — run once on a single input.
  • batch() — run on many inputs concurrently.
  • stream() — yield output tokens as they're generated.
  • async variants — ainvoke, abatch, astream.
Compose in parallel with RunnableParallel (run branches concurrently and collect a dict) and inject inputs with RunnablePassthrough. Build the pipeline once; get sync, async, batch, and streaming execution without extra code.

04Output Parsers & Structured Output

Models return free-form text; output parsers turn that into something your code can use, as the last step of a chain.

  • StrOutputParser — pull the plain string out of an AIMessage.
  • JSON / Pydantic parsers — coerce the response into a typed object and validate it.

The more robust modern approach binds a schema directly to the model:

structured = model.with_structured_output(MySchema)
structured.invoke("Extract the fields from: ...")
with_structured_output uses the provider's native tool/JSON-mode support to guarantee the shape, which is more reliable than parsing free text and re-prompting on failure. Reach for it whenever you need machine-readable results.

05Retrieval & RAG

RAG (Retrieval-Augmented Generation) grounds an LLM in your own data: fetch relevant documents, put them in the prompt, then answer. The ingestion pipeline:

  1. Document loaders read sources (PDFs, web pages, databases) into Document objects.
  2. Text splitters chunk long documents so each piece fits the embedding and context limits.
  3. Embeddings convert each chunk into a vector capturing its meaning.
  4. Vector store indexes the vectors for similarity search (Chroma, FAISS, pgvector, Pinecone).

At query time a retriever embeds the question, finds the nearest chunks, and the chain stuffs them into the prompt as context.

The retriever is itself a Runnable, so RAG is just another LCEL pipeline: retriever → format docs → promptmodelparser. Chunking strategy and retrieval quality usually matter more to answer quality than the model choice.

06Memory & Conversation State

LLM calls are stateless — the model only knows what's in the current request. To hold a conversation you must pass prior turns back in yourself.

  • Keep a message history per session and prepend it to each new prompt, often via a MessagesPlaceholder in the template.
  • RunnableWithMessageHistory wraps a chain to load and save history automatically, keyed by a session id.
  • For long chats, trim or summarize old turns so you stay within the context window and control token cost.
The older ConversationBufferMemory-style classes are largely superseded. Current guidance is explicit message-history wiring or LangGraph persistence (checkpointers), which also survives restarts and supports branching.

07Tools & Agents

A tool is a function the model can choose to call — a calculator, a search API, a database query. An agent is an LLM in a loop: it decides which tool to call, sees the result, and repeats until it can answer.

from langchain_core.tools import tool

@tool
def get_weather(city: str) -> str:
    """Return the weather for a city."""
    return lookup(city)

model_with_tools = model.bind_tools([get_weather])

The flow: bind_tools exposes the schemas to the model; the model replies with tool calls; your code runs the tools and feeds results back as ToolMessages; the loop continues.

The LLM never executes anything itself — it only requests a call. Your code runs the tool, which is also where you enforce permissions and validation. This tool-calling loop is the foundation every agent framework builds on.

08LangGraph & Ecosystem

LangGraph is LangChain's library for stateful, multi-step agent workflows modeled as a graph: nodes do work, edges decide what runs next, and a shared state object flows through. Unlike a straight LCEL chain, a graph supports cycles, branching, persistence, and human-in-the-loop pauses.

Rule of thumb: a linear pipeline → LCEL chain; a looping, branching, stateful agent → LangGraph. It's the recommended path for production agents over the older AgentExecutor.

Around the framework:

  • LangSmith — tracing, debugging, and evaluation; see every step, token, and latency of a run.
  • Streaming & callbacks — stream tokens or intermediate events to a UI; hook callbacks for logging and metrics.
LangSmith is provider-agnostic and works even without LangChain, but pairs tightly with it — observability is what turns a demo chain into something you can debug and trust in production.

09Rapid-Fire Q&A

Reveal each answer to self-check, then test yourself with the quiz.

What does LCEL's pipe operator (|) rely on to compose components?

Prompts, models, parsers, and retrievers all implement the Runnable interface, so they share invoke/batch/stream/async methods and compose with the pipe operator into a single Runnable chain.

In LangChain's tool-calling loop, who actually executes a tool?

The LLM only emits tool calls (name + arguments). Your code executes the tool and returns a ToolMessage — which is also where you enforce permissions and validation. The model never runs code itself.

When should you choose LangGraph over a plain LCEL chain?

A linear pipeline is a fine LCEL chain; LangGraph models looping, branching, stateful agent workflows as a graph with persistence and human-in-the-loop, and is the recommended path for production agents.

What is the correct order of the RAG ingestion pipeline?

Document loaders read sources, text splitters chunk them, embeddings turn chunks into vectors, and a vector store indexes those for similarity search. At query time a retriever embeds the question and finds nearest chunks.

Why prefer model.with_structured_output(Schema) over parsing free-form text into JSON?

with_structured_output leans on the model's native structured-output/tool support to return a validated, typed object — more reliable than parsing free text and retrying when it doesn't match.

Why do you need to manage memory/history yourself for a chatbot?

Each model call is stateless, so prior turns must be passed back in. RunnableWithMessageHistory (or LangGraph persistence) loads and saves a per-session message history and prepends it to each prompt; long chats are trimmed or summarized.

What is the role of langchain-core versus the integration packages like langchain-openai?

langchain-core defines the lightweight base interfaces (Runnable, messages, prompts, output parsers); provider packages such as langchain-openai and langchain-anthropic implement those interfaces for a specific model provider.

What is LangSmith used for?

LangSmith is the observability platform: it traces every step, token, and latency of a run and supports evaluation. It's provider-agnostic and works with or without LangChain, but pairs tightly with it.