LangChain Tutorial: Building Your First AI Agent (Free, Step-by-Step)
What LangChain actually is
Strip away the abstractions and LangChain is a library for composing four things: a prompt template, a model call, an output parser, and — optionally — tools and memory bolted onto that core loop. It is not a replacement for understanding how the underlying model works, and it will not make a badly-designed prompt good. What it gives you is a consistent interface across model providers and a set of prebuilt patterns (agents, retrieval chains, memory stores) so you are not rebuilding them from scratch on every project.
The best way to learn it is to build the same small thing four times, adding one capability at a time: a plain chain, a chain with a tool, a chain with memory, and a chain with retrieval.
1. The core building block: a chain
A chain is a prompt template piped into a model piped into an output parser. In LangChain's expression language, that pipe is literal — you compose these three pieces with the `|` operator, and the result is itself a runnable object you can call, stream, or batch.
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
prompt = ChatPromptTemplate.from_template(
"Explain {topic} to a {audience} in three sentences."
)
model = ChatOpenAI(model="gpt-4o-mini")
parser = StrOutputParser()
chain = prompt | model | parser
result = chain.invoke({"topic": "hash maps", "audience": "beginner"})
print(result)2. Giving it a tool
A tool is a plain function with a description the model can read, so it knows when to call it. Bind one or more tools to the model, and instead of always returning text, the model can return a structured request to call a tool — your code executes it and feeds the result back in.
from langchain_core.tools import tool
@tool
def get_stock_price(ticker: str) -> str:
"""Look up the current price for a given stock ticker."""
price = fetch_price(ticker) # your own implementation
return f"{ticker} is trading at ${price}"
model_with_tools = model.bind_tools([get_stock_price])
response = model_with_tools.invoke("What's AAPL trading at?")
# response.tool_calls will contain the tool the model wants to run3. Adding memory
Without memory, every call to a chain is stateless — the model has no idea what you asked five seconds ago. Memory is just a way of collecting prior messages and re-injecting them into the prompt on the next call. The simplest version keeps the full conversation history; production systems usually summarize or trim older turns once the conversation gets long, to control token cost and stay inside the context window.
The pattern to remember: memory is not magic state living inside the model — it is your code re-sending the relevant history on every single call. That fact is worth stating explicitly in an interview, because it is exactly the kind of detail that separates "I used LangChain memory" from actually understanding what memory costs in tokens and latency.
4. Retrieval: grounding answers in your own data
Retrieval-augmented generation swaps "hope the model already knows this" for "look it up first." You embed your documents into a vector store, retrieve the most relevant chunks for a given query, and stuff them into the prompt alongside the question so the model answers from the retrieved text instead of its training data.
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
vectorstore = FAISS.from_texts(document_chunks, OpenAIEmbeddings())
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
relevant_docs = retriever.invoke("What is our refund policy?")
context = "\n\n".join(doc.page_content for doc in relevant_docs)
rag_prompt = ChatPromptTemplate.from_template(
"Answer using only this context:\n{context}\n\nQuestion: {question}"
)
rag_chain = rag_prompt | model | parser
answer = rag_chain.invoke({"context": context, "question": "What is our refund policy?"})The pitfall almost everyone hits
The most common mistake is chaining too much "magic" together — high-level agent executors that hide exactly what prompt is being sent and when a tool is being called — and then being unable to debug why the agent did something unexpected. Start with the explicit, composable pieces shown above before reaching for the fully automatic agent executors. You will understand failures faster, and you will be able to explain your own system in an interview instead of gesturing at a black box.
Once a fixed sequence like this stops being enough — once you need looping, branching, or a pause for human approval — that is the point to move from a plain chain to LangGraph, which we cover in a companion tutorial. For coordinating multiple specialized agents built this way, our LangChain Agent Patterns premium lab and multi-agent orchestration system design lab go further into executor and memory-store patterns.