/pattern/sequential-pipeline/

02 · WorkflowPrompt ChainingLinear WorkflowSequential Process

Sequential Pipeline.
Fixed steps. No back-edges.

Multiple steps are executed in a fixed order, where each step utilizes the output of the previous one.

When to reach for it

  • The task naturally breaks down into phases, each delivering a verifiable intermediate product.
  • Control is more important than autonomy.

When it backfires

  • The workflow branches heavily or results dynamically generate new goals.
  • Steps can be parallelized without dependencies.

The tradeoff

High control-flow predictability is gained at the cost of low flexibility; output content is still non-deterministic at every LLM node.

The mental model

A shape you can draw on a napkin.

Every job follows the same four-stage chain. Predictability is the point — adaptivity belongs in a different pattern.

  1. Developers wire every stage and edge in code before the run.
  2. Each stage transforms the state and hands it to the next exactly once.
  3. The graph is identical on every run; only the model output inside the nodes varies.

Compare all six coordination patterns

InputStage AStage BOutput
Walk it through

A real run, step by step.

Task"Summarize this incident report and produce a remediation checklist."
1 / 5
Stage 1ingest_report(text) → { paragraphs[], entities[] }
Stage 2analyze(paragraphs, entities) → { rootCause, severity, affectedSystems }
Stage 3synthesize(analysis) → { summary, checklist:[fix1, fix2, fix3] }
Stage 4respond(payload) → MD output
AnswerMarkdown summary + checklist returned. Total: 4 LLM calls in fixed order; no branching, no retries inside the pipeline.
In code

The loop is already built in.

LangGraphpython
from typing import TypedDict
from langgraph.graph import StateGraph, END

class State(TypedDict):
    raw: str
    parsed: dict | None
    analysis: dict | None
    payload: dict | None
    answer: str | None

def ingest(state):    return {"parsed": parse(state["raw"])}
def analyze(state):   return {"analysis": analyze_llm(state["parsed"])}
def synthesize(state): return {"payload": synth_llm(state["analysis"])}
def respond(state):   return {"answer": render_md(state["payload"])}

g = StateGraph(State)
g.add_node("ingest", ingest)
g.add_node("analyze", analyze)
g.add_node("synthesize", synthesize)
g.add_node("respond", respond)
g.add_edge("ingest", "analyze")
g.add_edge("analyze", "synthesize")
g.add_edge("synthesize", "respond")
g.add_edge("respond", END)
g.set_entry_point("ingest")
pipeline = g.compile()
Pitfalls

Three ways this pattern will hurt you.

Treating every job as a pipeline

Jobs that need a different shape (route by content, loop on failure) get jammed into the same four stages and either over-process or silently fail.

Fix · Reach for Routing or Orchestrator-Workers when the work isn't uniform; keep Sequential Pipeline for genuinely homogeneous flows.

No place to catch a bad intermediate

Stage 2 produces low-confidence output but the pipeline has no conditional edge to halt or escalate — the bad analysis flows downstream and corrupts the response.

Fix · Add a HITL gate after the risky stage, or wrap the pipeline in an evaluator-optimizer loop that can request a re-run.

Stage boundaries that match function names instead of state

Authors stage on 'what the next function does' rather than 'what new state is observable.' The pipeline becomes a chain of micro-LLM calls with overlapping responsibility.

Fix · Define each stage by the state field it produces (parsed → analysis → payload → answer); collapse stages that share an output.

Framework support

Where Sequential Pipeline is native.

CrewAINative
Microsoft Agent FrameworkNative
Anthropic Cookbookdocumented as a recipeAdaptable

Search

Search patterns, frameworks, and pages.