From api-ai-langchain
LangChain.js patterns for building LLM applications — chat models, LCEL chains, prompt templates, structured output, agents, tools, RAG, streaming, and LangSmith tracing
How this skill is triggered — by the user, by Claude, or both
Slash command
/api-ai-langchain:api-ai-langchainThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **Quick Guide:** Use LangChain.js (v1.x) to build composable LLM applications. Use LCEL (`prompt.pipe(model).pipe(parser)`) for all chain composition -- never use legacy `LLMChain`. Use `withStructuredOutput(zodSchema)` for typed responses. Use `createAgent()` (LangGraph-backed) for agentic workflows -- `AgentExecutor` is legacy. All `@langchain/*` packages must share the same `@langchain/cor...
Quick Guide: Use LangChain.js (v1.x) to build composable LLM applications. Use LCEL (
prompt.pipe(model).pipe(parser)) for all chain composition -- never use legacyLLMChain. UsewithStructuredOutput(zodSchema)for typed responses. UsecreateAgent()(LangGraph-backed) for agentic workflows --AgentExecutoris legacy. All@langchain/*packages must share the same@langchain/coreversion or you get cryptic type errors at runtime.
<critical_requirements>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST use LCEL pipe composition (prompt.pipe(model).pipe(parser)) for all chains -- never use legacy LLMChain, ConversationChain, or SequentialChain)
(You MUST ensure all @langchain/* packages depend on the same version of @langchain/core -- version mismatches cause cryptic runtime errors)
(You MUST use withStructuredOutput(zodSchema) for structured LLM responses -- never manually parse JSON from completion text)
(You MUST use createAgent() from langchain for new agent code -- AgentExecutor and createToolCallingAgent are legacy patterns)
(You MUST never hardcode API keys -- use environment variables (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.))
</critical_requirements>
Auto-detection: LangChain, langchain, @langchain/core, @langchain/openai, @langchain/anthropic, @langchain/google-genai, ChatOpenAI, ChatAnthropic, ChatPromptTemplate, StringOutputParser, RunnableSequence, pipe, withStructuredOutput, createAgent, createToolCallingAgent, AgentExecutor, tool, DynamicStructuredTool, RecursiveCharacterTextSplitter, MemoryVectorStore, OpenAIEmbeddings, LCEL, LangSmith, LANGCHAIN_TRACING_V2
When to use:
Key patterns covered:
.pipe() and RunnableSequenceChatPromptTemplate, MessagesPlaceholder)withStructuredOutput() and Zod schemastool() function and Zod schemascreateAgent() (LangGraph-backed)When NOT to use:
useChat, useCompletion) -- use a framework-integrated AI SDKwithStructuredOutput, tool definition, binding tools to modelscreateAgent, tool-calling agents, chat history, streaming agentsLangChain.js provides a composable framework for building LLM-powered applications. Its core abstraction is the Runnable -- any component that takes an input and produces an output. Runnables compose via LCEL (.pipe()) to form chains, and every Runnable supports .invoke(), .stream(), .batch() uniformly.
Core principles:
prompt.pipe(model).pipe(parser). Each step is independently testable and replaceable. Legacy chain classes (LLMChain, ConversationChain) are deprecated.ChatOpenAI, ChatAnthropic, ChatGoogleGenerativeAI) share a common interface. Swap providers by changing one import and model name. Use initChatModel() for runtime provider selection.model.withStructuredOutput(zodSchema) constrains LLM responses to your schema. No manual JSON parsing.@langchain/core holds abstractions, provider packages (@langchain/openai, @langchain/anthropic) hold implementations, langchain holds higher-level composables. All must share the same @langchain/core version.LANGCHAIN_TRACING_V2=true and every chain/agent/tool call is traced to LangSmith automatically.When to use LangChain:
When NOT to use:
Initialize chat models from any provider. They all share the same interface.
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI({
model: "gpt-4o",
temperature: 0,
});
const response = await model.invoke("Explain TypeScript generics.");
console.log(response.text);
Why good: Explicit model name, temperature set for determinism, .text accessor for content
// BAD: Hardcoded API key, no model specified
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI({ apiKey: "sk-1234..." });
Why bad: Hardcoded API key is a security risk, missing model name uses unpredictable defaults
import { ChatAnthropic } from "@langchain/anthropic";
const model = new ChatAnthropic({ model: "claude-sonnet-4-20250514" });
// Or use initChatModel for runtime provider selection
import { initChatModel } from "langchain";
const model = await initChatModel("openai:gpt-4o", { temperature: 0 });
See: examples/core.md for full provider examples and configuration options
Compose chains using .pipe(). Every component is a Runnable.
import { ChatOpenAI } from "@langchain/openai";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
const prompt = ChatPromptTemplate.fromTemplate(
"Summarize this in one sentence: {text}",
);
const model = new ChatOpenAI({ model: "gpt-4o" });
const parser = new StringOutputParser();
const chain = prompt.pipe(model).pipe(parser);
const result = await chain.invoke({ text: "LangChain is a framework..." });
// result is a plain string
Why good: Each step is independently testable, streaming propagates through the entire chain, swapping model is one line change
// BAD: Legacy LLMChain (deprecated)
import { LLMChain } from "langchain/chains";
const chain = new LLMChain({ llm: model, prompt });
Why bad: LLMChain is deprecated, does not support streaming propagation, harder to compose
See: examples/core.md for RunnableSequence.from(), RunnablePassthrough, RunnableParallel
Use withStructuredOutput() for type-safe LLM responses.
import { ChatOpenAI } from "@langchain/openai";
import { z } from "zod";
const MovieSchema = z.object({
title: z.string().describe("The movie title"),
year: z.number().describe("Release year"),
genres: z.array(z.string()).describe("List of genres"),
});
const structuredModel = new ChatOpenAI({
model: "gpt-4o",
}).withStructuredOutput(MovieSchema);
const movie = await structuredModel.invoke("Tell me about Inception.");
// movie is typed: { title: string; year: number; genres: string[] }
Why good: Output is validated against schema, fully typed, no manual JSON parsing
// BAD: Manual JSON parsing from completion text
const response = await model.invoke("Return JSON with title and year...");
const data = JSON.parse(response.text); // Fragile, untyped, can throw
Why bad: No schema validation, untyped result, model may return malformed JSON
See: examples/structured-output-tools.md for complex schemas and edge cases
Define tools with the tool() function and Zod schemas. Use snake_case for tool names.
import { tool } from "@langchain/core/tools";
import { z } from "zod";
const getWeather = tool(
async ({ location }) => {
// Call real weather API here
return `Weather in ${location}: 22C, sunny`;
},
{
name: "get_weather",
description: "Get current weather for a city",
schema: z.object({
location: z.string().describe("City name, e.g. 'San Francisco'"),
}),
},
);
Why good: Zod schema validates input, .describe() guides model's argument generation, snake_case name avoids provider compatibility issues
// BAD: Using DynamicStructuredTool (verbose, legacy pattern)
import { DynamicStructuredTool } from "@langchain/core/tools";
const tool = new DynamicStructuredTool({
name: "getWeather", // camelCase breaks some providers
description: "...",
schema: z.object({ ... }),
func: async (input) => { ... },
});
Why bad: DynamicStructuredTool is verbose compared to tool(), camelCase name causes issues with some providers
See: examples/structured-output-tools.md for binding tools to models and handling tool calls
createAgent()Use createAgent() for agentic workflows. It is backed by LangGraph and handles tool calling loops automatically.
import { createAgent } from "langchain";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
const search = tool(async ({ query }) => `Results for: ${query}`, {
name: "search",
description: "Search for information",
schema: z.object({ query: z.string() }),
});
const agent = createAgent({
model: "openai:gpt-4o",
tools: [search],
systemPrompt: "You are a helpful research assistant.",
});
const stream = await agent.stream({
messages: [{ role: "user", content: "Find info about LangChain" }],
});
for await (const step of stream) {
console.log(step.messages.at(-1));
}
Why good: createAgent handles the tool-call loop, supports streaming, manages state via LangGraph
// BAD: Legacy AgentExecutor pattern
import { AgentExecutor, createToolCallingAgent } from "langchain/agents";
const agent = createToolCallingAgent({ llm, tools, prompt });
const executor = new AgentExecutor({ agent, tools });
Why bad: AgentExecutor is legacy, does not integrate with LangGraph state management, less composable
See: examples/agents.md for chat history, custom state, and middleware patterns
Load documents, split into chunks, embed, store in a vector store, and retrieve.
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
import { OpenAIEmbeddings } from "@langchain/openai";
import { MemoryVectorStore } from "@langchain/classic/vectorstores/memory";
const CHUNK_SIZE = 1000;
const CHUNK_OVERLAP = 200;
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: CHUNK_SIZE,
chunkOverlap: CHUNK_OVERLAP,
});
const chunks = await splitter.splitDocuments(docs);
const embeddings = new OpenAIEmbeddings({ model: "text-embedding-3-small" });
const vectorStore = new MemoryVectorStore(embeddings);
await vectorStore.addDocuments(chunks);
// Retrieve
const results = await vectorStore.similaritySearch("query", 3);
Why good: Named constants for chunk parameters, explicit embedding model, MemoryVectorStore for prototyping
See: examples/rag.md for full RAG chains, agent-based RAG, and production vector stores
All Runnables support .stream(). Streaming propagates through LCEL chains.
const chain = prompt.pipe(model).pipe(parser);
const stream = await chain.stream({ text: "Explain quantum computing." });
for await (const chunk of stream) {
process.stdout.write(chunk);
}
Why good: Streaming propagates through the entire chain, progressive output for better UX
// BAD: Collecting all output then displaying
const result = await chain.invoke({ text: "..." });
console.log(result); // User waits for full response
Why bad: User waits for full generation before seeing anything, bad UX for long responses
See: examples/streaming.md for model streaming, stream events, agent streaming
Enable tracing by setting environment variables. No code changes needed.
LANGCHAIN_TRACING_V2=true
LANGCHAIN_API_KEY=lsv2_...
LANGCHAIN_PROJECT=my-project
# Recommended for non-serverless environments:
LANGCHAIN_CALLBACKS_BACKGROUND=true
Why good: Zero-code setup, traces every chain/model/tool invocation, LANGCHAIN_CALLBACKS_BACKGROUND=true reduces latency in long-running processes
See: reference.md for all environment variables
<decision_framework>
Do you need multi-step LLM workflows (prompt -> model -> parser -> ...)?
+-- YES -> Use LangChain (LCEL chains)
+-- NO -> Do you need to swap between LLM providers?
+-- YES -> Use LangChain (unified chat model interface)
+-- NO -> Do you need RAG or agent tool calling?
+-- YES -> Use LangChain
+-- NO -> Use the provider SDK directly (simpler, fewer deps)
Which provider?
+-- OpenAI -> ChatOpenAI from @langchain/openai
+-- Anthropic -> ChatAnthropic from @langchain/anthropic
+-- Google -> ChatGoogleGenerativeAI from @langchain/google-genai
+-- Runtime selection -> initChatModel("provider:model") from langchain
+-- Other -> Check @langchain/community
Does the model need to autonomously decide when to call tools?
+-- YES -> createAgent() (handles tool-call loops, state management)
+-- NO -> Is it a fixed sequence of steps?
+-- YES -> LCEL chain (prompt.pipe(model).pipe(parser))
+-- NO -> RunnableSequence.from() with branching
Are you writing new code?
+-- YES -> ALWAYS use LCEL (.pipe()) -- never legacy chains
+-- NO -> Is the existing code using LLMChain/ConversationChain?
+-- YES -> Migrate to LCEL when touching the code
+-- NO -> Keep as-is if it works
</decision_framework>
<red_flags>
High Priority Issues:
LLMChain, ConversationChain, SequentialChain) instead of LCEL -- these are deprecated@langchain/core versions across packages -- causes instanceof checks to fail silently, methods to be undefined, and type errorswithStructuredOutput()AgentExecutor for new code instead of createAgent()Medium Priority Issues:
getWeather) instead of snake_case (get_weather) -- some providers reject camelCase.describe() to Zod schema fields for tools -- model gets no guidance on argument formatBufferMemory / ConversationSummaryMemory -- these are deprecated, use LangGraph checkpointing or RunnableWithMessageHistoryLANGCHAIN_CALLBACKS_BACKGROUND=true in non-serverless environments -- adds latency to every LLM call when tracing is onlangchain/ (main package) when the import should come from @langchain/core/ or a provider packageCommon Mistakes:
langchain without @langchain/core -- @langchain/core is a required peer dependency@langchain/core v0.x with langchain v1.x -- all packages must be on compatible versionsRunnableLambda in a chain and expecting .stream() to work -- lambda functions do not propagate streaming by default; subclass Runnable and implement transform insteadChatPromptTemplate.fromTemplate() creates a single user message -- use ChatPromptTemplate.fromMessages() for multi-message prompts with system/assistant/user rolesMemoryVectorStore in production -- it is in-memory only, all data is lost on restart; use a persistent vector storeGotchas & Edge Cases:
@langchain/core is a peer dependency, not a transitive dependency. You must install it explicitly: npm install @langchain/core. If you see "cannot resolve @langchain/core" or instanceof checks failing, you likely have duplicate core versions -- run npm ls @langchain/core to check.withStructuredOutput() uses function calling under the hood, not JSON mode. Not all models support it -- check provider docs. If the model does not support function calling, use JsonOutputParser with a prompt instead.ChatPromptTemplate.fromMessages() uses tuple syntax ["system", "..."] or ["human", "..."] -- the role names are system, human, ai, not developer, user, assistant.tool() from @langchain/core/tools vs tool() from langchain -- both exist. The langchain re-export is a convenience wrapper. Use whichever matches your import pattern but be consistent.initChatModel() requires the provider package to be installed. If you call initChatModel("anthropic:claude-sonnet-4-20250514") without @langchain/anthropic installed, you get a confusing module resolution error, not a clear "package not installed" message.withStructuredOutput() as of early 2026 -- stick with Zod v3.x for now.RecursiveCharacterTextSplitter now lives in @langchain/textsplitters (separate package), not langchain/text_splitter.createAgent(), use streamMode: "values" to get full state at each step, or omit for incremental updates.</red_flags>
<critical_reminders>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST use LCEL pipe composition (prompt.pipe(model).pipe(parser)) for all chains -- never use legacy LLMChain, ConversationChain, or SequentialChain)
(You MUST ensure all @langchain/* packages depend on the same version of @langchain/core -- version mismatches cause cryptic runtime errors)
(You MUST use withStructuredOutput(zodSchema) for structured LLM responses -- never manually parse JSON from completion text)
(You MUST use createAgent() from langchain for new agent code -- AgentExecutor and createToolCallingAgent are legacy patterns)
(You MUST never hardcode API keys -- use environment variables (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.))
Failure to follow these rules will produce fragile, hard-to-debug LLM applications with version conflicts and untyped outputs.
</critical_reminders>
npx claudepluginhub agents-inc/skills --plugin api-ai-langchainGuides LangChain JS/TS development for LLM apps: models (OpenAI, Anthropic), chains, agents, tools, RAG, prompts, streaming, and structured outputs with Zod.
Builds LangChain applications with agents, chains, memory, and document processing. Includes best practices for autonomous agents, multi-step workflows, and production LLM apps.
Designs LLM applications using LangChain 1.x and LangGraph for agents, state management, memory, tool integration, and workflows.