llama-index-integrations/tools/llama-index-tools-exa/examples/exa.ipynb
<a href="https://colab.research.google.com/github/run-llama/llama_index/blob/main/llama-index-integrations/tools/llama-index-tools-exa/examples/exa.ipynb" target="_parent"></a>
This tutorial walks through using the LLM tools provided by the Exa API to allow LLMs to use semantic queries to search for and retrieve rich web content from the internet.
To get started, you will need an OpenAI api key and an Exa API key
We will import the relevant agents and tools and pass them our keys here:
# # Install the relevant LlamaIndex packages, incl. core and Exa tool
!pip install llama-index llama-index-core llama-index-tools-exa
# Get OS for environment variables
import os
# Set up OpenAI
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.llms.openai import OpenAI
# NOTE:
# You must have an OpenAI API key in the environment variable OPENAI_API_KEY
# You must have an Exa API key in the environment variable EXA_API_KEY
# Set up the Exa search tool
# from llama_index.tools.exa import ExaToolSpec
# Instantiate
exa_tool = ExaToolSpec(
api_key=os.environ["EXA_API_KEY"],
# max_characters=2000 # this is the default
)
# Get the list of tools to see what Exa offers
exa_tool_list = exa_tool.to_tool_list()
for tool in exa_tool_list:
print(tool.metadata.name)
We've imported our OpenAI agent, set up the API keys, and initialized our tool, checking the methods that it has available. Let's test out the tool before setting up our Agent.
All of the Exa search tools make use of the AutoPrompt option where Exa will pass the query through an LLM to refine it in line with Exa query best-practice.
exa_tool.search_and_retrieve_documents("machine learning transformers", num_results=3)
exa_tool.find_similar(
"https://www.mihaileric.com/posts/transformers-attention-in-disguise/"
)
exa_tool.search_and_retrieve_documents(
"This is a summary of recent research around diffusion models:", num_results=1
)
While search_and_retrieve_documents returns raw text from the source document, search_and_retrieve_highlights returns relevant curated snippets.
exa_tool.search_and_retrieve_highlights(
"This is a summary of recent research around diffusion models:", num_results=1
)
There are additional parameters that you can pass to Exa methods.
You can filter return results based on the date that entity was published
# Example 1: Calling search_and_contents with date filters
exa_tool.search_and_retrieve_documents(
"Advancements in quantum computing",
num_results=3,
start_published_date="2024-01-01",
end_published_date="2024-07-10",
)
You can constrain results to only return from specified domains (or exclude domains)
# Example 2: Calling search_and_contents with include_domains filters
exa_tool.search_and_retrieve_documents(
"Climate change mitigation strategies",
num_results=3,
include_domains=["www.nature.com", "www.sciencemag.org", "www.pnas.org"],
)
You can turn off autoprompt, enabling more direct and fine grained control of Exa querying.
# Example 3: Calling search_and_contents with autoprompt off
exa_tool.search_and_retrieve_documents(
"Here is an article on the advancements of quantum computing",
num_results=3,
use_autoprompt=False,
)
Exa also has an option to do standard keyword based seach by specifying type="keyword".
# Example 4: Calling search_and_retrieve_highlights with keyword search type
exa_tool.search_and_retrieve_highlights(
"Advancements in quantum computing", num_results=3, type="keyword"
)
Last, Magic Search is a new feature available in Exa, where queries will route to the best suited search type intelligently: either their proprietary neural search or industry-standard keyword search mentioned above
# Example 5: Calling search_and_retrieve_highlights with magic search (explicitly)
exa_tool.search_and_retrieve_highlights(
"Advancements in quantum computing", num_results=3, type="magic"
)
We can see we have different tools to search for results, retrieve the results, find similar results to a web page, and finally a tool that combines search and document retrieval into a single tool. We will test them out in LLM Agents below:
We can create an agent with access to the above tools and start testing it out:
# We don't give the Agent our unwrapped retrieve document tools, instead pass the wrapped tools
agent = FunctionAgent(
tools=exa_tool_list,
llm=OpenAI(model="gpt-4.1"),
)
print(await agent.run("What are the best resturants in toronto?"))
The above example shows the core uses of the Exa tool. We can easily retrieve a clean list of links related to a query, and then we can fetch the content of the article as a cleaned up html extract. Alternatively, the search_and_retrieve_documents tool directly returns the documents from our search result.
We can see that the content of the articles is somewhat long and may overflow current LLM context windows.
Use search_and_retrieve_highlights: This is an endpoint offered by Exa that directly retrieves relevant highlight snippets from the web, instead of full web articles. As a result you don't need to worry about indexing/chunking offline yourself!
Wrap search_and_retrieve_documents with LoadAndSearchToolSpec: We set up and use a "wrapper" tool from LlamaIndex that allows us to load text from any tool into a VectorStore, and query it for retrieval. This is where the search_and_retrieve_documents tool become particularly useful. The Agent can make a single query to retrieve a large number of documents, using a very small number of tokens, and then make queries to retrieve specific information from the documents.
search_and_retrieve_highlightsThe easiest is to just use search_and_retrieve_highlights from Exa. This is essentially a "web RAG" endpoint - they handle chunking/embedding under the hood.
tools = exa_tool.to_tool_list(
spec_functions=["search_and_retrieve_highlights", "current_date"]
)
agent = FunctionAgent(
tools=tools,
llm=OpenAI(model="gpt-4.1"),
)
response = await agent.run("Tell me more about the recent news on semiconductors")
print(f"Response: {str(response)}")
LoadAndSearchToolSpecHere we wrap the search_and_retrieve_documents functionality with the load_and_search_tool_spec.
from llama_index.core.tools.tool_spec.load_and_search import (
LoadAndSearchToolSpec,
)
# The search_and_retrieve_documents tool is the third in the tool list, as seen above
search_and_retrieve_docs_tool = exa_tool.to_tool_list(
spec_functions=["search_and_retrieve_documents"]
)[0]
date_tool = exa_tool.to_tool_list(spec_functions=["current_date"])[0]
wrapped_retrieve = LoadAndSearchToolSpec.from_defaults(search_and_retrieve_docs_tool)
Our wrapped retrieval tools separate loading and reading into separate interfaces. We use load to load the documents into the vector store, and read to query the vector store. Let's try it out again
wrapped_retrieve.load("This is the best explanation for machine learning transformers:")
print(wrapped_retrieve.read("what is a transformer"))
print(wrapped_retrieve.read("who wrote the first paper on transformers"))
We now are ready to create an Agent that can use Exa's services to their full potential. We will use our wrapped read and load tools, as well as the get_date utility for the following agent and test it out below:
# Just pass the wrapped tools and the get_date utility
agent = FunctionAgent(
tools=[*wrapped_retrieve.to_tool_list(), date_tool],
llm=OpenAI(model="gpt-4.1"),
)
print(
await agent.run(
"Can you summarize everything published in the last month regarding news on superconductors"
)
)
We asked the agent to retrieve documents related to superconductors from this month. It used the get_date tool to determine the current month, and then applied the filters in Exa based on publication date when calling search. It then loaded the documents using retrieve_documents and read them using read_retrieve_documents.
We can make another query to the vector store to read from it again, now that the articles are loaded: