首页
/ CrewAI BraveSearchTool 实战解析:从安装配置到源码级请求流程

CrewAI BraveSearchTool 实战解析:从安装配置到源码级请求流程

2026-09-05 17:42:48作者:胡易黎Nicole

本篇围绕 CrewAI 工具包中的 BraveSearchTool 展开,说明如何通过 Brave Web Search API 为 Agent 接入实时网页搜索能力:包括安装 crewai[tools]、配置 BRAVE_API_KEY、调用参数与结果解析的完整流程。读完本文,你不仅能直接复制运行该工具,还能从源码层面理解它的参数映射、限流、结果精化与错误处理机制,并了解如何平滑迁移到新版 BraveWebSearchTool 等细分工具。

工具定位:Brave Web Search API 的 Agent 封装

BraveSearchTool 是 CrewAI 官方工具包(crewai-tools)内置的网页搜索工具,其设计目标是:针对给定查询词,通过 Brave Web Search API 这个 REST 接口查询互联网并返回 JSON 格式的搜索结果,供 Agent 在推理过程中直接消费。其官方说明位于 brave_search_tool 模块 README

同一模块目录下还组织了完整的 Brave 工具家族(Web / News / Image / Video / Local POIs / LLM Context),官方文档 Brave Search Tools 文档 明确标注:BraveSearchTool 属于遗留(legacy)类,仅为向后兼容保留;新代码推荐直接按端点选择 BraveWebSearchToolBraveNewsSearchTool 等更细粒度的工具。本文以 README 所描述的 BraveSearchTool 为主体,同时在结尾给出迁移路径。

安装与准备工作

按 README 给出的三步完成接入:

  1. 安装依赖包:确认 Python 环境中安装了带 tools 附加依赖的 crewai:
pip install 'crewai[tools]'
  1. 获取 API Key:到 Brave Search API 官方站点申请一个订阅密钥。

  2. 配置环境变量:把密钥存入名为 BRAVE_API_KEY 的环境变量,工具在初始化时会读取它:

export BRAVE_API_KEY="your-key-here"

这一点在源码中有直接印证:BraveSearchTool 构造函数__init__ 中检查环境变量,缺失时立即抛出异常,属于"快速失败"设计:

def __init__(self, *args: Any, **kwargs: Any) -> None:
    super().__init__(*args, **kwargs)
    if "BRAVE_API_KEY" not in os.environ:
        raise ValueError(
            "BRAVE_API_KEY environment variable is required for BraveSearchTool"
        )

最小可运行示例

README 给出的初始化示例:

from crewai_tools import BraveSearchTool

# Initialize the tool for internet searching capabilities
tool = BraveSearchTool()

在此基础上执行一次搜索(run 参数名支持 q / query / search_query 三种写法,源码中有兼容映射,见下文解析),典型调用如下:

tool = BraveSearchTool()
results = tool.run(query="CrewAI agent framework")
print(results)

返回值为一个 JSON 字符串,内容是经过精简的结构化结果列表,例如:

[
  {
    "url": "https://example.com/crewai",
    "title": "CrewAI — ...",
    "description": "..."
  }
]

源码解析:一次搜索的完整处理链路

以下分析基于 brave_search_tool.py_run 方法(L60-L172)。

1. 内置限流:至少 1 秒间隔

工具在每次执行前做了一次简单的速率检查(L64-L69):若距上次请求不足 1 秒(_min_request_interval = 1.0,ClassVar),就 sleep 补齐间隔,避免触发 Brave 侧的按秒滑动窗口限流(429 RATE_LIMITED)。

current_time = time.time()
if (current_time - self._last_request_time) < self._min_request_interval:
    time.sleep(
        self._min_request_interval - (current_time - self._last_request_time)
    )
BraveSearchTool._last_request_time = time.time()

需要留意:这是类级别的全局时钟(ClassVar),同一进程内所有 BraveSearchTool 实例共享 1 秒间隔;而新版基类已改为实例级限流且速率可配置(见后文)。

2. 参数解析与请求体组装

_run 从 kwargs 中逐项提取参数并组装 payload,关键的兼容逻辑如下:

源码行为 说明
kwargs.get("q") or kwargs.get("query") or kwargs.get("search_query") 三种查询参数写法等价,缺失则抛出 ValueError("Query is required")
country 地理定向(ISO 两位国家码,如 "US"
search_lang / search_language 结果语言码(如 "en"),旧名 search_language 自动映射
count 显式结果数;未提供时回落到构造参数 n_results(默认 10)
offset 跳过的结果页数,采用 is not None 判断以兼容 0 值
safesearch 内容过滤:off / moderate / strict
spellchecktext_decorationsextra_snippetsoperators 布尔开关,均为 is not None 时透传
freshness 时效过滤:pd / pw / pm / pyYYYY-MM-DDtoYYYY-MM-DD 区间

此外源码强制注入 payload["result_filter"] = "web"(L124-L126),注释说明当前仅限制 web 结果类型,因为该工具还没有 news/videos/locations 的独立处理分支——这正是后续拆分为多个细分工具的动因。

请求最终发往 search_url = "https://api.search.brave.com/res/v1/web/search",携带 X-Subscription-Token(API 密钥)与 Accept: application/json 请求头,HTTP 超时固定 30 秒:

headers = {
    "X-Subscription-Token": os.environ["BRAVE_API_KEY"],
    "Accept": "application/json",
}

response = requests.get(
    self.search_url, headers=headers, params=payload, timeout=30
)

3. 响应精化与 save_file

拿到 JSON 后,工具只提取 results["web"]["results"] 中同时具有 urltitle 的条目,并压缩为 url / title / description / snippets(来自 extra_snippets)四个字段,最后 json.dumps 成字符串返回(L140-L164)。

save_file=True 时,结果会额外通过 base.py 中的 _save_results_to_file 写入本地文件,文件名带时间戳:

def _save_results_to_file(content: str) -> None:
    """Saves the search results to a file."""
    filename = f"search_results_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.txt"
    with open(filename, "w") as file:
        file.write(content)

即每次保存生成形如 search_results_2026-09-05_04-26-51.txt 的文件,注意它会写到当前工作目录。

4. 错误处理:以字符串返回而非抛异常

遗留实现把网络异常"软化"为对 LLM 友好的文本(L165-L168):requests.RequestException(连接失败、超时等)返回 "Error performing search: ..."KeyError 返回 "Error parsing search results: ..."。这种设计的意图是让 Agent 能读到错误信息并自行决策(例如换个查询词重试),而不是让整个 Crew 直接崩溃。

新版基类 BraveSearchToolBase:可靠性机制的升级

README 描述的 BraveSearchTool 是单文件实现,而同目录下的 base.py 为新一代工具族提供了统一基类 BraveSearchToolBase。对照阅读可以清楚看到官方在哪些方面补强了可靠性,这些机制也代表了这个模块当前推荐的使用形态:

构造参数全表

所有 Brave 工具共享以下构造参数(来自 BraveSearchToolBase.__init__,L113-L138,并有 测试用例 test_custom_constructor_args 验证):

参数 类型 默认值 说明
api_key str | None None 显式传入密钥,优先于环境变量
headers dict | None None 附加 HTTP 头,会经 schema 校验并合并
requests_per_second float 1.0 实例级限流速率;<= 0 时关闭限流
save_file bool False 每次响应写入时间戳 .txt 文件
raw bool False True 时跳过响应精化,返回完整 API JSON
timeout int 30 HTTP 请求超时(秒)
country str | None None 遗留快捷参数,作为 country 查询参数缺省值
n_results int 10 遗留快捷参数,作为 count 查询参数缺省值

API 密钥解析顺序为:显式 api_key 参数 > BRAVE_API_KEY 环境变量,两者皆无则抛出 ValueError("BRAVE_API_KEY environment variable is required")

重试与退避策略

_make_request(L179-L243)实现了完整的重试状态机:

  • 可重试条件_is_retryable):429 且错误码不是配额耗尽类,或 5xx 服务端错误;
  • 终态错误QUOTA_LIMITEDUSAGE_LIMIT_EXCEEDED 两种配额错误码被显式排除在重试之外——注释解释得很直白:计费周期未重置前重试永远不会成功;401422(如 OPTION_NOT_IN_PLAN)等也直接抛出;
  • 等待策略_retry_delay):优先读服务端 Retry-After 响应头,缺失时按 2^attempt 指数退避(1s、2s、4s);
  • 默认最多 3 次尝试,重试用尽后把完整 JSON 错误体(含 codedetailmeta)拼进 RuntimeError 消息,非 JSON 错误体则回落到前 500 字符的文本。

