Durable Agentic Workflows: Building Resilient AI with Temporal and LangGraph
The transition from simple LLM prompts to complex agentic workflows represents a shift from 'stateless' interactions to 'stateful' software engineering. As we move beyond basic RAG (Retrieval-Augmented Generation) and into the realm of agents that can reason, loop, and use tools, we encounter a classic distributed systems problem: reliability.
When an agentic process takes minutes, hours, or even days to complete—perhaps because it’s waiting for a human review or processing a massive dataset—standard request-response architectures fail. This is where the combination of LangGraph and Temporal becomes a powerhouse for production-grade AI.
The Core Challenge of Agentic Workflows
In a typical agentic workflow, an LLM might decide to call a tool, analyze the output, and then decide its next move. This often involves cycles (loops), which standard linear chains (like LangChain’s early versions) couldn't handle well. LangGraph was built to solve this by treating agents as state machines.
However, LangGraph—while excellent at defining the logic of the state machine—runs in memory. If your server restarts, your network blips, or your LLM provider hits a rate limit halfway through a 10-step reasoning loop, the state is lost. You can implement persistence in LangGraph using checkpointers (like Postgres or Redis), but you are still responsible for managing the retry logic, the execution environment, and the recovery of the process itself.
Temporal, on the other hand, is a workflow orchestration engine designed for 'durable execution.' It ensures that code runs to completion regardless of infrastructure failures. By combining LangGraph’s cognitive architecture with Temporal’s execution reliability, we can build agents that are effectively 'immortal.'
Why LangGraph for the Brain?
LangGraph allows you to define agents as a directed graph where nodes are functions and edges define the flow. Its primary strengths are:
- Cycles: Unlike Directed Acyclic Graphs (DAGs), LangGraph allows for loops, which are essential for iterative reasoning.
- Fine-grained State Management: You can define exactly what the state looks like at every step of the conversation or task.
- Human-in-the-loop: It has native support for 'interrupts,' allowing the graph to pause for human intervention and resume once input is provided.
While LangGraph is great for the what and how of the agent's logic, it doesn't solve the where and when of execution reliability at scale.
Why Temporal for the Nervous System?
Temporal provides a set of primitives (Workflows and Activities) that turn transient code into durable processes. When you run a Temporal Workflow, the state of the execution—including local variables and the instruction pointer—is persisted.
Key features include:
- Exponential Backoff: Automatic retries on failures (e.g., LLM rate limits).
- State Persistence: If the worker process dies, another worker picks up exactly where the first one left off.
- Signals and Queries: The ability to send data into a running process or ask for its current status.
- Timers: Native support for waiting days or months without consuming CPU resources.
Architecture: Integrating LangGraph into Temporal
There are two primary patterns for integrating these tools: 'LangGraph inside an Activity' and 'Temporal as the Graph Orchestrator.'
Pattern 1: The LangGraph-in-Activity Pattern
In this model, you define your LangGraph logic and wrap the execution of that graph inside a single Temporal Activity.
# A simplified conceptual example @activity.defn async def run_agent_task(input_data: dict) -> dict: graph = compile_my_langgraph() initial_state = {"messages": [HumanMessage(content=input_data['prompt'])]} result = await graph.ainvoke(initial_state) return result
When to use it: This is suitable for short-lived agentic tasks (seconds to a few minutes) where the primary concern is retrying the LLM calls if they fail. If the activity fails, Temporal will retry the entire graph from the beginning.
Pattern 2: The Durable Checkpoint Pattern (Recommended)
For complex, long-running agents, you want Temporal to manage the state of the LangGraph. This involves using Temporal's Workflow state as the source of truth for the LangGraph checkpointer.
In this scenario, each step of the LangGraph (or groups of steps) is executed as a Temporal Activity. If a step fails, Temporal retries only that specific step. If the whole system goes down, the Workflow knows exactly which node in the graph was executing.
Practical Implementation: A Legal Document Review Agent
Let’s imagine a real-world use case: An AI agent that reviews legal contracts. The workflow is:
- OCR/Extract: Convert PDF to text.
- Analyze: Agent identifies potential risks.
- Human Review: A lawyer must approve or edit the risks identified.
- Final Report: Generate a summary and email it to the client.
The LangGraph Definition
We define the nodes for extraction, analysis, and a 'wait' state for human review.
from langgraph.graph import StateGraph, END workflow = StateGraph(AgentState) workflow.add_node("extract", extract_node) workflow.add_node("analyze", analyze_node) workflow.add_node("human_review", human_review_placeholder) workflow.add_node("notify", notification_node) workflow.set_entry_point("extract") workflow.add_edge("extract", "analyze") workflow.add_edge("analyze", "human_review") workflow.add_edge("human_review", "notify") workflow.add_edge("notify", END)
The Temporal Wrapper
In the Temporal Workflow, we manage the progression through these nodes. The 'human_review' step is handled by a Temporal Signal.
@workflow.defn class LegalReviewWorkflow: @workflow.run async def run(self, document_url: str): # 1. Extraction Activity text = await workflow.execute_activity(extract_text_activity, document_url) # 2. Analysis Activity (Retried automatically if LLM is down) risks = await workflow.execute_activity(analyze_risks_activity, text) # 3. Human-in-the-loop (The workflow sleeps here, no resources consumed) self.human_approved = False await workflow.wait_condition(lambda: self.human_approved) # 4. Final Notification await workflow.execute_activity(send_report_activity, self.final_data) @workflow.signal def approve_review(self, data: dict): self.human_approved = True self.final_data = data
Solving the 'Non-Determinism' Problem
One of the cardinal rules of Temporal is that Workflow code must be deterministic. LLMs are inherently non-deterministic.
To bridge this gap, you must never call an LLM directly inside a Temporal Workflow function. Always wrap LLM calls (and the LangGraph invoke calls) inside Temporal Activities. Activities are allowed to be non-deterministic and have side effects. Temporal records the result of the activity in its history; during a replay, it simply returns the recorded result instead of re-executing the LLM call.
Observability and Debugging
One of the most significant advantages of this stack is the observability.
- LangSmith: You can use LangSmith to trace the internal reasoning of your LangGraph agent—seeing the prompts, the token usage, and the tool outputs.
- Temporal Web UI: You can see the high-level execution flow, exactly which activities succeeded, which are retrying, and the full history of the state changes.
If an agent fails because of a bug in your parsing logic, you can fix the code, and Temporal can 'replay' the workflow. It will skip the expensive LLM calls that already finished and resume exactly at the failing step with your new code.
Best Practices for Production
- Idempotency is Key: Ensure your activities (like sending an email or charging a credit card) are idempotent. Since Temporal might retry an activity, you don't want to perform the same side effect twice.
- Small State Payloads: Avoid passing massive PDF texts directly through Temporal Workflow inputs. Store the large data in S3/Blob storage and pass the reference (URI) instead. Temporal has a limit on the size of the history.
- Versioning: Both LangGraph and Temporal support versioning. When you update your agent's logic, use Temporal's versioning features to ensure that currently running workflows continue using the logic they started with, while new ones use the updated graph.
- Timeout Strategies: Set aggressive 'Schedule-To-Start' timeouts for activities to detect if your workers are down, but generous 'Start-To-Close' timeouts for LLM calls, which can sometimes be slow.
Conclusion
Building AI agents that work in a lab is easy; building agents that work reliably in production is hard. By decoupling the reasoning logic (LangGraph) from the execution durability (Temporal), you create a system that can handle the messy reality of distributed computing.
Actionable Next Steps:
- Audit your current agents: Identify where they fail. Is it a logic error (LangGraph) or an infrastructure/timeout error (Temporal)?
- Start Small: Wrap your existing LangGraph chains in Temporal Activities to gain immediate retry benefits.
- Implement Signals: Move your human-in-the-loop logic from custom database flags to Temporal Signals for a more robust state machine.