Skip to content

ExaToolkit

autogen.beta.tools.search.exa.ExaToolkit #

ExaToolkit(api_key=None, *, num_results=None, max_characters=None, client=None, middleware=())

Bases: Toolkit

Toolkit that exposes the Exa neural search engine as four related tools sharing one HTTP client.

The four tools mirror Exa's primary endpoints: - exa_search: neural/keyword/hybrid web search with optional text content - exa_find_similar: find pages similar to a given URL - exa_get_contents: fetch full text for specific URLs - exa_answer: get an AI-generated answer with citations

By default, passing the whole toolkit to an agent registers all four tools. To use a subset, or to customise per-tool parameters, call the factory methods directly and pass the returned tools to the agent::

toolkit = ExaToolkit(api_key=...)

# all four tools
agent = Agent("a", config=config, tools=[toolkit])

# only two, with custom parameters
agent = Agent(
    "a",
    config=config,
    tools=[
        toolkit.search(num_results=5, search_type="neural"),
        toolkit.answer(),
    ],
)

The constructor reads EXA_API_KEY from the environment when api_key is omitted (handled by the underlying exa_py.Exa SDK).

Source code in autogen/beta/tools/search/exa.py
def __init__(
    self,
    api_key: str | None = None,
    *,
    num_results: int | Variable | None = None,
    max_characters: int | Variable | None = None,
    client: Exa | None = None,
    middleware: Iterable[ToolMiddleware] = (),
) -> None:
    self._client = client if client is not None else Exa(api_key=api_key)

    super().__init__(
        self.search(num_results=num_results, max_characters=max_characters),
        self.find_similar(num_results=num_results),
        self.get_contents(),
        self.answer(),
        name="exa_toolkit",
        middleware=middleware,
    )

name instance-attribute #

name = name or __name__

tools property #

tools

search #

search(*, num_results=None, max_characters=None, search_type=None, category=None, include_domains=None, exclude_domains=None, start_published_date=None, end_published_date=None, start_crawl_date=None, end_crawl_date=None, use_autoprompt=None, livecrawl=None, name='exa_search', description="Search the web using Exa's neural search engine. Returns ranked results with titles, URLs, relevance scores, and optional text content.", middleware=())
Source code in autogen/beta/tools/search/exa.py
def search(
    self,
    *,
    num_results: int | Variable | None = None,
    max_characters: int | Variable | None = None,
    search_type: SearchType | Variable | None = None,
    category: Category | Variable | None = None,
    include_domains: Sequence[str] | Variable | None = None,
    exclude_domains: Sequence[str] | Variable | None = None,
    start_published_date: str | Variable | None = None,
    end_published_date: str | Variable | None = None,
    start_crawl_date: str | Variable | None = None,
    end_crawl_date: str | Variable | None = None,
    use_autoprompt: bool | Variable | None = None,
    livecrawl: Livecrawl | Variable | None = None,
    name: str = "exa_search",
    description: str = (
        "Search the web using Exa's neural search engine. "
        "Returns ranked results with titles, URLs, relevance scores, and optional text content."
    ),
    middleware: Iterable[ToolMiddleware] = (),
) -> FunctionTool:
    client = self._client

    @tool(name=name, description=description, middleware=middleware)
    def exa_search(
        query: Annotated[str, Field(description="The search query string.")],
        ctx: Context,
    ) -> ExaSearchResponse:
        """Search the web using Exa and return ranked results."""
        kwargs = _clean({
            "num_results": resolve_variable(num_results, ctx, param_name="num_results"),
            "type": resolve_variable(search_type, ctx, param_name="search_type"),
            "category": resolve_variable(category, ctx, param_name="category"),
            "include_domains": resolve_variable(include_domains, ctx, param_name="include_domains"),
            "exclude_domains": resolve_variable(exclude_domains, ctx, param_name="exclude_domains"),
            "start_published_date": resolve_variable(start_published_date, ctx, param_name="start_published_date"),
            "end_published_date": resolve_variable(end_published_date, ctx, param_name="end_published_date"),
            "start_crawl_date": resolve_variable(start_crawl_date, ctx, param_name="start_crawl_date"),
            "end_crawl_date": resolve_variable(end_crawl_date, ctx, param_name="end_crawl_date"),
            "use_autoprompt": resolve_variable(use_autoprompt, ctx, param_name="use_autoprompt"),
            "livecrawl": resolve_variable(livecrawl, ctx, param_name="livecrawl"),
        })
        resolved_max_chars = resolve_variable(max_characters, ctx, param_name="max_characters")
        if resolved_max_chars is not None:
            kwargs["text"] = {"maxCharacters": resolved_max_chars}
            raw = client.search_and_contents(query, **kwargs)
        else:
            raw = client.search(query, **kwargs)

        return ExaSearchResponse(
            query=query,
            results=[
                ExaSearchResult(
                    title=r.title or "",
                    url=r.url,
                    score=getattr(r, "score", None),
                    published_date=getattr(r, "published_date", None),
                    author=getattr(r, "author", None),
                    text=getattr(r, "text", None),
                )
                for r in raw.results
            ],
            autoprompt_string=getattr(raw, "autoprompt_string", None) or getattr(raw, "autoprompt", None),
        )

    return exa_search

find_similar #

