pip install langchain now installs langchain 1.x, and that version number isn’t cosmetic — LangChain’s 1.0 release split the package in two. Legacy Chain subclasses, most retrievers, the prompt hub, and the old agent constructors moved out of the core package entirely into a separate langchain-classic distribution that a fresh pip install langchain doesn’t pull in. from langchain.chains import LLMChain, a line that has appeared in years of tutorials, Stack Overflow answers, and therefore Claude Code’s training data, now raises ImportError on a clean v1 install unless langchain-classic is installed alongside it. Claude Code has no way to know which era of LangChain a given project is pinned to unless CLAUDE.md says so explicitly.
This isn’t LangChain’s first API generation to displace the previous one. Agent construction alone has gone through three recommended patterns since 2023 — initialize_agent, then AgentExecutor built with LCEL, then LangGraph’s create_react_agent, now langchain.agents.create_agent in v1.0 — and each one still shows up in code an agent might reach for. Our gallery’s LangChain rule set documents how to contribute to LangChain’s own Python monorepo — its three-layer core/langchain/community architecture, uv package management, partner integration conventions. That’s a different problem from what this post covers: the rules a CLAUDE.md needs when your project is a consumer of LangChain, not a contributor to it.
Why LangChain Needs Different CLAUDE.md Rules Than Most Python Frameworks
The core package now excludes what most training data assumes is built in. langchain-classic holds LLMChain, ConversationChain, MultiQueryRetriever and the rest of langchain.retrievers, the indexing API, the langchain.hub module, and CacheBackedEmbeddings. None of it disappeared — it moved to a package that isn’t installed unless someone adds it on purpose, and Claude Code has no signal that the split happened unless CLAUDE.md states the installed version.
Three generations of “the right way to build an agent” are all still findable online. initialize_agent predates LCEL. AgentExecutor assembled with LCEL’s prompt | model | parser piping was the mid-2020s recommendation. create_react_agent from langgraph.prebuilt took over after that. create_agent from langchain.agents is the v1.0 answer. All four patterns compile against some installed version of the ecosystem; only one is correct for whichever version your pyproject.toml actually pins.
Model instantiation has a similarly ambiguous history. Direct instantiation — ChatAnthropic(model="..."), ChatOpenAI(model="...") — still works, but init_chat_model("anthropic:claude-sonnet-4-6") is the current recommendation specifically because it lets you swap providers without rewriting call sites, and new model names work immediately since the string is passed straight through to the provider package. A CLAUDE.md that doesn’t name a preferred initialization pattern gets a mix of both across a codebase.
Nothing in a stack trace says “wrong package generation.” A missing langchain-classic install produces a plain ModuleNotFoundError, not a message pointing at the v1 migration. An agent debugging that error without project context is just as likely to “fix” it by downgrading langchain as by installing the missing package — and downgrading reintroduces every API surface CLAUDE.md was written to avoid.
Complete CLAUDE.md Template for LangChain Projects
This targets langchain>=1.0 with langgraph for stateful/multi-step orchestration. If your project is still on a 0.x pin, keep the version line and see the generation-comparison section below for the equivalent 0.x patterns.
# LangChain Application: [ProjectName]
## Build & Run
- Install deps: `uv sync` (or `pip install -e .` if not using uv)
- Run the app: `uv run python -m app.main`
- Run all tests: `uv run pytest`
- Run a single test file: `uv run pytest tests/test_agent.py -v`
- Type check: `uv run mypy app/`
## Installed Versions (check before writing any import)
- `langchain` >= 1.0 — legacy Chain classes and old retrievers are NOT in this package
- `langgraph` — used for multi-step/stateful orchestration beyond a single create_agent call
- `langchain-classic` — [installed / not installed]. Only import from `langchain_classic` if it appears in pyproject.toml; do not add it to fix an import error without asking first
## Agent Construction
- Use `from langchain.agents import create_agent` — this is the v1.0 API, not `langgraph.prebuilt.create_react_agent` (its v0 predecessor) and not `initialize_agent` (pre-LCEL, do not use)
- The system prompt parameter is `system_prompt`, not `prompt` — `prompt` was the LangGraph-era parameter name
- Tools are plain functions decorated with `@tool` from `langchain.tools`, or `BaseTool` subclasses — never hand-roll an OpenAI-style function-calling JSON schema when a `@tool`-decorated function will do
- Agents are invoked with a `thread_id` in `config={"configurable": {"thread_id": "..."}}` for conversation persistence — do not manage history manually in a list unless the agent is explicitly stateless
## Model Initialization
- Use `init_chat_model("anthropic:claude-sonnet-4-6")` (or the provider:model string for this project) — do not instantiate `ChatAnthropic`/`ChatOpenAI` directly unless a provider-specific parameter is needed that `init_chat_model` doesn't expose
- Never hardcode API keys — read from `ANTHROPIC_API_KEY`/`OPENAI_API_KEY` environment variables only
## Legacy Code (do not introduce new instances of these)
- `LLMChain`, `ConversationChain`, `SimpleSequentialChain` — replaced by LCEL composition (`prompt | model | StrOutputParser()`) or `create_agent`
- `initialize_agent` — replaced by `create_agent`
- Direct `AgentExecutor` construction — replaced by `create_agent`, unless a specific low-level control need is documented here
## Testing
- Unit tests use `FakeListChatModel` or an equivalent in-memory fake — never call a real provider API in a unit test
- Integration tests (real API calls) live in `tests/integration/` and are excluded from the default `pytest` run — run explicitly with `uv run pytest tests/integration/`
- Mock tool outputs deterministically; do not assert on exact LLM-generated text, assert on tool calls made and final state shape instead
The langchain-classic Trap
The failure here is silent in the worst way: the import statement is exactly what years of tutorials show, and it worked in every langchain version before 1.0.
# Works on langchain 0.x, raises ImportError on a clean langchain 1.x install
from langchain.chains import LLMChain
from langchain.retrievers import MultiQueryRetriever
from langchain import hub
# Correct for langchain 1.x — requires langchain-classic installed separately
# pip install langchain-classic
from langchain_classic.chains import LLMChain
from langchain_classic.retrievers import MultiQueryRetriever
from langchain_classic import hub
The better fix in almost every case isn’t installing langchain-classic at all — it’s rewriting the legacy Chain subclass as LCEL or create_agent, since that’s the whole point of the split. langchain-classic exists as a bridge for code that can’t be migrated immediately, not as the default home for new code:
# Legacy: LLMChain wraps a prompt + model + parser in a subclass
from langchain_classic.chains import LLMChain
chain = LLMChain(llm=model, prompt=prompt_template)
result = chain.run(topic="agents")
# LCEL: the same composition as a pipe, no Chain subclass needed
from langchain_core.output_parsers import StrOutputParser
chain = prompt_template | model | StrOutputParser()
result = chain.invoke({"topic": "agents"})
State the installed langchain major version and whether langchain-classic is a dependency at the top of CLAUDE.md, next to the Python version. Without it, an agent hitting ModuleNotFoundError: No module named 'langchain.chains' has no way to tell whether the fix is “install langchain-classic” or “this project intentionally doesn’t use it — rewrite this as LCEL instead,” and picking wrong reintroduces the exact code surface the migration was meant to retire.
Three Generations of Agent Creation
All three patterns below are real, working LangChain code — they just belong to different releases, and Claude Code’s training data doesn’t reliably distinguish which one matches your pyproject.toml.
# Generation 1 (pre-LCEL, pre-2024 era) — do not use in new code
from langchain.agents import initialize_agent, AgentType
agent = initialize_agent(
tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True
)
agent.run("What's the weather in Tokyo?")
# Generation 2 (LangGraph prebuilt, v0.x era) — superseded by langchain 1.0's create_agent
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(
model="claude-sonnet-4-6", tools=[check_weather], prompt="You are a helpful assistant"
)
# Generation 3 (langchain 1.0, current) — this is what a new project should use
from langchain.agents import create_agent
agent = create_agent(
model="anthropic:claude-sonnet-4-6",
tools=[check_weather],
system_prompt="You are a helpful assistant",
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "What's the weather in Tokyo?"}]},
config={"configurable": {"thread_id": "session-1"}},
)
The parameter rename from prompt (generation 2) to system_prompt (generation 3) is easy to miss in review because both are valid Python — passing prompt= to create_agent just raises a TypeError for an unexpected keyword, which reads like a typo, not a version mismatch. If your project still needs LangGraph’s lower-level graph control beyond what create_agent’s middleware system exposes, say so explicitly in CLAUDE.md — otherwise an agent has no way to know whether langgraph.prebuilt usage in the codebase is an intentional choice for a specific orchestration need or leftover code from before the v1.0 migration.
Testing LangChain Agents Without Burning API Credits
A test suite that calls a real model on every run is slow, costs money per CI invocation, and is nondeterministic by construction — three problems most Python test suites don’t have to solve at all.
# tests/test_agent.py — unit test with a fake model, no API call
from langchain_core.language_models.fake_chat_models import FakeListChatModel
from langchain.agents import create_agent
def test_agent_calls_weather_tool():
fake_model = FakeListChatModel(responses=["I'll check the weather for you."])
agent = create_agent(model=fake_model, tools=[check_weather], system_prompt="Test agent")
result = agent.invoke({"messages": [{"role": "user", "content": "Weather in Tokyo?"}]})
# Assert on tool calls made and final message shape, not on exact generated text
assert result["messages"][-1].content
# tests/integration/test_agent_live.py — real API call, excluded from default pytest run
import pytest
@pytest.mark.integration
def test_agent_live_weather_query():
agent = create_agent(model="anthropic:claude-sonnet-4-6", tools=[check_weather])
result = agent.invoke({"messages": [{"role": "user", "content": "Weather in Tokyo?"}]})
assert "tokyo" in result["messages"][-1].content.lower()
The rule that matters most here for an agent writing new tests: assert on which tools were called and the shape of the final state, not on the literal text a model generated. LangChain’s own docs draw the same line between unit tests (fast, deterministic, mocked model) and integration tests (real API, validates credentials and provider behavior) — a CLAUDE.md that doesn’t say which one a new test belongs in gets tests that either flake on model wording or silently never run against a real provider at all.
Hook-Driven Verification for LangChain Projects
Catching an import from a package that isn’t installed doesn’t require running the whole test suite — a fast import check on every edit catches the langchain-classic trap immediately.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "uv run python -c \"import ast, sys; ast.parse(open(sys.argv[1]).read())\" $CLAUDE_FILE_PATH 2>/dev/null || true"
}
]
}
]
}
}
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "uv run pytest --ignore=tests/integration -q"
}
]
}
]
}
}
Keep tests/integration/ out of the Stop hook the same way a live payment-gateway test gets excluded from an Express or mobile CLAUDE.md hook config — a hook that occasionally calls a real model API on every session end is a cost and latency problem, not a safety net.
AGENTS.md Compatible Version
# AGENTS.md — LangChain Application
## Commands
- install: `uv sync`
- test: `uv run pytest --ignore=tests/integration`
- typecheck: `uv run mypy app/`
## Critical Rules
1. langchain >= 1.0 — legacy Chain classes (LLMChain, ConversationChain) and langchain.retrievers moved to the separate langchain-classic package, not installed by default
2. Use langchain.agents.create_agent for new agents, not langgraph.prebuilt.create_react_agent (v0) or initialize_agent (pre-LCEL)
3. create_agent's system prompt parameter is system_prompt, not prompt
4. Use init_chat_model("provider:model") for model initialization, not direct ChatAnthropic/ChatOpenAI instantiation, unless a provider-specific param requires it
5. Never call a real provider API in a unit test — use FakeListChatModel; real-API tests go in tests/integration/ and run separately
## Testing
- Unit tests: fake model, assert on tool calls and state shape, not generated text
- Integration tests: real API, marked and excluded from default test run
Common AI + LangChain Mistakes to Watch For
from langchain.chains import LLMChain on a v1.0 project. The single most common failure mode — it’s the pattern years of tutorials show, and it silently stops working the moment langchain-classic isn’t a dependency. Fix by rewriting as LCEL, not by adding the dependency, unless the codebase has a specific reason to keep the legacy class.
Passing prompt= to create_agent instead of system_prompt=. Reads like a typo in review, and the TypeError doesn’t mention the parameter rename — it’s leftover muscle memory from langgraph.prebuilt.create_react_agent, which used prompt.
Mixing langgraph.prebuilt.create_react_agent and langchain.agents.create_agent in the same codebase without a documented reason. Usually means half the codebase was written against v1.0 patterns and half against the LangGraph-era ones, and nobody decided which one is current for this project.
Direct ChatAnthropic()/ChatOpenAI() instantiation scattered across call sites instead of a single init_chat_model() call site. Makes swapping providers or models a multi-file change instead of a one-line one, and defeats the entire point of the unified interface.
Asserting on exact LLM-generated text in a unit test. Nondeterministic by construction — the correct assertion target is which tools were called and what the final state looks like, not the literal string a model happened to generate on that run.
The pattern is the same one that shows up whenever a fast-moving framework accumulates several “correct” API generations in the wild at once: the failure isn’t a crash, it’s silently-wrong or a plain ImportError that doesn’t explain which era of the framework it’s complaining about. Pin the installed major version and the current agent-construction pattern explicitly in CLAUDE.md, and an agent stops guessing between four valid-looking ways to write the same thing.
Browse the LangChain rule set in our gallery for the contributor-side guide to LangChain’s own monorepo, or browse all AI/LLM framework rule sets for more examples of framework-specific CLAUDE.md patterns.
FAQ
Do I need langchain-classic installed for a new LangChain project?
Almost never. langchain-classic exists as a migration bridge for codebases that can’t immediately rewrite legacy Chain subclasses as LCEL or create_agent. A new project should default to not installing it — if ModuleNotFoundError points at langchain.chains or langchain.retrievers, the fix is usually rewriting that code with the current API, not adding the dependency.
What’s the actual difference between create_react_agent and create_agent?
create_react_agent from langgraph.prebuilt was the pre-1.0 recommended way to build a tool-calling agent. create_agent from langchain.agents is its v1.0 successor, with a renamed system_prompt parameter (was prompt) and a middleware system for composing agent behavior. Both are real, currently-shipping code — the version installed in pyproject.toml determines which one is correct for a given project.
Should CLAUDE.md name the exact LangChain version, or just say “recent”?
Name it explicitly, ideally with the major version pin from pyproject.toml (e.g., langchain>=1.0,<2.0). “Recent” doesn’t tell an agent whether legacy Chain classes are available without an extra dependency, and that single fact determines whether an entire class of import looks correct or immediately breaks.
Is init_chat_model required, or is direct ChatAnthropic instantiation still fine?
Direct instantiation still works and isn’t deprecated. init_chat_model is recommended because it standardizes model initialization behind a single string and lets provider or model swaps happen without touching call sites — worth stating as a project convention in CLAUDE.md rather than leaving it ambiguous, since both patterns compile equally well.