Skip to main content
Articles/langchain/langchain-101-chains-prompts-and-llm-integration
Series: RAG & Vector Search Masteryβ€’Part 7 of 14
Scenario Article

LangChain 101: Chains, Prompts, and LLM Integration

One LLM API Today, Rewrite Tomorrow: The Customer Support Bot Problem

Abstract Algorithmsβ€’Mar 28, 2026
XLinkedInReddit
πŸ’‘ Executive Summary

LangChain's LCEL pipe operator ( ) wires prompts, models, and output parsers into composable chains β€” swap OpenAI for Anthropic or Ollama by changing one line without touching the rest of your code.

🎯 System Scenario & Requirements

System Goal

LangChain's LCEL pipe operator ( ) wires prompts, models, and output parsers into composable chains β€” swap OpenAI for Anthropic or Ollama by changing one line without touching the rest of your code.

Actors & Components

  • Edge Traffic Ingress
  • Stateless Microservices
  • Datastore Infrastructure
  • API Gateway
  • Domain Service
  • Redis Cache
  • Transactional DB

Functional Requirements

  • TLDR: LangChain's LCEL pipe operator ( ) wires prompts, models, and output parsers into composable chains β€” swap OpenAI for Anthropic or Ollama by changing one line without touching the rest of your code.
  • Imagine you are three days into building a customer support bot.
  • You hardcoded the OpenAI API, hand-rolled the request payload, parsed the JSON response yourself, and added a retry loop for rate limits.

Constraints & Assumptions

  • LangChain's abstractions deliver genuine value, but they introduce their own costs.
  • Knowing both sides saves you from unpleasant surprises in production.

⚑ Execution Stages & Decision Trace

1

Stage 1 β€” LangChain's Core Primitives: Prompts, Models, and Parsers

Action / Decision:

Every LangChain pipeline β€” no matter how complex β€” is built from three building blocks.

Component Behavior:

Understanding these three objects is 80% of what you need to be productive with LangChain.

Observable Output: ChatPromptTemplate and the {variable} Syntax A ChatPromptTemplate is a reusable message template with named placeholders.
Read Full Stage in Deep Dive ↓
πŸ“–

Deep Dive

Detailed Architectural Analysis

Scenario context

LangChain's LCEL pipe operator (|) wires prompts, models, and output parsers into composable chains β€” swap OpenAI for Anthropic or Ollama by changing one line without touching the rest of your code.


Imagine you are three days into building a customer support bot. You hardcoded the OpenAI API, hand-rolled the request payload, parsed the JSON response yourself, and added a retry loop for rate limits. It works.…

This walkthrough focuses on One LLM API Today, Rewrite Tomorrow: The Customer Support Bot Problem.

Before making a design choice, clarify:

  • Imagine you are three days into building a customer support bot.
  • You hardcoded the OpenAI API, hand-rolled the request payload, parsed the JSON response yourself, and added a retry loop for rate limits.
  • Then your manager asks you to benchmark GPT-4o against Claude 3.5 Sonnet.

Concepts in play: llm-agents-and-langgraph, deep-learning-foundations, api-gateway, rate-limiting

Stage 1 β€” LangChain's Core Primitives: Prompts, Models, and Parsers

  • Every LangChain pipeline β€” no matter how complex β€” is built from three building blocks.
  • Understanding these three objects is 80% of what you need to be productive with LangChain.
  • ChatPromptTemplate and the {variable} Syntax A ChatPromptTemplate is a reusable message template with named placeholders.
  • You define it once with {curly-brace} variables, then call .invoke({"variable": "value"}) to fill them in at runtime.
from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant that explains topics clearly."),
    ("human", "Explain {topic} in simple terms for a {audience} audience."),
])

# Fill in the variables
filled = prompt.invoke({"topic": "neural networks", "audience": "high school student"})
print(filled.messages)
# [SystemMessage(content='You are a helpful assistant...'),
#  HumanMessage(content='Explain neural networks in simple terms for a high school student audience.')]
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage

messages = [
    SystemMessage(content="You are a concise technical writer."),
    HumanMessage(content="What is a transformer model?"),
    AIMessage(content="A transformer is a neural network that uses attention mechanisms..."),
    HumanMessage(content="Can you give a one-sentence summary?"),
]

Observable outcome: You define it once with {curly-brace} variables, then call .invoke({"variable": "value"}) to fill them in at runtime.

Stage 2 β€” LCEL: How the Pipe Operator Assembles a Chain

  • LCEL (LangChain Expression Language) is the modern way to compose LangChain components.
  • Its key innovation is a single operator: the pipe ( ).
  • You have already seen all three pieces β€” prompt, model, and parser.
  • LCEL lets you connect them left to right: That one line creates a fully functional pipeline.
chain = prompt | llm | parser
# pip install langchain langchain-openai
import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

os.environ["OPENAI_API_KEY"] = "your-openai-api-key"

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful technical explainer."),
    ("human", "Explain {topic} in one paragraph for a {level} audience."),
])
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
parser = StrOutputParser()

chain = prompt | llm | parser

result = chain.invoke({"topic": "REST APIs", "level": "beginner"})
print(result)

Observable outcome: LCEL lets you connect them left to right: That one line creates a fully functional pipeline.

