« 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-core substrate, 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 phaseLangGraphPhase 18
What it isdeclarative composition of components into pipelinesa stateful, cyclic agent runtime (Pregel/BSP super-steps)
Control-flow shapelinear + branching data flow (a DAG: a | b | c, parallel, branch)arbitrary graphs with cycles — reason→act→observe loops
Statenone — data flows through; each invoke is statelessfirst-class: channels + reducers, persisted every super-step
Persistence / durabilitynot built in (wrap it yourself)checkpointer + thread_id: memory, crash-resume, time travel
Human-in-the-loopnot built ininterrupt() / Command(resume=...) — a durable pause
The unitthe Runnable (invoke/batch/stream, composed with |)the StateGraph (nodes, edges, reducers, super-steps)
Sweet spotRAG chains, extraction, routing, prompt pipelines — one-pass workflowscomplex agents: loops, multi-agent, approval gates, long-running state
Agent storythe classic AgentExecutor (ReAct loop) — deprecated for new workcreate_react_agent and everything beyond it — the successor
Relationshipboth sit on langchain-core; a compiled LangGraph graph is a Runnable and drops into an LCEL pipelineLangGraph 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 classic AgentExecutor ReAct loop — plus memory/message history and why LangGraph superseded the executor (Lab 03).
  • Ops: LangSmith tracing and evaluation, LangServe/RunnableWithMessageHistory at a high level — covered in the Warmup, not built as labs.

The labs

LabYou buildProves you understand
01 — LCEL & the Runnable Protocolthe Runnable interface (invoke/batch/stream) and the whole composition algebra: |, parallel, passthrough/assign, branch, fallbacks, retrywhat | actually does; why everything composes; how streaming flows; invoke == folded stream
02 — A RAG Chain From Runnablesretriever → format → prompt → injected model → citation-aware parser, wired as {"docs": retriever, "question": passthrough} | assign(...) | parsethe canonical LangChain RAG shape; where citations come from; why the question threads through; the empty-retrieval path
03 — The Classic AgentExecutorthe ReAct tool-calling loop: Tool, AgentAction/AgentFinish, scratchpad, max_iterations, error observations, return_intermediate_steps, cross-invoke memorythe 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 pytest and your own lab.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_iterations and 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 AgentExecutor is 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 — with RunnableParallel/assign carrying 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.