Learnixo

LangChain Mastery · Lesson 5 of 33

When to Use LangChain vs Raw OpenAI SDK

The Core Question

LangChain is an abstraction layer over LLM APIs. Like all abstractions, it adds value in some contexts and adds friction in others. The question isn't "is LangChain good?" — it's "does LangChain's abstraction cost match the benefit for my use case?"


What LangChain Adds (Benefits)

1. Provider switching without rewriting:

Python
# Raw OpenAI
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(model="gpt-4o", messages=[...])

# Raw Anthropic
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(model="claude-sonnet-4-6", messages=[...])

# LangChain  same code for both
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
model = ChatOpenAI(model="gpt-4o")   # or ChatAnthropic(model="claude-sonnet-4-6")
result = model.invoke([HumanMessage(content="...")])  # identical

2. Composable chains with LCEL:

Python
# Raw SDK: manual chaining
response1 = client.chat.completions.create(model="gpt-4o", messages=[...])
step1_output = response1.choices[0].message.content
messages2 = [{"role": "user", "content": f"Now summarize: {step1_output}"}]
response2 = client.chat.completions.create(model="gpt-4o", messages=messages2)

# LangChain: declarative, readable
chain = step1_prompt | model | StrOutputParser() | step2_prompt | model | StrOutputParser()
result = chain.invoke({"input": "..."})

3. Built-in streaming, batching, async (same syntax):

Python
# LangChain provides .stream(), .batch(), .ainvoke() on every component
# Raw SDK requires implementing these yourself for chains

4. Observability via LangSmith:

Python
import os
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "..."
# Now every chain.invoke() is automatically traced in LangSmith
# Latency, tokens, inputs, outputs  all captured

5. Rich ecosystem:

  • 100+ document loaders (PDF, web, CSV, database)
  • 40+ vector store integrations
  • Pre-built agent patterns (ReAct, tool calling)
  • Memory types (buffer, summary, vector)

What LangChain Costs (Drawbacks)

1. Abstraction overhead:

Python
# LangChain adds ~20-50ms of Python overhead per chain invocation
# Matters for latency-sensitive real-time applications

# Raw SDK
import time
t0 = time.time()
response = client.chat.completions.create(model="gpt-4o", messages=[...])
print(f"Raw: {(time.time() - t0)*1000:.0f}ms")  # e.g., 800ms (network-bound)

# LangChain
t0 = time.time()
result = chain.invoke({"input": "..."})
print(f"LangChain: {(time.time() - t0)*1000:.0f}ms")  # e.g., 850ms (50ms overhead)
# ~6% overhead  usually negligible since LLM latency dominates

2. Learning curve for abstractions: When something goes wrong in a LangChain chain, debugging requires understanding the abstraction stack. A raw SDK error is immediately obvious; a LangChain error may point to an internal abstraction.

3. Version instability: LangChain has had major breaking changes. LLMChain was deprecated. LCEL replaced older chain patterns. Teams using LangChain must keep up with the deprecation cycle.

4. Over-engineering simple tasks:

Python
# Overkill: LangChain for a simple single API call
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

chain = (
    ChatPromptTemplate.from_template("Translate '{text}' to French.")
    | ChatOpenAI(model="gpt-4o")
    | StrOutputParser()
)
result = chain.invoke({"text": "Hello"})

# Simpler: raw SDK
from openai import OpenAI
client = OpenAI()
result = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Translate 'Hello' to French."}],
).choices[0].message.content

Decision Framework

Python
# Use LangChain when:
use_langchain = any([
    multiple_llm_providers,              # Need to switch between OpenAI/Claude/Gemini
    multi_step_pipeline,                 # More than 2 chained steps
    complex_agents_with_tools,           # ReAct or tool-calling agents
    rag_system_with_documents,           # Document loaders + retrievers
    multi_turn_conversation_with_memory, # Need memory management
    needs_observability,                 # LangSmith tracing
    building_framework_not_one_off,      # Platform others will build on
])

# Use raw SDK when:
use_raw_sdk = any([
    single_api_call,              # Just one LLM call
    latency_is_critical,          # Sub-100ms requirement (unlikely with LLMs)
    team_unfamiliar_with_langchain, # Cognitive cost too high
    output_format_requires_precise_api_control,  # Complex response_format specs
    using_provider_specific_features,   # Structured outputs, audio, specific params
])

Side-by-Side: RAG Pipeline

Python
# Raw OpenAI SDK  full RAG pipeline
from openai import OpenAI
import chromadb
import numpy as np

openai_client = OpenAI()
chroma_client = chromadb.PersistentClient(path="./chroma_db")
collection = chroma_client.get_collection("clinical_docs")

def rag_raw(query: str) -> str:
    # Embed
    emb = openai_client.embeddings.create(
        model="text-embedding-3-small", input=[query]
    )
    query_embedding = emb.data[0].embedding

    # Retrieve
    results = collection.query(query_embeddings=[query_embedding], n_results=5)
    context = "\n".join(results["documents"][0])

    # Generate
    response = openai_client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "Answer using only the context."},
            {"role": "user", "content": f"Context:\n{context}\n\nQuestion: {query}"},
        ],
        temperature=0,
    )
    return response.choices[0].message.content


# LangChain  same RAG pipeline
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma(
    persist_directory="./chroma_db",
    collection_name="clinical_docs",
    embedding_function=embeddings,
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})

prompt = ChatPromptTemplate.from_messages([
    ("system", "Answer using only the context."),
    ("human", "Context:\n{context}\n\nQuestion: {question}"),
])

def format_docs(docs):
    return "\n".join(d.page_content for d in docs)

rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | ChatOpenAI(model="gpt-4o", temperature=0)
    | StrOutputParser()
)

result = rag_chain.invoke("What is the warfarin dose for AFib?")

Assessment: For RAG, LangChain reduces boilerplate significantly and handles streaming, batching, and retriever integration cleanly. Raw SDK gives more control over each step.


Hybrid Approach: LangChain for Structure, Raw SDK for Performance

Python
# Use LangChain for complex orchestration
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.tools import tool

@tool
def specialized_query(input: str) -> str:
    """Run a specialized query using raw SDK for performance."""
    # Use raw SDK inside a LangChain tool
    from openai import OpenAI
    raw_client = OpenAI()
    response = raw_client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": input}],
        response_format={"type": "json_object"},  # LangChain doesn't expose this cleanly
        temperature=0,
    )
    return response.choices[0].message.content

# Agent orchestration with LangChain, raw SDK where precision needed
agent = create_tool_calling_agent(ChatOpenAI(model="gpt-4o"), [specialized_query], prompt)

Recommendation by Use Case

| Use Case | Recommendation | |---|---| | Single API call | Raw SDK | | Translation/summarization tool | Raw SDK | | Multi-step reasoning pipeline | LangChain (LCEL) | | RAG system | LangChain | | Multi-agent with tools | LangChain | | Conversational chatbot | LangChain (memory) | | Real-time sub-second response | Raw SDK | | Team already knows LangChain | LangChain | | Greenfield project, simple requirements | Raw SDK first, add LangChain if needed |