Integrating LangGraph for Graph-Based AI Agents

When linear chains no longer handle the logic of an AI agent, a graph-based approach comes to the rescue. We develop and integrate LangGraph solutions that enable complex multi-agent scenarios with loops and human oversight. Our team delivers the project turnkey—from graph design to deployment and ongoing support.

AI Development Areas

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1344
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1306
  • B2B Advance company logo design
    B2B Advance company logo design
    753
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1049
  • AIDER company logo development
    AIDER company logo development
    992
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1097

When developing complex AI agents, we often encounter situations where linear LangChain Expression Language (LCEL) chains stop being effective. When cyclical processing, conditional transitions based on intermediate step results, or pauses for human confirmation are required, LCEL falls short. That's exactly where LangGraph comes in—a library that extends LangChain and allows building agents as directed graphs with explicit state. Our experience deploying LangGraph in production includes projects for fintech, logistics, and legal services. We guarantee a fault-tolerant architecture and full documentation.

LangGraph implements the StateGraph concept, where each node is a function and edges are transitions. The agent's state is described with a TypedDict and can automatically merge messages. This makes it easy to implement loops and multi-agent systems.

Why agents need a graph, not a chain?

Linear LCEL chains are fine for simple pipelines: take input, apply a sequence of steps, get output. But real agent scenarios often require going back to a previous step, running parallel checks, or pausing execution for manual control. LangGraph's graph model solves these problems naturally: cycles are edges leading back; parallelism is multiple nodes executing simultaneously; human-in-the-loop is built-in interruptions.

Basic graph structure

from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
from langgraph.prebuilt import ToolNode
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage
from typing import TypedDict, Annotated
import operator

class AgentState(TypedDict):
    messages: Annotated[list, operator.add]  # Automatically concatenated
    user_id: str
    iteration_count: int

llm = ChatOpenAI(model="gpt-4o")

def agent_node(state: AgentState) -> AgentState:
    response = llm.bind_tools(tools).invoke(state["messages"])
    return {"messages": [response], "iteration_count": state["iteration_count"] + 1}

def should_continue(state: AgentState) -> str:
    last_msg = state["messages"][-1]
    if last_msg.tool_calls:
        return "tools"
    return END

# Build the graph
graph = StateGraph(AgentState)
graph.add_node("agent", agent_node)
graph.add_node("tools", ToolNode(tools))
graph.set_entry_point("agent")
graph.add_conditional_edges("agent", should_continue, {"tools": "tools", END: END})
graph.add_edge("tools", "agent")  # Loop: after tools, back to agent
app = graph.compile(checkpointer=MemorySaver())

Persistent state and interrupts

LangGraph supports checkpoint saving between runs and pauses for human approval:

from langgraph.checkpoint.postgres import PostgresSaver
from psycopg import Connection

# Persistence in PostgreSQL
conn = Connection.connect("postgresql://user:pass@localhost/langgraph_db")
checkpointer = PostgresSaver(conn)

# Interrupt: graph stops before the specified node
app = graph.compile(
    checkpointer=checkpointer,
    interrupt_before=["execute_payment"],  # Requires human confirmation
)

config = {"configurable": {"thread_id": "order_12345"}}

# Run until the break point
result = app.invoke({"messages": [HumanMessage("Pay the invoice for 50000 RUB")]}, config)
# Graph stopped before execute_payment

# After human review, continue
app.invoke(None, config)  # None = resume from current state

Multi-agent: Supervisor pattern

from langgraph.graph import StateGraph, END
from typing import Literal

class SupervisorState(TypedDict):
    messages: Annotated[list, operator.add]
    next_agent: str

AGENTS = ["researcher", "analyst", "writer"]

supervisor_prompt = f"""You are a supervisor of a multi-agent system.
Based on the request and current progress, choose the next agent: {AGENTS}
Or return FINISH if the task is complete.
"""

def supervisor_node(state: SupervisorState):
    response = llm.with_structured_output(
        {"next": {"type": "string", "enum": AGENTS + ["FINISH"]}}
    ).invoke([{"role": "system", "content": supervisor_prompt}] + state["messages"])
    return {"next_agent": response["next"]}

def route_to_agent(state: SupervisorState) -> str:
    if state["next_agent"] == "FINISH":
        return END
    return state["next_agent"]

# Create agents
def make_agent_node(name: str, system_prompt: str):
    agent_llm = ChatOpenAI(model="gpt-4o").bind_tools(get_tools_for(name))
    def node(state):
        result = agent_llm.invoke(
            [{"role": "system", "content": system_prompt}] + state["messages"]
        )
        return {"messages": [result]}
    return node

graph = StateGraph(SupervisorState)
graph.add_node("supervisor", supervisor_node)
graph.add_node("researcher", make_agent_node("researcher", "Research the topic and find facts"))
graph.add_node("analyst", make_agent_node("analyst", "Analyze data and draw conclusions"))
graph.add_node("writer", make_agent_node("writer", "Formulate the final answer"))
graph.set_entry_point("supervisor")
graph.add_conditional_edges("supervisor", route_to_agent)
for agent in AGENTS:
    graph.add_edge(agent, "supervisor")
multi_agent = graph.compile()

Streaming and real-time output

# Streaming events from the graph
async for event in app.astream_events(
    {"messages": [HumanMessage("Analyze Q1 sales")]},
    config={"configurable": {"thread_id": "analysis_001"}},
    version="v2",
):
    kind = event["event"]
    if kind == "on_chat_model_stream":
        print(event["data"]["chunk"].content, end="", flush=True)
    elif kind == "on_tool_start":
        print(f"\n[Tool call: {event['name']}]")
    elif kind == "on_tool_end":
        print(f"[Tool result received]")

How nested graphs simplify modularity?

LangGraph supports SubGraphs—nested graphs. You can define a graph for document processing and then include it as a regular node in the parent graph. This allows decomposing complex systems into reusable components.

# Subgraph for document processing
doc_graph = StateGraph(DocumentState)
doc_graph.add_node("extract", extract_text)
doc_graph.add_node("classify", classify_document)
doc_graph.add_node("validate", validate_structure)
# ... build the subgraph
doc_subgraph = doc_graph.compile()

# Include subgraph in parent
main_graph = StateGraph(MainState)
main_graph.add_node("process_document", doc_subgraph)  # Subgraph as node
main_graph.add_node("send_result", send_to_crm)
main_graph.add_edge("process_document", "send_result")

Practical case: contract review system (from our practice)

Task: one of our clients—a legal department of a large company—received 30–50 contracts daily. Each contract required 1–2 hours of lawyer time. We built a graph agent on LangGraph that automated the review.

Graph:

  1. extract_node — parse PDF, extract structure
  2. classify_node — contract type (supply, services, lease, NDA)
  3. risk_check_node — parallel checks: financial terms, duration, liability
  4. legal_rules_node — check against corporate list of prohibited clauses
  5. human_review — interrupt for contracts with risk_score > 7
  6. finalize_node — generate conclusion and recommendations
app = graph.compile(
    checkpointer=PostgresSaver(conn),
    interrupt_before=["human_review"],  # Pause only for risky ones
)

Routing: low risk → automatic approval; high risk → pause for lawyer with agent's draft conclusion. Results:

  • Standard contract review time: 90 min → 8 min
  • Automatic approval without lawyer: 61% of contracts
  • Missed non-standard clauses: 0 (vs ~3% manual due to fatigue)
  • Legal department workload: -58%

Comparison of LangGraph and LCEL

Criteria LCEL LangGraph
Structure Linear chain Arbitrary graph
Loops No Yes
State Passed via pipe TypedDict with merge strategy
Checkpoint No PostgreSQL, Redis, SQLite
Human-in-the-loop No interrupt_before/after
Use case Simple pipelines Agents, multi-agent systems

LangGraph components

Component Purpose Example
StateGraph Defines state and nodes StateGraph(AgentState)
Node Processing function agent_node
Edge Connection between nodes graph.add_edge("tools", "agent")
Conditional Edge Conditional transition should_continue
Checkpointer State persistence MemorySaver, PostgresSaver
Interrupt Pause for HITL interrupt_before=["node"]

What our LangGraph integration includes

  • Architecture session: analyze the task, select graph topology, define human-in-the-loop points.
  • Code development: implement nodes, edges, state, integrate with your LLM and tools.
  • Persistence setup: PostgreSQL, Redis, or SQLite for checkpoints.
  • Environment integration: deploy via Docker/Kubernetes, connect monitoring (LangSmith).
  • Testing: unit tests, load testing with latency recording (p99).
  • Documentation: full graph description, API, operation guide.
  • Team training: workshop on LangGraph for your developers.

Estimated timelines

  • Basic ReAct agent on LangGraph: 3 to 5 days.
  • Multi-agent system with supervisor: 2 to 3 weeks.
  • Human-in-the-loop workflow with persistence: 1 to 2 weeks.
  • Production integration with PostgreSQL checkpoint: +3–5 days.

Pricing is determined individually—we assess the project within one business day. Contact us for a consultation: our certified engineers will help choose the architecture for your task. Request a preliminary assessment, and we will prepare a commercial proposal with a quality guarantee and transparent timelines.