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:

ProductRoleWhen to Use
Deep AgentsHigh-level agent harness ("batteries included")Quickly build agents with planning, subagents, and filesystem tools
LangChainAgent framework (core abstractions)Custom agent harness with flexible model/tools/middleware composition
LangGraphOrchestration runtime (low-level control)Fine-grained workflow control, persistence, human-in-the-loop
LangSmithObservability platformTracing, 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_url to 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_agent for 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_url to 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 step

Graph

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

ConceptAnalogy
LLMA knowledgeable consultant who can't take action
AgentA versatile assistant who can both think and act
ToolThe assistant's toolbox (calculator, search engine, database...)
ChainAn assembly line — step one, step two, step three
StateThe assistant's notebook — recording all intermediate info and conversation history
GraphThe complete workflow diagram — which path to take under what conditions
MiddlewareQuality checkpoints in the workflow — intercept, inspect, modify each step
LangSmithA surveillance camera — recording every move the agent makes

Common Misconceptions Clarified

  1. 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.
  2. Misconception: LangGraph only works with LangChain. Reality: LangGraph can be used standalone, without any LangChain dependency.
  3. Misconception: DeepSeek is a competitor to LangChain. Reality: DeepSeek is an LLM provider, complementary to LangChain — LangChain provides the framework, DeepSeek provides the model.
  4. Misconception: Agents must be complex. Reality: LangChain's create_agent can 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:

  1. State: Shared state defining the graph's "memory"
  2. Node: Node functions executing specific logic
  3. 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_content contains the chain-of-thought reasoning process
  • temperature, top_p, and similar parameters are not supported (setting them won't cause errors but has no effect)
  • In multi-turn conversations, reasoning_content from 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":

  1. All nodes start in an inactive state
  2. A node becomes active when it receives a new message (state update)
  3. Active nodes execute their function and send updates
  4. Nodes with no incoming messages vote to halt and become inactive
  5. Execution terminates when all nodes are inactive and 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 LangSmith

4. 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/KeywordDetailed Notes
create_agentLangChain's core API; accepts model + tools + prompt + middleware, returns an invocable agent
StateGraphLangGraph's main graph class, parameterized by a user-defined State object
StateThe graph's shared data structure, typically defined with TypedDict; supports Reducer functions
NodeA function in the graph; receives state + config + runtime, returns state updates
EdgeConnects nodes: normal edges (fixed path) or conditional edges (dynamic routing)
ReducerDefines how State updates are applied. Default is overwrite; use Annotated[list, add] for append
MessagesStatePrebuilt State with a messages field and add_messages Reducer
CheckpointerState persistence component; supports InMemorySaver, PostgreSQL, MongoDB
interruptPauses graph execution, waiting for external input (Human-in-the-Loop)
CommandMulti-purpose primitive: update + goto + resume — unifies state updates and control flow
Thinking ModeDeepSeek's chain-of-thought mode, outputting CoT via reasoning_content
MiddlewareBehavior interception mechanism introduced in LangChain v1; each middleware handles one concern
LangSmithObservability platform: tracing, evaluation, prompt engineering, deployment

Core API Quick Reference

API / ClassPurposeExample
create_agent(model, tools, system_prompt)Create a LangChain agentagent = create_agent(model="openai:gpt-5.4", tools=[fn])
StateGraph(state_schema)Create a LangGraph state graphgraph = StateGraph(MessagesState)
graph.add_node(name, fn)Add a nodegraph.add_node("agent", agent_fn)
graph.add_edge(from, to)Add a normal edgegraph.add_edge(START, "agent")
graph.add_conditional_edges(node, fn)Add a conditional edgegraph.add_conditional_edges("agent", route_fn)
graph.compile(checkpointer=...)Compile the graphapp = graph.compile(checkpointer=InMemorySaver())
app.invoke(input, config)Execute the graphresult = app.invoke({"messages": [...]}, config)
interrupt(question)Pause for human inputanswer = interrupt("Confirm continue?")
Command(update=..., goto=..., resume=...)State update + control flowCommand(resume="yes")
Send(node, state)Dynamically send state to a nodeSend("worker", {"item": x})
DeepSeek API (OpenAI format)Call DeepSeek modelsclient.chat.completions.create(model="deepseek-v4-flash", ...)
DeepSeek Thinking ModeEnable reasoning modeextra_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

  1. LangChain is an agent frameworkcreate_agent is the core API, composing agents from model + tools + prompt + middleware
  2. LangGraph is an orchestration runtime — models complex workflows with StateGraph (State + Node + Edge), supporting persistence, interrupts, and streaming
  3. DeepSeek integrates via the OpenAI-compatible interface — just change base_url; supports Thinking Mode (CoT reasoning)
  4. LangSmith provides observability: tracing, evaluation, prompt engineering, one-click deployment
  5. 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

  1. Forgetting to compile: StateGraph must be .compile()d before invocation — calling invoke directly will raise an error
  2. Incorrect State update strategy: The default Reducer is overwrite; to append to a list, use Annotated[list, add]
  3. Not returning reasoning_content in DeepSeek Thinking Mode: When tool calls are involved, reasoning_content must be passed back to the API, or it returns a 400 error
  4. Exceeding recursion limits: LangGraph's default recursion limit is 1000 steps. Complex agents may exceed this — use RemainingSteps for proactive detection
  5. Confusing LangChain and LangGraph responsibilities: LangChain handles "what an agent is"; LangGraph handles "how an agent runs"
  6. Non-idempotent side effects in Nodes: LangGraph re-runs nodes when resuming from checkpoints. Ensure node logic is idempotent

Further Reading