Back
intermediate

MCP Connectors & Workflows

Workflows: make every step and failure visible

Design explicit states, separate deterministic steps from model calls, and plan recovery.

Lesson 34 of 44About 25 min with practice

A document assistant receives a file, extracts text, finds evidence, and drafts an answer. If extraction fails, should it continue writing? The answer becomes easier when you describe the work as explicit steps instead of one long prompt.

Before you begin: Understand function calls, tool results, and the difference between a missing result and an error.

Turn the request into a state transition

A workflow coordinates operations in a defined control flow. A step can be ordinary code, a model call, a human decision, or a tool request. State is the information saved between steps. The word “AI” does not make every step probabilistic.

For our document assistant, use states such as received, extracted, evidence_ready, draft_ready, and failed. Each transition needs a condition. Reaching draft_ready should mean a draft was produced from available evidence, not that a spinner ran for long enough.

Suppose extraction returns an empty string. That might mean a blank file, a scanned image requiring OCR, or a parser problem. Save a useful outcome and branch appropriately. Passing an empty string to the model can turn an extraction failure into a confident unsupported answer.

Make one small workflow concrete

This local Python program uses fictional text and a deterministic draft. It does not call an LLM or parse real PDFs. Its purpose is to expose state changes you can predict.

python
# Runnable: Python 3, standard library.
def run(text):
    state = {"status": "received", "events": ["received"]}
    cleaned = text.strip()
    if not cleaned:
        state.update(status="failed", reason="no_text")
        state["events"].append("failed:no_text")
        return state
    state["events"].append("extracted")
    state["evidence"] = {"source_id": "N1", "text": cleaned}
    state["events"].append("evidence_ready")
    state["draft"] = "Source N1 says: " + cleaned
    state.update(status="draft_ready")
    state["events"].append("draft_ready")
    return state

assert run(" ")["status"] == "failed"
result = run("W1 starts at 15:00.")
assert result["evidence"]["source_id"] == "N1"
assert result["events"][-1] == "draft_ready"
print(result["events"])

The event list explains how the result was reached. It exists only in memory here. Closing the process loses it. Durable state requires a storage system and a policy for identifying, updating, and reading the correct user's run.

Ask what a retry could repeat

A retry attempts an operation again after failure. It is useful when a temporary problem may disappear. It is harmful when it repeats a completed side effect, such as sending the same notification twice.

An idempotent operation can be repeated with the same operation identity without creating an additional effect. This requires cooperation from the service performing the write. Saving “send email” in a workflow log does not make an email provider's send operation idempotent.

For read-only extraction, a bounded retry may be acceptable. For a write with an uncertain outcome, first determine whether it already succeeded. A timeout describes what your caller observed, not necessarily what the remote service did.

Design the failure route

A worker saves a draft, loses its connection, and receives the same request again. Should it create another draft with a new ID?

Compare a recovery design

Give the user's operation a stable ID and look up its saved result. If the first write succeeded, return that result. If its outcome is still unknown, reconcile before repeating the write. Bind the operation to the authenticated user so a guessed ID cannot reveal another person's draft.

For practice, add canceled to the state design. Decide whether cancellation stops work already in progress, prevents future steps, or both. The interface must describe what actually stopped. This matters when a phone loses connectivity while a server continues working.

Next, express these state and branching ideas with LangGraph, then examine its persistence boundary.

Sources

LangGraph workflows and agents explains control-flow patterns. Durable execution discusses replay and side effects.

Continue to the next lesson.

Practice for this lesson

Turn a request into state transitions with a failure route

Model work as states, then design what a retry may safely repeat.

About 11 min55 points3 checks and one written task
Loading your lesson progress...