测试文件 对这些路径逐一做了覆盖,例如:test_429_rate_limited_retries_then_succeeds 验证带 Retry-After: 2 的 429 会精确 sleep 2 秒后第二次成功;test_quota_limited_429_raises_immediately 验证配额错误只发 1 次 HTTP 请求;test_5xx_is_retried 验证 502 后重试成功。

请求头校验与实例级限流

  • _build_and_validate_headers 会把请求头统一小写化,补上 x-subscription-tokenaccept: application/json,并用 Pydantic 的 header_schema 校验合法性(例如给 Accepttext/xml 会抛 ValueError: Invalid headers,测试见 test_invalid_header_value_raises);
  • set_headers() 支持链式调用并在初始化后追加热头,Web 工具还支持 x-loc-lat / x-loc-long / x-loc-city 等地理位置头做本地化检索;
  • 限流时钟改为每实例独立_last_request_time + threading.Lock),源码注释指出:进程总速率等于各实例限额之和。

空值剥离:适配 LLM 严格模式的一个细节

_common_payload_refinement(base.py L284-L318)有一段很有意思的防御性逻辑:crewAI 的 schema 管道会为兼容 OpenAI strict-mode 结构化输出把所有属性标记为 required,副作用是 LLM 会把真正可选的参数也填上 None"""null"[] 之类的占位值。基类会把这些空值从可选字段中剔除,并把 query / search_query 统一归一为 q;同时把构造期传入的 n_results / country 作为 count / country 的缺省值注入(调用时显式传入则优先)。test_common_refinement_strips_null_like_values 专门验证了这一行为。

从 BraveSearchTool 迁移到 BraveWebSearchTool

结合官方文档的迁移指南,从遗留类切换到新工具族只需替换导入并调整参数风格:

# Before (legacy)
from crewai_tools import BraveSearchTool

tool = BraveSearchTool(country="US", n_results=5, save_file=True)
results = tool.run(search_query="AI agents")

# After (recommended)
from crewai_tools import BraveWebSearchTool

tool = BraveWebSearchTool(save_file=True)
results = tool.run(q="AI agents", country="US", count=5)

关键差异归纳:

  • 导入BraveWebSearchTool(或 News/Image/Video 变体)替代 BraveSearchTool
  • 查询参数:推荐 q=query / search_query 仍被兼容接受);
  • 结果数与地域:由构造期 n_results / country 改为调用期 count / country 查询参数;
  • 密钥:除环境变量外还支持 api_key= 直传;
  • 可靠性requests_per_second 可调,且内置 429/5xx 自动重试。

BraveWebSearchTool 为例,子类只需声明 search_urlargs_schemaheader_schema 并实现 _refine_request_payload / _refine_response——Web 工具的精化逻辑是把 web.results 收敛为 {url, title, snippets} 列表,snippets 优先取 extra_snippets,缺失时回落到 description。查询参数约束则定义在 schemas.py 的 WebSearchParamsq 最长 400 字符、count 范围 1–20、offset 范围 0–9、country 必须是两位大写国家码等,请求前都会经过 Pydantic 校验(例如 count=999 会抛 Invalid parameters,测试见 test_invalid_params_raises_value_error)。

一个 Agent 集成示例(引自官方文档):

from crewai import Agent
from crewai.project import agent
from crewai_tools import BraveWebSearchTool, BraveNewsSearchTool

web_search = BraveWebSearchTool()
news_search = BraveNewsSearchTool()

@agent
def researcher(self) -> Agent:
    return Agent(
        config=self.agents_config["researcher"],
        tools=[web_search, news_search],
    )

小结

  • BraveSearchTool 是 CrewAI 接入 Brave Web Search API 的遗留工具:安装 crewai[tools]、设置 BRAVE_API_KEY 后即可使用;它的 run 支持 q/query/search_query 三种查询写法,内置 1 秒类级限流、30 秒超时、web 结果强制过滤与 save_file 落盘能力,网络错误以文本形式返回以便 Agent 容错。
  • 同模块的新版工具族基于 BraveSearchToolBase,补齐了实例级可调限流、429/5xx 指数退避重试(配额错误不重试)、Pydantic 请求头/参数校验、LLM 空值剥离等可靠性机制,并有完整的单元测试覆盖。
  • 新代码建议直接使用 BraveWebSearchTool 等细分工具;涉及 enable_snippets、Local POIs 等能力时需注意部分参数/工具要求付费订阅计划,以 Brave 官方 API 文档为准。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384