A basic workflow runs known steps in order. An advanced agent flow becomes useful when some decisions depend on uncertain input, yet the application must preserve state, pause for review, and recover after interruption. That does not mean giving a model unlimited control. LangGraph describes combining deterministic and model-driven steps in one graph; its workflow guide documents routing, parallelization, orchestrator-worker, and evaluator-optimizer patterns. Choose only the pattern that solves a concrete branching problem.[7][10]
Consider a support-ticket draft that may need evidence from a policy repository before a human approves a reply. The graph below is a design, not a tested implementation or a promise about a framework’s reliability:
| Node | Reads | Emits | Next edge |
|---|---|---|---|
| Intake | Ticket ID and authorized body | Validated case record, sensitivity flag | Reject missing/unauthorized cases; otherwise classify |
| Classify | Case record | One of billing, technical, other plus confidence note | Route to a scoped retrieval branch |
| Retrieve | Approved corpus and category | Source IDs and snippets, or explicit not_found | Draft or manual review |
| Draft | Case + retrieved evidence | Proposed text with source references | Validate |
| Validate | Draft, policy constraints | pass, needs_revision, or escalate | Review, bounded retry, or stop |
| Review | Proposal and citations | Human decision and edits | Stop on rejection; queue approved send |
| Send | Approved immutable payload | Receipt or typed failure | Done or reconciliation |
The classification output is not a permission grant. Retrieval must independently enforce the case’s access scope. A not_found result should not invite an invented policy. Limit the revision edge to a documented maximum, then escalate; otherwise an evaluator-optimizer loop can repeatedly spend tokens without converging. LangGraph’s examples illustrate routing and evaluator-optimizer as possible patterns, not as evidence that they outperform a single deterministic flow for this ticket.[10]
Persist the right state
A useful state record contains a stable case ID, current step, permitted source IDs, proposed response, review status, and a unique send key. Store only what recovery needs; a full copied ticket body in every checkpoint can multiply sensitive data. Keep secrets and raw credentials outside model-visible state. LangGraph’s persistence documentation distinguishes checkpointers from stores and says persisted state can support resumption after interruption or failure. An in-memory saver is not a durable restart strategy; select a storage backend and retention policy deliberately.[8]
Define a transition as current state + validated result → next state. On each edge, reject invalid statuses and tool outputs rather than asking the model to repair arbitrary JSON indefinitely. Model-written text can be reviewed; authorization, permitted recipients, budgets, and send eligibility belong to application code. If a fork retrieves independent documents in parallel, merge on identifiers and a specified conflict rule. Parallel calls do not make contradictory evidence consistent by themselves.[10]
Before implementing the graph, write the state schema and edge contract on one page. For each node, distinguish a recoverable tool failure from an invalid input and a policy denial. A failed retrieval might take a bounded retry path; an unauthorized ticket should stop without retry. Specify what an operator sees after each stop and which actor may restart the case. Version the graph and the validation rules so an in-flight case is not silently resumed under a different route after deployment. If the policy corpus changes between drafting and approval, show the reviewer which source version informed the proposal. These are application rules to implement and test, not guarantees conferred by choosing a graph library.
Pause before the irreversible edge
The review node should show the actual draft, recipient, source references, and action to be performed. A reviewer can approve, edit, or reject; silence is not approval. The MCP tools guidance recommends confirmation prompts so a person can deny operations, but the host application must implement the gate it needs.[1] LangGraph documents an interrupt/resume mechanism and warns that the node containing an interrupt restarts from its beginning on resume.[9]
That restart behavior shapes the graph. Do not place an unprotected email send or payment call before an interrupt in the same node. Persist the approval and immutable outgoing payload; assign a unique send key and reconcile that key with the destination before attempting a retry. A local marker alone cannot prove that a remote send succeeded, and a transport timeout cannot prove it failed. Where the destination supports idempotency keys, use them; otherwise require reconciliation or manual handling of ambiguous outcomes. The framework’s checkpoint is not a distributed transaction with the email provider.[9]
Test transitions, not just fluent answers
Make a small transition matrix: missing ticket; retrieved document from another account; no applicable policy; reviewer rejects; duplicate resume; send succeeds but acknowledgement is lost. Verify the resulting state, number of outbound attempts, and whether a person is asked to resolve ambiguity. Keep a trace of node names and outcomes while redacting sensitive content. If a linear process can do all this with two explicit branches, use that instead. A graph is worthwhile when its visible state and recovery rules reduce ambiguity, not because its diagram looks more autonomous.
Sources
- [1] https://modelcontextprotocol.io/specification/2025-11-25/server/tools
- [7] https://docs.langchain.com/oss/python/langgraph/overview
- [8] https://docs.langchain.com/oss/python/langgraph/persistence
- [9] https://docs.langchain.com/oss/python/langgraph/interrupts
- [10] https://docs.langchain.com/oss/python/langgraph/workflows-agents

