Right, chains. The name’s a bit of a misnomer; it doesn’t chain the LLM to a radiator. Think of it less like a constraint and more like a production line. You’re orchestrating a sequence of operations, some of which involve an LLM, some of which might be simple Python functions, to get a specific, repeatable result. It’s how you move from a fun party trick to a real application.

The simplest and most ubiquitous of these is the LLMChain. Don’t let its simplicity fool you; it’s the foundational building block. An LLMChain is essentially a recipe: it combines a PromptTemplate and an LLM. You feed it your input variables, it formats the prompt, passes it to the LLM, and returns the output. It’s the difference between doing this manually every time:

from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate

llm = OpenAI(temperature=0)
prompt_template = PromptTemplate.from_template(
    "Translate this sentence from {input_language} to {output_language}: {text}"
)
# The manual, annoying way:
my_inputs = {
    "input_language": "English",
    "output_language": "French",
    "text": "I love programming"
}
formatted_prompt = prompt_template.format(**my_inputs)
raw_llm_output = llm(formatted_prompt)
print(raw_llm_output)

…and the civilized, chain-based way:

from langchain.chains import LLMChain

# Create the chain once
chain = LLMChain(llm=llm, prompt=prompt_template)

# Run it with inputs, anytime
result = chain.run({
    "input_language": "English",
    "output_language": "French",
    "text": "I love programming"
})
print(result['text'])  # Outputs: J'adore la programmation.

The LLMChain handles the boring stuff: prompt formatting, calling the LLM, and parsing the basic response. It’s your workhorse.

The Power of SequentialChain

Now, what if your task requires multiple steps? You don’t just translate a sentence; you translate it, then summarize the translation, then critique the summary. You could run three separate chains, manually passing the output of one as the input to the next. But that’s clunky and error-prone. Enter SequentialChain.

This is where LangChain starts to earn its name. A SequentialChain executes a series of chains in a defined order, passing the outputs from one chain as inputs to the next. It’s the assembly line for your LLM-powered thought process.

There are two main flavors: SimpleSequentialChain, where each step has a single input and output, and the more powerful SequentialChain, where you can map specific output variables from one chain to specific input variables of the next. You almost always want the latter because it gives you explicit control.

Let’s build a reviewer. We’ll write a summary, then have a harsh critic review it.

from langchain.chains import SequentialChain, LLMChain
from langchain.prompts import PromptTemplate

# Chain 1: Summarize a product
summary_prompt = PromptTemplate.from_template("""
You are a creative writer. Write a compelling 2-sentence summary for a product called '{product_name}' designed for {audience}.
""")
summary_chain = LLMChain(llm=llm, prompt=summary_prompt, output_key="summary")

# Chain 2: Critique the summary (this LLM is a jerk)
critique_prompt = PromptTemplate.from_template("""
You are a brutally honest, cynical product critic. Tear apart the following product summary. Point out its flaws, clichés, and empty marketing speak.

Summary: {summary}
"""
)
critique_chain = LLMChain(llm=llm, prompt=critique_prompt, output_key="critique")

# Build the Sequential Chain
overall_chain = SequentialChain(
    chains=[summary_chain, critique_chain],
    input_variables=["product_name", "audience"],  # Initial inputs
    output_variables=["summary", "critique"],  # Final outputs we care about
    verbose=True  # Let's watch the magic happen
)

# Run it
result = overall_chain({
    "product_name": "Quantum Toaster",
    "audience": "busy quantum physicists"
})

print("SUMMARY:", result['summary'])
print("\nCRITIQUE:", result['critique'])

The verbose=True flag is your best friend here. It shows you the exact prompts and outputs for each step, which is invaluable for debugging when your chains inevitably do something bizarre.

RouterChain: The Traffic Cop

Here’s where things get genuinely clever. What if you have a task that could go down one of several paths? You need a way to decide which chain to use dynamically, based on the input. This is the job of the RouterChain.

You set up a router that examines the input and chooses from a set of destination chains. It’s perfect for building a helpdesk bot (“is this a billing question or a technical question?”), a content classifier, or a multi-tool agent.

A RouterChain uses a special LLM call to decide where to route the input. You define the destinations and provide a description of what each chain is for. The router’s LLM uses those descriptions to make the choice.

from langchain.chains.router import MultiRouteChain
from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE
from langchain.chains import ConversationChain

# Let's pretend we have two specialist chains and one generalist
physics_chain = LLMChain(...) # A chain primed for physics questions
math_chain = LLMChain(...)   # A chain primed for math questions
default_chain = ConversationChain(llm=llm) # A good default for chit-chat

# Define the routing options
prompt_infos = [
    {
        "name": "physics",
        "description": "Good for questions about quantum mechanics, astrophysics, and theoretical physics",
        "prompt_template": "...physics specialized prompt..."
    },
    {
        "name": "mathematics",
        "description": "Good for questions about algebra, calculus, and number theory",
        "prompt_template": "...math specialized prompt..."
    }
]

# Build the router chain
router_chain = MultiRouteChain.from_prompts(
    llm=llm,
    prompt_infos=prompt_infos,
    default_chain=default_chain
)

# Ask a question and see where it goes!
result = router_chain.run("What is the significance of Planck's constant?")
# This will almost certainly be routed to the physics_chain

The beauty here is that the router isn’t a rigid if-else statement. It uses the LLM’s understanding of language to make a nuanced decision, which is far more powerful.

Pitfalls and Sharp Edges

This power comes with headaches. Here’s what to watch for:

  1. Propagating Errors: A SequentialChain fails if any single chain in the sequence fails. Your error handling needs to be robust.
  2. Context Bleed: This is the big one. The chains are largely stateless. In a SequentialChain, the second chain doesn’t “remember” the prompt or context of the first chain unless you explicitly pass it along. This is why mapping output/input variables correctly is critical.
  3. The Verbosity Trap: It’s easy to build long, complex chains that become impossible to debug. Use verbose=True during development and add logging. If a chain is getting too long, it might be a sign to break it into a separate, simpler agent or function.
  4. Cost and Latency: Each step in a sequential chain is a separate LLM call. A chain with 5 steps is 5x more expensive and 5x slower than a single call. Always ask yourself if you can achieve the same result with a smarter prompt in a single LLMChain.

Chains are the core abstraction for creating deterministic, structured workflows with LLMs. Master the LLMChain, wield the SequentialChain with explicit control, and use the RouterChain to build smart, branching applications. Just remember you’re the one designing the production line, so when it breaks—and it will—you’re the one who has to fix it.