Learn LangChain - Core Concepts & Main Architecture
Episode 2 of 23

Learn LangChain - Core Concepts & Main Architecture

Breaking down the LangChain v1.x architecture: the core langchain-core primitives (chat models, prompt templates, output parsers, tools, embeddings, vector stores, retrievers), the LCEL composition language with the pipe operator, and the package structure map that is now modular per provider.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

In episode 1, you learned about LangChain's history and why it exists: provider standardization, composability, and stateful orchestration. Now it's time to dissect its architecture — which components you'll be handling most often, how those components are assembled into pipelines, and how the packages are organized in v1.x.

Episode 2 is a concept map. There isn't much executing code here, but every subsequent episode will refer to the terms we establish in this episode — the langchain-core primitives, LCEL, and the package structure. Understand them well, because they'll become our shared language from here on.

Primitives in langchain-core

langchain-core is the heart of LangChain. It houses the abstract interfaces that act as contracts for all other components. Get to know these seven primitives — they're the main building blocks of almost every LangChain application:

PrimitiveFunction
BaseChatModelThe chat model interface for all providers
PromptTemplate / ChatPromptTemplateAssembles prompts from input variables
OutputParserConverts model output into a usable structure
ToolAn external function the model can call
EmbeddingsConverts text into vectors
VectorStoreStores and searches vectors
RetrieverRetrieves relevant documents for RAG context

All model providers — OpenAI, Anthropic, and others — implement BaseChatModel. All commercial and open source vector stores implement the VectorStore interface. With this pattern, you can swap implementations without changing your application logic.

Chat Models and Prompt Templates

Chat models are the most frequently used primitives. They take a sequence of messages (typically system, then human/assistant) and return a reply message. The simplest example:

PythonMinimal chat model with langchain-openai
from langchain_openai import ChatOpenAI
 
llm = ChatOpenAI(model="gpt-4o-mini")
response = llm.invoke("Apa itu LangChain dalam satu kalimat?")
print(response.content)

Prompt templates separate the prompt from the data. Instead of writing a full prompt with embedded text, you define placeholders and fill in their values at invoke time:

PythonChatPromptTemplate with input variables
from langchain_core.prompts import ChatPromptTemplate
 
prompt = ChatPromptTemplate.from_messages([
    ("system", "Kamu adalah asisten yang menjawab dengan ringkas."),
    ("human", "Jelaskan konsep {topik} dalam {jumlah} kalimat."),
])
 
formatted = prompt.invoke({"topik": "LCEL", "jumlah": "dua"})
print(formatted.to_messages())

Output Parsers, Tools, Embeddings, Vector Stores, and Retrievers

The other four primitives work at different stages of the pipeline:

  • Output parser takes the model's free-text answer and converts it into a form code can use — for example a plain str, JSON, or a Pydantic object. We'll dig into the details in episode 6.
  • Tool wraps a Python function (database query, API call) together with its parameter schema, so the model can choose and call it. This is the foundation of agents — covered in episode 8.
  • Embeddings convert sentences into numeric vectors, and a vector store stores those vectors for similarity-based search — the main ingredient of RAG in episode 10.
  • Retriever is the interface that wraps search (usually from a vector store) to fetch relevant documents as context for the model's answer — the core of RAG in episode 11.

All these primitives are designed to be composable and interchangeable — that's where LCEL comes in.

LCEL: The Chain Composition Language

LCEL (LangChain Expression Language) is the declarative way to assemble components into pipelines. The idea: every component is a Runnable, and runnables can be connected with the pipe operator |, just like passing the output of one command to the next command in a terminal.

The Pipe Operator and Runnable

The basic concept is very simple — output on the left flows in as input on the right:

PythonBuilding a chain with the pipe operator
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
 
prompt = ChatPromptTemplate.from_messages([
    ("system", "Kamu ahli bahasa Indonesia."),
    ("human", "{pertanyaan}"),
])
 
chain = prompt | ChatOpenAI(model="gpt-4o-mini") | StrOutputParser()
 
hasil = chain.invoke({"pertanyaan": "Apa itu operator pipa?"})
print(hasil)

Notice the {pertanyaan} variable in the template — upstream of the pipe operator, the input value is sent to the prompt, the result is forwarded to the model, then the model's answer is converted into a string by StrOutputParser. One expression, one complete pipeline.

Besides sequential chains, LCEL provides specialized runnables for orchestration:

RunnableRole
RunnableSequenceSequential execution — this is what the pipe operator builds
RunnableParallelRuns several branches at the same time
RunnablePassthroughPasses input through unchanged, useful for adding context
RunnableLambdaWraps an arbitrary Python function into a runnable

An example of parallelism: one input processed by two different chains at once:

PythonRunnableParallel runs two branches at once
from langchain_core.runnables import RunnableParallel, RunnablePassthrough
 
rantai_ringkas = prompt | llm | StrOutputParser()
rantai_detail = prompt | llm | StrOutputParser()
 
paralel = RunnableParallel(
    ringkas=rantai_ringkas,
    detail=rantai_detail,
    input_asli=RunnablePassthrough(),
)

invoke, batch, and stream

Every runnable in LCEL shares the same three execution modes — this uniform interface is what makes composition so easy:

  • invoke — run a single input, wait for the complete result.
  • batch — run a list of inputs; optionally with concurrency.
  • stream — receive results incrementally chunk by chunk.

All three have async versions (ainvoke, abatch, astream) that we'll use when handling concurrent applications. These modes are always available on every runnable, so your code doesn't change shape just because the component behind it changes.

The LangChain Package Structure

Understanding the package structure helps you know where to import from. In v1.x the split is clear:

PackageContents
langchain-coreCore primitives: runnables, prompts, parsers, tools, interfaces
langchainGeneral integrations and aggregate components
langchain-openaiOpenAI model, embeddings, and tools integrations
langchain-anthropicAnthropic (Claude) model integration
langgraphAgent orchestration with state graphs
langsmithTracing and evaluation

The rule of thumb: abstractions in langchain-core, provider-specific integrations in per-provider packages. Imports starting with langchain_openai or langchain_anthropic are integrations; those starting with langchain_core are abstract contracts. Don't worry about memorizing every package — you'll be importing the same ones over and over until they stick.

Info

In this series we use the Python packages. LangChain is also available for TypeScript/JavaScript with an equivalent structure (for example @langchain/openai), so the primitives and LCEL concepts still apply — only the syntax differs.

Conclusion

Episode 2 gave you a map of LangChain's architecture: the seven langchain-core primitives that act as contracts between components, the LCEL composition language that assembles runnables with the pipe operator along with the invoke, batch, and stream modes, and the modular package structure that separates core from provider integrations.

Key takeaways:

  • langchain-core provides abstract primitives: chat models, prompt templates, output parsers, tools, embeddings, vector stores, and retrievers.
  • All primitives implement standard interfaces, so they can be swapped out.
  • LCEL assembles runnables with the pipe operator |; left output flows to the right.
  • RunnableParallel, RunnablePassthrough, and RunnableLambda enrich non-sequential composition.
  • Every runnable supports invoke, batch, stream, and their async variants.

In episode 3 we start actually writing code that runs: project setup, chat model initialization, and all invocation modes — from invoke and ainvoke, streaming with stream/astream, to batch/abatch along with the contents of the response object. See you there!