« Phase 29 README · Warmup · Deep Dive · Principal Deep Dive · Core Contributor · Staff Notes
Phase 29 — Hitchhiker's Guide (LangChain Core)
The compressed practitioner tour. Read the WARMUP for the mechanism; this is the stuff you say in the meeting.
30-second mental model
LangChain core = one interface taken seriously. Everything — prompt, model, parser,
retriever, tool, your own function — is a Runnable (invoke/batch/stream + async twins),
and LCEL's | composes Runnables into a Runnable. That closure is why prompt | model | parser gets streaming, batching, fallbacks, and tracing for free. LCEL builds pipelines
(DAGs: sequence, parallel, branch — no cycles, no state). The moment you need a loop, durable
state, or a human pause, you've left LCEL and entered LangGraph
(Phase 18). The classic AgentExecutor was
LangChain's loop — now deprecated in favor of exactly that graph.
The facts to tattoo on your arm
| Fact | Why |
|---|---|
Runnable = invoke / batch / stream (+ ainvoke…) | the one interface everything implements |
composition is closed: a | b is a Runnable | streaming/fallbacks/tracing apply to whole chains |
invoke == stream folded with + | chunks are additive (AIMessageChunk.__add__) |
dict in a pipe → RunnableParallel; fn → RunnableLambda | why the RAG chain opens with a dict literal |
RunnablePassthrough.assign(k=...) | add a computed key, keep the rest — the RAG workhorse |
| a sequence streams only its last stream-capable step | model + parser go LAST or you lose streaming |
retriever contract: invoke(query) -> list[Document] | the chain depends on the contract, not the scorer |
.with_structured_output constrains; parsers post-process | different mechanism, different failure mode |
AgentExecutor default max_iterations=15, forced stop | "Agent stopped due to iteration limit." is a real string |
| errors become observations, not crashes | the agent's repair loop (unknown tool, handle_tool_error) |
| LCEL = DAG; LangGraph = cycles + state + checkpoints | the whole decision framework in one line |
Framework one-liners
ChatPromptTemplate.from_messages([...])+MessagesPlaceholder("chat_history")— prompts.{"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | model | StrOutputParser()— the canonical RAG chain.model.bind_tools(tools)→AIMessage.tool_calls→ run tools →ToolMessage— tool calling.model.with_structured_output(Schema)— typed output via the native channel.chain.with_fallbacks([...])/.with_retry()/.with_config(...)— resilience wrappers.RunnableWithMessageHistory(chain, get_history)+session_idconfig — chat memory.vectorstore.as_retriever(search_kwargs={"k": 4})— store → retriever adapter.langchain-core/langchain/langchain-openaietc. / LangGraph / LangSmith / LangServe — the package map.LANGCHAIN_TRACING_V2=true— the LangSmith on-switch.
The distinctions that signal seniority
- LCEL vs LangGraph → pipeline vs runtime. One-way data flow → LCEL; anything with a back-edge (loops, retries-with-state, approval gates) → LangGraph. They compose — a compiled graph is a Runnable.
- Parser vs
.with_structured_output→ post-processing text vs constraining generation through the provider's native channel. The second fails less; the first works everywhere. - Scratchpad vs chat history → per-run working memory (the action/observation trace) vs cross-run conversation memory. Different lifetimes, different bugs.
Retrieve-style vsRetrieveAndGenerate-style → in LangChain terms: a raw retriever you prompt yourself vscreate_retrieval_chaindoing prompt+generate+sources for you.- Deprecated vs dead →
AgentExecutoris deprecated for new work but very alive in prod; "maintain it, migrate it, don't start on it" is the senior posture.
War stories
- The chain that stopped streaming after a one-line change. Someone appended a tidy post-processing lambda after the parser. It needed the whole string, so the UI went from token-by-token to a 9-second freeze. Fix: keep the model + stream-capable parser last (or make the step chunk-wise). "Streaming is a property of the pipeline shape" — learned in prod.
- The RAG bot that answered confidently about a product that didn't exist. Empty retrieval, and the prompt didn't force grounding — the model freestyled. The fix was Lab 02's exact pattern: refuse on empty context, and return citations so every answer is attributable. Retrieval bugs masquerade as model bugs.
- The agent that burned $400 overnight. A legacy
AgentExecutorwith a flaky tool: the error observation ("not a valid tool") made the model retry the same hallucinated tool forever — nobody had loweredmax_iterationsfrom 15 for a two-step task, and a cron kept re-invoking it. Budgets are a feature; the trace (return_intermediate_steps) is how it was diagnosed.
Vocabulary
Runnable (invoke/batch/stream) · LCEL / | / RunnableSequence ·
RunnableParallel (fan-out) · RunnablePassthrough / .assign · RunnableLambda
(coercion) · RunnableBranch (routing) · with_fallbacks / with_retry / bind ·
PromptTemplate / ChatPromptTemplate / MessagesPlaceholder / few-shot ·
output parser / StrOutputParser / PydanticOutputParser / .with_structured_output ·
Document / loader / splitter / Embeddings / VectorStore / as_retriever ·
RAG chain / citations / grounded refusal · @tool / bind_tools / tool_calls /
ToolMessage · AgentExecutor / AgentAction / AgentFinish / scratchpad /
max_iterations / return_intermediate_steps · RunnableWithMessageHistory /
session_id · LangSmith (tracing/eval) · LangServe (deploy) · LangGraph (the
successor runtime — Phase 18).
Beginner mistakes
- Reaching for LangGraph for a one-pass RAG pipeline — or hacking loops into LCEL. Match the runtime to the control-flow shape.
- Consuming the retrieved docs instead of carrying them (
assign), then wondering where citations should come from. - Putting a whole-input step after the model and losing streaming silently.
- Writing new agents on
AgentExecutorbecause the tutorial you found predates LangGraph. - Testing chains against a live model API — inject a fake model (it's just a Runnable) and assert the wiring.
- Treating tool docstrings as comments. The model reads them to choose tools — they are prompts.
- Importing everything from the legacy monolith instead of
langchain-core+ per-provider integration packages, then fighting version conflicts.