Building Custom AI Agents with LangGraph 2026: Intelligent Workflows

·12 min read·Evergreen Tools Team
AI Agents

💡 Tool TipNeed to process data? Try Evergreen Tools' JSON Validator and Base64 Encoder — all free!

In 2026, AI agents have evolved from simple chatbots into intelligent systems capable of executing complex tasks. LangGraph, as the core framework for building AI agents, provides powerful state management, tool integration, and workflow orchestration capabilities. This article will guide you through building custom AI agents from scratch to achieve true task automation.

1. LangGraph Core Concepts

LangGraph is based on graph theory, modeling AI agents as state graphs (StateGraph). Core concepts include: nodes (executing specific tasks), edges (defining flow logic), and state (storing context information). By combining these elements, you can build workflows of arbitrary complexity.

from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from typing import TypedDict, Annotated
import operator

# Define agent state
class AgentState(TypedDict):
    messages: Annotated[list, operator.add]
    next_action: str
    context: dict

# Initialize LLM
llm = ChatOpenAI(model="gpt-4-turbo", temperature=0)

# Define nodes
def analyze_request(state: AgentState):
    """Analyze user request and determine next action"""
    messages = state["messages"]
    response = llm.invoke([
        {"role": "system", "content": "Analyze the request and decide: research, calculate, or respond"},
        *messages
    ])
    
    action = "research" if "research" in response.content.lower() else "respond"
    
    return {
        "messages": [response],
        "next_action": action,
        "context": state["context"]
    }

def research_node(state: AgentState):
    """Perform research using tools"""
    messages = state["messages"]
    response = llm.invoke([
        {"role": "system", "content": "You are a research assistant. Use available tools to gather information."},
        *messages
    ])
    
    return {
        "messages": [response],
        "next_action": "respond",
        "context": state["context"]
    }

def respond_node(state: AgentState):
    """Generate final response"""
    messages = state["messages"]
    response = llm.invoke([
        {"role": "system", "content": "Generate a comprehensive response based on the conversation."},
        *messages
    ])
    
    return {
        "messages": [response],
        "next_action": "end",
        "context": state["context"]
    }

# Build graph
workflow = StateGraph(AgentState)

workflow.add_node("analyze", analyze_request)
workflow.add_node("research", research_node)
workflow.add_node("respond", respond_node)

workflow.set_entry_point("analyze")

workflow.add_conditional_edges(
    "analyze",
    lambda state: state["next_action"],
    {
        "research": "research",
        "respond": "respond"
    }
)

workflow.add_edge("research", "respond")
workflow.add_edge("respond", END)

# Compile
agent = workflow.compile()

# Run
result = agent.invoke({
    "messages": [{"role": "user", "content": "What are the latest AI trends in 2026?"}],
    "next_action": "",
    "context": {}
})

print(result["messages"][-1].content)
Workflow

2. Building Basic AI Agents

A basic AI agent contains three core nodes: analysis node (understanding user intent), execution node (calling tools to complete tasks), and response node (generating final answers). Conditional edges enable intelligent routing, allowing the agent to choose different processing paths based on task type.

from langgraph.graph import StateGraph
from langchain.tools import Tool
from langchain.agents import initialize_agent
from typing import TypedDict

# Define tools
def search_web(query: str) -> str:
    """Search the web for information"""
    # Implementation here
    return f"Search results for: {query}"

def calculate(expression: str) -> str:
    """Calculate mathematical expressions"""
    try:
        result = eval(expression)
        return str(result)
    except:
        return "Error in calculation"

def query_database(sql: str) -> str:
    """Query database with SQL"""
    # Implementation here
    return f"Query executed: {sql}"

# Create tools
tools = [
    Tool(
        name="WebSearch",
        func=search_web,
        description="Useful for searching the web for current information"
    ),
    Tool(
        name="Calculator",
        func=calculate,
        description="Useful for performing mathematical calculations"
    ),
    Tool(
        name="Database",
        func=query_database,
        description="Useful for querying the database with SQL"
    )
]

# Define multi-agent state
class MultiAgentState(TypedDict):
    task: str
    research_result: str
    analysis_result: str
    final_answer: str

# Research Agent
def research_agent(state: MultiAgentState):
    agent = initialize_agent(tools=[tools[0]], llm=llm)
    result = agent.run(state["task"])
    return {"research_result": result}

# Analysis Agent
def analysis_agent(state: MultiAgentState):
    agent = initialize_agent(tools=[tools[1], tools[2]], llm=llm)
    prompt = f"Analyze this research: {state['research_result']}"
    result = agent.run(prompt)
    return {"analysis_result": result}

