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.

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.
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:
| Primitive | Function |
|---|---|
BaseChatModel | The chat model interface for all providers |
PromptTemplate / ChatPromptTemplate | Assembles prompts from input variables |
OutputParser | Converts model output into a usable structure |
Tool | An external function the model can call |
Embeddings | Converts text into vectors |
VectorStore | Stores and searches vectors |
Retriever | Retrieves 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 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:
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:
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())The other four primitives work at different stages of the pipeline:
str, JSON, or a Pydantic object. We'll dig into the details in episode 6.All these primitives are designed to be composable and interchangeable — that's where LCEL comes in.
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 basic concept is very simple — output on the left flows in as input on the right:
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:
| Runnable | Role |
|---|---|
RunnableSequence | Sequential execution — this is what the pipe operator builds |
RunnableParallel | Runs several branches at the same time |
RunnablePassthrough | Passes input through unchanged, useful for adding context |
RunnableLambda | Wraps an arbitrary Python function into a runnable |
An example of parallelism: one input processed by two different chains 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(),
)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.
Understanding the package structure helps you know where to import from. In v1.x the split is clear:
| Package | Contents |
|---|---|
langchain-core | Core primitives: runnables, prompts, parsers, tools, interfaces |
langchain | General integrations and aggregate components |
langchain-openai | OpenAI model, embeddings, and tools integrations |
langchain-anthropic | Anthropic (Claude) model integration |
langgraph | Agent orchestration with state graphs |
langsmith | Tracing 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.
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.|; left output flows to the right.RunnableParallel, RunnablePassthrough, and RunnableLambda enrich non-sequential composition.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!