Why Claude + LangChain.js for Agentic Workflows?
Agentic workflows—where AI autonomously plans, executes tools, and iterates—represent the future of automation. Claude models from Anthropic shine here due to their exceptional reasoning, long context windows (up to 200K tokens in Claude 3.5 Sonnet), and native tool-calling support. But wiring everything manually via the Claude API can be tedious.
Enter LangChain.js: a JavaScript/TypeScript framework that abstracts chains, agents, memory, and retrieval into reusable components. It pairs perfectly with Claude, enabling robust agents that handle complex, multi-step tasks like research pipelines or customer support bots.
In this guide, we'll build from basics to advanced agents, contrasting LangChain.js approaches with native Claude API for clarity. Expect practical TypeScript code you can copy-paste.
Prerequisites
- Node.js 18+
- Anthropic API key (get one at console.anthropic.com)
- Basic TypeScript knowledge
Install dependencies:
npm init -y
npm install @langchain/anthropic @langchain/core langchain @langchain/community zod
npm install -D typescript @types/node tsx
Create tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"strict": true,
"esModuleInterop": true
}
}
Set your API key:
export ANTHROPIC_API_KEY=your_key_here
Basic Setup: Your First Claude Chain
Start simple: a conversational chain.
// basic-chain.ts
import { ChatAnthropic } from "@langchain/anthropic";
import { HumanMessage, AIMessage } from "@langchain/core/messages";
import { ChatPromptTemplate } from "@langchain/core/prompts";
async function simpleChain() {
const model = new ChatAnthropic({
model: "claude-3-5-sonnet-20240620",
temperature: 0,
});
const prompt = ChatPromptTemplate.fromMessages([
["system", "You are a helpful assistant."],
["human", "{input}"],
]);
const chain = prompt.pipe(model);
const response = await chain.invoke({ input: "Explain quantum computing simply." });
console.log(response.content);
}
simpleChain();
Vs. Native Claude API: LangChain handles prompt templating and message formatting automatically. Native API requires manual messages array construction:
// Native equivalent (simplified)
const response = await anthropic.messages.create({
model: "claude-3-5-sonnet-20240620",
max_tokens: 1024,
messages: [{ role: "user", content: "Explain quantum computing simply." }],
});
LangChain shines for scaling to agents.
Building Agents with Tool Calling
Claude's tool use is state-of-the-art: it decides when to call tools and parses structured outputs.
Define a tool using Zod for schema validation:
// tools.ts
import { z } from "zod";
import { tool } from "@langchain/core/tools";
const calculatorSchema = z.object({
operation: z.enum(["add", "subtract", "multiply", "divide"]).describe("The operation"),
a: z.number().describe("First number"),
b: z.number().describe("Second number"),
});
export const calculatorTool = tool(async ({ operation, a, b }) => {
switch (operation) {
case "add": return a + b;
case "subtract": return a - b;
case "multiply": return a * b;
case "divide": return a / b;
default: throw new Error("Invalid operation");
}
}, {
name: "calculator",
description: "Performs basic math operations.",
schema: calculatorSchema,
});
Create a ReAct agent (Reason + Act):
// agent.ts
import { createReactAgent } from "@langchain/core/agents";
import { ChatAnthropic } from "@langchain/anthropic";
import { pull } from "langchain/hub";
import { calculatorTool } from "./tools";
async function createAgent() {
const model = new ChatAnthropic({
model: "claude-3-5-sonnet-20240620",
temperature: 0,
});
// Pull ReAct prompt from LangChain hub (optimized for Claude)
const prompt = await pull("hwchase17/react");
const agent = createReactAgent({
llm: model,
tools: [calculatorTool],
prompt,
});
const agentRunnable = agent.asTool();
// Or use with input
const result = await agent.invoke({
input: "What is (3 + 5) * 2 - 4?",
tools: {}, // Internal
});
console.log(result.output);
}
createAgent();
Run with npx tsx agent.ts. Claude reasons: calls calculator multiple times, computes 12.
Comparison: Native Claude requires loop for tool calls:
- Send message with tools.
- Parse
tool_useblocks. - Execute, append
tool_result. - Repeat until
stop_reason: 'end_turn'.
LangChain abstracts this into agent.invoke(), handling retries and parsing.
Adding Memory: Stateful Conversations
Agents forget without memory. Use ChatMessageHistory:
// memory-agent.ts
import { ChatMessageHistory } from "langchain/stores/message/in_memory";
import { BufferWindowMemory } from "langchain/memory";
import { createReactAgent } from "@langchain/core/agents";
// ... model, prompt, tools as above
const memory = new BufferWindowMemory({
chatHistory: new ChatMessageHistory(),
returnMessages: true,
k: 5, // Last 5 exchanges
});
const agent = createReactAgent({ llm: model, tools: [calculatorTool], prompt });
const agentWithMemory = memory.pipe(agent);
// Simulate multi-turn
await agentWithMemory.invoke({
input: "Remember this number: 42. Add 8 to it."
});
await agentWithMemory.invoke({
input: "What was the original number?"
});
Claude recalls 42 seamlessly, thanks to context injection.
Pro Tip: For production, use RedisChatMessageHistory from @langchain/community for persistence across sessions.
Multi-Step Workflow: Research Agent Example
Combine tools for real-world use. Add a mock search tool:
// search-tool.ts
const searchSchema = z.object({
query: z.string().describe("Search query"),
});
export const searchTool = tool(async ({ query }) => {
// Mock; replace with TavilySearchResults or DuckDuckGo
const results = {
"claude langchain": "LangChain.js integrates Anthropic via ChatAnthropic.",
"best claude model": "Claude 3.5 Sonnet for reasoning.",
};
return results[query] || "No results found.";
}, {
name: "search",
description: "Searches for info on Claude/LangChain.",
schema: searchSchema,
});
Full agent:
// research-agent.ts
import { searchTool } from "./search-tool";
// ... other imports
const agent = createReactAgent({
llm: model,
tools: [calculatorTool, searchTool],
prompt,
});
const result = await agent.invoke({
input: "Using search, find best Claude model and compute its token price if 1M tokens cost $3 (per million).",
});
console.log(result.output);
Claude: Searches, reasons "Sonnet", calculates $3.
Reliability Best Practices
- Model Selection: Sonnet for balance; Opus for complex reasoning; Haiku for speed.
- Error Handling: Use
handleParsingErrors: truein agent. - Max Iterations: Set
maxIterations: 10to prevent loops. - Structured Output: Bind tools with
model.bindTools([tool])for better parsing. - Streaming:
agent.stream({ input })for real-time UI. - Costs: Monitor via Anthropic dashboard; LangChain adds minimal overhead.
LangChain.js vs. Native:
| Aspect | LangChain.js | Native API |
|---|---|---|
| Setup | 5 lines | 20+ lines loop |
| Memory | Built-in | Manual context mgmt |
| Tools | Auto-binding | Manual parse/execute |
| Debugging | Traces | Console logs |
| Scalability | Graphs/Deploy | Custom infra |
LangChain wins for iteration speed.
Advanced: LangGraph.js for Complex Flows
For non-linear workflows, use LangGraph (LangChain's graph lib):
// Quick LangGraph intro
import { StateGraph, END } from "@langchain/langgraph";
import { Annotation } from "@langchain/langgraph";
const graphState = Annotation.Root({
messages: Annotation<Message[]>(),
});
// Define nodes/tools, compile graph
const app = new StateGraph(graphState)
.addNode("agent", agentNode)
.addEdge("__end__", END);
const result = await app.invoke({ messages: [...] });
Ideal for conditional routing (e.g., if math → calculator, else search).
Conclusion
Claude + LangChain.js delivers production-ready agents with minimal boilerplate. Start with chains, scale to memory-enhanced ReAct agents, then graphs. Experiment with Claude 3.5 Sonnet—its tool precision outshines GPT-4o in benchmarks.
Repo: github.com/yourname/claude-langchain-examples (fork and extend).
Next: Integrate with n8n for no-code triggers or MCP servers for custom tools.
Word count: ~1450
Stay ahead of the AI curve
The most important updates, news, and content — delivered in one weekly newsletter.