In the last module, you built a small conversation by hand, felt the pain of doing it separately for two providers, and then watched LangChain quietly absorb that pain into one line: init_chat_model.
Before you write any more code, it’s worth pausing for one entire module and doing something less exciting but genuinely important: building a map in your head of what LangChain is actually made of, and where the pieces you’re about to learn — models, tools, agents, middleware — each belong.
Here’s why this matters. Without a map, every new LangChain concept you meet will feel like a brand-new, disconnected thing to memorize. With a map, every new concept will feel like “oh, that’s just another piece slotting into a spot I already understand.” That second feeling is the entire goal of this module.
Start with a question: what does a real LangChain app actually contain?
Think back to the looping diagram from Module 1 — model, tool, model, retrieve, model, respond. If you had to build a real, working version of that, what actual categories of code would you need?
Take a moment and actually think about it before reading on. You’d probably need:
- Something to talk to the AI model itself.
- Something to give that model the ability to look things up or take action.
- Something to search through your own documents, if the app needs to answer questions about them.
- Something to tie all of that together into a single, coherent loop.
- Something to keep track of what’s happened so far, across that loop.
That list — models, tools, retrieval, agents, state — is not something we made up. It’s genuinely how LangChain itself is organized. Let’s put it into a picture.
The map itself
flowchart TD
APP["Your LangChain Application"]
APP --> MODELS["Models\n(talk to OpenAI, Gemini, etc.)"]
APP --> TOOLS["Tools\n(functions the model can ask you to run)"]
APP --> RETRIEVAL["Retrieval\n(search your own documents)"]
MODELS --> AGENT["Agent\n(the loop that ties it all together)"]
TOOLS --> AGENT
RETRIEVAL --> AGENT
AGENT --> MIDDLEWARE["Middleware\n(customizes what happens\nbefore/after each step)"]
MIDDLEWARE --> STATE["State\n(what's happened so far)"]
STATE --> RESULT["Final Application"]
Let’s walk through this slowly, one box at a time, because each one of these words is going to become an entire module of its own later in this course, and it’s worth knowing exactly what job each one does before you meet it in depth.
Models. This is the piece you already touched directly in Module 1 — init_chat_model and everything built around it. Its one job is sending your input to an AI provider and handing back the reply. It doesn’t know or care about tools, memory, or documents. It just talks to the model.
Tools. These are ordinary Python functions — a calculator, a weather lookup, a database query — described in a way the model can understand and ask to use. A tool, by itself, does nothing special. It only becomes useful once a model decides to call it.
Retrieval. This is LangChain’s machinery for searching through your own documents — splitting them into pieces, turning them into a searchable form, and pulling back the pieces relevant to a question. You already know why this matters from your earlier RAG course. This box is specifically about how LangChain implements it.
Agent. This is the box that actually ties Models, Tools, and Retrieval together into one working loop — the thing that decides “should I answer now, or should I call a tool first?” and repeats that decision until the task is genuinely done.
Middleware. This is a newer, and genuinely important, idea in current LangChain. Middleware lets you step in before or after any single piece of that agent loop runs — to log what happened, to filter out sensitive information, to swap which model gets used for a particular step — without having to rewrite the agent’s core loop yourself. Think of it as a set of checkpoints the agent passes through on every single cycle.
State. This is simply “what has happened so far” — the conversation history, any information gathered along the way, anything the agent needs to remember between one step and the next.
Notice that none of these six boxes do anything magical on their own. Each one does one honest, specific job. The actual skill of using LangChain well is knowing which of these boxes a given problem belongs in — which, again, is exactly why this map is worth having clear in your head before we go further.
Now, the question you’re probably already wondering: where does LangGraph fit?
You’ve heard of LangGraph in your earlier courses, and you’ll formally learn it right after this one. So it’s worth being precise, right now, about the actual relationship between the two — because a lot of confusion online comes from people treating LangChain and LangGraph as if they’re competing with each other. They’re not. One is built directly on top of the other.
Here’s the honest, current relationship:
flowchart TD
LG["LangGraph\n(low-level orchestration engine)"]
LC["LangChain\n(high-level building blocks, built ON TOP of LangGraph)"]
LG --> LC
LangGraph is a lower-level engine for running stateful, looping, multi-step workflows — it handles things like pausing a workflow to wait for a human, saving progress so it can resume later, and precisely controlling the exact path a multi-step process takes. It’s genuinely powerful, but also genuinely more work to use directly, because you design the exact steps and connections yourself.
LangChain, and specifically the create_agent function you’ll meet in a few modules, is a friendlier, higher-level layer built directly on top of LangGraph. When you call create_agent, LangChain quietly builds a LangGraph workflow underneath, using sensible defaults for you, so you don’t have to design that workflow by hand.
This gives you a genuinely useful rule of thumb, which we’ll revisit properly in a much later module:
Start with LangChain’s high-level tools, like
create_agent. If you eventually hit a wall — you need very precise, custom control over the exact steps your agent takes — you can drop down into LangGraph directly, because LangChain was built on top of it from the start, not as something separate.
For now, all you need to hold onto is this: LangChain is the friendly front door. LangGraph is the engine running quietly behind that door. You’ll walk through that front door for the rest of this course, and meet the engine properly in the next one.
And where does LangSmith fit?
There’s a third name you’ll see mentioned constantly around LangChain: LangSmith. Let’s define it clearly, in one sentence, and then set it aside until much later in this course, because it deserves its own proper treatment when we reach observability.
LangSmith is a separate, companion tool for watching, recording, and evaluating exactly what your LangChain (or LangGraph) application actually did — every model call, every tool call, every decision — after the fact, so you can debug it and measure how well it’s really performing.
Here’s a simple way to place all three names relative to each other:
flowchart LR
LG["LangGraph\nruns the workflow"]
LC["LangChain\nbuilds the workflow's pieces"]
LS["LangSmith\nwatches what happened"]
LC --> LG
LG -.observed by.-> LS
We are not going to teach LangSmith deeply in this course — that’s genuinely its own subject, and it belongs properly in the observability module later on, and even more so in the dedicated production course after that. For now, just remember it exists, and remember its one job: watching and recording, not building.
One more piece of the map: what’s actually inside the langchain package itself
There’s one last, very practical thing worth understanding before you start installing anything in the next module — because it will directly explain a strange-looking import you’re going to see constantly.
LangChain isn’t one single package. It’s split into several, and understanding why will save you real confusion later:
langchain-corecontains the foundational pieces — the shared interfaces that everything else builds on. You won’t often import from this directly, but everything else depends on it.langchaincontains the main, current building blocks you’ll use constantly:langchain.chat_models,langchain.messages,langchain.tools,langchain.agents.- Provider packages, like
langchain-openaiandlangchain-google-genai, contain the specific code needed to actually talk to each company’s servers. This is exactly why you had topip installa separate package for each provider. langchain-classiccontains everything considered legacy — the olderLLMChain, the older agent system — kept around only so old projects don’t suddenly break. We mentioned this briefly in Module 1, and now you know exactly why it’s a separate package rather than just being deleted: so nobody’s existing, working code gets broken by LangChain’s own cleanup.
You don’t need to memorize this list right now. Just recognize the shape of it, so that when you see an import like from langchain_google_genai import ChatGoogleGenerativeAI in a later module, you immediately understand why it’s coming from a separate package, rather than being confused by it.
Let’s actually see this map in one small piece of real code
We’ve spent this whole module in diagrams and definitions. Let’s ground it with one small, genuine example — proof that these aren’t just boxes on a whiteboard, but real, separate pieces of code you can literally point to.
# Models — talking to the AI provider
from langchain.chat_models import init_chat_model
# Tools — giving the model something it can ask to run
from langchain.tools import tool
# Agents — the loop that ties Models and Tools together
from langchain.agents import create_agent
@tool
def add_numbers(a: int, b: int) -> int:
"""Add two whole numbers together."""
return a + b
model = init_chat_model("openai:gpt-4o-mini")
# model = init_chat_model("google_genai:gemini-2.0-flash")
agent = create_agent(model=model, tools=[add_numbers])
result = agent.invoke({"messages": [{"role": "user", "content": "What is 238 plus 47?"}]})
print(result["messages"][-1].content)
Don’t worry about fully understanding @tool or create_agent yet — you’ll get several entire modules dedicated to each of them, with plenty of time to build up to this properly. Right now, just notice something simple but important: every single import in this file comes from exactly the part of the map you’d expect. chat_models for Models. tools for Tools. agents for the Agent that ties them together. The map isn’t an abstraction sitting somewhere separate from the code — it is the code’s own organization.
What you should take away from this module
You now have a real map, not just a list of words:
- Models, Tools, Retrieval are the raw ingredients.
- Agent is the loop that combines them.
- Middleware lets you customize that loop without rewriting it.
- State is what the loop remembers as it runs.
- LangGraph is the lower-level engine LangChain’s agents are quietly built on top of.
- LangSmith is the separate tool that watches and records what actually happened, for later inspection.
- The
langchainpackage itself is deliberately split — core, main package, provider-specific packages, and a legacylangchain-classicpackage — specifically so the framework can keep evolving without breaking everyone’s existing projects.
Every module from here forward will slot neatly into one of these boxes. When you learn about SystemMessage next module, that’s Models. When you learn about @tool, that’s Tools. When you eventually learn about PIIMiddleware, that’s Middleware. You now have somewhere to put each new idea as it arrives.
Where this goes next
In the next module, you’ll stop looking at the map and actually set up a real project — a proper folder structure, a .env file for your API keys, and the exact packages you need installed, so every module after this one can focus purely on the concept being taught, not on setup.
- Author
- TechByteByByte Editorial Team
- Reviewed by
- TechByteByByte Admin
- Published
- Last reviewed