# Synthesis Agent
def synthesis_agent(state: MultiAgentState):
    prompt = f"""
    Task: {state['task']}
    Research: {state['research_result']}
    Analysis: {state['analysis_result']}
    
    Synthesize a comprehensive answer.
    """
    response = llm.invoke(prompt)
    return {"final_answer": response.content}

# Build multi-agent workflow
workflow = StateGraph(MultiAgentState)

workflow.add_node("research", research_agent)
workflow.add_node("analysis", analysis_agent)
workflow.add_node("synthesis", synthesis_agent)

workflow.set_entry_point("research")
workflow.add_edge("research", "analysis")
workflow.add_edge("analysis", "synthesis")
workflow.add_edge("synthesis", END)

multi_agent = workflow.compile()

3. Multi-Agent Collaboration Systems

Complex tasks require collaboration between multiple specialized agents. For example: a research agent handles information gathering, an analysis agent handles data processing, and a synthesis agent generates reports. LangGraph supports building multi-agent workflows where each agent focuses on a specific domain and collaborates through state sharing.

from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.graph import StateGraph
import sqlite3

# Setup persistent memory
memory = SqliteSaver(sqlite3.connect("agent_memory.db"))

class PersistentAgentState(TypedDict):
    conversation_history: list
    user_preferences: dict
    learned_patterns: list

def memory_node(state: PersistentAgentState):
    """Node that manages long-term memory"""
    # Store conversation
    memory.put(
        thread_id="user_123",
        values={
            "history": state["conversation_history"],
            "preferences": state["user_preferences"]
        }
    )
    
    # Retrieve relevant memories
    relevant_memories = memory.search(
        thread_id="user_123",
        query=state["conversation_history"][-1]
    )
    
    return {
        "conversation_history": state["conversation_history"],
        "user_preferences": state["user_preferences"],
        "learned_patterns": relevant_memories
    }

def learning_node(state: PersistentAgentState):
    """Node that learns from interactions"""
    # Extract patterns from conversation
    patterns = extract_patterns(state["conversation_history"])
    
    # Update user preferences
    preferences = update_preferences(
        state["user_preferences"],
        state["conversation_history"]
    )
    
    return {
        "conversation_history": state["conversation_history"],
        "user_preferences": preferences,
        "learned_patterns": patterns
    }

# Build agent with memory
workflow = StateGraph(PersistentAgentState)
workflow.add_node("memory", memory_node)
workflow.add_node("learning", learning_node)

workflow.set_entry_point("memory")
workflow.add_edge("memory", "learning")
workflow.add_edge("learning", END)

# Compile with checkpointing
agent = workflow.compile(checkpointer=memory)

# Run with thread_id for persistence
result = agent.invoke(
    {
        "conversation_history": [{"role": "user", "content": "I prefer concise answers"}],
        "user_preferences": {},
        "learned_patterns": []
    },
    config={"configurable": {"thread_id": "user_123"}}
)

4. Persistent Memory and Learning

AI agents need to remember historical interactions to provide personalized service. LangGraph integrates persistent storage to save conversation history, user preferences, and learned patterns. This allows agents to maintain context across sessions, becoming smarter with use.

from langgraph.graph import StateGraph
from langchain_openai import ChatOpenAI
import asyncio

class ParallelAgentState(TypedDict):
    task: str
    subtasks: list
    results: dict

async def decompose_task(state: ParallelAgentState):
    """Break down complex task into subtasks"""
    llm = ChatOpenAI(model="gpt-4-turbo")
    
    response = await llm.ainvoke([
        {"role": "system", "content": "Break this task into 3-5 independent subtasks"},
        {"role": "user", "content": state["task"]}
    ])
    
    subtasks = parse_subtasks(response.content)
    return {"subtasks": subtasks, "results": {}}

async def parallel_executor(state: ParallelAgentState):
    """Execute subtasks in parallel"""
    async def execute_subtask(subtask):
        llm = ChatOpenAI(model="gpt-4-turbo")
        result = await llm.ainvoke([
            {"role": "user", "content": f"Solve this: {subtask}"}
        ])
        return subtask, result.content
    
    # Execute all subtasks concurrently
    tasks = [execute_subtask(st) for st in state["subtasks"]]
    results_list = await asyncio.gather(*tasks)
    
    results = {task: result for task, result in results_list}
    return {"results": results}

async def synthesize_results(state: ParallelAgentState):
    """Combine parallel results into final answer"""
    llm = ChatOpenAI(model="gpt-4-turbo")
    
    results_text = "\n".join([
        f"Subtask: {k}\nResult: {v}" 
        for k, v in state["results"].items()
    ])
    
    response = await llm.ainvoke([
        {"role": "system", "content": "Synthesize these results into a coherent answer"},
        {"role": "user", "content": results_text}
    ])
    
    return {"final_answer": response.content}

