A langchain tavily web search agent grounds LLM outputs in fresh web data without you maintaining a crawler or vector store. This guide builds one with LangChain’s tool-calling agent and Tavily’s search API, using a standard OpenAI-compatible chat model so you can swap providers freely. You will end up with a runnable Python script that decides when to search, fetches cleaned results, and answers with citations.
Step 1: Install dependencies
Set up a clean virtual environment, then pull the packages you need. LangChain split into modular packages in 2024; install only what you use to keep your dependency tree auditable.
pip install langchain langchain-openai langchain-community tavily-python python-dotenv
The tavily-python client backs the LangChain community tool. langchain-openai gives you the ChatOpenAI class, which speaks the OpenAI chat protocol and works against any compatible endpoint. If you later decide to route through a gateway, the import stays identical.
A langchain tavily web search agent does not require LangGraph, but the underlying create_tool_calling_agent uses the same message types, so the mental model transfers.
Step 2: Set up API keys and environment
Tavily requires an API key from its dashboard. For the LLM, you can use OpenAI directly or an OpenAI-compatible gateway. Create a .env file:
TAVILY_API_KEY=tvly-xxxxxxxxxxxxxxxx
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx
# Optional: point to n4n.ai's OpenAI-compatible endpoint for 240+ models
LLM_BASE_URL=https://api.n4n.ai/v1
Load it in Python:
from dotenv import load_dotenv
load_dotenv()
Keep keys out of source control. If you run this in a container, inject them as secrets, not baked layers. Tavily’s free tier is enough for local development; production volumes need a paid plan with higher rate limits.
Step 3: Configure the chat model
Instantiate ChatOpenAI. If you set LLM_BASE_URL to an OpenAI-compatible gateway, the same code targets that endpoint. n4n.ai exposes one OpenAI-compatible endpoint that addresses 240+ models and applies automatic fallback when a provider is rate-limited or degraded, so a single base URL gives you redundancy without code changes.
import os
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0,
api_key=os.environ["OPENAI_API_KEY"],
base_url=os.environ.get("LLM_BASE_URL"), # None falls back to OpenAI
)
For a langchain tavily web search agent, low temperature reduces hallucinated tool arguments. Pick a model that supports parallel tool calls if you plan to fan out searches. Smaller models like gpt-4o-mini or mistral-small handle single-search loops cheaply; reserve larger models for multi-step reasoning.
Test the model independently before wiring tools:
print(llm.invoke("Say 'tool ready'").content)
Step 4: Wrap Tavily as a LangChain tool
LangChain’s community package ships a ready tool. It returns a list of result dicts with url, content, and score. You can tune search_depth and include_answer to trade latency for recall.
from langchain_community.tools.tavily_search import TavilySearchResults
tavily_tool = TavilySearchResults(
max_results=5,
search_depth="advanced",
include_answer=False,
api_key=os.environ["TAVILY_API_KEY"],
)
tools = [tavily_tool]
Tavily’s search is tuned for LLM context: it returns cleaned text slices, not raw HTML. That matters when you are stuffing results into a prompt with a tight token budget. If you need raw links only, set max_results to 3 and parse url fields.
The tool’s schema is auto-generated. Inspect it:
print(tavily_tool.args_schema.schema())
You will see a query string property. The agent uses this schema to construct arguments, so clear descriptions in the schema improve call accuracy.
Step 5: Build the tool-calling agent
Use create_tool_calling_agent with a minimal prompt. The agent loop will decide when to call Tavily, pass a query string, and ingest the results. Tool calling is more reliable than the legacy ReAct text loop because arguments are JSON-validated.
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents import AgentExecutor, create_tool_calling_agent
prompt = ChatPromptTemplate.from_messages([
("system", "You are a research assistant. Use the web search tool to answer questions with cited sources. If the question is timeless, you may answer directly."),
("human", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
max_iterations=5,
handle_parsing_errors=True,
)
verbose=True prints the intermediate steps. In production you’ll replace that with structured logging. max_iterations caps the loop; handle_parsing_errors prevents a malformed tool response from crashing the run.
Step 6: Run a query and verify
Invoke the executor with a question that requires current data:
response = executor.invoke({
"input": "What is the latest stable LangChain version and does it include a Tavily tool?"
})
print(response["output"])
A correct run prints agent actions showing TavilySearchResults being called, followed by a synthesized answer that references version numbers and the tool’s location in langchain_community. If you see the model answer from prior knowledge without a tool call, raise the prompt’s insistence on searching or set force_tool_call patterns.
Verification checklist
- The logs show at least one tool call to Tavily before the final answer.
- The output mentions a source URL or explicitly states it used web search.
- Running the same query twice with an empty Tavily key raises an auth error, confirming the tool is actually exercised.
- Token usage is visible in your LLM provider’s metering; n4n.ai reports per-token usage per request if you route through it.
- The agent terminates within
max_iterationsand returns a string inresponse["output"].
Write a small pytest fixture to assert these properties in CI:
def test_agent_searches():
out = executor.invoke({"input": "Who won the 2024 Nobel Prize in Physics?"})
assert "Tavily" in str(out["intermediate_steps"]) or "http" in out["output"]
Step 7: Production hardening
The happy path above breaks under load. Address these before shipping.
Rate limits and fallback
Tavily and your LLM both rate-limit. Wrap tool calls with retries:
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
def safe_search(query):
return tavily_tool.invoke({"query": query})
If you use a gateway that honors client routing directives and forwards provider cache-control hints, you can pin a model per request or leverage prompt caching without changing agent code. That keeps p95 latency stable when one provider degrades.
Streaming and latency
Tool calling adds a round trip. Stream intermediate tokens to users with executor.stream_events (LangChain 0.2+) or migrate to LangGraph for finer control. Set a max_iterations on AgentExecutor to avoid runaway loops.
Structured output and citations
Parse the final message for citations. Tavily results include URLs; instruct the model to append them:
prompt = ChatPromptTemplate.from_messages([
("system", "Answer using web search. End with 'Sources:' and list consumed URLs."),
("human", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
Cost control
Web search agents burn tokens on long page snippets. Cap max_results at 3–5 and truncate content fields before returning them to the model. If your gateway provides per-token metering, log usage from the response to track spend per agent run. A langchain tavily web search agent can easily triple token cost versus a static prompt if you skip truncation.
Step 8: Extend the agent
A langchain tavily web search agent is a baseline. Add a second tool—say a calculator or a SQL reader—by appending to tools. The same create_tool_calling_agent handles multi-tool routing. For complex workflows, move to LangGraph’s state machine to enforce search-before-answer constraints.
Keep the agent’s system prompt explicit about when not to search (e.g., pure math) to cut latency. Tool schema descriptions are the only signal the model gets; write them like API docs, not marketing copy.
If you need to audit which provider served the LLM, an OpenAI-compatible gateway that forwards routing directives lets you pass extra_headers with a model pin. That’s useful when benchmarking Tavily grounding quality across model families.
You now have a runnable agent that cites live web sources, with a clear path to production redundancy via an OpenAI-compatible endpoint.