LangChain has quickly become one of the most talked-about frameworks in AI development.
It helps developers connect large language models (LLMs) with real-world tools, APIs, and data sources — essentially letting AI “do things” instead of just chatting.
But if you’re new to LangChain, the terminology can feel overwhelming: agents, runnables, memory, output parsers… what do they all mean?
This post breaks down LangChain’s core building blocks in simple terms, with real-world analogies and examples you can relate to — so you can start building smarter AI applications with confidence.
๐งฉ 1. Agent Executor (The “Brain Runner”)
What it is
In older versions of LangChain, you might have seen the term “chain.”
That’s now replaced with Agent Executor — the component that manages how an agent thinks, decides, and acts.
Think of it like a movie director:
-
The agent is the actor (decides what to do)
-
The tools are the props (things it can use)
-
The agent executor is the director who ensures everything happens in the right order
Real-world analogy
Imagine you’re using ChatGPT to plan a trip.
The agent executor is what takes your request (“Plan a 3-day trip to Japan”) and manages the entire process:
-
Ask the model what it needs to know
-
Call APIs for flights and hotels
-
Combine all results into one itinerary
It’s the brain that coordinates the steps.
๐งฉ Simple Example
from langchain.agents import AgentExecutor, load_tools, initialize_agent
from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI(model="gpt-4-turbo")
tools = load_tools(["serpapi"]) # Google search API
agent = initialize_agent(tools, llm, agent_type="zero-shot-react-description")
agent_executor = AgentExecutor.from_agent_and_tools(agent, tools)
agent_executor.invoke({"input": "Find the current weather in Tokyo"})The Agent Executor handles your request, figures out it needs the search tool, calls it, and combines results into a response.
๐งฐ 2. Tools — Giving the Agent Superpowers
Tools are the actions your agent can perform — like calling APIs, searching, or running code.
๐ง Example
from langchain.tools import tool
@tool
def multiply(a: int, b: int) -> int:
"""Multiply two numbers."""
return a * b๐ฌ 3.Messages — How the Model Communicates
LangChain structures all conversations as message objects — not plain strings — to keep context organized.
๐ป Example
from langchain.schema import SystemMessage, HumanMessage from langchain.chat_models import ChatOpenAI llm = ChatOpenAI(model="gpt-4-turbo") messages = [ SystemMessage(content="You are a helpful assistant."), HumanMessage(content="Explain LangChain in one sentence.") ] print(llm.invoke(messages).content)
⚙️ 4. Models — The AI Engine
The model is the LLM itself (e.g., GPT-4, Claude, Mistral).
LangChain provides unified interfaces for both chat and text models.
Example
from langchain.llms import OpenAI llm = OpenAI(model="gpt-3.5-turbo-instruct") response = llm.invoke("Summarize: LangChain helps connect LLMs to data and APIs.") print(response)
๐งฉ You can swap models easily — the interface stays consistent.
๐ 5. Short-Term Memory — Keeping Context
Memory stores recent conversation history so the AI can “remember” previous messages.
Example
from langchain.memory import ConversationBufferMemory from langchain.chains import ConversationChain from langchain.chat_models import ChatOpenAI memory = ConversationBufferMemory() llm = ChatOpenAI(model="gpt-4-turbo") conversation = ConversationChain(llm=llm, memory=memory) conversation.invoke({"input": "Hi, my name is Sam."}) conversation.invoke({"input": "What’s my name?"})
“Your name is Sam.”
LangChain remembered that from earlier thanks to memory.
6. Streaming — See the Response in Real Time
Streaming sends the model’s response token by token — great for live chat apps.
Example
from langchain.chat_models import ChatOpenAI def on_token(token): print(token, end="", flush=True) llm = ChatOpenAI(model="gpt-4-turbo", streaming=True, callbacks=[{"on_llm_new_token": on_token}]) llm.invoke("Explain LangChain in 10 words.")
You’ll see text appear word by word, like ChatGPT typing.
๐งฑ 7. Middleware (Runnable Lambda) — Custom Logic Blocks
Runnable Lambda lets you wrap any custom logic into a reusable block.
Example
from langchain.schema.runnable import RunnableLambda uppercase = RunnableLambda(lambda text: text.upper()) reverse = RunnableLambda(lambda text: text[::-1]) workflow = uppercase | reverse print(workflow.invoke("langchain"))
๐งฉ Output:
“NIAHCGNAL”
Think of Runnable Lambdas as LEGO blocks — each does one thing, but you can snap them together to build workflows.
๐ง8. Piping Components(Connecting the Pieces)
LangChain allows you to pipe runnables together using the | operator — just like connecting pipes.
Example
clean_text = RunnableLambda(lambda x: x.strip()) uppercase = RunnableLambda(lambda x: x.upper()) workflow = clean_text | uppercase print(workflow.invoke(" hello world ")) # Output: HELLO WORLD
๐ 9. LangChain Hub — The Community Library
LangChain Hub is a public repository where developers share reusable prompts, agents, and chains.
It’s like GitHub for AI workflows — browse, copy, and reuse tested components.
๐งฉ 10. Agent Models & RAG Applications
LangChain applications typically fall into two buckets:
-
Agents: AI systems that can decide which tools to use (search, API calls, etc.)
-
RAG (Retrieval-Augmented Generation): AI that pulls external knowledge before answering.
Example
-
Agent: “Find today’s weather and summarize it.”
-
RAG: “Summarize our internal Q2 financial report.”
Together, these cover ~80% of real-world LLM apps.
๐ 11. Runnable Interface — Consistency for Developers
Anything that can be “run” (a model, tool, or lambda) implements the same Runnable Interface — making LangChain modular and composable.
Think of it as the USB port of LangChain — any compatible part fits.
๐ง 12. Memory Types
LangChain offers multiple memory types:
-
ConversationBufferMemory– remembers everything -
ConversationBufferWindowMemory– remembers the last N messages -
ConversationKGMemory– builds a mini knowledge graph
Analogy
Like chat history — it helps the model keep context.
13. Structured Output — Making AI Results
Developer-Friendly
Structured output ensures model results are machine-readable — JSON, Pydantic, or tables.
Example
from langchain.output_parsers import PydanticOutputParser from pydantic import BaseModel class CityInfo(BaseModel): name: str country: str parser = PydanticOutputParser(pydantic_object=CityInfo) result = parser.parse('{"name": "Tokyo", "country": "Japan"}') print(result.name) # Tokyo
๐ ReAct Agent Core Concepts
⚙️ Core Components of a LangChain ReAct Agent
| Component | Description | Analogy |
|---|---|---|
| ๐ง LLM | The brain that reasons and plans | The thinker |
| ๐งฐ Tools | APIs or functions for data/actions | The hands |
| ๐งพ Prompt | The structured reasoning format | The script |
Example ReAct Prompt Template
You are a helpful agent that can reason and act. Use this format: Thought: reason about what to do Action: tool to use Action Input: tool input Observation: result of that action Repeat until you have the Final Answer.
๐ The Thought–Action–Observation Loop
The ReAct agent’s reasoning loop:
Thought → Action → Observation → (repeat) → Final Answer
Example
Thought: Need the capital of France Action: Search("Capital of France") Observation: "Paris" Thought: Now find weather in Paris Action: WeatherAPI("Paris") Observation: "21°C and clear" Final Answer: Paris, 21°C clear
๐งญ How the Agent Refines Actions
After each Observation, the agent refines its Thought and decides the next Action — a self-correcting loop.
Example
Thought: Find Elon Musk’s birth year Action: Search("Elon Musk birth year") Observation: "Born 1971" Thought: Calculate his age Action: Calculator("2025 - 1971") Observation: "54" Final Answer: Elon Musk is 54 years old.
User Input ↓ [Thought] → “I need data” ↓ [Action] → “Call search tool” ↓ [Observation] → “Got result” ↓ [Thought] → “Use calculator now” ↓ [Final Answer]
Full Example — All Components Combined
from langchain.chat_models import ChatOpenAI from langchain.agents import initialize_agent, load_tools from langchain.memory import ConversationBufferMemory from langchain.output_parsers import PydanticOutputParser from pydantic import BaseModel class CityInfo(BaseModel): city: str country: str parser = PydanticOutputParser(pydantic_object=CityInfo) memory = ConversationBufferMemory() llm = ChatOpenAI(model="gpt-4-turbo", streaming=True) tools = load_tools(["serpapi"]) agent = initialize_agent(tools, llm, agent_type="zero-shot-react-description", memory=memory) response = agent.invoke({"input": "Find the country for the city Tokyo in JSON format."}) parsed = parser.parse(response["output"]) print(parsed.country)
๐ง Step-by-Step Flow with Inputs and Outputs
๐ INPUT
User provides:
{"input": "Find the country for the city Tokyo in JSON format."}
You’re asking the agent to find which country Tokyo belongs to and return it in JSON.
⚙️ STAGE 1: Agent Executor
- The Agent Executor receives your input.
- It decides how to solve the problem (maybe it needs to use a tool).
- It parses your request and sends it to the LLM for reasoning.
๐งฉ Intermediate Output (LLM reasoning trace)
Thought: I need to find out which country Tokyo is in. Action: Search API Action Input: "Tokyo" Observation: "Tokyo is the capital of Japan." Final Answer: {"city": "Tokyo", "country": "Japan"}
๐งฐ STAGE 2: Tools (Search API)
The serpapi tool performs a real-time web search.
Example search query:
"What country is Tokyo in?"
๐ Intermediate Tool Output:
{
"answer": "Tokyo is the capital of Japan."
}
{ "answer": "Tokyo is the capital of Japan." }
This output goes back to the agent, which now knows the answer.
๐ง STAGE 3: Memory
Before finalizing, the ConversationBufferMemory stores the context:
If you later ask: “Now find the country for Paris.”
The memory helps it understand you’re continuing the same pattern.
๐งฎ STAGE 4: LLM (Language Model)
The LLM generates the final formatted JSON text based on all the gathered information and your prompt requirement.
Output from model (raw text):
{"city": "Tokyo", "country": "Japan"}
This is text, not Python data — so we’ll cleanly parse it next.
๐งพ STAGE 5: Output Parser (Pydantic)
Now the PydanticOutputParser takes the model’s string output and converts it into a strongly typed Python object.
Parsing:
parsed = parser.parse('{"city": "Tokyo", "country": "Japan"}')
Parsed Object:
CityInfo(city='Tokyo', country='Japan')

