langchain
LangChain is the most widely used Python framework for building LLM applications and agents (this page covers the PyPI package named langchain; there is a separate npm package with the same name for JavaScript). Since the 1.0 rewrite it centers on one idea: create_agent, a production-ready agent loop built on the LangGraph runtime, plus init_chat_model so you can address OpenAI, Anthropic, Google, and dozens of other providers through one interface. The old 0.x chains and AgentExecutor now live in separate legacy packages.
The 1.x rewrite is a real improvement: one blessed agent abstraction on a solid runtime, with the largest integration catalog in the Python LLM space. Pay the abstraction tax only if you need provider portability or that catalog; for a single provider and simple calls, the plain SDK is less to learn and less to debug.
Use it if
- You want to swap model providers (or run several) without rewriting call sites; the provider abstraction is the core product
- You are building a tool-calling agent and want the loop, message state, and structured output handled for you instead of hand-rolling a while loop
- You need its integration catalog: hundreds of maintained packages for vector stores, retrievers, and tools that would each be bespoke glue code otherwise
- You plan to grow into LangGraph for multi-step workflows; create_agent already returns a LangGraph graph, so the upgrade path is native
- You call one provider with straightforward prompts: the official openai or anthropic SDK is fewer layers, and every layer here is one more place to debug when output looks wrong
- You maintain 0.x code: the 1.0 rewrite moved chains, LLMChain, and AgentExecutor out to langchain-classic, and most tutorials, blog posts, and Stack Overflow answers still show pre-1.0 APIs that no longer import
- You want a small dependency footprint: the base install pulls langchain-core, langgraph, and pydantic, and every provider is yet another package on top
- Deep debugging matters more than speed of assembly: tracing a bad answer through middleware, graph nodes, and message transforms is real work, and the first-party observability tool (LangSmith) is a commercial product
Setup reality
pip install langchain brings in langchain-core and langgraph, but no model provider: you also need langchain-openai, langchain-anthropic, or the matching extra like langchain[openai], plus the provider API key in your environment. The sharpest edge is version skew. Pre-1.0 examples dominate search results and fail on import in 1.x, and the docs are split between docs.langchain.com and a separate reference site, so expect to check which era a snippet comes from before trusting it. Pinning versions matters; minor releases arrive frequently.
Patterns
One interface for any providerinit-chat-model
from langchain.chat_models import init_chat_model
model = init_chat_model("openai:gpt-4o", temperature=0)
result = model.invoke("Say hi in one word")
print(result.content)The provider prefix (openai:, anthropic:, ollama:) selects which integration package to load; that package must be installed separately or you get an ImportError at runtime, not install time.
Tool-calling agent in a few linescreate-agent-basic
from langchain.agents import create_agent
agent = create_agent(
model="anthropic:claude-sonnet-4-5",
tools=[get_weather],
system_prompt="You are a terse assistant.",
)
result = agent.invoke({"messages": [{"role": "user", "content": "Weather in Pune?"}]})
print(result["messages"][-1].content)create_agent replaced 0.x AgentExecutor and returns a compiled LangGraph graph; input and output are dicts with a messages list, not a plain string.
Define a tool with the decoratordefine-tool
from langchain.tools import tool
@tool
def get_weather(city: str) -> str:
"""Get current weather for a city."""
return f"18C and cloudy in {city}"The docstring is not decoration: it is the description the model reads to decide when to call the tool. A vague docstring produces an agent that ignores or misuses the tool.
Get a Pydantic object back from an agentstructured-output
from pydantic import BaseModel
from langchain.agents import create_agent
class Verdict(BaseModel):
rating: int
summary: str
agent = create_agent(model="openai:gpt-4o", tools=[], response_format=Verdict)
result = agent.invoke({"messages": [{"role": "user", "content": "Rate this printer: ..."}]})
print(result["structured_response"].rating)The parsed object arrives under the structured_response key, separate from messages. Validation failures surface as retries or errors depending on strategy, so keep schemas simple.
Multi-turn conversation with message objectsconversation-messages
from langchain.messages import HumanMessage, SystemMessage
messages = [
SystemMessage("You answer in Hindi."),
HumanMessage("What is a copier drum?"),
]
reply = model.invoke(messages)
messages.append(reply)In 1.x these classes are importable from langchain.messages directly; older code imports them from langchain_core.messages, which still works but reads as pre-1.0 style.
Stream tokens from a modelstream-tokens
for chunk in model.stream("Explain RESP3 in two sentences"):
print(chunk.text, end="", flush=True)Chunks are AIMessageChunk objects, not strings. With reasoning models some chunks carry non-text content blocks, so chunk.text is safer than assuming chunk.content is a string.
Stream agent progress step by stepstream-agent-steps
for step in agent.stream(
{"messages": [{"role": "user", "content": "Weather in Pune?"}]},
stream_mode="values",
):
step["messages"][-1].pretty_print()stream_mode matters: values yields full state each step, updates yields deltas, and messages yields token-level chunks. Picking the wrong mode is the usual reason streaming output looks duplicated or empty.
Give an agent conversation memoryagent-memory-thread
from langgraph.checkpoint.memory import InMemorySaver
agent = create_agent(model="openai:gpt-4o", tools=[], checkpointer=InMemorySaver())
config = {"configurable": {"thread_id": "user-42"}}
agent.invoke({"messages": [{"role": "user", "content": "I am Keyoor"}]}, config)
agent.invoke({"messages": [{"role": "user", "content": "Who am I?"}]}, config)Memory is per thread_id and InMemorySaver evaporates on restart; production needs a persistent checkpointer such as the Postgres one from the langgraph ecosystem.
Bind tools without the agent loopmanual-tool-calling
model_with_tools = model.bind_tools([get_weather])
response = model_with_tools.invoke("Weather in Pune?")
for call in response.tool_calls:
print(call["name"], call["args"])bind_tools only makes the model emit tool calls; nothing executes them. You own the loop of running tools and feeding ToolMessage results back, which is exactly what create_agent automates.
Prompt template piped to a modelprompt-pipeline
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_template("Translate to {lang}: {text}")
chain = prompt | model | StrOutputParser()
print(chain.invoke({"lang": "German", "text": "good morning"}))The pipe-operator (LCEL) style still works through langchain-core, but 1.x docs push agents instead; treat this as the tool for fixed transformations, not decision-making flows.
Middleware to keep context under controlsummarize-long-history
from langchain.agents import create_agent
from langchain.agents.middleware import SummarizationMiddleware
agent = create_agent(
model="openai:gpt-4o",
tools=[get_weather],
middleware=[SummarizationMiddleware(model="openai:gpt-4o-mini")],
)Middleware is new in 1.x and the hooks run inside the graph, so a misbehaving middleware shows up as confusing agent behavior; add them one at a time and test.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| openai | PyPI | One provider, direct calls, no framework; the SDK plus a small hand-written loop covers many agent use cases |
| pydantic-ai | PyPI | You want a typed, Pydantic-first agent framework with a smaller surface area |
| llama-index | PyPI | Your app is mostly retrieval over documents; its indexing and query pipeline is more focused for RAG |
| langgraph | PyPI | You want explicit graph-level control of an agent workflow and can skip the higher-level conveniences |