« Track Overview · Warmup · Hitchhiker's · Deep Dive · Principal Deep Dive · Core Contributor · Staff Notes
Phase 29 — LangChain (Core): LCEL, Chains, Retrievers & Agents
Answers these JD lines: the enterprise AI/ML Engineer role requires "Experience with
Hugging Face and LangChain for building LLM-powered applications" outright — "GenAI/LLM
frameworks: Hugging Face + LangChain; prompt engineering, RAG, embeddings, vector databases" —
and the agentic-platform roles list LangChain by name alongside LangGraph in their required
"LLM orchestration frameworks." LangChain is still the most widely deployed LLM application
framework in existence; the millions of lines of production LCEL and AgentExecutor code out
there are exactly what these roles will ask you to build, maintain, or migrate.
This phase and Phase 18 (LangGraph) are a pair. Same company, same
langchain-coresubstrate, two different execution models — and a principal engineer is expected to know when to use which without hesitating. Phase 18 built the stateful graph runtime; this phase builds the declarative composition layer underneath and beside it.
Why this phase exists
Most engineers can paste chain = prompt | model | parser from a tutorial. Far fewer can explain
what | actually does, why a bare Python function or dict can appear in the middle of a chain,
how streaming propagates through a pipeline, where a RAG chain's source citations come from, or why
LangChain itself now tells you to build new agents in LangGraph instead of AgentExecutor. Those
are the staff/principal questions, because they reveal whether you can debug a chain that returns
the wrong thing, design one that degrades gracefully, and choose the right runtime for a given
control-flow shape.
The key insight this phase drills: LangChain core is one abstraction — the Runnable — taken
seriously. Every component (prompt template, model, output parser, retriever, tool, plain
function) implements the same invoke/batch/stream interface; LCEL's | composes them, and
the composition is itself a Runnable. That closure property gives you free streaming, batching,
fallbacks, retries, and tracing on anything you build — and its limits (linear/branching data flow,
no cycles, no durable state) are precisely where LangGraph begins.
LangChain vs LangGraph — the distinction that signals seniority
| LangChain (LCEL) — this phase | LangGraph — Phase 18 | |
|---|---|---|
| What it is | declarative composition of components into pipelines | a stateful, cyclic agent runtime (Pregel/BSP super-steps) |
| Control-flow shape | linear + branching data flow (a DAG: a | b | c, parallel, branch) | arbitrary graphs with cycles — reason→act→observe loops |
| State | none — data flows through; each invoke is stateless | first-class: channels + reducers, persisted every super-step |
| Persistence / durability | not built in (wrap it yourself) | checkpointer + thread_id: memory, crash-resume, time travel |
| Human-in-the-loop | not built in | interrupt() / Command(resume=...) — a durable pause |
| The unit | the Runnable (invoke/batch/stream, composed with |) | the StateGraph (nodes, edges, reducers, super-steps) |
| Sweet spot | RAG chains, extraction, routing, prompt pipelines — one-pass workflows | complex agents: loops, multi-agent, approval gates, long-running state |
| Agent story | the classic AgentExecutor (ReAct loop) — deprecated for new work | create_react_agent and everything beyond it — the successor |
| Relationship | both sit on langchain-core; a compiled LangGraph graph is a Runnable and drops into an LCEL pipeline | LangGraph nodes freely call LCEL chains inside them |
The one-liner: LCEL composes pipelines; LangGraph runs stateful agents. If your data flows one way through the steps, LCEL is simpler and better. The moment you need a loop, durable state, or a human pause, you have left LCEL's design envelope — reach for the graph.
Concept map
- The ecosystem:
langchain-core(Runnable, prompts, messages, output parsers, tools) ·langchain(chains, retrievers, the classic agents) · integration packages (langchain-openai,langchain-anthropic, …) · LangGraph (agents) · LangSmith (tracing/eval) · LangServe (deploy). - The Runnable protocol:
invoke/batch/stream(+ async twins) — one interface for every component, closed under composition (Lab 01). - LCEL composition:
RunnableSequence(|),RunnableParallel(fan-out),RunnablePassthrough(+.assign),RunnableLambda(coercion),RunnableBranch(routing),.with_fallbacks/.with_retry/.with_config(resilience) (Lab 01). - Prompts & parsers: string/chat/few-shot templates in;
StrOutputParser, JSON, Pydantic structured output (+.with_structured_output) out — the model sandwich. - Retrieval & RAG:
Document, loaders → splitters → embeddings → vector store →as_retriever(); the canonical RAG chain with source citations (Lab 02; mechanisms in Phase 05 and Phase 06). - Tools & agents:
Tool/@tool, tool-calling models, and the classicAgentExecutorReAct loop — plus memory/message history and why LangGraph superseded the executor (Lab 03). - Ops: LangSmith tracing and evaluation, LangServe/
RunnableWithMessageHistoryat a high level — covered in the Warmup, not built as labs.
The labs
| Lab | You build | Proves you understand |
|---|---|---|
| 01 — LCEL & the Runnable Protocol | the Runnable interface (invoke/batch/stream) and the whole composition algebra: |, parallel, passthrough/assign, branch, fallbacks, retry | what | actually does; why everything composes; how streaming flows; invoke == folded stream |
| 02 — A RAG Chain From Runnables | retriever → format → prompt → injected model → citation-aware parser, wired as {"docs": retriever, "question": passthrough} | assign(...) | parse | the canonical LangChain RAG shape; where citations come from; why the question threads through; the empty-retrieval path |
| 03 — The Classic AgentExecutor | the ReAct tool-calling loop: Tool, AgentAction/AgentFinish, scratchpad, max_iterations, error observations, return_intermediate_steps, cross-invoke memory | the loop under every LangChain 0.x agent — and exactly why LangGraph replaced it with an explicit graph |
Integrated scenario
An enterprise team ships a support assistant: answer from the product docs with citations, fall
back to a cheaper model when the primary throttles, and escalate complex multi-step cases to an
agent. The docs Q&A is a textbook LCEL RAG chain (Lab 02): {"context": retriever | format, "question": passthrough} | prompt | model.with_fallbacks([cheap_model]) | parser — one-pass data
flow, streaming to the UI for free because every step is a Runnable (Lab 01). The escalation path
is an agent — it must loop over tools (search tickets, query billing, draft a refund) and pause
for human approval before the refund executes. That is not an LCEL job: the loop is LangGraph's
agent ↔ tools graph with a checkpointer and interrupt() (Phase 18), and the legacy
AgentExecutor version of it (Lab 03) is what you will find in the codebase you inherit and be
asked to migrate. Knowing where the pipeline ends and the graph begins is the design review.
Deliverables checklist
-
Lab 01 green under
LAB_MODULE=solution pytestand your ownlab.py(29 tests). - Lab 02 green (25 tests); you can trace where the RAG chain's citations come from.
-
Lab 03 green (21 tests); you can explain
max_iterationsand the error-observation repair loop. -
You can state the Runnable protocol and why LCEL composes (closure under
|). - You can draw the LangChain vs LangGraph table from memory and defend a "which runtime?" call for a given feature.
-
You can explain why
AgentExecutoris deprecated and sketch its migration to a LangGraph graph.
Key takeaways
- LangChain core is the Runnable protocol. One interface (
invoke/batch/stream), closed under|— that is why prompts, models, parsers, retrievers, and your own functions all snap together, and why streaming/batching/fallbacks come free. - LCEL is a pipeline language, not an agent runtime. Linear and branching data flow is its design envelope; loops, durable state, and human pauses belong to LangGraph (Phase 18).
- RAG in LangChain is five Runnables wired with
|— retrieve, format, prompt, generate, parse — withRunnableParallel/assigncarrying the question and the sources through. - The classic AgentExecutor is a while-loop you can now build in your sleep — and its opaqueness (no pause, no checkpoint, no mid-loop editing) is the precise reason LangGraph superseded it.
- Cross-links: the Runnable's injected-model seam is Phase 00's testability discipline; the RAG chain is Phase 05's retriever behind a stable contract; the executor loop is Phase 01's ReAct, framework-ified.