Install
$ agentstack add skill-agents-inc-skills-api-ai-langchain ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
LangChain.js Patterns
> 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/core version or you get cryptic type errors at runtime.
CRITICAL: Before Using This Skill
> 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.))
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, LANGCHAINTRACINGV2
When to use:
- Building LLM applications that compose prompts, models, and output parsers into chains
- Creating agentic workflows where models decide which tools to call
- Implementing RAG pipelines with document loading, splitting, embedding, and retrieval
- Needing structured output from LLMs with type-safe Zod schema validation
- Streaming LLM responses token-by-token to users
- Switching between LLM providers (OpenAI, Anthropic, Google) with a unified interface
- Tracing and debugging LLM applications with LangSmith
Key patterns covered:
- Chat model initialization and provider switching (ChatOpenAI, ChatAnthropic, ChatGoogleGenerativeAI)
- LCEL chain composition with
.pipe()andRunnableSequence - Prompt templates (
ChatPromptTemplate,MessagesPlaceholder) - Structured output with
withStructuredOutput()and Zod schemas - Tool definition with
tool()function and Zod schemas - Agent creation with
createAgent()(LangGraph-backed) - RAG pipelines: document loaders, text splitters, vector stores, retrievers
- Streaming from chains, models, and agents
- LangSmith tracing setup
When NOT to use:
- You only call one LLM provider and want the thinnest wrapper -- use the provider's SDK directly
- You need React-specific chat UI hooks (
useChat,useCompletion) -- use a framework-integrated AI SDK - You want a simple single-call completion with no chaining -- a direct SDK call is simpler
- You need real-time bidirectional communication -- LangChain does not cover WebSocket/Realtime APIs
Examples Index
- [Core: Setup, LCEL & Chat Models](examples/core.md) -- Package installation, chat model init, LCEL chains, prompt templates, output parsers
- [Structured Output & Tools](examples/structured-output-tools.md) --
withStructuredOutput, tool definition, binding tools to models - [Agents](examples/agents.md) --
createAgent, tool-calling agents, chat history, streaming agents - [RAG Pipelines](examples/rag.md) -- Document loaders, text splitters, vector stores, retrieval chains
- [Streaming](examples/streaming.md) -- Model streaming, chain streaming, agent streaming
- [Quick API Reference](reference.md) -- Package map, import paths, environment variables, model IDs
Philosophy
LangChain.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:
- Composability via LCEL -- Chains are built by piping Runnables:
prompt.pipe(model).pipe(parser). Each step is independently testable and replaceable. Legacy chain classes (LLMChain,ConversationChain) are deprecated. - Provider-agnostic models -- Chat models (
ChatOpenAI,ChatAnthropic,ChatGoogleGenerativeAI) share a common interface. Swap providers by changing one import and model name. UseinitChatModel()for runtime provider selection. - Type-safe structured output --
model.withStructuredOutput(zodSchema)constrains LLM responses to your schema. No manual JSON parsing. - Split package architecture --
@langchain/coreholds abstractions, provider packages (@langchain/openai,@langchain/anthropic) hold implementations,langchainholds higher-level composables. All must share the same@langchain/coreversion. - Observability built in -- Set
LANGCHAIN_TRACING_V2=trueand every chain/agent/tool call is traced to LangSmith automatically.
When to use LangChain:
- You need to compose multi-step LLM workflows (prompt -> model -> parser -> next step)
- You want to swap LLM providers without rewriting business logic
- You need agent-style tool calling with automatic routing
- You need RAG with document loading, chunking, embedding, and retrieval
- You want built-in tracing and evaluation via LangSmith
When NOT to use:
- Single-provider, single-call use cases -- the provider SDK is simpler and has less overhead
- You want full control over HTTP requests -- LangChain abstracts the transport layer
- Extremely latency-sensitive applications where the abstraction overhead matters
Core Patterns
Pattern 1: Chat Model Initialization
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
Provider Switching
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](examples/core.md) for full provider examples and configuration options
Pattern 2: LCEL Chain Composition
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](examples/core.md) for RunnableSequence.from(), RunnablePassthrough, RunnableParallel
Pattern 3: Structured Output with Zod
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](examples/structured-output-tools.md) for complex schemas and edge cases
Pattern 4: Tool Definition
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](examples/structured-output-tools.md) for binding tools to models and handling tool calls
Pattern 5: Agents with 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](examples/agents.md) for chat history, custom state, and middleware patterns
Pattern 6: RAG Pipeline
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](examples/rag.md) for full RAG chains, agent-based RAG, and production vector stores
Pattern 7: Streaming
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](examples/streaming.md) for model streaming, stream events, agent streaming
Pattern 8: LangSmith Tracing
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](reference.md) for all environment variables
Decision Framework
When to Use LangChain vs Direct SDK
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 Chat Model Class
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
LCEL vs createAgent
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
Legacy Chain vs LCEL
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
RED FLAGS
High Priority Issues:
- Using legacy chains (
LLMChain, `Conv
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: agents-inc
- Source: agents-inc/skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.