| name | langchain-agents |
| description | Create and use LangChain agents with create_agent - includes agent loops, ReAct pattern, tool execution, and state management |
| language | python |
langchain-agents (Python)
Overview
Agents combine language models with tools to create systems that can reason about tasks, decide which tools to use, and iteratively work towards solutions. The create_agent() function provides a production-ready agent implementation built on LangGraph.
Key Concepts:
- Agent Loop: The model decides → calls tools → observes results → repeats until done
- ReAct Pattern: Reasoning and Acting - the agent reasons about what to do, then acts by calling tools
- Graph-based Runtime: Agents run on a LangGraph graph with nodes (model, tools, middleware) and edges
When to Use Agents
| Scenario | Use Agent? | Why |
|---|
| Need to call external APIs/databases | ✅ Yes | Agents can dynamically choose which tools to call |
| Multi-step task with decision points | ✅ Yes | Agent loop handles iterative reasoning |
| Simple prompt-response | ❌ No | Use a chat model directly |
| Predetermined workflow | ❌ No | Use LangGraph workflow instead |
| Need tool calling without iteration | ⚠️ Maybe | Consider using model.bind_tools() directly |
Decision Tables
Choosing Agent Configuration
| Need | Configuration | Example |
|---|
| Basic agent with tools | create_agent(model, tools) | Search, calculator, weather |
| Custom system instructions | Add system_prompt | Domain-specific behavior |
| Human approval for sensitive operations | Add human_in_the_loop_middleware | Database writes, emails |
| Persistence across sessions | Add checkpointer | Multi-turn conversations |
| Structured output format | Add response_format | Extract contact info, parse forms |
Tool Strategy
| Tool Type | When to Use | Example |
|---|
| Static tools | Tools don't change during execution | Search, weather, calculator |
| Dynamic tools | Tools depend on runtime state | User-specific APIs |
| Built-in tools | Need common functionality | File system, code execution |
| Custom tools | Domain-specific operations | Your business logic |
Code Examples
Basic Agent with Tools
from langchain.agents import create_agent
from langchain.tools import tool
@tool
def search(query: str) -> str:
"""Search for information on the web.
Args:
query: The search query
"""
return f"Results for: {query}"
@tool
def get_weather(location: str) -> str:
"""Get current weather for a location.
Args:
location: City name
"""
return f"Weather in {location}: Sunny, 72°F"
agent = create_agent(
model="gpt-4.1",
tools=[search, get_weather],
)
result = agent.invoke({
"messages": [
{"role": "user", "content": "What's the weather in San Francisco?"}
]
})
print(result["messages"][-1].content)
Agent with System Prompt
from langchain.agents import create_agent
agent = create_agent(
model="gpt-4.1",
tools=[search, calculator],
system_prompt="""You are a helpful research assistant.
Always cite your sources when using the search tool.
Show your work when performing calculations.""",
)
Agent Loop Execution Flow
agent = create_agent(
model="gpt-4.1",
tools=[search, get_weather],
)
result = agent.invoke({
"messages": [{
"role": "user",
"content": "Search for the capital of France, then get its weather"
}]
})
Streaming Agent Progress
from langchain.agents import create_agent
agent = create_agent(
model="gpt-4.1",
tools=[search],
)
for mode, chunk in agent.stream(
{"messages": [{"role": "user", "content": "Search for LangChain"}]},
stream_mode=["updates"],
):
print(f"Step: {chunk}")
for mode, chunk in agent.stream(
{"messages": [{"role": "user", "content": "Search for LangChain"}]},
stream_mode=["messages"],
):
token, metadata = chunk
if token.content:
print(token.content, end="", flush=True)
Agent with Persistence
from langchain.agents import create_agent
from langgraph.checkpoint.memory import MemorySaver
checkpointer = MemorySaver()
agent = create_agent(
model="gpt-4.1",
tools=[search],
checkpointer=checkpointer,
)
config = {"configurable": {"thread_id": "user-123"}}
agent.invoke({
"messages": [{"role": "user", "content": "My name is Alice"}]
}, config=config)
result = agent.invoke({
"messages": [{"role": "user", "content": "What's my name?"}]
}, config=config)
Multiple Tool Calls in Parallel
agent = create_agent(
model="gpt-4.1",
tools=[get_weather, get_news],
)
result = agent.invoke({
"messages": [{
"role": "user",
"content": "Get weather for NYC and latest news for SF"
}]
})
Dynamic Tools (Runtime-Dependent)
from langchain.agents import create_agent
def get_tools(state):
"""Tools can depend on current state."""
user_id = state.get("config", {}).get("configurable", {}).get("user_id")
return [
get_user_specific_tool(user_id),
common_tool,
]
agent = create_agent(
model="gpt-4.1",
tools=get_tools,
)
Error Handling in Agents
from langchain.agents import create_agent, wrap_tool_call
@wrap_tool_call
async def error_handler(tool_call, handler):
try:
return await handler(tool_call)
except Exception as error:
return {
**tool_call,
"content": f"Tool error: {str(error)}",
}
agent = create_agent(
model="gpt-4.1",
tools=[risky_tool],
middleware=[error_handler],
)
Tool with Type Hints
from langchain.tools import tool
from typing import Literal
@tool
def calculate(
operation: Literal["add", "subtract", "multiply", "divide"],
a: float,
b: float,
) -> float:
"""Perform a mathematical calculation.
Args:
operation: The operation to perform
a: First number
b: Second number
"""
if operation == "add":
return a + b
elif operation == "subtract":
return a - b
elif operation == "multiply":
return a * b
elif operation == "divide":
return a / b
Boundaries
What Agents CAN Configure
✅ Model: Any chat model (OpenAI, Anthropic, Google, etc.)
✅ Tools: Custom tools, built-in tools, dynamic tools
✅ System Prompt: Instructions for agent behavior
✅ Middleware: Human-in-the-loop, error handling, logging
✅ Checkpointer: Memory/persistence across conversations
✅ Response Format: Structured output schemas (Pydantic, TypedDict, JSON Schema)
✅ Max Iterations: Prevent infinite loops
What Agents CANNOT Configure
❌ Direct Graph Structure: Use LangGraph directly for custom flows
❌ Tool Execution Order: Model decides which tools to call
❌ Interrupt Model Decision: Can only interrupt before tool execution
❌ Multiple Models: One agent = one model (use subagents for multiple)
Gotchas
1. Agent Doesn't Stop (Infinite Loop)
agent = create_agent(
model="gpt-4.1",
tools=[search],
)
result = agent.invoke({
"messages": [{"role": "user", "content": "Keep searching until perfect"}]
})
agent = create_agent(
model="gpt-4.1",
tools=[search],
max_iterations=10,
)
2. Tool Not Being Called
@tool
def bad_tool(input: str) -> str:
"""Does stuff."""
return "result"
@tool
def web_search(query: str) -> str:
"""Search the web for current information about a topic.
Use this when you need recent data that wasn't in your training.
Args:
query: The search query (2-10 words)
"""
return "result"
3. State Not Persisting
agent = create_agent(
model="gpt-4.1",
tools=[search],
)
agent.invoke({"messages": [{"role": "user", "content": "Hi, I'm Bob"}]})
agent.invoke({"messages": [{"role": "user", "content": "What's my name?"}]})
from langgraph.checkpoint.memory import MemorySaver
agent = create_agent(
model="gpt-4.1",
tools=[search],
checkpointer=MemorySaver(),
)
config = {"configurable": {"thread_id": "session-1"}}
agent.invoke({"messages": [{"role": "user", "content": "Hi, I'm Bob"}]}, config=config)
agent.invoke({"messages": [{"role": "user", "content": "What's my name?"}]}, config=config)
4. Messages vs State Confusion
result = agent.invoke({
"messages": [{"role": "user", "content": "Hello"}]
})
print(result["messages"])
print(result.get("structured_response"))
5. Tool Results Must Be Serializable
from datetime import datetime
@tool
def bad_get_time() -> datetime:
"""Get current time."""
return datetime.now()
@tool
def good_get_time() -> str:
"""Get current time."""
return datetime.now().isoformat()
6. Streaming Modes Matter
for mode, chunk in agent.stream(input, stream_mode=["values"]):
print(chunk["messages"])
for mode, chunk in agent.stream(input, stream_mode=["updates"]):
print(chunk)
for mode, chunk in agent.stream(input, stream_mode=["messages"]):
token, metadata = chunk
print(token.content, end="", flush=True)
7. Async Tools Must Be Awaited Properly
from langchain.tools import tool
@tool
async def async_search(query: str) -> str:
"""Async search tool."""
result = await some_async_api_call(query)
return result
agent = create_agent(
model="gpt-4.1",
tools=[sync_tool, async_search],
)
result = await agent.ainvoke({
"messages": [{"role": "user", "content": "Search something"}]
})
Links to Documentation