Stage 3 β€” Visualizing the LCEL Data Flow

  • The pipe operator is easy to read left-to-right, but it helps to see what data type flows between each step.
  • Here is the complete transformation: Notice that each arrow represents a type transformation : a dict becomes a list of messages, messages become an AIMessage , and AIMessage becomes a str .
  • This is why the Runnable protocol is powerful β€” each component only needs to know its input and output type, not anything about its neighbors.
graph TD
    A[Input Dictionary {topic: '...', level: '...'}] --> B["ChatPromptTemplate (fills variables)"]
    B --> C[List of ChatMessages [SystemMessage, HumanMessage]]
    C --> D["ChatOpenAI / ChatAnthropic / ChatOllama (calls LLM API)"]
    D --> E["AIMessage (raw model response object)"]
    E --> F["StrOutputParser / JsonOutputParser (extracts content)"]
    F --> G["Plain str or dict (your application data)"]

    style A fill:#e8f4f8,stroke:#2196F3
    style D fill:#fff3e0,stroke:#FF9800
    style G fill:#e8f5e9,stroke:#4CAF50

Observable outcome: This is why the Runnable protocol is powerful β€” each component only needs to know its input and output type, not anything about its neighbors.

Stage 4 β€” Real-World Applications: LLM-Agnostic Deployment from Prototype to Production

  • The single biggest benefit of LCEL is that swapping a model is a one-line change.
  • The prompt, the parser, and all your business logic stay identical.
  • Only the object assigned to llm changes.
  • Here is the same explain topic function running against three different providers without a single structural change: All three produce a well-formed plain-text answer.
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a concise technical explainer. Keep answers to 3 sentences max."),
    ("human", "Explain {topic} to a {level} developer."),
])
parser = StrOutputParser()

def explain_topic(topic: str, level: str, llm) -> str:
    chain = prompt | llm | parser
    return chain.invoke({"topic": topic, "level": level})

# ── OpenAI ──────────────────────────────────────────────────────
openai_llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
print("OpenAI:", explain_topic("LCEL", "beginner", openai_llm))

# ── Anthropic ───────────────────────────────────────────────────
anthropic_llm = ChatAnthropic(model="claude-3-5-haiku-20241022", temperature=0)
print("Anthropic:", explain_topic("LCEL", "beginner", anthropic_llm))

# ── Ollama (local β€” no API key required) ────────────────────────
# Run: ollama pull llama3.2 first
ollama_llm = ChatOllama(model="llama3.2", temperature=0)
print("Ollama:", explain_topic("LCEL", "beginner", ollama_llm))

Observable outcome: Here is the same explain topic function running against three different providers without a single structural change: All three produce a well-formed plain-text answer.

Stage 5 β€” Deep Dive: How LCEL's Runnable Protocol Wires Components Together

  • Every component you have used β€” ChatPromptTemplate , ChatOpenAI , StrOutputParser β€” implements LangChain's Runnable protocol.
  • This single interface contract is what makes the pipe operator work.
  • A Runnable exposes four methods, and you get all of them for free on every LCEL chain: When you call chain.invoke({"topic": "..."}) , LangChain walks the pipe left to right β€” it calls .invoke() on the prompt, passes the…
  • The same walk applies to .stream() , meaning streaming is built into every LCEL chain without any extra plumbing: This uniformity also means you can inject custom Python logic anywhere in the chain using RunnableLambda(…
# Token-by-token streaming β€” no custom iterator needed
for chunk in chain.stream({"topic": "LCEL", "level": "beginner"}):
    print(chunk, end="", flush=True)

Observable outcome: The same walk applies to .stream() , meaning streaming is built into every LCEL chain without any extra plumbing: This uniformity also means you can inject custom Python logic anywhere in the chain using RunnableLambda(…

Stage 6 β€” LangChain's Open-Source Architecture: Installation, LCEL, and the Runnable Protocol

  • LangChain (github.com/langchain-ai/langchain) is an open-source Python and TypeScript framework maintained by LangChain, Inc.
  • It is built around a layered package structure that keeps the core abstractions separate from provider-specific integrations: - langchain-core β€” The foundational Runnable protocol, base classes for prompts, models, pars…
  • This package is the only dependency shared by everything.
  • Installed automatically when you install langchain .
# Core framework
pip install langchain langchain-core

# Provider integrations
pip install langchain-openai      # OpenAI and Azure OpenAI
pip install langchain-anthropic   # Anthropic Claude
pip install langchain-ollama      # Ollama local models

# Optional: for JSON parsing utilities
pip install langchain-community

Observable outcome: Installed automatically when you install langchain .

End-to-end outcome

Imagine you are three days into building a customer support bot.

Alternatives considered

Decision Guide: LangChain vs. Direct API Calls

Decision Guide: LangChain vs. Direct API Calls

Failure and degraded modes

Trade-offs and Failure Modes: What LangChain Does Not Fix

  • Detection: LangChain's abstractions deliver genuine value, but they introduce their own costs.
  • User or operator impact: LangChain's abstractions deliver genuine value, but they introduce their own costs.

Share this case study

Discuss architecture trade-offs and lessons with your team.

Share:XLinkedInReddit