LANGCHAIN
Last reviewed: 2026-06-16
Purpose: Comprehensive knowledge-base article on the LangChain framework โ chains, agents, tools, memory, retrievers, LangChain Expression Language (LCEL), tool/function calling, RAG, agent types, and LangSmith observability.
Table of Contents
- Core Concepts
- Chains
- Agents
- Tools
- Memory
- Retrievers
- LangChain Expression Language (LCEL)
- Tool / Function Calling
- RAG Chains
- Agents: ReAct & OpenAI Tools
- LangSmith Tracing
- Complete Code Examples
- Example 1: LCEL Chain with Memory
- Example 2: RAG Retrieval Chain
- Example 3: ReAct Agent with Tools
- References
Core Concepts
Chains
A chain is a sequence of calls โ to an LLM, a tool, a retriever, or arbitrary logic โ composed together so the output of one step becomes the input of the next. LangChain provides ready-made chains (LLMChain, ConversationChain, RetrievalQA) and the ability to build fully custom chains with LCEL.
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
prompt = ChatPromptTemplate.from_template("Tell me a {adjective} joke about {topic}.")
model = ChatOpenAI(model="gpt-4o")
chain = prompt | model
print(chain.invoke({"adjective": "funny", "topic": "programmers"}))
Agents
An agent uses an LLM to decide which action to take next โ which tool to call, whether to call one at all, or when to return a final answer. Unlike a fixed chain, an agent loops: observe โ think โ act โ observe โ ... โ final answer. The main built-in agent types are:
| Agent Type | Description |
|---|---|
openai-tools |
Uses OpenAI's native tool-calling API. Best for function calling. |
react |
ReAct (Reason + Act) pattern. Prompt-driven reasoning with tool invocations. |
structured-chat |
Similar to ReAct but tool inputs are structured JSON. |
xml |
ReAct agent that formats thoughts and actions as XML. |
json-chat |
JSON-encoded agent steps; useful for structured output. |
Tools
Tools are functions an agent can invoke. LangChain wraps any callable as a Tool with a name, description, and argument schema. The description is critical โ the LLM reads it to decide which tool to use.
from langchain_core.tools import tool
@tool
def get_weather(location: str) -> str:
"""Return the current weather for a given city."""
# ... API call ...
return f"Sunny, 22ยฐC in {location}"
Built-in toolkits include: SQLDatabaseToolkit, ArxivQueryRun, TavilySearch, WikipediaQueryRun, and many more.
Memory
Memory gives conversational state to chains and agents. Without memory, each call is stateless. Types:
| Memory Class | Behavior |
|---|---|
ConversationBufferMemory |
Stores the full message history. |
ConversationBufferWindowMemory |
Keeps only the last k turns. |
ConversationSummaryMemory |
Summarizes past conversation instead of storing raw history. |
VectorStoreRetrieverMemory |
Stores memories in a vector store; retrieves the most relevant past context. |
ChatMessageHistory |
Persists history manually (e.g. Redis, SQL, file). |
In LCEL you attach memory with RunnableWithMessageHistory.
Retrievers
A retriever fetches relevant documents from a vector store (or other index) based on an unstructured query. LangChain normalises all retrieval backends (Chroma, Pinecone, Qdrant, Weaviate, FAISS, etc.) behind the BaseRetriever interface.
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
vectorstore = Chroma.from_documents(docs, embedding=OpenAIEmbeddings())
retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 4})
You compose a retriever into a chain with LCEL's itemgetter pipe or dedicated RetrievalQA / create_retrieval_chain helpers.
LangChain Expression Language (LCEL)
LCEL is the declarative way to compose LangChain components using the | (pipe) operator. Every LCEL object implements the Runnable interface.
Key Runnable methods:
| Method | Purpose |
|---|---|
.invoke(input) |
Run synchronously with a single input. |
.batch(inputs) |
Run a list of inputs. |
.stream(input) |
Stream output token-by-token. |
.astream_events(input) |
Stream structured events (start/end of each step). |
Composition examples:
# Simple pipe
chain = prompt | model | output_parser
# Parallel branching
from langchain_core.runnables import RunnableParallel
chain = RunnableParallel({"answer": prompt | model, "date": lambda _: date.today()})
RunnablePassthrough passes input unchanged or assigns intermediate values:
from langchain_core.runnables import RunnablePassthrough
chain = {"context": retriever, "question": RunnablePassthrough()} | prompt | model
RunnableWithMessageHistory adds memory to any LCEL chain:
chain = prompt | model
with_history = RunnableWithMessageHistory(
chain,
get_session_history=lambda sid: SQLChatMessageHistory(session_id=sid, connection="sqlite:///history.db"),
input_messages_key="input",
history_messages_key="history"
)
LCEL is the recommended way to build all LangChain applications going forward. Legacy Chain subclasses are still supported but not encouraged for new projects.
Tool / Function Calling
OpenAI, Anthropic, Google, and other providers expose a native tool-calling (a.k.a. function calling) API. LangChain binds tools to the model and lets the model decide when and with what arguments to call them.
Binding tools to a model:
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
@tool
def multiply(a: int, b: int) -> int:
"""Multiply two integers."""
return a * b
model = ChatOpenAI(model="gpt-4o").bind_tools([multiply])
response = model.invoke("What is 123 * 456?")
# response.tool_calls -> [{"name": "multiply", "args": {"a": 123, "b": 456}, ...}]
To actually execute the tool call and feed the result back to the model, use a tool executor or an agent loop. LangGraph is the recommended engine for production-grade tool-calling loops.
RAG Chains
Retrieval-Augmented Generation (RAG) chains answer questions by first retrieving relevant documents, then feeding them as context to the LLM.
Classic RAG pipeline (LCEL):
from operator import itemgetter
from langchain_core.runnables import RunnablePassthrough
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
rag_prompt = ChatPromptTemplate.from_template(
"Answer using only the context below.\n\nContext:\n{context}\n\nQuestion: {question}"
)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| rag_prompt
| model
| StrOutputParser()
)
rag_chain.invoke("What is the capital of France?")
Production enhancements:
- Query transformation โ rewrite the user query before retrieval (e.g., HyDE, Multi-Query).
- Document compression โ rerank retrieved docs with a cross-encoder (
ContextualCompressionRetriever). - Multi-modal RAG โ embed images and text jointly (e.g., CLIP + GPT-4o).
- Graph RAG โ retrieve from a knowledge graph in addition to vector search.
LangChain's create_retrieval_chain and create_history_aware_retriever helpers provide battle-tested templates for these patterns.
Agents: ReAct & OpenAI Tools Agent
ReAct Agent
The ReAct (Reason + Act) agent interleaves reasoning traces with tool calls. LangChain's create_react_agent function builds one:
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o")
tools = [get_weather, multiply]
agent = create_react_agent(model, tools)
for event in agent.stream({"messages": [("human", "What's the weather in Paris?")]}):
for key, value in event.items():
if "messages" in value:
print(value["messages"][-1].content)
The agent loops โ LLM decides a tool call, the tool executes, the result comes back, and the LLM either calls another tool or produces the final answer.
OpenAI Tools Agent
The openai-tools agent uses the model's native tool-calling API directly, resulting in fewer prompt tokens and more reliable structured arguments:
from langchain.agents import create_tool_calling_agent, AgentExecutor
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant with access to tools."),
("placeholder", "{chat_history}"),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
])
agent = create_tool_calling_agent(model, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools)
executor.invoke({"input": "Multiply 42 by 99 and tell me the result."})
Key differences from ReAct:
- openai-tools uses structured function-calling arguments; ReAct formats tool calls in plain text.
- openai-tools is generally more reliable and cheaper for tool-heavy tasks.
- ReAct can work with any LLM (including local models); openai-tools requires a provider that supports tool calling.
LangSmith Tracing
LangSmith is LangChain's observability and evaluation platform. It captures traces of every run (LLM calls, retriever queries, tool invocations) for debugging and improvement.
Setup (one line):
export LANGCHAIN_TRACING_V2=true
export LANGCHAIN_API_KEY=<your-langsmith-api-key>
export LANGCHAIN_PROJECT=my-project-name
Alternatively, configure in code:
from langsmith import Client
import os
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "lsv2_..."
os.environ["LANGCHAIN_PROJECT"] = "my-kb-agent"
client = Client()
What gets traced:
- Every invoke, stream, batch call.
- Token counts, latency, model name, temperature.
- Full input/output payloads per step.
- Retriever queries and returned document chunks.
- Tool names, input arguments, and return values.
Evaluation โ LangSmith supports dataset-driven evaluation:
from langsmith import evaluate
from langsmith.schemas import Example, Run
def accuracy(run: Run, example: Example) -> dict:
predicted = run.outputs.get("output", "")
reference = example.outputs.get("output", "")
return {"score": predicted.strip() == reference.strip()}
results = evaluate(
lambda inputs: rag_chain.invoke(inputs["question"]),
data="my-rag-dataset",
evaluators=[accuracy],
)
Best practices:
- Tag runs with metadata (langchain_metadata={"env": "prod", "user_id": "abc"}).
- Use the LangSmith UI to trace failed turns by filtering on error status.
- Export traces as datasets for regression testing before model deploys.
Complete Code Examples
Example 1: LCEL Chain with Memory
Build a conversational assistant that remembers the last k turns.
# requirements: pip install langchain langchain-openai langchain-community
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import SQLChatMessageHistory
# --- Setup ---
model = ChatOpenAI(model="gpt-4o", temperature=0.7)
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant. Answer concisely."),
MessagesPlaceholder(variable_name="history"),
("human", "{input}"),
])
chain = prompt | model
# --- Wrap with memory backed by SQLite ---
chain_with_memory = RunnableWithMessageHistory(
chain,
get_session_history=lambda sid: SQLChatMessageHistory(
session_id=sid,
connection="sqlite:///conversation_history.db"
),
input_messages_key="input",
history_messages_key="history",
)
# --- Usage ---
config = {"configurable": {"session_id": "user-session-1"}}
reply1 = chain_with_memory.invoke({"input": "Hi! My name is Paul."}, config)
print(reply1.content)
# -> "Hello Paul! How can I help you today?"
reply2 = chain_with_memory.invoke({"input": "What is my name?"}, config)
print(reply2.content)
# -> "Your name is Paul."
Example 2: RAG Retrieval Chain
A complete RAG pipeline using Chroma as the vector store.
# requirements: pip install langchain langchain-openai chromadb pypdf
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# --- 1. Load and chunk documents ---
loader = PyPDFLoader("handbook.pdf")
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
chunks = splitter.split_documents(docs)
# --- 2. Embed and store ---
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(chunks, embedding=embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
# --- 3. Build the RAG chain ---
template = """Answer the question using ONLY the following context.
Context:
{context}
Question: {question}
Answer:"""
prompt = ChatPromptTemplate.from_template(template)
def format_docs(docs):
return "\n\n".join(d.page_content for d in docs)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| ChatOpenAI(model="gpt-4o")
| StrOutputParser()
)
# --- 4. Query ---
answer = rag_chain.invoke("What is the company holiday policy?")
print(answer)
Example 3: ReAct Agent with Tools
A ReAct agent using LangGraph that can search the web (via Tavily) and do basic arithmetic.
# requirements: pip install langchain langchain-openai langgraph tavily-python
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langgraph.prebuilt import create_react_agent
# --- Define tools ---
@tool
def multiply(a: int, b: int) -> int:
"""Multiply two integers and return the product."""
return a * b
@tool
def add(a: int, b: int) -> int:
"""Add two integers and return the sum."""
return a + b
@tool
def web_search(query: str) -> str:
"""Search the web for current information using Tavily."""
from tavily import TavilyClient
client = TavilyClient(api_key="<your-tavily-key>")
results = client.search(query, max_results=3)
return "\n".join(r["content"] for r in results["results"])
# --- Build the agent ---
model = ChatOpenAI(model="gpt-4o", temperature=0)
tools = [multiply, add, web_search]
agent = create_react_agent(model, tools)
# --- Run ---
def run_agent(query: str):
for event in agent.stream(
{"messages": [("human", query)]},
stream_mode="values",
):
msg = event["messages"][-1]
if msg.content:
print(f"{msg.type.upper()}: {msg.content}")
run_agent("What is 2024 * 37? Also, what is the latest LangChain version?")
References
| Resource | Link |
|---|---|
| LangChain Docs | https://python.langchain.com/docs |
| LangChain Expression Language | https://python.langchain.com/docs/concepts/lcel/ |
| LangGraph | https://langchain-ai.github.io/langgraph/ |
| LangSmith | https://smith.langchain.com |
| Tool/Function Calling | https://python.langchain.com/docs/concepts/tools/ |
| RAG patterns | https://python.langchain.com/docs/tutorials/rag/ |
| Agents overview | https://python.langchain.com/docs/concepts/agents/ |
| GitHub | https://github.com/langchain-ai/langchain |