# Build parallel workflow
workflow = StateGraph(ParallelAgentState)

workflow.add_node("decompose", decompose_task)
workflow.add_node("execute", parallel_executor)
workflow.add_node("synthesize", synthesize_results)

workflow.set_entry_point("decompose")
workflow.add_edge("decompose", "execute")
workflow.add_edge("execute", "synthesize")
workflow.add_edge("synthesize", END)

parallel_agent = workflow.compile()

# Run
result = await parallel_agent.ainvoke({
    "task": "Analyze the impact of AI on healthcare, finance, and education in 2026",
    "subtasks": [],
    "results": {}
})
Data Processing

5. Parallel Execution and Performance Optimization

For complex tasks, subtasks can be decomposed and executed in parallel. LangGraph supports asynchronous execution and concurrency control, significantly improving processing efficiency. For example, researching multiple topics simultaneously, querying multiple data sources in parallel, and synthesizing results at the end.

from langgraph.graph import StateGraph
from langchain_openai import ChatOpenAI
from typing import TypedDict

class HumanInTheLoopState(TypedDict):
    proposal: str
    human_feedback: str
    revision_count: int
    approved: bool

def generate_proposal(state: HumanInTheLoopState):
    """Generate initial proposal"""
    llm = ChatOpenAI(model="gpt-4-turbo")
    
    response = llm.invoke([
        {"role": "system", "content": "Generate a detailed proposal for the given task"},
        {"role": "user", "content": state.get("task", "Default task")}
    ])
    
    return {
        "proposal": response.content,
        "revision_count": 0,
        "approved": False
    }

def human_review_node(state: HumanInTheLoopState):
    """Wait for human review - this is an interrupt point"""
    # In production, this would send notification and wait
    # For demo, we simulate human feedback
    return state

def revise_proposal(state: HumanInTheLoopState):
    """Revise proposal based on human feedback"""
    llm = ChatOpenAI(model="gpt-4-turbo")
    
    response = llm.invoke([
        {"role": "system", "content": "Revise the proposal based on feedback"},
        {"role": "user", "content": f"Proposal: {state['proposal']}\nFeedback: {state['human_feedback']}"}
    ])
    
    return {
        "proposal": response.content,
        "revision_count": state["revision_count"] + 1
    }

def check_approval(state: HumanInTheLoopState):
    """Check if proposal is approved"""
    return {"approved": state.get("approved", False)}

# Build workflow with human-in-the-loop
workflow = StateGraph(HumanInTheLoopState)

workflow.add_node("generate", generate_proposal)
workflow.add_node("review", human_review_node)
workflow.add_node("revise", revise_proposal)

workflow.set_entry_point("generate")
workflow.add_edge("generate", "review")

# Conditional edge based on approval
workflow.add_conditional_edges(
    "review",
    lambda state: "end" if state.get("approved") else "revise",
    {
        "end": END,
        "revise": "revise"
    }
)

workflow.add_edge("revise", "review")

hitl_agent = workflow.compile()

# Run with human interaction
config = {"configurable": {"thread_id": "session_1"}}

# First run - generates proposal
result = hitl_agent.invoke(
    {"task": "Design a new feature for our app"},
    config=config
)

print("Proposal:", result["proposal"])

# Simulate human feedback
feedback = "Make it more user-friendly and add analytics"

# Continue with feedback
result = hitl_agent.invoke(
    {"human_feedback": feedback},
    config=config
)

print("Revised:", result["proposal"])

6. Human-in-the-Loop and Approval Workflows

Some critical decisions require human intervention. LangGraph supports human-in-the-loop mode, pausing at key nodes to wait for human approval and adjusting plans based on feedback. This ensures AI agent decisions align with business requirements while maintaining automation efficiency.

📌 Frequently Asked Questions

What's the difference between LangGraph and LangChain?

LangChain is the base framework providing LLM calls and tool integration; LangGraph is an advanced framework focused on building complex multi-step workflows and AI agents. LangGraph is built on LangChain but provides stronger state management and flow control capabilities.

How to debug LangGraph workflows?

LangGraph provides detailed execution logs and state snapshots. You can use the LangSmith platform for visual debugging, tracking input/output of each node to quickly locate issues.

Which LLMs does LangGraph support?

It supports all LangChain-compatible LLMs, including OpenAI, Anthropic, Google, local models, etc. You can use different models at different nodes based on task requirements.

How to handle long-running tasks?

Use asynchronous execution and checkpoint mechanisms. LangGraph supports task pause and resume, allowing you to save progress in long-running tasks to avoid resource waste.

How is LangGraph's performance?

Through parallel execution and caching mechanisms, LangGraph can efficiently handle complex workflows. For large-scale applications, distributed deployment and load balancing are recommended.