Build an AI Agent with Claude and LangChain (2025 Guide)
What You're Actually Building
An "AI agent" built with Claude and LangChain is a program that uses Claude as the reasoning engine and LangChain as the orchestration layer: it decides when to call tools, feeds results back into the model, and keeps looping until it has a final answer. LangChain handles the plumbing — prompt formatting, tool schemas, memory, parsing — while Claude does the thinking.
If you're searching for this, you probably want one of two things: a working code example you can adapt today, or a clear picture of the architecture so you can decide whether LangChain is even the right abstraction for your use case. This article covers both.
Why Claude Works Well as the Reasoning Engine
Claude models are strong at structured tool use and follow multi-step instructions reliably, which matters a lot in an agent loop where a single bad tool call can derail the whole chain. LangChain's ChatAnthropic integration maps directly onto Claude's native tool-use format, so you don't need custom parsing logic to extract function calls from free text.
For agents specifically, two things matter more than raw benchmark scores:
- Tool-call consistency — does the model reliably emit valid, schema-matching tool calls instead of hallucinating arguments.
- Instruction adherence over long loops — does the model stay on task after five or six tool round-trips instead of drifting.
Claude handles both reasonably well out of the box, which is why it's a common default for LangChain agents.
Setting Up the Environment
Install the core packages:
pip install langchain langchain-anthropic langgraph
Set your API key as an environment variable:
export ANTHROPIC_API_KEY=sk-ant-...
Then instantiate the model:
from langchain_anthropic import ChatAnthropic
llm = ChatAnthropic(
model="claude-sonnet-4-5-20250929",
temperature=0,
max_tokens=1024,
)
Keeping temperature=0 for agent work is a good default — you want deterministic tool selection, not creative variation.
Defining Tools for Your Agent
Tools are just Python functions with a schema LangChain can describe to Claude. The @tool decorator handles most of the boilerplate:
from langchain_core.tools import tool
@tool
def get_weather(city: str) -> str:
"""Get the current weather for a given city."""
# call a real weather API here
return f"It's 18°C and cloudy in {city}."
@tool
def search_orders(order_id: str) -> str:
"""Look up an order by ID in the internal orders database."""
return f"Order {order_id} shipped on 2025-01-12."
tools = [get_weather, search_orders]
Write docstrings carefully — Claude uses them to decide which tool to call and how to fill in arguments. Vague descriptions are the single most common cause of agents calling the wrong tool.
Building the Agent Loop with LangChain
LangGraph (LangChain's agent framework) gives you a prebuilt ReAct-style loop so you don't have to write the reasoning cycle by hand:
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(llm, tools)
result = agent.invoke({
"messages": [("user", "What's the weather in Lisbon, and what's the status of order 4521?")]
})
print(result["messages"][-1].content)
Under the hood, create_react_agent runs a loop: Claude decides a tool is needed, LangChain executes it, the result goes back into the conversation, and Claude either calls another tool or produces a final answer. For multi-step tasks this loop can run several times before resolving.
If you need custom control flow — conditional branches, human approval steps, parallel tool calls — build the graph manually with langgraph.graph.StateGraph instead of the prebuilt wrapper. It's more code but gives you full control over what happens between each model call.
Giving Your Agent Memory
A stateless agent forgets everything between requests, which is fine for a one-off script but not for anything conversational. Add a checkpointer to persist state across turns:
from langgraph.checkpoint.memory import MemorySaver
memory = MemorySaver()
agent = create_react_agent(llm, tools, checkpointer=memory)
config = {"configurable": {"thread_id": "user-123"}}
agent.invoke({"messages": [("user", "Remember my order ID is 4521.")]}, config)
agent.invoke({"messages": [("user", "What's its status?")]}, config)
MemorySaver keeps state in process memory, which is fine for local development. For production you'll want a persistent backend (Postgres, Redis) keyed by thread ID so conversations survive restarts and scale across multiple instances.
From Prototype to Production: API Keys, Streaming, and Usage Tracking
A LangChain script talking directly to the Anthropic API works well for development, but once you wrap this agent in a product — a SaaS feature, an internal tool with multiple team members, a customer-facing chatbot — you run into problems LangChain doesn't solve: per-application API keys, usage visibility, and streaming the agent's output to a frontend without building your own SSE handling.
This is where SubToAPI fits in alongside your LangChain code rather than replacing it. You keep your agent logic exactly as written — tools, memory, the LangGraph loop — but point the underlying client at SubToAPI instead of calling Anthropic directly:
const response = await fetch('https://api.subtoapi.app/v1/messages', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SUBTOAPI_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'claude-sonnet-4-5-20250929',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Check order 4521 and summarize status.' }],
}),
});
You get a scoped sub_live_... key per application instead of sharing one Anthropic key across environments, built-in streaming support (see /docs/streaming), and usage metadata per request so you can see which part of your agent is burning the most tokens. Tool use works the same way Claude's native API expects it — see /docs/tools — so your LangChain tool schemas don't need to change.
For a solo project the Solo plan at €9 covers one key with full access; teams building agents collaboratively usually move to Team at €19/seat once more than one developer needs their own key and visibility into usage. Full breakdown on /pricing, and /docs/quickstart has a five-minute setup if you want to try it before committing.
Common Pitfalls
- Overloaded tool descriptions. If two tools sound similar, Claude will sometimes pick the wrong one. Keep names and docstrings distinct.
- No max iteration limit. Agent loops can run away if a tool keeps returning ambiguous results. Set a step limit in LangGraph to avoid runaway token usage.
- Treating memory as free. Every turn of conversation history gets re-sent to the model. Long-running agent threads get expensive fast — trim or summarize history periodically.
FAQ
Do I need LangGraph, or is plain LangChain enough? For a single tool call or two, plain LangChain chains are fine. For anything with multiple tools, retries, or conditional logic, LangGraph's explicit graph structure is easier to debug and extend.
Can I use Claude's tool use without LangChain at all? Yes — Claude's native API supports tool use directly, and some developers prefer writing the loop themselves for full control. LangChain mainly saves you boilerplate around parsing, memory, and multi-tool orchestration.
What's the fastest way to add streaming to a LangChain Claude agent? LangChain's .stream() method works out of the box with ChatAnthropic. If you're serving the agent through SubToAPI instead, the streaming endpoint documented at /docs/streaming returns the same token-by-token format you'd get from Anthropic directly.