How to Develop Apps Using LangChain and LLMs
Lego Bricks for LLM Apps
LangChain is a framework that simplifies building LLM applications.
π― System Scenario & Requirements
System Goal
LangChain is a framework that simplifies building LLM applications.
Actors & Components
- Application or API tier
- Core service under design
- Operator or SRE observer
Functional Requirements
- TLDR: LangChain is a framework that simplifies building LLM applications.
- It provides abstractions for Chains (linking steps), Memory (remembering chat history), and Agents (using tools).
- It turns raw API calls into composable building blocks.
Constraints & Assumptions
- When to skip LangChain: If your use case is a single LLM call with a fixed prompt, the raw API (OpenAI SDK) is simpler and more debuggable.
- LangChain pays off when you have multi-step chains, conditional tool use, or complex memory strategies.
β‘ Execution Stages & Decision Trace
Stage 1 β Core Concepts: What Makes LangChain Different
Raw LLM APIs hand you a hammer and leave you to build the house.
Every call is stateless β the model forgets everything the moment you hang up.
Deep Dive
Scenario context
LangChain is a framework that simplifies building LLM applications. It provides abstractions for Chains (linking steps), Memory (remembering chat history), and Agents (using tools). It turns raw API calls into composable building blocks.
TLDR: LangChain chains, agents, and memory turn raw LLM calls into composable applications β from retrieval-augmented Q&A to multi-step autonomous agents.
Beβ¦
This walkthrough focuses on Lego Bricks for LLM Apps.
Before making a design choice, clarify:
- Before we explain how LangChain works, here is what it looks like in practice.
- This five-line chain translates text to French β prompt template, LLM call, and output parsing wired together: That pipe β connecting a prompt template, an LLM, and an output parser β is LangChain's core abstraction.
- You will understand every part of that line by the end of this guide.
Concepts in play: llm-agents-and-langgraph, python-concurrency-nogil, vector-databases-rag, api-gateway
Stage 1 β Core Concepts: What Makes LangChain Different
- Raw LLM APIs hand you a hammer and leave you to build the house.
- Every call is stateless β the model forgets everything the moment you hang up.
- You must manually format prompt strings, append conversation history to each request, parse the model's text output into structured data, and wire up tool calls yourself.
- For a one-off script that's fine; for a production chatbot or document Q&A system it becomes hundreds of lines of brittle glue.
chain = prompt | model | parser # nothing runs yet
chain.invoke({"text": "hello"}) # pipeline executes here
Observable outcome: For a one-off script that's fine; for a production chatbot or document Q&A system it becomes hundreds of lines of brittle glue.
Stage 2 β The Three Core Abstractions
- Chains β Linking Steps A Chain connects: User Input β Prompt Template β LLM β Output Parser .
- The operator in LCEL (LangChain Expression Language) pipes the output of one step into the next: Chains are composable β the output of chain can be piped into another chain.
- π LangChain Chain Flow The sequence diagram shows the four-step data flow inside a basic LCEL chain.
- User-provided variables flow into the PromptTemplate, which formats them into a complete prompt and forwards it to the LLM; the LLM's raw text output then passes to the OutputParser, which transforms it into the final sβ¦
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
model = ChatOpenAI(model="gpt-4o")
prompt = ChatPromptTemplate.from_template("Translate to French: {text}")
chain = prompt | model | StrOutputParser()
result = chain.invoke({"text": "Hello, how are you?"})
# "Bonjour, comment allez-vous ?"
sequenceDiagram
participant U as User
participant PT as PromptTemplate
participant L as LLM
participant OP as OutputParser
U->>PT: Input variables
PT->>L: Formatted prompt
L->>OP: Raw LLM output
OP-->>U: Parsed response
Observable outcome: User-provided variables flow into the PromptTemplate, which formats them into a complete prompt and forwards it to the LLM; the LLM's raw text output then passes to the OutputParser, which transforms it into the final sβ¦
Stage 3 β Building a RAG Pipeline with LangChain
- Retrieval-Augmented Generation (RAG) is the most common real-world LangChain pattern: load documents β embed them β retrieve relevant chunks β answer with context.
- The diagram shows the five-step runtime flow of a RAG query.
- The user's question is first embedded into a vector, which is compared against the vector store (Chroma or FAISS) to retrieve the top-K semantically similar document chunks; those chunks are then injected as context intβ¦
- The takeaway: RAG lets the LLM answer questions about your private documents without any fine-tuning β the retrieval step scopes the model's attention to the relevant content at query time.
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.chains import RetrievalQA
# 1. Load and split documents
loader = TextLoader("my_docs.txt")
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # ~500 tokens: focused enough for precise retrieval, large enough to preserve sentence context
chunk_overlap=50, # 10% overlap so sentences split across boundaries appear in both adjacent chunks
)
chunks = splitter.split_documents(docs)
# 2. Embed and store
vectorstore = Chroma.from_documents(chunks, OpenAIEmbeddings())
# 3. Build the QA chain
qa = RetrievalQA.from_chain_type(
llm=model,
retriever=vectorstore.as_retriever(search_kwargs={"k": 4}) # k=4: retrieve 4 chunks β balances context richness vs token budget
)
qa.invoke("What is the refund policy?")
flowchart LR
Q[User Question]
Embed[Embed Question]
VDB["Vector Store (Chroma/FAISS)"]
Chunks[Top-K Chunks]
LLM[LLM + Context]
A[Answer]
Q --> Embed --> VDB --> Chunks --> LLM --> A
Observable outcome: The takeaway: RAG lets the LLM answer questions about your private documents without any fine-tuning β the retrieval step scopes the model's attention to the relevant content at query time.
Stage 4 β Deep Dive: LangSmith Observability for LLM Chains
- In production, you need to debug why a chain produced a wrong answer.
- LangSmith (LangChain's tracing backend) records every step: - Which prompt was sent.
- Which tool was called and with what arguments.
- Total latency and token cost per step.
import os
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "your_key"
Observable outcome: - Total latency and token cost per step.
Stage 5 β Key Lessons from Building with LangChain
- LCEL chains are lazy β design for it.
- The expression only builds the pipeline; nothing executes until .invoke() , .stream() , or .batch() is called.
- This enables streaming tokens to a UI and parallel batch processing without any code changes.
- Choose memory type by session length.
Observable outcome: Choose memory type by session length.
End-to-end outcome
Before we explain how LangChain works, here is what it looks like in practice.
Alternatives considered
Decision Guide: LangChain Application Architecture
A multi-turn agent application wires together every abstraction from the sections above.
Failure and degraded modes
Trade-offs & Failure Modes: LangChain
- Detection: When to skip LangChain: If your use case is a single LLM call with a fixed prompt, the raw API (OpenAI SDK) is simpler and more debuggable.
- User or operator impact: When to skip LangChain: If your use case is a single LLM call with a fixed prompt, the raw API (OpenAI SDK) is simpler and more debuggable.
