langchain-core-workflow
Compose LangChain 1.0 chains with RunnableParallel, RunnableBranch, RunnablePassthrough.assign, and RunnableLambda — correct input/output shapes, debug probes, and typed composition that catches dict-shape bugs before invocation. Use when wiring multi-step chains, parallel retrievals, conditional routing, or threading state through a chain for RAG, classification, or extraction pipelines. Trigger with "runnable parallel", "runnable branch", "langchain rag composition", "passthrough assign", "langchain lcel", "runnable lambda", "debug probe".
What this skill does
# LangChain Core Workflow (Python)
## Overview
An engineer wires a four-stage LCEL chain: classify the question, retrieve
context, format the prompt, invoke the LLM. It looks clean:
```python
chain = (
RunnablePassthrough.assign(category=classifier)
| RunnablePassthrough.assign(docs=retriever)
| prompt
| llm
| StrOutputParser()
)
chain.invoke({"question": "What's our refund policy?"})
```
The call returns this:
```
Traceback (most recent call last):
...
File ".../runnables/base.py", line 3421, in _call_with_config
output = call_func_with_variable_args(func, input, ...)
File ".../prompts/chat.py", line 1021, in _format_messages
return await ... await self.ainvoke({**kwargs})
KeyError: 'question'
```
Nothing in that stack says **which stage produced the wrong dict shape**. The
`RunnablePassthrough.assign(docs=retriever)` call silently rebuilt the dict
and — because `retriever` was itself a `Runnable[str, list[Document]]` that
took the `question` *string*, not the dict — a mis-piped intermediate value
overwrote the `question` key. The prompt template expected `{question}` and
blew up. This is P06 in the pack's pain catalog: `.pipe()` on mismatched dict
shape raises `KeyError` deep in runnable internals with no hint at the
offending stage.
The fix is **two patterns you install once and never remove**:
1. **Debug probes** — a `RunnableLambda` that logs dict keys between every two
stages. <1ms overhead per invocation. Surfaces the exact stage that mutates
the shape.
2. **Typed composition** — annotate each chain with `RunnableSerializable[InputT, OutputT]`
plus pydantic `BaseModel` types so mypy flags the mismatch at lint time
instead of at `.invoke()`.
Meanwhile, a second trap waits for anyone tempted to wrap tool-using chains
in the legacy `AgentExecutor`: it silently swallows intermediate tool errors
as empty-string observations and the agent cheerfully answers "I couldn't
find the answer" (P09). For agent loops in LangChain 1.0, skip `AgentExecutor`
and use LangGraph's `create_react_agent` — errors raise, not vanish. This
skill cross-references `langchain-langgraph-agents` (L26) for that path.
Composition primitives covered — with input/output shapes and use cases — are
`RunnableParallel` (fan-out, 2–3× wall-clock win on 2 independent retrievals),
`RunnableBranch` (conditional routing with mandatory default), `RunnablePassthrough.assign`
(merge computed fields without losing input), and `RunnableLambda` (arbitrary
Python, used here for debug probes and shape assertions). Pin: `langchain-core 1.0.x`.
Pain-catalog anchors: P06, P09.
## Prerequisites
- Python 3.10+
- `langchain-core >= 1.0, < 2.0`
- `pydantic >= 2.0` for typed composition
- At least one chat provider installed (see `langchain-model-inference`)
- Familiarity with the composition primitives introduced in `langchain-sdk-patterns`
## Instructions
### Step 1 — Fan-out with `RunnableParallel` for independent sub-tasks
`RunnableParallel({"a": chain_a, "b": chain_b})` forwards the same input to
both branches and runs them concurrently, merging results into a dict.
Wall-clock collapses to the slower branch — 2–3× speedup on two parallel
retrievals is typical (dense vector search + BM25, or tool-use + analysis).
```python
from langchain_core.runnables import RunnableParallel, RunnablePassthrough
# Hybrid retrieval: dense vector search and BM25 at the same time
hybrid = RunnableParallel(
dense=dense_retriever, # Runnable[str, list[Document]]
bm25=bm25_retriever, # Runnable[str, list[Document]]
query=RunnablePassthrough() # keep the original string for downstream stages
)
# Output: {"dense": [...], "bm25": [...], "query": "..."}
result = hybrid.invoke("refund policy for damaged goods")
```
Input shape: whatever the sub-chains accept (all must accept the same shape).
Output shape: `dict` with one key per sub-chain. If one branch takes 5× longer,
your total latency is that branch — not the sum. See
[Parallel vs Sequential](references/parallel-vs-sequential.md) for the async
`.abatch()` variant, shared-state gotchas, and a benchmarking template.
### Step 2 — Route on input with `RunnableBranch`
`RunnableBranch((cond, runnable), ..., default)` dispatches per-input to the
first matching branch. **The default is mandatory** — without one you get a
silent fallthrough on unmatched inputs.
```python
from langchain_core.runnables import RunnableBranch
router = RunnableBranch(
(lambda x: x["category"] == "refund", refund_chain),
(lambda x: x["category"] == "shipping", shipping_chain),
(lambda x: len(x["question"]) > 2000, long_form_chain),
general_chain, # default — required
)
```
Classifier-gated routes are the common case: a cheap small-model classifier
runs first, its label goes into the dict via `RunnablePassthrough.assign`, and
`RunnableBranch` dispatches. See [Branch Routing Patterns](references/branch-routing-patterns.md)
for the signature, classifier-gated route recipe, fallback route pattern, and
pytest patterns for each branch in isolation.
### Step 3 — Thread state with `RunnablePassthrough.assign`
`.assign(field=...)` merges a computed field into the input dict without
losing any existing keys. This is the primary pattern for staged context
assembly in RAG:
```python
from langchain_core.runnables import RunnablePassthrough
def format_docs(docs: list) -> str:
return "\n\n".join(d.page_content for d in docs)
# At each step, the dict grows: {question} -> {question, docs} -> {question, docs, context}
staged = (
RunnablePassthrough.assign(docs=retriever) # adds "docs"
| RunnablePassthrough.assign(context=lambda x: format_docs(x["docs"])) # adds "context"
)
staged.invoke({"question": "..."})
# {"question": "...", "docs": [...], "context": "..."}
```
The input dict passes through unchanged; the new field is the only mutation.
This is the safest shape-preserving primitive in LCEL — use it whenever a
downstream stage needs both the original input and a computed value. See
[Passthrough Assign Patterns](references/passthrough-assign-patterns.md) for
staged context assembly, the `itemgetter` variant for pulling a single field
into a typed chain, and anti-patterns that re-shadow input keys.
### Step 4 — Use `RunnableLambda` for debug probes (and little else)
`RunnableLambda(fn)` wraps any Python callable into a runnable. Its best use
is **debug probes** — log intermediate values without breaking the pipe:
```python
from langchain_core.runnables import RunnableLambda
def probe(stage: str):
"""<1ms overhead per invocation. Returns input unchanged."""
def _probe(x):
keys = list(x.keys()) if isinstance(x, dict) else type(x).__name__
print(f"[probe:{stage}] keys={keys}")
return x
return RunnableLambda(_probe)
chain = (
probe("input")
| RunnablePassthrough.assign(category=classifier)
| probe("after-classify")
| RunnablePassthrough.assign(docs=retriever)
| probe("after-retrieve")
| prompt
| probe("after-prompt")
| llm
| StrOutputParser()
)
```
When P06's `KeyError` strikes, the last probe that printed tells you exactly
which stage produced the wrong shape. Remove the probes (or gate them on an
env var) after debugging. For production chains that stay observable, prefer
`langchain.debug = True` or LangSmith tracing over `print` probes.
See [Debug Probes](references/debug-probes.md) for a shape-assertion decorator,
the `langchain.debug` flag, verbose mode, and a probe that raises instead of
prints (useful in CI).
Avoid `RunnableLambda` for real logic — it loses LangSmith tracing fidelity
(input/output become opaque blobs) and a >3-line lambda is a sign you want a
concrete `Runnable` subclass. See the anti-pattern note in
`langchain-sdk-patterns/references/runnable-composition-matrix.md`.
### Step 5 — Type chains with `RunnableSerializable[InputT, OutputT]`
The root fix for P06 is static typing at chain Related in AI Agents
skill-development
IncludedComprehensive meta-skill for creating, managing, validating, auditing, and distributing Claude Code skills and slash commands (unified in v2.1.3+). Provides skill templates, creation workflows, validation patterns, audit checklists, naming conventions, YAML frontmatter guidance, progressive disclosure examples, and best practices lookup. Use when creating new skills, validating existing skills, auditing skill quality, understanding skill architecture, needing skill templates, learning about YAML frontmatter requirements, progressive disclosure patterns, tool restrictions (allowed-tools), skill composition, skill naming conventions, troubleshooting skill activation issues, creating custom slash commands, configuring command frontmatter, using command arguments ($ARGUMENTS, $1, $2), bash execution in commands, file references in commands, command namespacing, plugin commands, MCP slash commands, Skill tool configuration, or deciding between skills vs slash commands. Delegates to docs-management skill for official documentation.
reprompter
IncludedTransform messy prompts into well-structured, effective prompts — single or multi-agent. Use when: "reprompt", "reprompt this", "clean up this prompt", "structure my prompt", rough text needing XML tags and best practices, "reprompter teams", "repromptception", "run with quality", "smart run", "smart agents", multi-agent tasks, audits, parallel work, anything going to agent teams. Don't use when: simple Q&A, pure chat, immediate execution-only tasks. See "Don't Use When" section for details. Outputs: Structured XML/Markdown prompt, quality score (before/after), optional team brief + per-agent sub-prompts, agent team output files. Success criteria: Single mode quality score ≥ 7/10; Repromptception per-agent prompt quality score 8+/10; all required sections present, actionable and specific.
adaptive-compaction
IncludedAdaptive add-on policy and recovery layer that decides WHEN to compact, prune, snapshot, or fork -- replacing fixed-percent auto-compaction across Claude Code, Codex, and MCP-capable hosts. Trigger on auto-compact timing or damage: "when should I compact", "is it safe to compact now or start a fresh session", "auto-compact fires too early/mid-task", "switching to an unrelated task but the window still has space", "context rot", "answers get worse the longer the session runs", "the agent forgot the plan or my decisions after it summarized", "add a layer on top that manages context without changing the agent", raising autoCompactWindow to give the policy room, or installing/tuning a cross-tool compaction policy or PreCompact hook -- even when "compaction" is never said but the problem is context-window pressure or post-summarization memory loss. Do NOT use to summarize a conversation, build RAG, write a summarization prompt (decides WHEN not HOW), or answer max-context-length trivia.
agent-skill-creator
IncludedCreate cross-platform agent skills from workflow descriptions. Activates when users ask to create an agent, automate a repetitive workflow, create a custom skill, or need advanced agent creation. Triggers on phrases like create agent for, automate workflow, create skill for, every day I have to, daily I need to, turn process into agent, need to automate, create a cross-platform skill, validate this skill, export this skill, migrate this skill. Supports single skills, multi-agent suites, transcript processing, template-based creation, interactive configuration, cross-platform export, and spec validation.
llm-wiki
IncludedUse when building or maintaining a persistent personal knowledge base (second brain) in Obsidian where an LLM incrementally ingests sources, updates entity/concept pages, maintains cross-references, and keeps a synthesis current. Triggers include "second brain", "Obsidian wiki", "personal knowledge management", "ingest this paper/article/book", "build a research wiki", "compound knowledge", "Memex", or whenever the user wants knowledge to accumulate across sessions instead of being re-derived by RAG on every query.
skill-master
IncludedAgent Skills authoring, evaluation, and optimization. Create, edit, validate, benchmark, and improve skills following the agentskills.io specification. Use when designing SKILL.md files, structuring skill folders (references, scripts, assets), ingesting external documentation into skills, running trigger evals, benchmarking skill quality, optimizing descriptions, or performing blind A/B comparisons. Keywords: agentskills.io, SKILL.md, skill authoring, eval, benchmark, trigger optimization.