Learning Objectives
- Understand what AI agents are and how they work
- Use the new
create_agentAPI (LangChain v1.0) - Define custom tools for agents to use
- Work with built-in tools (search, calculator, etc.)
- Handle agent execution and monitor tool calls
3.1 What are AI Agents?
An AI agent is an LLM that can autonomously decide which actions to take to accomplish a goal. Unlike simple chains that follow a fixed path, agents can:
- Reason about what steps are needed
- Choose which tools to use
- Execute actions and observe results
- Iterate until the task is complete
Agent Loop
The cycle of: (1) LLM decides which tool to call, (2) Tool executes and returns result, (3) LLM observes result and decides next action. This continues until the LLM determines the task is complete.
3.2 The create_agent API
LangChain v1.0 introduced create_agent as the standard way to build agents:
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
# Define a simple tool
def get_weather(city: str) -> str:
"""Get the current weather for a city."""
# In production, this would call a real weather API
return f"The weather in {city} is sunny, 22°C"
def calculate(expression: str) -> str:
"""Evaluate a mathematical expression.
Warning: This example uses eval() for simplicity. In production,
use a safe math parser like `numexpr` or `asteval` instead.
"""
try:
# WARNING: eval() is dangerous with untrusted input!
# For production, use: import numexpr; numexpr.evaluate(expression)
return str(eval(expression))
except Exception as e:
return f"Error: {e}"
# Create the agent
agent = create_agent(
model=ChatOpenAI(model="gpt-4"),
tools=[get_weather, calculate],
system_prompt="You are a helpful assistant that can check weather and do math."
)
# Run the agent
result = agent.invoke({
"messages": [
{"role": "user", "content": "What's the weather in Tokyo, and what's 25 * 4?"}
]
})
print(result["messages"][-1].content)
3.3 Defining Custom Tools
Tools can be defined in several ways. The simplest is using the @tool decorator:
from langchain_core.tools import tool
@tool
def search_database(query: str) -> str:
"""Search the product database for items matching the query.
Args:
query: The search term to look for in the database.
"""
# Simulated database search
products = {
"laptop": "MacBook Pro - $1999",
"phone": "iPhone 15 - $999",
"tablet": "iPad Pro - $799"
}
query_lower = query.lower()
for key, value in products.items():
if key in query_lower:
return value
return "No products found"
@tool
def send_email(to: str, subject: str, body: str) -> str:
"""Send an email to the specified recipient.
Args:
to: Email address of the recipient.
subject: Subject line of the email.
body: Body content of the email.
"""
# In production, this would actually send an email
return f"Email sent to {to} with subject '{subject}'"
# Use the tools
print(search_database.name) # "search_database"
print(search_database.description) # From docstring
print(search_database.invoke("laptop")) # "MacBook Pro - $1999"
Tool Schema
LangChain automatically extracts the schema from type hints and docstrings:
Best Practices for Tool Definitions
- Use clear, descriptive function names
- Write detailed docstrings—the LLM uses these to decide when to use the tool
- Add type hints for all parameters
- Keep tools focused on a single task
3.4 Built-in Tools
LangChain provides many pre-built tools through langchain-community:
from langchain_community.tools import DuckDuckGoSearchRun
from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
# Web search tool
search = DuckDuckGoSearchRun()
result = search.invoke("Latest news about AI")
print(result)
# Wikipedia tool
wikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())
result = wikipedia.invoke("Machine Learning")
print(result)
Common Built-in Tools
| Tool | Package | Purpose |
|---|---|---|
| DuckDuckGoSearchRun | langchain-community | Web search |
| WikipediaQueryRun | langchain-community | Wikipedia lookup |
| ArxivQueryRun | langchain-community | Academic paper search |
| PythonREPLTool | langchain-community | Execute Python code |
| ShellTool | langchain-community | Run shell commands |
Security Warning: High-Risk Tools
PythonREPLTool and ShellTool execute arbitrary code with full system access. Only use these tools in sandboxed environments or with strict input validation. Consider using allowlists, containerization (Docker), or restricted execution environments for production deployments.
3.5 Structured Tool Output
For complex tools, you can define structured input/output with Pydantic:
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain_core.tools import StructuredTool
from pydantic import BaseModel, Field
# Define input schema
class FlightSearchInput(BaseModel):
origin: str = Field(description="Departure airport code (e.g., 'NRT')")
destination: str = Field(description="Arrival airport code (e.g., 'LAX')")
date: str = Field(description="Travel date in YYYY-MM-DD format")
def search_flights(origin: str, destination: str, date: str) -> str:
"""Search for available flights."""
# Simulated flight search
return f"Found 3 flights from {origin} to {destination} on {date}. Cheapest: $450"
# Create structured tool
flight_tool = StructuredTool.from_function(
func=search_flights,
name="flight_search",
description="Search for flights between airports",
args_schema=FlightSearchInput
)
# Use in agent
agent = create_agent(
model=ChatOpenAI(model="gpt-4"),
tools=[flight_tool],
system_prompt="You are a travel assistant."
)
3.6 Agent Execution Patterns
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
# Assumes get_weather and calculate are defined as in Code Example 1
agent = create_agent(
model=ChatOpenAI(model="gpt-4"),
tools=[get_weather, calculate],
system_prompt="You are a helpful assistant."
)
# Stream the agent's thinking process
for event in agent.stream({
"messages": [{"role": "user", "content": "What's 15% of 250?"}]
}):
if "messages" in event:
for msg in event["messages"]:
if hasattr(msg, "tool_calls") and msg.tool_calls:
print(f"🔧 Calling tool: {msg.tool_calls[0]['name']}")
elif hasattr(msg, "content") and msg.content:
print(f"💬 {msg.content}")
3.7 Error Handling
from langchain_core.tools import tool
@tool
def divide(a: float, b: float) -> str:
"""Divide a by b.
Args:
a: The numerator.
b: The denominator.
"""
if b == 0:
return "Error: Cannot divide by zero"
return str(a / b)
# The agent will receive the error message and can respond appropriately
agent = create_agent(
model=ChatOpenAI(model="gpt-4"),
tools=[divide],
system_prompt="You are a math assistant. If a calculation fails, explain why."
)
result = agent.invoke({
"messages": [{"role": "user", "content": "What is 10 divided by 0?"}]
})
print(result["messages"][-1].content)
# Agent will explain that division by zero is not possible
Exercises
Exercise 1: Build a Research Agent
Create an agent with tools for:
- Searching the web (use DuckDuckGoSearchRun)
- Looking up Wikipedia articles
- Summarizing text (a custom tool that uses the LLM)
Test it by asking: "Research the history of neural networks and summarize the key milestones."
Exercise 2: Task Automation Agent
Build an agent for a fictional task management system with tools:
create_task(title, description, due_date)list_tasks()complete_task(task_id)
Store tasks in a simple dictionary and test the agent's ability to manage tasks.
Summary
- Agents are LLMs that can autonomously use tools to accomplish tasks
- Use
create_agent(v1.0) to build agents with tools - Define tools with
@tooldecorator orStructuredTool - Clear docstrings help the LLM understand when to use each tool
- Built-in tools available for search, Wikipedia, code execution, etc.
- Stream agent execution to see the reasoning process