🎯 By the end of this module, you will:
Building Multi-Step LLM Workflows
Sequential chains give you 100% predictable, deterministic pipelines — when you know every step in advance, chains beat autonomous loops every time for speed, cost, and reliability.
# Block 1: Prompt Template with dynamic variables
from langchain_core.prompts import ChatPromptTemplate
draft_prompt = ChatPromptTemplate.from_messages([
("system", "You are an expert technical writer."),
("human", "Write a detailed but concise blog post about: {topic}")
])
# Variables are safely injected at runtime, not as f-strings
# This prevents prompt injection attacks from user input⚖️ When to Use Chains vs. Agents
- • Steps are known in advance (deterministic)
- • You need strict cost control per request
- • Predictable latency is required (<2s SLA)
- • Output quality is consistent across runs
- • Steps cannot be predicted in advance
- • Task requires adaptive tool selection
- • Multiple attempts/retries may be needed
- • Task complexity varies widely per query
🔬 Multi-Step Chain Builder
Multi-Step LCEL Chain SimulatorPrompt | Model | Parser
Visualize how deterministic multi-step chains pipe state cleanly from one node to the next
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
# 1. Compose chain with pipe operator (|)
prompt = ChatPromptTemplate.from_template("Improve this text: {input}")
model = ChatOpenAI(model="gpt-4o-mini")
parser = StrOutputParser()
# Pure, clean, deterministic chain
chain = prompt | model | parser
# 2. Invoke chain
result = chain.invoke({"input": "Draft text..."})Multi-Step Chain Traps
By default, each chain step only sees what you explicitly pass to it. If Step 2 needs context from Step 1 AND the original input, you must manually pass both. Always design state passing explicitly — nothing flows automatically.
Don't force a sequential chain onto tasks that require conditional branching (e.g., "if the search fails, try a different query"). Conditional logic belongs in a LangGraph agent, not a linear LCEL chain.
Key Takeaways
- 1.Chains for Determinism: Use LCEL chains when every step is known, latency is critical, and cost must be predictable. Chains give you assembly-line reliability.
- 2.Pipe Operator (|) is Declarative:
prompt | model | parserreads exactly like the data flow. LCEL chains are self-documenting by design. - 3.Explicit State Passing: Nothing flows between chain steps unless you pass it. Design your state contracts (what each step receives) before writing any code.
Create an Agent Class from Scratch in Python
Strip away all frameworks. Build a raw Python Agent class with an LLM caller, a tool executor, and a manual ReAct while loop — so you understand every abstraction from the inside out.