From 07c4a30f2eb4ed6858a4463be5da46bbf83faba0 Mon Sep 17 00:00:00 2001 From: Ishan Goswami Date: Fri, 1 May 2026 18:55:23 +0530 Subject: [PATCH] feat(crewai-tools): add highlights to ExaSearchTool, rename from EXASearchTool * feat(crewai-tools): add highlights to ExaSearchTool, rename from EXASearchTool - Add a highlights init param so agents can get token-efficient excerpts instead of full pages - Rename EXASearchTool to ExaSearchTool; keep EXASearchTool as a deprecated alias so existing imports keep working - Update the docs and example to use highlights as the recommended option - Add a small note that says Exa is the fastest and most accurate web search API - Add tests for the new highlights param and the deprecation alias * fix(crewai-tools): import order and module-level Exa for tests - Reorder std-lib imports so ruff is happy with force-sort-within-sections. - Import Exa at module level (with a fallback) so the existing test mocks resolve. The lazy install prompt still works if exa_py is missing. - Allow content and summary to be a dict, matching highlights. - Trim test file to the cases this PR introduces (highlights param and the EXASearchTool deprecation alias). Existing init-shape tests stay. Co-Authored-By: ishan * chore(crewai-tools): drop self-explanatory comment on schema alias Co-Authored-By: ishan * docs(crewai-tools): default highlights to True, drop summary from examples Co-Authored-By: ishan * docs(crewai-tools): simplify highlights examples to highlights=True Co-Authored-By: ishan * feat(crewai-tools): add x-exa-integration header for usage tracking Co-Authored-By: ishan * docs(crewai-tools): add Exa MCP section and resources links Co-Authored-By: ishan --------- Co-authored-by: ishan Co-authored-by: Greyson LaLonde Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com> --- docs/ar/concepts/tools.mdx | 2 +- .../tools/search-research/exasearchtool.mdx | 55 +++++++++---- docs/en/concepts/tools.mdx | 2 +- .../tools/search-research/exasearchtool.mdx | 74 +++++++++++++---- docs/ko/concepts/tools.mdx | 2 +- .../tools/search-research/exasearchtool.mdx | 42 ++++++++-- docs/pt-BR/concepts/tools.mdx | 2 +- .../tools/search-research/exasearchtool.mdx | 44 +++++++++-- lib/crewai-tools/README.md | 2 +- lib/crewai-tools/src/crewai_tools/__init__.py | 3 +- .../src/crewai_tools/tools/__init__.py | 3 +- .../crewai_tools/tools/exa_tools/README.md | 24 +++--- .../tools/exa_tools/exa_search_tool.py | 79 ++++++++++++++----- .../tests/tools/exa_search_tool_test.py | 43 ++++++++-- lib/crewai/src/crewai/cli/templates/AGENTS.md | 2 +- 15 files changed, 286 insertions(+), 93 deletions(-) diff --git a/docs/ar/concepts/tools.mdx b/docs/ar/concepts/tools.mdx index 8b1e07aa1..2740f723d 100644 --- a/docs/ar/concepts/tools.mdx +++ b/docs/ar/concepts/tools.mdx @@ -133,7 +133,7 @@ crew.kickoff() | **DirectorySearchTool** | أداة RAG للبحث في المجلدات، مفيدة للتنقل في أنظمة الملفات. | | **DOCXSearchTool** | أداة RAG للبحث في مستندات DOCX، مثالية لمعالجة ملفات Word. | | **DirectoryReadTool** | تسهّل قراءة ومعالجة هياكل المجلدات ومحتوياتها. | -| **EXASearchTool** | أداة مصممة لإجراء عمليات بحث شاملة عبر مصادر بيانات متنوعة. | +| **ExaSearchTool** | أداة مصممة لإجراء عمليات بحث شاملة عبر مصادر بيانات متنوعة. | | **FileReadTool** | تُمكّن قراءة واستخراج البيانات من الملفات، مع دعم تنسيقات ملفات متنوعة. | | **FirecrawlSearchTool** | أداة للبحث في صفحات الويب باستخدام Firecrawl وإرجاع النتائج. | | **FirecrawlCrawlWebsiteTool** | أداة لزحف صفحات الويب باستخدام Firecrawl. | diff --git a/docs/ar/tools/search-research/exasearchtool.mdx b/docs/ar/tools/search-research/exasearchtool.mdx index dfa3d32fa..18f339e00 100644 --- a/docs/ar/tools/search-research/exasearchtool.mdx +++ b/docs/ar/tools/search-research/exasearchtool.mdx @@ -1,11 +1,11 @@ --- title: "أداة بحث Exa" -description: "ابحث في الويب باستخدام Exa Search API للعثور على النتائج الأكثر صلة لأي استعلام، مع خيارات لمحتوى الصفحة الكامل والمقتطفات والملخصات." +description: "ابحث في الويب باستخدام Exa Search API للعثور على النتائج الأكثر صلة لأي استعلام، مع خيارات لمحتوى الصفحة الكامل والمقتطفات." icon: "magnifying-glass" mode: "wide" --- -تتيح أداة `EXASearchTool` لوكلاء CrewAI البحث في الويب باستخدام [Exa](https://exa.ai/) search API. تُرجع النتائج الأكثر صلة لأي استعلام، مع خيارات لمحتوى الصفحة الكامل والملخصات المولّدة بالذكاء الاصطناعي. +تتيح أداة `ExaSearchTool` لوكلاء CrewAI البحث في الويب باستخدام [Exa](https://exa.ai/) search API. تُرجع النتائج الأكثر صلة لأي استعلام، مع خيارات لمحتوى الصفحة الكامل والمقتطفات الموفرة للرموز. ## التثبيت @@ -27,15 +27,15 @@ export EXA_API_KEY='your_exa_api_key' ## مثال على الاستخدام -إليك كيفية استخدام `EXASearchTool` مع وكيل CrewAI: +إليك كيفية استخدام `ExaSearchTool` مع وكيل CrewAI: ```python import os from crewai import Agent, Task, Crew -from crewai_tools import EXASearchTool +from crewai_tools import ExaSearchTool # Initialize the tool -exa_tool = EXASearchTool() +exa_tool = ExaSearchTool() # Create an agent that uses the tool researcher = Agent( @@ -66,11 +66,11 @@ print(result) ## خيارات التكوين -تقبل أداة `EXASearchTool` المعاملات التالية أثناء التهيئة: +تقبل أداة `ExaSearchTool` المعاملات التالية أثناء التهيئة: - `type` (str، اختياري): نوع البحث المستخدم. الافتراضي هو `"auto"`. الخيارات: `"auto"`، `"instant"`، `"fast"`، `"deep"`. +- `highlights` (bool أو dict، اختياري): إرجاع مقتطفات موفرة للرموز أكثر صلة بالاستعلام بدلاً من الصفحة الكاملة. الافتراضي هو `True`. مرر قاموسًا مثل `{"max_characters": 4000}` للتكوين، أو `False` للتعطيل. - `content` (bool، اختياري): ما إذا كان يجب تضمين محتوى الصفحة الكامل في النتائج. الافتراضي هو `False`. -- `summary` (bool، اختياري): ما إذا كان يجب تضمين ملخصات مولّدة بالذكاء الاصطناعي لكل نتيجة. يتطلب `content=True`. الافتراضي هو `False`. - `api_key` (str، اختياري): مفتاح Exa API الخاص بك. يعود إلى متغير البيئة `EXA_API_KEY` إذا لم يتم تقديمه. - `base_url` (str، اختياري): عنوان URL مخصص لخادم API. يعود إلى متغير البيئة `EXA_BASE_URL` إذا لم يتم تقديمه. @@ -86,25 +86,52 @@ print(result) يمكنك تكوين الأداة بمعاملات مخصصة للحصول على نتائج أغنى: ```python -# Get full page content with AI summaries -exa_tool = EXASearchTool( - content=True, - summary=True, +# Use 'deep' for thorough, multi-step searches +exa_tool = ExaSearchTool( + highlights=True, type="deep" ) # Use it in an agent agent = Agent( role="Deep Researcher", - goal="Conduct thorough research with full content and summaries", + goal="Conduct thorough research", tools=[exa_tool] ) ``` +## استخدام Exa عبر MCP + +يمكنك أيضًا ربط وكيلك بخادم MCP المستضاف من Exa. مرّر مفتاح API الخاص بك عبر ترويسة `x-api-key`: + +```python +from crewai import Agent +from crewai.mcp import MCPServerHTTP + +agent = Agent( + role="Research Analyst", + goal="Find and analyze information on the web", + backstory="Expert researcher with access to Exa's tools", + mcps=[ + MCPServerHTTP( + url="https://mcp.exa.ai/mcp", + headers={"x-api-key": "YOUR_EXA_API_KEY"}, + ), + ], +) +``` + +احصل على مفتاح API من [لوحة تحكم Exa](https://dashboard.exa.ai/api-keys). لمزيد من المعلومات حول MCP في CrewAI، راجع [نظرة عامة على MCP](/ar/mcp/overview). + ## الميزات +- **مقتطفات موفرة للرموز**: الحصول على المقتطفات الأكثر صلة من كل نتيجة، باستخدام رموز أقل بكثير من النص الكامل - **البحث الدلالي**: العثور على نتائج بناءً على المعنى، وليس الكلمات المفتاحية فقط - **استرجاع المحتوى الكامل**: الحصول على النص الكامل لصفحات الويب مع نتائج البحث -- **ملخصات الذكاء الاصطناعي**: الحصول على ملخصات موجزة مولّدة بالذكاء الاصطناعي لكل نتيجة - **تصفية التاريخ**: تقييد النتائج لفترات زمنية محددة باستخدام فلاتر تاريخ النشر -- **تصفية النطاقات**: تقييد عمليات البحث على نطاقات محددة \ No newline at end of file +- **تصفية النطاقات**: تقييد عمليات البحث على نطاقات محددة + +## موارد + +- [توثيق Exa](https://exa.ai/docs) +- [لوحة تحكم Exa — إدارة مفاتيح API والاستخدام](https://dashboard.exa.ai) \ No newline at end of file diff --git a/docs/en/concepts/tools.mdx b/docs/en/concepts/tools.mdx index f634c9f95..52e568073 100644 --- a/docs/en/concepts/tools.mdx +++ b/docs/en/concepts/tools.mdx @@ -133,7 +133,7 @@ Here is a list of the available tools and their descriptions: | **DirectorySearchTool** | A RAG tool for searching within directories, useful for navigating through file systems. | | **DOCXSearchTool** | A RAG tool aimed at searching within DOCX documents, ideal for processing Word files. | | **DirectoryReadTool** | Facilitates reading and processing of directory structures and their contents. | -| **EXASearchTool** | A tool designed for performing exhaustive searches across various data sources. | +| **ExaSearchTool** | Search the web with Exa, the fastest and most accurate web search API. Supports token-efficient highlights and full page content. | | **FileReadTool** | Enables reading and extracting data from files, supporting various file formats. | | **FirecrawlSearchTool** | A tool to search webpages using Firecrawl and return the results. | | **FirecrawlCrawlWebsiteTool** | A tool for crawling webpages using Firecrawl. | diff --git a/docs/en/tools/search-research/exasearchtool.mdx b/docs/en/tools/search-research/exasearchtool.mdx index 3136cdcbb..7361c58b0 100644 --- a/docs/en/tools/search-research/exasearchtool.mdx +++ b/docs/en/tools/search-research/exasearchtool.mdx @@ -1,11 +1,11 @@ --- title: "Exa Search Tool" -description: "Search the web using the Exa Search API to find the most relevant results for any query, with options for full page content, highlights, and summaries." +description: "Search the web with Exa, the fastest and most accurate web search API. Get token-efficient highlights and full page content." icon: "magnifying-glass" mode: "wide" --- -The `EXASearchTool` lets CrewAI agents search the web using the [Exa](https://exa.ai/) search API. It returns the most relevant results for any query, with options for full page content and AI-generated summaries. +The `ExaSearchTool` lets CrewAI agents search the web using [Exa](https://exa.ai/), the fastest and most accurate web search API. It returns the most relevant results for any query, with options for token-efficient highlights and full page content. ## Installation @@ -27,15 +27,15 @@ Get an API key from the [Exa dashboard](https://dashboard.exa.ai/api-keys). ## Example Usage -Here's how to use the `EXASearchTool` within a CrewAI agent: +Here's how to use the `ExaSearchTool` within a CrewAI agent: ```python import os from crewai import Agent, Task, Crew -from crewai_tools import EXASearchTool +from crewai_tools import ExaSearchTool # Initialize the tool -exa_tool = EXASearchTool() +exa_tool = ExaSearchTool() # Create an agent that uses the tool researcher = Agent( @@ -66,11 +66,11 @@ print(result) ## Configuration Options -The `EXASearchTool` accepts the following parameters during initialization: +The `ExaSearchTool` accepts the following parameters during initialization: - `type` (str, optional): The search type to use. Defaults to `"auto"`. Options: `"auto"`, `"instant"`, `"fast"`, `"deep"`. +- `highlights` (bool or dict, optional): Return token-efficient excerpts most relevant to the query instead of the full page. Defaults to `True`. Pass a dict like `{"max_characters": 4000}` to configure, or `False` to disable. - `content` (bool, optional): Whether to include full page content in results. Defaults to `False`. -- `summary` (bool, optional): Whether to include AI-generated summaries of each result. Requires `content=True`. Defaults to `False`. - `api_key` (str, optional): Your Exa API key. Falls back to the `EXA_API_KEY` environment variable if not provided. - `base_url` (str, optional): Custom API server URL. Falls back to the `EXA_BASE_URL` environment variable if not provided. @@ -83,28 +83,70 @@ When calling the tool (or when an agent invokes it), the following search parame ## Advanced Usage -You can configure the tool with custom parameters for richer results: +For most agent workflows we recommend `highlights` — it returns the most relevant excerpts from each result and uses far fewer tokens than full page content: ```python -# Get full page content with AI summaries -exa_tool = EXASearchTool( - content=True, - summary=True, - type="deep" +# Get token-efficient excerpts most relevant to the query +exa_tool = ExaSearchTool( + highlights=True, + type="auto", ) # Use it in an agent agent = Agent( - role="Deep Researcher", - goal="Conduct thorough research with full content and summaries", + role="Researcher", + goal="Answer questions with current web data", tools=[exa_tool] ) ``` +For thorough, multi-step searches, use `type="deep"`: + +```python +exa_tool = ExaSearchTool( + highlights=True, + type="deep", +) +``` + +For more on choosing between highlights and full content, see the [Exa search best practices](https://exa.ai/docs/reference/search-best-practices). + +## Using Exa via MCP + +You can also connect your agent to Exa's hosted MCP server. Pass your API key with the `x-api-key` header: + +```python +from crewai import Agent +from crewai.mcp import MCPServerHTTP + +agent = Agent( + role="Research Analyst", + goal="Find and analyze information on the web", + backstory="Expert researcher with access to Exa's tools", + mcps=[ + MCPServerHTTP( + url="https://mcp.exa.ai/mcp", + headers={"x-api-key": "YOUR_EXA_API_KEY"}, + ), + ], +) +``` + +Get your API key from the [Exa dashboard](https://dashboard.exa.ai/api-keys). For more on MCP in CrewAI, see the [MCP overview](/en/mcp/overview). + ## Features +- **Token-Efficient Highlights**: Get the most relevant excerpts from each result, ~10x fewer tokens than full text - **Semantic Search**: Find results based on meaning, not just keywords - **Full Content Retrieval**: Get the full text of web pages alongside search results -- **AI Summaries**: Get concise, AI-generated summaries of each result - **Date Filtering**: Limit results to specific time periods with published date filters - **Domain Filtering**: Restrict searches to specific domains + + +`EXASearchTool` is a deprecated alias for `ExaSearchTool`. Existing imports continue to work but will emit a deprecation warning; please migrate to `ExaSearchTool`. + + +## Resources + +- [Exa documentation](https://exa.ai/docs) +- [Exa dashboard — manage API keys and usage](https://dashboard.exa.ai) diff --git a/docs/ko/concepts/tools.mdx b/docs/ko/concepts/tools.mdx index de346e069..cd0e96173 100644 --- a/docs/ko/concepts/tools.mdx +++ b/docs/ko/concepts/tools.mdx @@ -132,7 +132,7 @@ crew.kickoff() | **DirectorySearchTool** | 디렉터리 내에서 검색하는 RAG 도구로, 파일 시스템을 탐색할 때 유용합니다. | | **DOCXSearchTool** | DOCX 문서 내에서 검색하는 데 특화된 RAG 도구로, Word 파일을 처리할 때 이상적입니다. | | **DirectoryReadTool** | 디렉터리 구조와 그 내용을 읽고 처리하도록 지원하는 도구입니다. | -| **EXASearchTool** | 다양한 데이터 소스를 폭넓게 검색하기 위해 설계된 도구입니다. | +| **ExaSearchTool** | 다양한 데이터 소스를 폭넓게 검색하기 위해 설계된 도구입니다. | | **FileReadTool** | 다양한 파일 형식을 지원하며 파일에서 데이터를 읽고 추출할 수 있는 도구입니다. | | **FirecrawlSearchTool** | Firecrawl을 이용해 웹페이지를 검색하고 결과를 반환하는 도구입니다. | | **FirecrawlCrawlWebsiteTool** | Firecrawl을 사용해 웹페이지를 크롤링하는 도구입니다. | diff --git a/docs/ko/tools/search-research/exasearchtool.mdx b/docs/ko/tools/search-research/exasearchtool.mdx index ad3aa70cf..f49414599 100644 --- a/docs/ko/tools/search-research/exasearchtool.mdx +++ b/docs/ko/tools/search-research/exasearchtool.mdx @@ -1,15 +1,15 @@ --- title: EXA 검색 웹 로더 -description: EXASearchTool은 인터넷 전반에 걸쳐 텍스트의 내용에서 지정된 쿼리에 대한 시맨틱 검색을 수행하도록 설계되었습니다. +description: ExaSearchTool은 인터넷 전반에 걸쳐 텍스트의 내용에서 지정된 쿼리에 대한 시맨틱 검색을 수행하도록 설계되었습니다. icon: globe-pointer mode: "wide" --- -# `EXASearchTool` +# `ExaSearchTool` ## 설명 -EXASearchTool은 텍스트의 내용을 기반으로 지정된 쿼리를 인터넷 전반에 걸쳐 의미론적으로 검색하도록 설계되었습니다. +ExaSearchTool은 텍스트의 내용을 기반으로 지정된 쿼리를 인터넷 전반에 걸쳐 의미론적으로 검색하도록 설계되었습니다. 사용자가 제공한 쿼리를 기반으로 가장 관련성 높은 검색 결과를 가져오고 표시하기 위해 [exa.ai](https://exa.ai/) API를 활용합니다. ## 설치 @@ -25,15 +25,15 @@ pip install 'crewai[tools]' 다음 예제는 도구를 초기화하고 주어진 쿼리로 검색을 실행하는 방법을 보여줍니다: ```python Code -from crewai_tools import EXASearchTool +from crewai_tools import ExaSearchTool # Initialize the tool for internet searching capabilities -tool = EXASearchTool() +tool = ExaSearchTool() ``` ## 시작 단계 -EXASearchTool을 효과적으로 사용하려면 다음 단계를 따르세요: +ExaSearchTool을 효과적으로 사용하려면 다음 단계를 따르세요: @@ -47,7 +47,35 @@ EXASearchTool을 효과적으로 사용하려면 다음 단계를 따르세요: +## MCP를 통한 Exa 사용 + +Exa가 호스팅하는 MCP 서버에 에이전트를 연결할 수도 있습니다. API 키는 `x-api-key` 헤더로 전달하세요: + +```python +from crewai import Agent +from crewai.mcp import MCPServerHTTP + +agent = Agent( + role="Research Analyst", + goal="Find and analyze information on the web", + backstory="Expert researcher with access to Exa's tools", + mcps=[ + MCPServerHTTP( + url="https://mcp.exa.ai/mcp", + headers={"x-api-key": "YOUR_EXA_API_KEY"}, + ), + ], +) +``` + +API 키는 [Exa 대시보드](https://dashboard.exa.ai/api-keys)에서 발급받을 수 있습니다. CrewAI에서의 MCP 사용에 대한 자세한 내용은 [MCP 개요](/ko/mcp/overview)를 참고하세요. + ## 결론 -`EXASearchTool`을 Python 프로젝트에 통합함으로써, 사용자는 애플리케이션 내에서 실시간으로 인터넷을 직접 검색할 수 있는 능력을 얻게 됩니다. +`ExaSearchTool`을 Python 프로젝트에 통합함으로써, 사용자는 애플리케이션 내에서 실시간으로 인터넷을 직접 검색할 수 있는 능력을 얻게 됩니다. 제공된 설정 및 사용 지침을 따르면, 이 도구를 프로젝트에 포함하는 과정이 간편하고 직관적입니다. + +## 참고 자료 + +- [Exa 공식 문서](https://exa.ai/docs) +- [Exa 대시보드 — API 키 및 사용량 관리](https://dashboard.exa.ai) diff --git a/docs/pt-BR/concepts/tools.mdx b/docs/pt-BR/concepts/tools.mdx index 88479e017..da7e6f24a 100644 --- a/docs/pt-BR/concepts/tools.mdx +++ b/docs/pt-BR/concepts/tools.mdx @@ -133,7 +133,7 @@ Aqui está uma lista das ferramentas disponíveis e suas descrições: | **DirectorySearchTool** | Ferramenta RAG para busca em diretórios, útil para navegação em sistemas de arquivos. | | **DOCXSearchTool** | Ferramenta RAG voltada para busca em documentos DOCX, ideal para processar arquivos Word. | | **DirectoryReadTool** | Facilita a leitura e processamento de estruturas de diretórios e seus conteúdos. | -| **EXASearchTool** | Ferramenta projetada para buscas exaustivas em diversas fontes de dados. | +| **ExaSearchTool** | Ferramenta projetada para buscas exaustivas em diversas fontes de dados. | | **FileReadTool** | Permite a leitura e extração de dados de arquivos, suportando diversos formatos. | | **FirecrawlSearchTool** | Ferramenta para buscar páginas web usando Firecrawl e retornar os resultados. | | **FirecrawlCrawlWebsiteTool** | Ferramenta para rastrear páginas web utilizando o Firecrawl. | diff --git a/docs/pt-BR/tools/search-research/exasearchtool.mdx b/docs/pt-BR/tools/search-research/exasearchtool.mdx index 25499dd52..7dbeab4aa 100644 --- a/docs/pt-BR/tools/search-research/exasearchtool.mdx +++ b/docs/pt-BR/tools/search-research/exasearchtool.mdx @@ -1,15 +1,15 @@ --- title: Carregador Web EXA Search -description: O `EXASearchTool` foi projetado para realizar uma busca semântica para uma consulta especificada a partir do conteúdo de um texto em toda a internet. +description: O `ExaSearchTool` foi projetado para realizar uma busca semântica para uma consulta especificada a partir do conteúdo de um texto em toda a internet. icon: globe-pointer mode: "wide" --- -# `EXASearchTool` +# `ExaSearchTool` ## Descrição -O EXASearchTool foi projetado para realizar uma busca semântica para uma consulta especificada a partir do conteúdo de um texto em toda a internet. +O ExaSearchTool foi projetado para realizar uma busca semântica para uma consulta especificada a partir do conteúdo de um texto em toda a internet. Ele utiliza a API da [exa.ai](https://exa.ai/) para buscar e exibir os resultados de pesquisa mais relevantes com base na consulta fornecida pelo usuário. ## Instalação @@ -25,15 +25,15 @@ pip install 'crewai[tools]' O exemplo a seguir demonstra como inicializar a ferramenta e executar uma busca com uma consulta determinada: ```python Code -from crewai_tools import EXASearchTool +from crewai_tools import ExaSearchTool # Initialize the tool for internet searching capabilities -tool = EXASearchTool() +tool = ExaSearchTool() ``` ## Etapas para Começar -Para usar o EXASearchTool de forma eficaz, siga estas etapas: +Para usar o ExaSearchTool de forma eficaz, siga estas etapas: @@ -47,7 +47,35 @@ Para usar o EXASearchTool de forma eficaz, siga estas etapas: +## Usando o Exa via MCP + +Você também pode conectar seu agente ao servidor MCP hospedado pelo Exa. Passe sua chave de API no cabeçalho `x-api-key`: + +```python +from crewai import Agent +from crewai.mcp import MCPServerHTTP + +agent = Agent( + role="Research Analyst", + goal="Find and analyze information on the web", + backstory="Expert researcher with access to Exa's tools", + mcps=[ + MCPServerHTTP( + url="https://mcp.exa.ai/mcp", + headers={"x-api-key": "YOUR_EXA_API_KEY"}, + ), + ], +) +``` + +Obtenha sua chave de API no [painel da Exa](https://dashboard.exa.ai/api-keys). Para mais informações sobre MCP no CrewAI, consulte a [visão geral do MCP](/pt-BR/mcp/overview). + ## Conclusão -Ao integrar o `EXASearchTool` em projetos Python, os usuários ganham a capacidade de realizar buscas relevantes e em tempo real pela internet diretamente de suas aplicações. -Seguindo as orientações de configuração e uso fornecidas, a incorporação desta ferramenta em projetos torna-se simples e direta. \ No newline at end of file +Ao integrar o `ExaSearchTool` em projetos Python, os usuários ganham a capacidade de realizar buscas relevantes e em tempo real pela internet diretamente de suas aplicações. +Seguindo as orientações de configuração e uso fornecidas, a incorporação desta ferramenta em projetos torna-se simples e direta. + +## Recursos + +- [Documentação do Exa](https://exa.ai/docs) +- [Painel do Exa — gerenciar chaves de API e uso](https://dashboard.exa.ai) \ No newline at end of file diff --git a/lib/crewai-tools/README.md b/lib/crewai-tools/README.md index 693e1a175..e0d1ddccb 100644 --- a/lib/crewai-tools/README.md +++ b/lib/crewai-tools/README.md @@ -26,7 +26,7 @@ CrewAI provides an extensive collection of powerful tools ready to enhance your - **Web Scraping**: `ScrapeWebsiteTool`, `SeleniumScrapingTool` - **Database Integrations**: `MySQLSearchTool` - **Vector Database Integrations**: `MongoDBVectorSearchTool`, `QdrantVectorSearchTool`, `WeaviateVectorSearchTool` -- **API Integrations**: `SerperApiTool`, `EXASearchTool` +- **API Integrations**: `SerperApiTool`, `ExaSearchTool` - **AI-powered Tools**: `DallETool`, `VisionTool`, `StagehandTool` And many more robust tools to simplify your agent integrations. diff --git a/lib/crewai-tools/src/crewai_tools/__init__.py b/lib/crewai-tools/src/crewai_tools/__init__.py index 225b2938a..33dbc3358 100644 --- a/lib/crewai-tools/src/crewai_tools/__init__.py +++ b/lib/crewai-tools/src/crewai_tools/__init__.py @@ -76,7 +76,7 @@ from crewai_tools.tools.e2b_sandbox_tool import ( E2BFileTool, E2BPythonTool, ) -from crewai_tools.tools.exa_tools.exa_search_tool import EXASearchTool +from crewai_tools.tools.exa_tools.exa_search_tool import EXASearchTool, ExaSearchTool from crewai_tools.tools.file_read_tool.file_read_tool import FileReadTool from crewai_tools.tools.file_writer_tool.file_writer_tool import FileWriterTool from crewai_tools.tools.files_compressor_tool.files_compressor_tool import ( @@ -258,6 +258,7 @@ __all__ = [ "E2BPythonTool", "EXASearchTool", "EnterpriseActionTool", + "ExaSearchTool", "FileCompressorTool", "FileReadTool", "FileWriterTool", diff --git a/lib/crewai-tools/src/crewai_tools/tools/__init__.py b/lib/crewai-tools/src/crewai_tools/tools/__init__.py index 11309c87e..18bf4e563 100644 --- a/lib/crewai-tools/src/crewai_tools/tools/__init__.py +++ b/lib/crewai-tools/src/crewai_tools/tools/__init__.py @@ -65,7 +65,7 @@ from crewai_tools.tools.e2b_sandbox_tool import ( E2BFileTool, E2BPythonTool, ) -from crewai_tools.tools.exa_tools.exa_search_tool import EXASearchTool +from crewai_tools.tools.exa_tools.exa_search_tool import EXASearchTool, ExaSearchTool from crewai_tools.tools.file_read_tool.file_read_tool import FileReadTool from crewai_tools.tools.file_writer_tool.file_writer_tool import FileWriterTool from crewai_tools.tools.files_compressor_tool.files_compressor_tool import ( @@ -242,6 +242,7 @@ __all__ = [ "E2BFileTool", "E2BPythonTool", "EXASearchTool", + "ExaSearchTool", "FileCompressorTool", "FileReadTool", "FileWriterTool", diff --git a/lib/crewai-tools/src/crewai_tools/tools/exa_tools/README.md b/lib/crewai-tools/src/crewai_tools/tools/exa_tools/README.md index 1d1d20150..3772de835 100644 --- a/lib/crewai-tools/src/crewai_tools/tools/exa_tools/README.md +++ b/lib/crewai-tools/src/crewai_tools/tools/exa_tools/README.md @@ -1,7 +1,7 @@ -# EXASearchTool Documentation +# ExaSearchTool Documentation ## Description -This tool is designed to perform a semantic search for a specified query from a text's content across the internet. It utilizes the `https://exa.ai/` API to fetch and display the most relevant search results based on the query provided by the user. +This tool lets CrewAI agents search the web using [Exa](https://exa.ai/), the fastest and most accurate web search API. By default the tool returns token-efficient highlights of the most relevant results for any query; you can also opt in to full page content. ## Installation To incorporate this tool into your project, follow the installation instructions below: @@ -10,21 +10,23 @@ uv add crewai[tools] exa_py ``` ## Example -The following example demonstrates how to initialize the tool and execute a search with a given query: +The following example demonstrates how to initialize the tool and run a search: ```python -from crewai_tools import EXASearchTool +from crewai_tools import ExaSearchTool -# Initialize the tool for internet searching capabilities -tool = EXASearchTool(api_key="your_api_key") +# Default: results with token-efficient highlights +tool = ExaSearchTool(api_key="your_api_key", highlights=True) ``` ## Steps to Get Started -To effectively use the `EXASearchTool`, follow these steps: +To effectively use the `ExaSearchTool`, follow these steps: 1. **Package Installation**: Confirm that the `crewai[tools]` package is installed in your Python environment. -2. **API Key Acquisition**: Acquire a `https://exa.ai/` API key by registering for a free account at `https://exa.ai/`. -3. **Environment Configuration**: Store your obtained API key in an environment variable named `EXA_API_KEY` to facilitate its use by the tool. +2. **API Key Acquisition**: Get an Exa API key from the [Exa dashboard](https://dashboard.exa.ai/api-keys). +3. **Environment Configuration**: Store your API key in an environment variable named `EXA_API_KEY` so the tool can pick it up automatically. -## Conclusion -By integrating the `EXASearchTool` into Python projects, users gain the ability to conduct real-time, relevant searches across the internet directly from their applications. By adhering to the setup and usage guidelines provided, incorporating this tool into projects is streamlined and straightforward. +For details on choosing between highlights and full content, see the [Exa search best practices](https://exa.ai/docs/reference/search-best-practices). + +## Note +`EXASearchTool` is a deprecated alias for `ExaSearchTool`. Existing imports continue to work but emit a deprecation warning; please migrate to `ExaSearchTool`. diff --git a/lib/crewai-tools/src/crewai_tools/tools/exa_tools/exa_search_tool.py b/lib/crewai-tools/src/crewai_tools/tools/exa_tools/exa_search_tool.py index 5a4ef36dd..8204b67bb 100644 --- a/lib/crewai-tools/src/crewai_tools/tools/exa_tools/exa_search_tool.py +++ b/lib/crewai-tools/src/crewai_tools/tools/exa_tools/exa_search_tool.py @@ -3,12 +3,19 @@ from __future__ import annotations from builtins import type as type_ import os from typing import Any, TypedDict +import warnings from crewai.tools import BaseTool, EnvVar from pydantic import BaseModel, ConfigDict, Field from typing_extensions import Required +try: + from exa_py import Exa +except ImportError: + Exa = None # type: ignore[assignment,misc] + + class SearchParams(TypedDict, total=False): """Parameters for Exa search API.""" @@ -18,7 +25,7 @@ class SearchParams(TypedDict, total=False): include_domains: list[str] -class EXABaseToolSchema(BaseModel): +class ExaBaseToolSchema(BaseModel): search_query: str = Field( ..., description="Mandatory search query you want to use to search the internet" ) @@ -31,14 +38,20 @@ class EXABaseToolSchema(BaseModel): ) -class EXASearchTool(BaseTool): +EXABaseToolSchema = ExaBaseToolSchema + + +class ExaSearchTool(BaseTool): model_config = ConfigDict(arbitrary_types_allowed=True) - name: str = "EXASearchTool" - description: str = "Search the internet using Exa" - args_schema: type_[BaseModel] = EXABaseToolSchema + name: str = "ExaSearchTool" + description: str = ( + "Search the web with Exa, the fastest and most accurate web search API." + ) + args_schema: type_[BaseModel] = ExaBaseToolSchema client: Any | None = None - content: bool | None = False - summary: bool | None = False + content: bool | dict[str, Any] | None = False + summary: bool | dict[str, Any] | None = False + highlights: bool | dict[str, Any] | None = True type: str | None = "auto" package_dependencies: list[str] = Field(default_factory=lambda: ["exa_py"]) api_key: str | None = Field( @@ -68,17 +81,17 @@ class EXASearchTool(BaseTool): def __init__( self, - content: bool | None = False, - summary: bool | None = False, + content: bool | dict[str, Any] | None = False, + summary: bool | dict[str, Any] | None = False, + highlights: bool | dict[str, Any] | None = True, type: str | None = "auto", **kwargs: Any, ) -> None: super().__init__( **kwargs, ) - try: - from exa_py import Exa - except ImportError as e: + global Exa + if Exa is None: import click if click.confirm( @@ -88,12 +101,13 @@ class EXASearchTool(BaseTool): subprocess.run(["uv", "add", "exa_py"], check=True) # noqa: S607 - # Re-import after installation - from exa_py import Exa + from exa_py import Exa as _Exa + + Exa = _Exa # type: ignore[misc] else: raise ImportError( - "You are missing the 'exa_py' package. Would you like to install it?" - ) from e + "You are missing the 'exa_py' package. Please install it to use ExaSearchTool." + ) client_kwargs: dict[str, str] = {} if self.api_key: @@ -101,8 +115,10 @@ class EXASearchTool(BaseTool): if self.base_url: client_kwargs["base_url"] = self.base_url self.client = Exa(**client_kwargs) + self.client.headers["x-exa-integration"] = "crewai" self.content = content self.summary = summary + self.highlights = highlights self.type = type def _run( @@ -126,10 +142,31 @@ class EXASearchTool(BaseTool): if include_domains: search_params["include_domains"] = include_domains + contents_kwargs: dict[str, Any] = {} if self.content: - results = self.client.search_and_contents( - search_query, summary=self.summary, **search_params + contents_kwargs["text"] = self.content + if self.highlights: + contents_kwargs["highlights"] = self.highlights + if self.summary: + contents_kwargs["summary"] = self.summary + + if contents_kwargs: + return self.client.search_and_contents( + search_query, **contents_kwargs, **search_params ) - else: - results = self.client.search(search_query, **search_params) - return results + return self.client.search(search_query, **search_params) + + +class EXASearchTool(ExaSearchTool): + """Deprecated alias for :class:`ExaSearchTool`. Kept for backwards compatibility.""" + + name: str = "ExaSearchTool" + + def __init__(self, *args: Any, **kwargs: Any) -> None: + warnings.warn( + "EXASearchTool is deprecated and will be removed in a future release; " + "use ExaSearchTool instead.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) diff --git a/lib/crewai-tools/tests/tools/exa_search_tool_test.py b/lib/crewai-tools/tests/tools/exa_search_tool_test.py index 0a4060503..3e34480a2 100644 --- a/lib/crewai-tools/tests/tools/exa_search_tool_test.py +++ b/lib/crewai-tools/tests/tools/exa_search_tool_test.py @@ -1,13 +1,13 @@ import os -from unittest.mock import patch +from unittest.mock import MagicMock, patch -from crewai_tools import EXASearchTool +from crewai_tools import EXASearchTool, ExaSearchTool import pytest @pytest.fixture def exa_search_tool(): - return EXASearchTool(api_key="test_api_key") + return ExaSearchTool(api_key="test_api_key") @pytest.fixture(autouse=True) @@ -22,11 +22,12 @@ def test_exa_search_tool_initialization(): "crewai_tools.tools.exa_tools.exa_search_tool.Exa" ) as mock_exa_class: api_key = "test_api_key" - tool = EXASearchTool(api_key=api_key) + tool = ExaSearchTool(api_key=api_key) assert tool.api_key == api_key assert tool.content is False assert tool.summary is False + assert tool.highlights is True assert tool.type == "auto" mock_exa_class.assert_called_once_with(api_key=api_key) @@ -36,7 +37,7 @@ def test_exa_search_tool_initialization_with_env(mock_exa_api_key): with patch( "crewai_tools.tools.exa_tools.exa_search_tool.Exa" ) as mock_exa_class: - EXASearchTool() + ExaSearchTool() mock_exa_class.assert_called_once_with(api_key="test_key_from_env") @@ -47,12 +48,13 @@ def test_exa_search_tool_initialization_with_base_url(): ) as mock_exa_class: api_key = "test_api_key" base_url = "https://custom.exa.api.com" - tool = EXASearchTool(api_key=api_key, base_url=base_url) + tool = ExaSearchTool(api_key=api_key, base_url=base_url) assert tool.api_key == api_key assert tool.base_url == base_url assert tool.content is False assert tool.summary is False + assert tool.highlights is True assert tool.type == "auto" mock_exa_class.assert_called_once_with(api_key=api_key, base_url=base_url) @@ -67,7 +69,7 @@ def test_exa_search_tool_initialization_with_env_base_url( mock_exa_api_key, mock_exa_base_url ): with patch("crewai_tools.tools.exa_tools.exa_search_tool.Exa") as mock_exa_class: - EXASearchTool() + ExaSearchTool() mock_exa_class.assert_called_once_with( api_key="test_key_from_env", base_url="https://env.exa.api.com" ) @@ -79,8 +81,33 @@ def test_exa_search_tool_initialization_without_base_url(): "crewai_tools.tools.exa_tools.exa_search_tool.Exa" ) as mock_exa_class: api_key = "test_api_key" - tool = EXASearchTool(api_key=api_key) + tool = ExaSearchTool(api_key=api_key) assert tool.api_key == api_key assert tool.base_url is None mock_exa_class.assert_called_once_with(api_key=api_key) + + +def test_exa_search_tool_highlights_uses_search_and_contents(): + with patch("crewai_tools.tools.exa_tools.exa_search_tool.Exa") as mock_exa_class: + mock_client = MagicMock() + mock_exa_class.return_value = mock_client + tool = ExaSearchTool( + api_key="test_api_key", highlights={"max_characters": 4000} + ) + + tool._run(search_query="hello world") + + mock_client.search_and_contents.assert_called_once_with( + "hello world", + highlights={"max_characters": 4000}, + type="auto", + ) + mock_client.search.assert_not_called() + + +def test_exasearchtool_alias_is_deprecated(): + with patch("crewai_tools.tools.exa_tools.exa_search_tool.Exa"): + with pytest.warns(DeprecationWarning, match="ExaSearchTool"): + tool = EXASearchTool(api_key="test_api_key") + assert isinstance(tool, ExaSearchTool) diff --git a/lib/crewai/src/crewai/cli/templates/AGENTS.md b/lib/crewai/src/crewai/cli/templates/AGENTS.md index ee822a2e8..bb54b7cb3 100644 --- a/lib/crewai/src/crewai/cli/templates/AGENTS.md +++ b/lib/crewai/src/crewai/cli/templates/AGENTS.md @@ -774,7 +774,7 @@ def calculator(expression: str) -> str: ``` ### Built-in Tools (install with `uv add crewai-tools`) -Web/Search: SerperDevTool, ScrapeWebsiteTool, WebsiteSearchTool, EXASearchTool, FirecrawlSearchTool +Web/Search: SerperDevTool, ScrapeWebsiteTool, WebsiteSearchTool, ExaSearchTool, FirecrawlSearchTool Documents: FileReadTool, DirectoryReadTool, PDFSearchTool, DOCXSearchTool, CSVSearchTool, JSONSearchTool, XMLSearchTool, MDXSearchTool Code: CodeInterpreterTool, CodeDocsSearchTool, GithubSearchTool Media: DALL-E Tool, YoutubeChannelSearchTool, YoutubeVideoSearchTool