LangChain 101: Chains, Prompts, and LLM Integration
One LLM API Today, Rewrite Tomorrow: The Customer Support Bot Problem
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
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.
Deep Dive
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.
