Deep AgentsDeep Agents 0.7 · Python 3.11+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
28 small wins to finish your pathNext lesson →

Tools: a travel search the agent can call

Tools are Python functions the model can ask the agent to run; a tool's name, arguments and docstring tell the model what it does and when to call it.

Last updated: 29 Sep, 2026 · Deep Agents 0.7

A web search tool with Tavily · from the Complete Deep Agents Course With LangChain · 26:40 to 31:27

The video's web_search tool

The video's first tool searches the internet. It creates a TavilyClient with the Tavily key, then writes web_search: a query string, max_results with a default of 5, a topic that must be one of a few fixed words, written with Literal, and include_raw_content. The parameters are the ones the Tavily client's search method takes, read from its documentation. The docstring, "Run a web search", is what the model reads to decide when to use it.

Installing tavily-python

web_search needs the Tavily client, tavily-python, which the video installs with the other packages. Add it next to the ones from Installation and setup:

pip install "tavily-python==0.8.4"

Getting a free Tavily keyOptional

Sign up at app.tavily.com. The API key is on the dashboard and starts with tvly-. The free Researcher plan gives 1,000 API credits a month and needs no card; a basic search, the kind web_search runs, costs one credit. Set the key the same way as the Groq key:

export TAVILY_API_KEY=tvly-...

Or add it to the .env file under the Groq key; load_dotenv() loads both:

bash
GROQ_API_KEY=gsk_...
# optional, only the web search examples use it
TAVILY_API_KEY=tvly-...

The key is optional. The course's own examples, from the catalog tool below to the finished trip planner, search a small travel catalog and run with the Groq key alone. web_search is the real-web version of the same idea: the examples from the video that search the internet, and the live search in Integrations: taking the trip planner to production, need this key.

The web_search function

Save the function as research.py. It is the video's code with two changes that keep each request inside the 8,000 tokens a minute that Groq's free tier allows: include_raw_content is left out, because one page's full text can be larger than that on its own, and max_results is capped at 5, because the model sometimes asks for 10. On a paid tier you can put both back. If your keys live in .env, the two load_dotenv lines from Installation and setup go at the very top, above tavily_client, because the client reads the key as soon as the file runs.

python
import os
from tavily import TavilyClient
from typing import Literal

tavily_client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))

def web_search(query: str, max_results: int = 5,
               topic: Literal["general", "sports", "news", "finance"] = "general"):
    """Run a web search"""
    return tavily_client.search(query, max_results=min(max_results, 5), topic=topic)

It runs in the next lesson, create_deep_agent: your first deep agent, where the video hands it to its first deep agent. The trip planner on these pages uses a tool that searches a small travel catalog instead, so every answer can be checked against the data. Web search results change every day; a catalog does not.

The search_travel tool

The catalog is a dictionary: one city, and for each kind of entry, a list of lines with rupee prices. The prices are the ones the trip planner uses in every later lesson.

The travel catalog

python
CATALOG = {
    "paris": {
        "flight": ["Return flight Delhi to Paris: 42,000 rupees"],
        "hotel": ["Seine Budget Inn, Latin Quarter: 5,200 rupees a night",
                  "Hotel Lumiere, Montmartre: 7,500 rupees a night",
                  "Le Grand Opera Hotel: 16,000 rupees a night"],
        "sight": ["Eiffel Tower summit: 3,100 rupees", "Louvre Museum: 2,000 rupees",
                  "Seine river cruise: 1,500 rupees", "Versailles day trip: 2,600 rupees",
                  "Montmartre walking tour: free"],
        "food": ["Cafe breakfast and bistro dinner: 3,000 rupees a day"],
    },
}

Turning a function into a tool with @tool

@tool from langchain.tools reads the function's name, type hints and docstring and builds the description the model sees. Deep Agents also accepts a plain function, as the video's web_search shows; @tool makes the conversion explicit and lets you call the tool yourself with .invoke. A city that is not in the catalog gets a clear sentence back, so the model can say so instead of guessing.

python
@tool
def search_travel(city: str, kind: str) -> str:
    """Search the travel catalog. kind is "flight", "hotel", "sight" or "food". Prices are in rupees."""
    entries = CATALOG.get(city.lower(), {}).get(kind)
    return "\n".join(entries) if entries else f"The catalog has no {kind} entries for {city}."

Calling search_travel directly

The file so far; save it as trip.py; later lessons start from it.

python
from langchain.tools import tool

CATALOG = {
    "paris": {
        "flight": ["Return flight Delhi to Paris: 42,000 rupees"],
        "hotel": ["Seine Budget Inn, Latin Quarter: 5,200 rupees a night",
                  "Hotel Lumiere, Montmartre: 7,500 rupees a night",
                  "Le Grand Opera Hotel: 16,000 rupees a night"],
        "sight": ["Eiffel Tower summit: 3,100 rupees", "Louvre Museum: 2,000 rupees",
                  "Seine river cruise: 1,500 rupees", "Versailles day trip: 2,600 rupees",
                  "Montmartre walking tour: free"],
        "food": ["Cafe breakfast and bistro dinner: 3,000 rupees a day"],
    },
}


@tool
def search_travel(city: str, kind: str) -> str:
    """Search the travel catalog. kind is "flight", "hotel", "sight" or "food". Prices are in rupees."""
    entries = CATALOG.get(city.lower(), {}).get(kind)
    return "\n".join(entries) if entries else f"The catalog has no {kind} entries for {city}."

Add two calls to the end of trip.py. A tool made with @tool takes its arguments as a dictionary. No model is involved yet, so this runs without a key:

Exampletrip.py, continued
print(search_travel.invoke({"city": "Paris", "kind": "hotel"}))
print(search_travel.invoke({"city": "Tokyo", "kind": "hotel"}))

What the two calls returned

  • Paris hotels came back as three lines, cheapest first, exactly as the catalog stores them.
  • Tokyo is not in the catalog, so the tool returned a sentence saying so. An agent that reads it can tell the traveller the city is missing.

A web search tool vs the catalog tool

web_search (the video)search_travel (this course)
DataLive results from the internetA fixed catalog in the file
KeysTavily and a model keyA model key only
Same question tomorrowMay return different pagesReturns the same lines
Good forResearch on current topicsLearning, testing and checking answers

Where custom tools fit

  • Your own data: a booking system, a price list, a database query.
  • Actions: sending a message, making a booking, which later lessons put behind a person's approval.
  • Anything the model cannot know on its own, such as today's prices.
Watch out. A vague docstring such as "travel tool" leads to calls with made-up arguments. The docstring is part of the prompt: "Search the travel catalog" with the allowed kind values tells the model how to call it.
Try it yourself
  • Call search_travel with kind="sight" and count the free entries.
  • Add a "rome" city with one hotel and call the tool for it.
  • Call it with kind="train" and read the sentence it returns.

You understood something today that you didn't yesterday.