Mastering LangChain: A Comprehensive Guide.
A systematic approach to mastering the LangChain ecosystem's open-source agent frameworks, guided by the Feynman Technique, Simon Learning Method, SQ3R Reading Method, and Cornell Note-Taking System.
1. Overview & Questions (SQ3R: Survey & Question)
SQ3R Step 1: Survey the big picture and formulate key questions.
What Are LangChain and LangGraph?
LangChain is an open-source platform for agent engineering. It provides the core abstractions for building AI agents: a unified model interface (Model), tools (Tool), prompts (Prompt), and middleware (Middleware). Through create_agent, it offers a minimal yet highly configurable agent harness — compose exactly the agent your use case needs from model, tools, prompt, and middleware.
LangGraph is the low-level orchestration framework and runtime within the LangChain ecosystem, designed for building, managing, and deploying long-running, stateful agents. It models agent workflows as graphs — composed of nodes, edges, and state — providing production-grade capabilities like durable execution, human-in-the-loop, and streaming. Inspired by Google's Pregel system and Apache Beam, LangGraph is trusted by companies like Klarna, Uber, and J.P. Morgan.
Understanding the product hierarchy:
| Product | Role | When to Use |
|---|---|---|
| Deep Agents | High-level agent harness ("batteries included") | Quickly build agents with planning, subagents, and filesystem tools |
| LangChain | Agent framework (core abstractions) | Custom agent harness with flexible model/tools/middleware composition |
| LangGraph | Orchestration runtime (low-level control) | Fine-grained workflow control, persistence, human-in-the-loop |
| LangSmith | Observability platform | Tracing, debugging, evaluating, and deploying agents |
DeepSeek's Position in the AI Ecosystem
DeepSeek is a Chinese AI company offering high-performance, ultra-low-cost large language model APIs. As of 2026, its core models include:
- DeepSeek-V4-Pro / V4-Flash: Flagship general-purpose models, rivaling GPT-5.5 and Claude Opus 4.7 at roughly 1/10th the cost
- DeepSeek-R1: A reasoning model supporting chain-of-thought "Thinking Mode" for superior performance on complex reasoning tasks
- DeepSeek Coder: A model specialized for programming tasks
Key advantages of the DeepSeek API:
- OpenAI SDK compatible: Simply change
base_urlto switch - Anthropic interface support: Also compatible with the Anthropic API format
- Extremely low pricing: As low as $0.0028 per million tokens (cache hit)
- Thinking Mode: Built-in chain-of-thought reasoning with controllable
reasoning_effort
Key Questions
- What is the relationship between LangChain and LangGraph? — LangChain provides high-level agent abstractions; LangGraph provides the low-level graph orchestration runtime. LangChain's agents are built on top of LangGraph.
- When should I use LangChain vs. LangGraph directly? — Use LangChain's
create_agentfor simple agents; use LangGraph when you need fine-grained workflow control, state persistence, or human-in-the-loop. - How does DeepSeek integrate with the LangChain ecosystem? — Through the OpenAI-compatible interface, just set
base_urlto the DeepSeek API endpoint. - What is the core programming model of StateGraph? — Define State, add Nodes, add Edges, compile, invoke.
Technology Landscape
LangChain Ecosystem
├── Core Fundamentals
│ ├── Models — Unified LLM interface
│ ├── Messages — Standardized message format
│ ├── Tools — Functions the agent can call
│ ├── Agents — The create_agent harness
│ └── Middleware — Behavior interception and enhancement
├── LangGraph Orchestration
│ ├── StateGraph — Stateful graph definition
│ ├── Node — Functions that execute logic
│ ├── Edge — Connections that control flow
│ ├── Conditional Edges — Dynamic routing
│ ├── Persistence — Checkpoint management
│ ├── Interrupts — Human-in-the-Loop
│ └── Streaming
├── DeepSeek Integration
│ ├── OpenAI-compatible interface
│ ├── Thinking Mode (Chain-of-Thought)
│ ├── Reasoning effort control
│ └── Streaming and non-streaming calls
└── Production Capabilities
├── LangSmith Observability
├── Deployment & Scaling
├── Evaluation & Testing
└── Multi-Agent Collaboration
2. Explaining in Plain Language (Feynman Technique)
Feynman Technique: If you can't explain it in simple terms, you don't truly understand it.
Core Concepts Explained
LLM (Large Language Model)
An LLM is like a super-brain that has read millions of books. You give it text (a prompt), and it predicts what should come next. DeepSeek, GPT, and Claude are all LLMs.
from openai import OpenAI
# Using DeepSeek API (OpenAI-compatible format)
client = OpenAI(
api_key="your-deepseek-api-key",
base_url="https://api.deepseek.com"
)
response = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is an Agent?"}
]
)
print(response.choices[0].message.content)Agent
An Agent = LLM + Tools + Loop. Think of it as an assistant that can use tools: it doesn't just answer questions — it decides "I need to look this up" or "I need to calculate this," calls the appropriate tool, and delivers the final answer.
from langchain.agents import create_agent
def search_web(query: str) -> str:
"""Search the web for information."""
return f"Search results: information about {query}..."
def calculate(expression: str) -> str:
"""Evaluate a mathematical expression."""
return str(eval(expression))
agent = create_agent(
model="openai:deepseek-v4-flash",
tools=[search_web, calculate],
system_prompt="You are a helpful assistant that can search and calculate."
)
result = agent.invoke({
"messages": [{"role": "user", "content": "Calculate 3^10 for me"}]
})Chain
A Chain links multiple steps together. For example: extract user intent, call a search engine, then summarize the results with an LLM. In LangChain v1, create_agent has replaced the legacy Chain concept as the primary way to build agents.
Tool
Tools are functions the agent can call. Any Python function can become a tool — LangChain automatically generates tool descriptions from the function signature and docstring, telling the LLM when to use it.
def get_weather(city: str) -> str:
"""Get weather information for a given city."""
# In production, this would call a real weather API
return f"It's sunny in {city}, 22°C"
# Pass directly to create_agent
agent = create_agent(
model="openai:deepseek-v4-flash",
tools=[get_weather],
system_prompt="You are a weather assistant."
)Memory
Memory lets the agent remember previous conversations. Just like humans, if every conversation starts from scratch, you can't handle context references in multi-turn dialogs. LangGraph implements short-term and long-term memory through State and Checkpointer.
State
State is LangGraph's core data structure. It records all information during graph execution — conversation history, intermediate results, user inputs, etc. Each Node reads the state, executes logic, and returns updates.
from typing import Annotated
from typing_extensions import TypedDict
from operator import add
class AgentState(TypedDict):
messages: Annotated[list, add] # Message list; new messages are appended
current_step: str # Current stepGraph
A Graph is LangGraph's core programming model. You draw your workflow as a "flowchart" — each node is a processing step, each edge determines where to go next.
from langgraph.graph import StateGraph, MessagesState, START, END
def chatbot(state: MessagesState):
"""A node that processes user messages."""
return {"messages": [{"role": "ai", "content": "Hello! I'm your assistant."}]}
graph = StateGraph(MessagesState)
graph.add_node(chatbot)
graph.add_edge(START, "chatbot")
graph.add_edge("chatbot", END)
graph = graph.compile()
result = graph.invoke({"messages": [{"role": "user", "content": "Hello!"}]})Analogies
| Concept | Analogy |
|---|---|
| LLM | A knowledgeable consultant who can't take action |
| Agent | A versatile assistant who can both think and act |
| Tool | The assistant's toolbox (calculator, search engine, database...) |
| Chain | An assembly line — step one, step two, step three |
| State | The assistant's notebook — recording all intermediate info and conversation history |
| Graph | The complete workflow diagram — which path to take under what conditions |
| Middleware | Quality checkpoints in the workflow — intercept, inspect, modify each step |
| LangSmith | A surveillance camera — recording every move the agent makes |
Common Misconceptions Clarified
- Misconception: LangChain is an LLM. Reality: LangChain is an orchestration framework. It doesn't contain an LLM — it provides a unified interface to call various LLMs.
- Misconception: LangGraph only works with LangChain. Reality: LangGraph can be used standalone, without any LangChain dependency.
- Misconception: DeepSeek is a competitor to LangChain. Reality: DeepSeek is an LLM provider, complementary to LangChain — LangChain provides the framework, DeepSeek provides the model.
- Misconception: Agents must be complex. Reality: LangChain's
create_agentcan create a working agent in just a few lines of code.
3. Cone-Shaped Deep Dive (Simon Learning Method)
Simon Learning Method: Focused, layered progression from basics to mastery.
Layer 1: Core Fundamentals
LangChain Core Components
Models
LangChain provides a unified model interface so you can seamlessly swap providers without changing business code. DeepSeek integrates through the OpenAI-compatible interface:
# pip install -qU langchain "langchain[openai]"
from langchain.agents import create_agent
# Using a DeepSeek model
agent = create_agent(
model="openai:deepseek-v4-flash",
system_prompt="You are a helpful assistant."
)LangChain standardizes API differences across providers, supporting OpenAI, Anthropic, Google, DeepSeek, and many more.
Tools
Tools are the bridge between agents and the outside world. Any Python function can become a tool:
def lookup_database(query: str) -> str:
"""Query the database for information.
Args:
query: SQL query string
"""
# In production, connect to a real database
return f"Query results: 3 matching records found"
def send_email(to: str, subject: str, body: str) -> str:
"""Send an email.
Args:
to: Recipient email address
subject: Email subject line
body: Email body content
"""
return f"Email sent to {to}"The function's docstring and type annotations are automatically parsed by LangChain, telling the LLM what the tool does and what parameters it needs.
Middleware
Middleware is a key concept introduced in LangChain v1 for intercepting and modifying agent behavior during execution. Each middleware handles one concern and composes freely:
from langchain.agents import create_agent
agent = create_agent(
model="openai:deepseek-v4-flash",
tools=[get_weather],
system_prompt="You are an assistant.",
# Add prebuilt or custom middleware here
)LangGraph Basics: StateGraph
LangGraph's core programming model is StateGraph — build a graph with three elements:
- State: Shared state defining the graph's "memory"
- Node: Node functions executing specific logic
- Edge: Edges determining the next destination
from langgraph.graph import StateGraph, MessagesState, START, END
# 1. Define node functions
def greet(state: MessagesState):
"""A greeting node."""
return {"messages": [{"role": "ai", "content": "Hello! How can I help you?"}]}
# 2. Build the graph
graph = StateGraph(MessagesState)
graph.add_node(greet)
graph.add_edge(START, "greet") # From START to greet node
graph.add_edge("greet", END) # From greet to END
# 3. Compile and execute
app = graph.compile()
result = app.invoke({"messages": [{"role": "user", "content": "Hello"}]})DeepSeek API: Basic Usage
The DeepSeek API is fully compatible with the OpenAI format — switching requires only a base_url change:
from openai import OpenAI
client = OpenAI(
api_key="your-api-key",
base_url="https://api.deepseek.com"
)
# Basic chat
response = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain quantum computing"}
],
stream=False
)
print(response.choices[0].message.content)
# Streaming output
stream = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[{"role": "user", "content": "Write a poem about spring"}],
stream=True
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")Layer 2: Advanced Usage
Custom Agents and Tool Integration
from langchain.agents import create_agent
def search_knowledge_base(query: str) -> str:
"""Search the knowledge base for relevant documents.
Args:
query: Search keywords
"""
# In production, connect to a vector database
return f"Found documents related to '{query}': LangGraph is a..."
def create_summary(text: str, max_words: int = 100) -> str:
"""Generate a summary for the given text.
Args:
text: Original text
max_words: Maximum word count for the summary
"""
return f"Summary ({max_words} words max): {text[:50]}..."
agent = create_agent(
model="openai:deepseek-v4-flash",
tools=[search_knowledge_base, create_summary],
system_prompt="You are a research assistant. Search the knowledge base first, then summarize."
)
result = agent.invoke({
"messages": [{"role": "user", "content": "Look up LangGraph's core features and summarize them"}]
})RAG Pipeline (Retrieval-Augmented Generation)
from langchain.agents import create_agent
def retrieve_documents(query: str) -> str:
"""Retrieve relevant documents from a vector database.
Args:
query: User query
"""
# In production, use LangChain's retriever
return f"Retrieved 3 relevant documents: [Doc1...], [Doc2...], [Doc3...]"
rag_agent = create_agent(
model="openai:deepseek-v4-flash",
tools=[retrieve_documents],
system_prompt="""You are a RAG assistant.
1. First, use the retrieve_documents tool to fetch relevant documents
2. Answer the user's question based on the retrieved content
3. If no relevant information is found, honestly tell the user"""
)LangGraph Conditional Edges and Loops
Conditional edges give the graph branching decision capabilities — the key to implementing complex agent logic:
from typing import Literal
from langgraph.graph import StateGraph, MessagesState, START, END
def agent_node(state: MessagesState):
"""Agent thinking node."""
return {"messages": [{"role": "ai", "content": "I need to use a tool"}]}
def tool_node(state: MessagesState):
"""Tool execution node."""
return {"messages": [{"role": "tool", "content": "Tool execution result"}]}
def should_continue(state: MessagesState) -> Literal["tools", "__end__"]:
"""Routing function: decide whether to continue using tools or end."""
last_message = state["messages"][-1]
if hasattr(last_message, "tool_calls") and last_message.tool_calls:
return "tools"
return "__end__"
# Build an Agent graph with a loop
builder = StateGraph(MessagesState)
builder.add_node("agent", agent_node)
builder.add_node("tools", tool_node)
builder.add_edge(START, "agent")
builder.add_conditional_edges("agent", should_continue)
builder.add_edge("tools", "agent") # After tool execution, return to agent — forming a loop
app = builder.compile()This is the classic ReAct pattern: Agent thinks, calls a tool, observes the result, thinks again, and so on until reaching a final answer.
Human-in-the-Loop Interrupts
LangGraph's interrupt mechanism lets you pause execution at critical nodes and wait for human approval:
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.types import Command, interrupt
def human_review(state: MessagesState):
"""Pause execution and wait for human approval."""
answer = interrupt("Please review the agent's action. Continue?")
return {"messages": [{"role": "user", "content": answer}]}
builder = StateGraph(MessagesState)
builder.add_node("review", human_review)
builder.add_edge(START, "review")
builder.add_edge("review", END)
checkpointer = InMemorySaver()
app = builder.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "review-1"}}
# First invocation — pauses at the interrupt
stream = app.invoke({"messages": []}, config)
# After human review, resume execution
result = app.invoke(Command(resume="Continue"), config)DeepSeek Reasoning Model (Thinking Mode)
DeepSeek's "Thinking Mode" outputs a chain-of-thought reasoning trace before producing the final answer, significantly improving accuracy on complex tasks:
from openai import OpenAI
client = OpenAI(
api_key="your-api-key",
base_url="https://api.deepseek.com"
)
# Enable thinking mode
response = client.chat.completions.create(
model="deepseek-v4-pro",
messages=[
{"role": "user", "content": "Prove that the square root of 2 is irrational"}
],
reasoning_effort="high", # Control reasoning effort: high or max
extra_body={"thinking": {"type": "enabled"}},
)
# Access the reasoning process and final answer
reasoning = response.choices[0].message.reasoning_content # CoT reasoning trace
answer = response.choices[0].message.content # Final answer
print("=== Reasoning Process ===")
print(reasoning)
print("=== Final Answer ===")
print(answer)Key points about Thinking Mode:
reasoning_contentcontains the chain-of-thought reasoning processtemperature,top_p, and similar parameters are not supported (setting them won't cause errors but has no effect)- In multi-turn conversations,
reasoning_contentfrom turns without tool calls can be omitted; turns with tool calls must include it
Multi-Agent Collaboration
LangGraph supports multi-agent collaboration through subgraphs:
from langgraph.graph import StateGraph, MessagesState, START, END
def research_agent(state: MessagesState):
"""Research agent: responsible for information gathering."""
return {"messages": [{"role": "ai", "content": "Research complete. Here's the gathered info..."}]}
def writer_agent(state: MessagesState):
"""Writer agent: responsible for content generation."""
return {"messages": [{"role": "ai", "content": "Based on the research, generating the article..."}]}
def reviewer_agent(state: MessagesState):
"""Reviewer agent: responsible for quality checks."""
return {"messages": [{"role": "ai", "content": "Review passed."}]}
# Build the collaboration pipeline
builder = StateGraph(MessagesState)
builder.add_node("researcher", research_agent)
builder.add_node("writer", writer_agent)
builder.add_node("reviewer", reviewer_agent)
builder.add_edge(START, "researcher")
builder.add_edge("researcher", "writer")
builder.add_edge("writer", "reviewer")
builder.add_edge("reviewer", END)
app = builder.compile()Layer 3: Deep Dive
How LangGraph's Graph Execution Engine Works
LangGraph's underlying algorithm is based on message passing, inspired by Google's Pregel system. Execution proceeds in discrete "super-steps":
- All nodes start in an
inactivestate - A node becomes
activewhen it receives a new message (state update) - Active nodes execute their function and send updates
- Nodes with no incoming messages vote to
haltand becomeinactive - Execution terminates when all nodes are
inactiveand no messages are in transit
Parallel nodes belong to the same super-step; sequential nodes belong to separate super-steps. This model supports both parallel execution and determinism.
Persistent State Management
LangGraph implements state persistence through Checkpointers. A checkpoint is saved at the end of every super-step, which means:
- Agents can resume after failures, continuing from where they left off
- Time travel: return to any historical state and fork to explore alternative paths
- Long-running tasks can span multiple sessions
from langgraph.checkpoint.memory import InMemorySaver
# Use an in-memory Checkpointer (for development)
checkpointer = InMemorySaver()
# For production, use PostgreSQL, MongoDB, or other persistent storage
app = builder.compile(checkpointer=checkpointer)
# Each invoke uses a different thread_id to isolate state
config = {"configurable": {"thread_id": "user-123"}}
result = app.invoke({"messages": [...]}, config)Agent Architecture Design Patterns
ReAct Pattern (Reasoning + Acting)
The classic agent pattern: think, act, observe, loop. LangGraph's conditional edges + loop edges natively support this pattern.
Plan-and-Execute Pattern
First create a complete plan, then execute step by step. Ideal for complex multi-step tasks:
from typing import Literal
from langgraph.graph import StateGraph, START, END
from typing_extensions import TypedDict
class PlanState(TypedDict):
goal: str
plan: list[str]
current_step: int
results: list[str]
def planner(state: PlanState):
"""Create an execution plan."""
plan = ["Step 1: Gather information", "Step 2: Analyze data", "Step 3: Generate report"]
return {"plan": plan, "current_step": 0, "results": []}
def executor(state: PlanState):
"""Execute the current step."""
step = state["plan"][state["current_step"]]
return {
"results": state["results"] + [f"Completed: {step}"],
"current_step": state["current_step"] + 1
}
def should_continue(state: PlanState) -> Literal["executor", "__end__"]:
"""Are there more steps to execute?"""
if state["current_step"] < len(state["plan"]):
return "executor"
return "__end__"
builder = StateGraph(PlanState)
builder.add_node("planner", planner)
builder.add_node("executor", executor)
builder.add_edge(START, "planner")
builder.add_edge("planner", "executor")
builder.add_conditional_edges("executor", should_continue)
app = builder.compile()Multi-Agent Pattern
Multiple specialized agents collaborate through a router that distributes tasks. LangGraph implements cross-graph navigation through subgraphs and Command.
Production Deployment and Observability
LangSmith is the observability platform in the LangChain ecosystem, providing:
- Tracing: Visualize every execution step, state transition, and runtime metric of your agents
- Evaluation: Test and score agent behavior on production or offline datasets
- Prompt Engineering: Version control, collaborative optimization, A/B testing
- One-Click Deployment: Deploy agents via LangSmith Deployment
- LangSmith Engine: Automatically detect issues in traces and propose fixes
# Enable LangSmith tracing (just set environment variables)
import os
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_API_KEY"] = "your-langsmith-key"
# All agent executions are now automatically logged to LangSmith4. Key Notes (Cornell Note-Taking System)
Cornell Method: Cues/keywords on the left, detailed notes on the right, summary at the bottom.
Quick Reference Table
| Cue/Keyword | Detailed Notes |
|---|---|
create_agent | LangChain's core API; accepts model + tools + prompt + middleware, returns an invocable agent |
| StateGraph | LangGraph's main graph class, parameterized by a user-defined State object |
| State | The graph's shared data structure, typically defined with TypedDict; supports Reducer functions |
| Node | A function in the graph; receives state + config + runtime, returns state updates |
| Edge | Connects nodes: normal edges (fixed path) or conditional edges (dynamic routing) |
| Reducer | Defines how State updates are applied. Default is overwrite; use Annotated[list, add] for append |
| MessagesState | Prebuilt State with a messages field and add_messages Reducer |
| Checkpointer | State persistence component; supports InMemorySaver, PostgreSQL, MongoDB |
| interrupt | Pauses graph execution, waiting for external input (Human-in-the-Loop) |
| Command | Multi-purpose primitive: update + goto + resume — unifies state updates and control flow |
| Thinking Mode | DeepSeek's chain-of-thought mode, outputting CoT via reasoning_content |
| Middleware | Behavior interception mechanism introduced in LangChain v1; each middleware handles one concern |
| LangSmith | Observability platform: tracing, evaluation, prompt engineering, deployment |
Core API Quick Reference
| API / Class | Purpose | Example |
|---|---|---|
create_agent(model, tools, system_prompt) | Create a LangChain agent | agent = create_agent(model="openai:gpt-5.4", tools=[fn]) |
StateGraph(state_schema) | Create a LangGraph state graph | graph = StateGraph(MessagesState) |
graph.add_node(name, fn) | Add a node | graph.add_node("agent", agent_fn) |
graph.add_edge(from, to) | Add a normal edge | graph.add_edge(START, "agent") |
graph.add_conditional_edges(node, fn) | Add a conditional edge | graph.add_conditional_edges("agent", route_fn) |
graph.compile(checkpointer=...) | Compile the graph | app = graph.compile(checkpointer=InMemorySaver()) |
app.invoke(input, config) | Execute the graph | result = app.invoke({"messages": [...]}, config) |
interrupt(question) | Pause for human input | answer = interrupt("Confirm continue?") |
Command(update=..., goto=..., resume=...) | State update + control flow | Command(resume="yes") |
Send(node, state) | Dynamically send state to a node | Send("worker", {"item": x}) |
| DeepSeek API (OpenAI format) | Call DeepSeek models | client.chat.completions.create(model="deepseek-v4-flash", ...) |
| DeepSeek Thinking Mode | Enable reasoning mode | extra_body={"thinking": {"type": "enabled"}} |
Section Summary
The LangChain ecosystem provides a complete toolchain for agent engineering: LangChain offers the create_agent high-level abstraction, LangGraph provides the StateGraph low-level orchestration, and DeepSeek delivers cost-effective LLM capabilities. All three can be used independently or seamlessly combined. The core programming model is the graph: State defines data, Nodes define logic, Edges define flow, and after compilation you invoke to execute.
5. Review & Practice (SQ3R: Recite & Review)
SQ3R Final Steps: Recite key points and consolidate understanding through practice.
Core Points Review
- LangChain is an agent framework —
create_agentis the core API, composing agents from model + tools + prompt + middleware - LangGraph is an orchestration runtime — models complex workflows with StateGraph (State + Node + Edge), supporting persistence, interrupts, and streaming
- DeepSeek integrates via the OpenAI-compatible interface — just change
base_url; supports Thinking Mode (CoT reasoning) - LangSmith provides observability: tracing, evaluation, prompt engineering, one-click deployment
- LangChain's agents are built on top of LangGraph — they are layered, not competing, technologies
Hands-On Exercises
Exercise 1: Create a Simple DeepSeek Agent
from langchain.agents import create_agent
def get_time() -> str:
"""Get the current time."""
from datetime import datetime
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
agent = create_agent(
model="openai:deepseek-v4-flash",
tools=[get_time],
system_prompt="You are a time assistant."
)
result = agent.invoke({
"messages": [{"role": "user", "content": "What time is it now?"}]
})Exercise 2: Build a LangGraph with Conditional Routing
from typing import Literal
from langgraph.graph import StateGraph, MessagesState, START, END
def classifier(state: MessagesState):
"""Classify user intent."""
return {"messages": [{"role": "ai", "content": "Classification result: weather query"}]}
def weather_handler(state: MessagesState):
"""Handle weather queries."""
return {"messages": [{"role": "ai", "content": "Sunny today, 25°C"}]}
def general_handler(state: MessagesState):
"""Handle general questions."""
return {"messages": [{"role": "ai", "content": "I'm your assistant. How can I help?"}]}
def route(state: MessagesState) -> Literal["weather", "general"]:
last = state["messages"][-1].content if hasattr(state["messages"][-1], "content") else ""
if "weather" in str(last).lower():
return "weather"
return "general"
builder = StateGraph(MessagesState)
builder.add_node("classifier", classifier)
builder.add_node("weather", weather_handler)
builder.add_node("general", general_handler)
builder.add_edge(START, "classifier")
builder.add_conditional_edges("classifier", route, {
"weather": "weather",
"general": "general"
})
builder.add_edge("weather", END)
builder.add_edge("general", END)
app = builder.compile()Exercise 3: DeepSeek Thinking Mode + Tool Calls
from openai import OpenAI
import json
client = OpenAI(
api_key="your-api-key",
base_url="https://api.deepseek.com"
)
tools = [
{
"type": "function",
"function": {
"name": "get_population",
"description": "Get population data for a city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"}
},
"required": ["city"]
}
}
}
]
def get_population(city: str) -> str:
populations = {"New York": "8.3M", "London": "9.0M", "Tokyo": "14.0M"}
return populations.get(city, "Data not available")
# Tool calling with thinking mode
response = client.chat.completions.create(
model="deepseek-v4-pro",
messages=[{"role": "user", "content": "Which city has more people: New York or London?"}],
tools=tools,
reasoning_effort="high",
extra_body={"thinking": {"type": "enabled"}},
)
# Handle tool calls
if response.choices[0].message.tool_calls:
for tool_call in response.choices[0].message.tool_calls:
args = json.loads(tool_call.function.arguments)
result = get_population(**args)
print(f"Tool call {tool_call.function.name}({args}) = {result}")Common Pitfalls
- Forgetting to compile: StateGraph must be
.compile()d before invocation — calling invoke directly will raise an error - Incorrect State update strategy: The default Reducer is overwrite; to append to a list, use
Annotated[list, add] - Not returning reasoning_content in DeepSeek Thinking Mode: When tool calls are involved,
reasoning_contentmust be passed back to the API, or it returns a 400 error - Exceeding recursion limits: LangGraph's default recursion limit is 1000 steps. Complex agents may exceed this — use
RemainingStepsfor proactive detection - Confusing LangChain and LangGraph responsibilities: LangChain handles "what an agent is"; LangGraph handles "how an agent runs"
- Non-idempotent side effects in Nodes: LangGraph re-runs nodes when resuming from checkpoints. Ensure node logic is idempotent
Further Reading
- LangChain Official Docs — Complete guide to the LangChain agent framework
- LangGraph Official Docs — Detailed reference for the LangGraph orchestration runtime
- LangGraph Graph API Overview — Core StateGraph concepts
- DeepSeek API Docs — DeepSeek model usage and pricing
- DeepSeek Thinking Mode Guide — Detailed Thinking Mode documentation
- LangSmith Observability — Tracing and debugging agents
- LangChain Academy — Official learning courses