find_similar(*, num_results=None, exclude_source_domain=None, name='exa_find_similar', description='Find web pages similar to a given URL. Useful for discovering related content, competitors, or alternative sources.', middleware=())
Source code in autogen/beta/tools/search/exa.py
def find_similar(
    self,
    *,
    num_results: int | Variable | None = None,
    exclude_source_domain: bool | Variable | None = None,
    name: str = "exa_find_similar",
    description: str = (
        "Find web pages similar to a given URL. Useful for discovering "
        "related content, competitors, or alternative sources."
    ),
    middleware: Iterable[ToolMiddleware] = (),
) -> FunctionTool:
    client = self._client

    @tool(name=name, description=description, middleware=middleware)
    def exa_find_similar(
        url: Annotated[str, Field(description="The URL to find similar pages for.")],
        ctx: Context,
    ) -> list[ExaSearchResult]:
        """Find pages similar to a given URL."""
        kwargs = _clean({
            "num_results": resolve_variable(num_results, ctx, param_name="num_results"),
            "exclude_source_domain": resolve_variable(
                exclude_source_domain, ctx, param_name="exclude_source_domain"
            ),
        })
        raw = client.find_similar(url, **kwargs)
        return [
            ExaSearchResult(
                title=r.title or "",
                url=r.url,
                score=getattr(r, "score", None),
                published_date=getattr(r, "published_date", None),
                author=getattr(r, "author", None),
                text=getattr(r, "text", None),
            )
            for r in raw.results
        ]

    return exa_find_similar

get_contents #

get_contents(*, name='exa_get_contents', description='Get the full text content of specific URLs. Useful when you already know which pages you need and want to read them.', middleware=())
Source code in autogen/beta/tools/search/exa.py
def get_contents(
    self,
    *,
    name: str = "exa_get_contents",
    description: str = (
        "Get the full text content of specific URLs. Useful when you already "
        "know which pages you need and want to read them."
    ),
    middleware: Iterable[ToolMiddleware] = (),
) -> FunctionTool:
    client = self._client

    @tool(name=name, description=description, middleware=middleware)
    def exa_get_contents(
        urls: Annotated[list[str], Field(description="URLs to fetch content for.")],
        ctx: Context,
    ) -> list[ExaContentResult]:
        """Fetch the full text content of specific URLs."""
        raw = client.get_contents(urls, text=True)
        return [
            ExaContentResult(
                url=r.url,
                title=r.title or "",
                text=r.text or "",
                author=getattr(r, "author", None),
                published_date=getattr(r, "published_date", None),
            )
            for r in raw.results
        ]

    return exa_get_contents

answer #

answer(*, name='exa_answer', description='Generate an AI-powered answer to a question with citations from web sources.', middleware=())
Source code in autogen/beta/tools/search/exa.py
def answer(
    self,
    *,
    name: str = "exa_answer",
    description: str = ("Generate an AI-powered answer to a question with citations from web sources."),
    middleware: Iterable[ToolMiddleware] = (),
) -> FunctionTool:
    client = self._client

    @tool(name=name, description=description, middleware=middleware)
    def exa_answer(
        query: Annotated[str, Field(description="The question to answer.")],
        ctx: Context,
    ) -> ExaAnswerResult:
        """Generate an AI answer with citations."""
        raw = client.answer(query, text=True)
        return ExaAnswerResult(
            answer=raw.answer,
            citations=[
                ExaAnswerCitation(url=c.url, title=c.title or "", text=c.text or "") for c in (raw.citations or [])
            ],
        )

    return exa_answer

set_provider #

set_provider(provider)
Source code in autogen/beta/tools/final/toolkit.py
def set_provider(self, provider: Provider) -> None:
    for t in self.tools:
        t.set_provider(provider)

schemas async #

schemas(context)
Source code in autogen/beta/tools/final/toolkit.py
async def schemas(self, context: "Context") -> Iterable[ToolSchema]:
    schemas: list[ToolSchema] = []
    for t in self.tools:
        schemas.extend(await t.schemas(context))
    return schemas

register #

register(stack, context, *, middleware=())
Source code in autogen/beta/tools/final/toolkit.py
def register(
    self,
    stack: "ExitStack",
    context: "Context",
    *,
    middleware: Iterable["BaseMiddleware"] = (),
) -> None:
    for t in self.tools:
        t.register(stack, context, middleware=middleware)

tool #

tool(function: Callable[..., Any], *, name: str | None = None, description: str | None = None, schema: FunctionParameters | None = None, sync_to_thread: bool = True, middleware: Iterable[ToolMiddleware] = ()) -> FunctionTool
tool(function: None = None, *, name: str | None = None, description: str | None = None, schema: FunctionParameters | None = None, sync_to_thread: bool = True, middleware: Iterable[ToolMiddleware] = ()) -> Callable[[Callable[..., Any]], FunctionTool]
tool(function=None, *, name=None, description=None, schema=None, sync_to_thread=True, middleware=())
Source code in autogen/beta/tools/final/toolkit.py
def tool(
    self,
    function: Callable[..., Any] | None = None,
    *,
    name: str | None = None,
    description: str | None = None,
    schema: FunctionParameters | None = None,
    sync_to_thread: bool = True,
    middleware: Iterable[ToolMiddleware] = (),
) -> FunctionTool | Callable[[Callable[..., Any]], FunctionTool]:
    def make_tool(f: Callable[..., Any]) -> FunctionTool:
        t = tool(
            f,
            name=name,
            description=description,
            schema=schema,
            sync_to_thread=sync_to_thread,
            middleware=middleware,
        )
        self._add_tool(t)
        return t

    if function:
        return make_tool(function)

    return make_tool