AI Workflows

Crafting Adaptive AI Workflows with LangGraph

Hanabi Technologies
September 24, 2025
5 min read
Crafting Adaptive AI Workflows with LangGraph

Large Language Models (LLMs) have evolved from single-shot text generators into dynamic collaborators that can reason over data, call tools, and maintain conversations. Orchestrating those capabilities reliably is the challenge LangGraph tackles. Think of LangGraph as an execution canvas where you declare what should happen in an AI workflow — state transitions, branching logic, tool calls — and let the runtime coordinate how it happens. In this end-to-end guide, we'll walk through building a production-ready conversational agent that mixes natural language, retrieval, and custom tools, complete with snippets you can adapt to your own stack.

Why LangGraph?

Traditional pipelines often hard-code LLM prompts, tools, and transitions. That approach collapses when requirements evolve or the agent needs to reason over multiple steps. LangGraph provides:

  • Stateful Execution — Maintain structured state across turns.
  • Deterministic Control Flow — Explicit nodes and edges define what happens after each step.
  • Tool-Aware Policies— Integrated retry, guardrails, and error handling.
  • Observability — Built-in event streams you can log or visualize.

By modelling workflows as graphs, you gain a blend of readability, maintainability, and scalability that's hard to achieve with ad-hoc imperative code.

Project Setup

LangGraph builds on LangChain, so Node.js or Python ecosystems are both welcome. The example below uses TypeScript.

1npm install langchain langgraph @langchain/openai zod

We'll rely on Zod for runtime type safety and OpenAI for the base LLM, though any LangChain-compatible model works. Remember to load OPENAI_API_KEY (or your provider's equivalent) via environment variables or a secrets manager.

Designing the Agent's State

Before writing code, define what "state" means for your agent. We want to track: conversation history, the user's latest request, tool outputs (e.g., knowledge base snippets), and the assistant's pending reply. LangGraph uses state schemas to ensure each node receives and emits the expected shape.

1import { z } from "zod";
2
3export const dialogueState = z.object({
4  transcript: z.array(
5    z.object({
6      speaker: z.enum(["user", "assistant"]),
7      content: z.string(),
8    }),
9  ),
10  activeQuery: z.string().nullable(),
11  contextBundle: z.array(z.string()),
12  pendingAnswer: z.string().nullable(),
13});
14
15export type DialogueState = z.infer<typeof dialogueState>;

Building the Graph Blueprint

LangGraph workflows start from a createGraph (TypeScript) or StateGraph (Python). We'll set up nodes for: ingesting the user request, running semantic retrieval over a memory store, calling the LLM to craft a response, optional tool callbacks (e.g., calendar actions), and final response formatting.

1import { createGraph } from "langgraph";
2import { OpenAI } from "@langchain/openai";
3import { RunnableToolLike } from "langchain/schema/runnable";
4
5const llm = new OpenAI({
6  modelName: "gpt-4o-mini",
7  temperature: 0.4,
8});
9
10const referenceSearcher: RunnableToolLike = /* your retriever implementation */;
11const calendarBridge: RunnableToolLike = /* custom function tool */;
12
13const flowComposer = createGraph<DialogueState>({
14  name: "assistant-orchestrator",
15  schema: dialogueState,
16});

Node 1: Capturing the User Message

1flowComposer.addNode("ingest-user", async (state) => {
2  const { transcript } = state;
3  const latestUserLine = transcript.at(-1);
4
5  return {
6    ...state,
7    activeQuery: latestUserLine?.content ?? "",
8  };
9});

Node 2: Retrieval

Use LangChain retrievers or vector stores to ground the model's response. Return the retrieved snippets via contextBundle.

1flowComposer.addNode("fetch-context", async (state) => {
2  if (!state.activeQuery) {
3    return state;
4  }
5  const snippets = await referenceSearcher.invoke(state.activeQuery);
6
7  return {
8    ...state,
9    contextBundle: snippets.map((item: { pageContent: string }) => item.pageContent),
10  };
11});

Production Checklist

  • Retries & Timeouts — Wrap long-running nodes with circuit breakers. LangGraph integrates with LangChain callbacks for monitoring.
  • Observability — Stream graph events to OpenTelemetry or your logging pipeline. Each node emits start/end hooks.
  • Schema Evolution — If you extend DialogueState, version it carefully to avoid breaking serialized runs.
  • Tool Governance — Validate tool outputs before feeding them back into the LLM. Zod refinements can enforce structure.
  • Testing — Mock the LLM for deterministic unit tests. LangGraph nodes are regular async functions, making them straightforward to test in isolation.

LangGraph lets you tame the complexity of modern AI systems by making control flow explicit, extensible, and testable. Once you model your use case as a graph, you can iterate quickly — adding new tools, refining prompts, or introducing guardrails without rewriting the entire agent.

More insights

Explore more articles from our AI and development experts