AutoGPT 平台 Exa Search 模块详解:基于神经检索与关键词检索的网页搜索 Block
AutoGPT 平台的 exa 提供者是一整套基于 Exa 网页索引的搜索类集成,其中 Exa Search 是使用频率最高的入口 Block:它用一句查询语句即可完成「语义检索 + 站点/时间/内容类型过滤 + 正文提取」的整条搜索流水线,并直接产出可供 LLM 消费的 context 上下文串与成本明细。本文以 docs/integrations/block-integrations/exa/search.md 为骨架,结合该 Block 在仓库中的真实实现(blocks/exa/search.py 等)逐项拆解其原理、每个输入输出的取值与默认语义、底层调用链与成本核算方式,帮你把它正确接入自己的 Agent 流程。
这是什么:一个用 Exa 语义检索能力搜索网页的 Block
模块定位
Exa Search Block(实现类 ExaSearchBlock,Block ID 996cec64-ac40-4dde-982f-b0dc60a5824d)在构建器中归属于 BlockCategory.SEARCH(见 search.py),功能描述只有一句话:
Searches the web using Exa's advanced search API
但与常见的网页搜索引擎不同,Exa 提供的不是单纯的「关键词命中」,而是一套理解语义的神经检索(neural search)能力——因此它特别擅长找到「类型明确、语义匹配」的内容,而不是简单依赖字面关键词的巧合。Block 官方文档对它的能力概括如下:
- 支持四种检索模式:关键词检索(keyword,传统)、神经检索(neural,语义理解)、快速检索(fast)与自动选择(auto);
- 强大的多维度过滤:按域名(include/exclude)、按抓取/发布日期区间、按内容类别(company、research paper、news 等)、按正文文本模式(include_text/exclude_text);
- 结果除 URL、标题外,还可选返回全文内容提取(text / highlights / summary 等),用于直接喂给下游 LLM。
在 Exa 家族中的位置
在仓库中 Exa 不止有这一个 Block。blocks/exa/ 目录下还包含 contents.py(按 URL/ID 取正文)、answers.py(搜索佐证的问答)、similar.py(相似页面)、code_context.py(代码上下文)以及一组 websets 系列 Block。它们在 Provider 层共享同一份 exa 配置(API Key 字段与计费规则均来自 _config.py)。本文聚焦「搜索」这一个动作,其他内容的补充读取可参见同目录文档 contents.md、answers.md。
接入前置条件:Exa 提供者凭证与计费基准
调用任何 Exa Block 前,都需要先为 exa 提供者配置 API Key。
ExaSearchBlock.Input中第一个字段就是credentials,其描述为 "The Exa integration requires an API Key."(见 search.py);- Provider 侧的凭证定义在 _config.py 中:
ProviderBuilder("exa")声明了提供者名称为exa,并通过.with_api_key("EXA_API_KEY", "Exa API Key")绑定 环境变量EXA_API_KEY作为密钥来源; - 在运行时,
run()方法会通过AsyncExa(api_key=credentials.api_key.get_secret_value())从 Block 凭证里读取密钥创建 SDK 客户端(见 search.py),因此你需要在平台「集成/凭证」管理里预先保存自己的 Exa API Key。
值得注意的一点是计费基准:_config.py 中为提供者设置了 .with_base_cost(100, BlockCostType.COST_USD),代码注释明确说明其口径是 每 1 美元消耗 100 积分(credit),即约 $0.01/credit;一次便宜的搜索只花 1–2 个积分,而一次 0.20 美元的深度任务会落在 20 积分左右,与实际 Provider 支出大致吻合。也就是说,Block 的真实费用以 Exa 响应中返回的 cost_dollars 为准,平台在此基础上做平台内积分的换算统计。
输入参数:字段、类型、默认值与高级语义
原文档给出了完整的输入清单。结合 search.py 中 Input 的 SchemaField 定义,可以补充出每个字段的默认值与 UI 上的展开层级(advanced=True 的字段通常被平台收纳进「高级选项」区域,仅在需要时展开):
| 输入 | 含义 | 类型 | 必填 | 源码默认值 / 备注 |
|---|---|---|---|---|
query |
搜索查询语句 | str |
是 | 唯一必填项,其余全部可选 |
type |
检索方式 | "keyword" | "neural" | "fast" | "auto" |
否 | 源码默认 auto,advanced |
category |
内容类别限定 | 见下文类别表 | 否 | 默认 None,advanced |
user_location |
用户所在国家(两位 ISO 国家码,如 'US') |
str |
否 | 默认 None,advanced,用于结果本地化排序 |
number_of_results |
返回结果条数 | int |
否 | 源码默认 10,advanced |
include_domains |
只在这些域名内搜索 | List[str] |
否 | 默认空列表 [] |
exclude_domains |
排除这些域名的结果 | List[str] |
否 | 默认 [],advanced |
start_crawl_date |
内容被 Exa 抓取时间区间的起点 | str (date-time) |
否 | 默认 None,advanced |
end_crawl_date |
内容抓取时间区间的终点 | str (date-time) |
否 | 默认 None,advanced |
start_published_date |
内容发布时间区间的起点 | str (date-time) |
否 | 默认 None,advanced |
end_published_date |
内容发布时间区间的终点 | str (date-time) |
否 | 默认 None,advanced |
include_text |
正文需匹配的文本模式列表 | List[str] |
否 | 默认 [],advanced |
exclude_text |
正文不得包含的文本模式列表 | List[str] |
否 | 默认 [],advanced |
contents |
内容检索/提取设置(嵌套对象) | ContentSettings |
否 | 默认 ContentSettings(),advanced |
moderation |
内容审核:过滤不安全结果 | bool |
否 | 源码默认 False,advanced |
type 与 category 的合法取值
原文档将取值范围以枚举形式给出,这与源码中定义的枚举完全一一对应:
- type(
ExaSearchTypes):keyword/neural/fast/auto。前三种分别代表传统关键词、语义神经检索、快速检索;auto是源码中的默认值,由 Exa 服务端根据查询自动挑选最优方式(这正是输出里出现search_type/resolved_search_type两个字段的原因)。 - category(
ExaSearchCategories):company/research paper/news/pdf/github/tweet/personal site/linkedin profile/financial report。枚举的完整声明可见 search.py。
当在构建器中选择 category 时,Exa 会把搜索范围收敛到指定类型的内容上(如只看 GitHub 仓库、只看论文),再叠加域名与日期过滤即可得到非常精确的结果集。
contents:ContentSettings 子配置详解
contents 是最复杂也最能拉开差距的输入。它的类型 ContentSettings 定义在 helpers.py,本质是一组控制「要不要、以及怎样提取正文内容」的开关。run() 会把它交由 process_contents_settings() 转成 Exa API 能识别的载荷(camelCase 风格参数),转换规则同样在 helpers.py 中可查。内部包含:
| 子字段 | 取值 | 说明 |
|---|---|---|
text |
bool,或 {maxCharacters, includeHtmlTags} |
是否返回页面全文。布尔 true/false 为简单开关;高级形态可限制最大字符数 maxCharacters,以及用 includeHtmlTags 保留 HTML 标签以便 LLM 理解文本结构(见 TextAdvanced)。兼容旧版直接传布尔值的用法 |
highlights |
{numSentences, highlightsPerUrl, query} |
从每个页面中抽取与查询最相关的若干句高亮片段。numSentences 每条高亮的句子数(默认 1,ge=1)、highlightsPerUrl 每 URL 高亮条数(默认 1,ge=1)、query 可自定义驱动 LLM 选句的查询 |
summary |
{query, output_schema} |
LLM 生成的页面摘要。output_schema 为结构化输出的 JSON Schema;该字段经历了 schema → output_schema 的更名,SummarySettings 通过 model_validator 做了向后兼容(旧键 schema 会自动映射,见 helpers.py) |
livecrawl |
never | fallback | always | preferred |
实时抓取策略:是否在命中缓存不足时现场抓取网页获取最新内容 |
livecrawl_timeout |
int(毫秒) |
实时抓取超时时间 |
subpages |
int(≥0) |
要顺带抓取的子页面数量 |
subpage_target |
str | List[str] |
定位搜索结果特定子页面的关键词 |
extras |
{links, imageLinks} |
附加内容:每个网页返回的外部链接数、图片数;值为 0 时不会发给 API(避免向 Exa 请求 0 个链接),见转换函数注释 |
context |
bool 或 {maxCharacters} |
是否把多条结果拼装成一段可直接喂给 LLM 的上下文串,可限制最大字符数 |
何时走 search、何时走 search_and_contents
原文档没有说明 contents 内部机制,但从 search.py 的 run() 实现可以看清两条底层路径:
- 当
contents完全没配置时,调用aexa.search(**sdk_kwargs),只返回链接/标题等元数据; - 一旦配置了
text/highlights/summary(对应字段被塞进sdk_kwargs),Block 会改用aexa.search_and_contents(**sdk_kwargs),把「搜索 + 内容提取」合并为一次调用。
这意味着 include_text/exclude_text 这类作用于页面正文的过滤项,只有搭配 contents.text 一起使用时才有实质效果;纯元数据搜索下它们对标题/链接层面的过滤能力有限。设计流程时请把「是否要正文」先想清楚,避免无效调用。
输出参数与数据模型
运行结束后,Block 会按 Output 的定义依次产出多个输出,其中部分输出会重复触发多次(即“流式产出”):
| 输出 | 含义 | 类型 |
|---|---|---|
error |
请求失败时的错误信息 | str |
results |
本次搜索结果完整列表(一次性产出整个 list) | List[ExaSearchResults] |
result |
单条搜索结果——对列表里每条结果都会产出一次,便于在图中逐条分叉处理 | ExaSearchResults |
context |
已格式化的搜索上下文串,可直接作为 LLM 提示词的输入 | str |
search_type |
auto 模式下最终由服务端挑选出的检索方式 |
str |
resolved_search_type |
本次请求实际生效的检索方式(neural 或 keyword) | str |
cost_dollars |
本次请求的成本明细 | CostDollars |
说明:在
run()的实现里(search.py),先yield "results"整体列表,再对每个结果yield "result",随后依次产出context、resolved_search_type、cost_dollars。context依赖服务端在response.context中返回(即 contents 子配置里的context开关开启时才存在)。
单条 result 的数据模型 ExaSearchResults(定义于 helpers.py,由 from_sdk 从 exa_py SDK 的响应转换而来)包含以下字段:
| 字段 | 含义 |
|---|---|
id |
文档 ID |
url |
结果网页 URL |
title |
网页标题 |
author |
作者 |
publishedDate |
发布日期 |
text |
抽取到的正文文本 |
highlights / highlightScores |
相关高亮片段及其打分 |
summary |
LLM 生成的摘要 |
subpages |
顺带抓取的子页面列表 |
image / favicon |
图片与站点图标(MediaFileType) |
extras |
{links, imageLinks} 附加链接信息 |
运行原理:一次搜索请求的完整调用链
把输入输出串起来,ExaSearchBlock.run() 的完整工作流如下(对应 search.py):
- 组装基础参数:
query与num_results(由number_of_results映射)必然进请求;随后逐字段追加可选参数——type/category取枚举的.value,user_location原样透传,include_domains/exclude_domains在非空时写入,include_text/exclude_text同理。 - 日期格式化:四个日期参数(
start/end_crawl_date、start/end_published_date)在非空时统一调用datetime.isoformat()转成 ISO 8601 字符串再传给 SDK(这正是这些字段类型被标注为str (date-time)的原因)。 - 内容配置转换:
process_contents_settings()把ContentSettings(含 livecrawl、subpages、context 等全部子项)转成 camelCase 的 API 参数对象。 - 创建客户端并发起请求:用凭证里的密钥
AsyncExa(api_key=...),依据上一步是否有内容提取需求,选择search_and_contents或search。 - 结果归一化:SDK 返回的每条
Result经ExaSearchResults.from_sdk()转成平台数据模型,再按顺序 yield 到各输出端。 - 成本回收:
merge_exa_cost(self, response)从响应中提取cost_dollars.total(兼容 dataclass/dict/camelCase/snake_case/裸字符串等各类响应形态,见 helpers.py),并写入NodeExecutionStats.provider_cost,用于平台的用量统计与积分结算。若 Exa 未报告成本信息(例如部分 webset CRUD 场景),该步骤会自动跳过,不会中断流程。
值得注意的是它不手动 catch SDK 异常:请求失败路径由 Block 运行框架统一兜底并把错误写入 error 输出。
典型应用场景
原文档给出了三类代表性的用法,这里结合上面的输入能力做进一步展开:
- 竞品研究(Competitive Research):把
type设为neural,category选company,再用published_date区间约束到最近几个月,即可抓取某行业内近期有新闻或融资动态的公司。在 AutoGPT 平台里可以接一个「每家公司发一条result」的分支,对逐条结果继续做正文抽取或 LLM 总结。 - 内容策展(Content Curation):面向 Newsletter 或内容聚合,用
category = research paper / news过滤来源类型,开启contents.text与context,一次运行就能拿到多篇可直接改写摘要的候选材料。context输出本身就是为「丢进提示词」而设计的格式化文本。 - 销售线索挖掘(Lead Generation):将行业、规模、近期活跃度等线索条件翻译成
query+user_location+ 发布时间窗口的组合,配合include_domains限定权威来源,输出results后即可交给后续数据清洗或 CRM 写入流程。
参考配置示例
下面给出一份贴近上述「竞品研究」场景的输入配置(字段取值全部来自上文输入表,可直接在构建器中对照填写,也可作为 API 层调用该 Block 时的参数参考):
query: "AI infrastructure companies with recent funding"
type: neural
category: company
number_of_results: 8
start_published_date: 2026-01-01T00:00:00Z
include_domains: []
exclude_domains: ["reddit.com"]
contents.text: { maxCharacters: 3000 }
contents.highlights: { numSentences: 2, highlightsPerUrl: 1, query: "funding round" }
contents.summary: { query: "company overview and recent funding" }
moderation: true
下游可直接使用 context 输出作为 LLM 的参考资料,或用逐条产出的 result 对每家公司分别触发后续 Block。
小结
Exa Search Block 在 AutoGPT 平台里扮演着「高质量网页入口」的角色:auto 模式让不熟悉 Exa 细节的用户也能直接上手,而 type / category / 域名与日期双轴过滤 / contents 内容提取的组合,则为深度流程提供了足够精细的控制面。要完全掌握它,记住三点即可:用 contents 决定是否走 search_and_contents 的正文提取路径;把 context 输出当作送给 LLM 的现成上下文;通过 cost_dollars 和 resolved_search_type 观察每次调用的成本与真实生效的检索方式。文中引用的输入输出定义、默认值与调用链均可回到 search.py、helpers.py 与 _config.py 中逐一验证。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00