AutoGPT Platform 中的 Exa Similar 块:用 Exa findSimilar API 实现语义相似网页发现
Exa Similar 是 AutoGPT Platform(autogpt_platform 前端画布 + backend 执行引擎)内 BlockCategory.SEARCH 分类下的一个搜索类块,核心能力是调用 Exa 的 findSimilar API,根据一个已知 URL 的网页内容与上下文,从全网检索出语义相近的其他网页。本文以 Exa Similar 的官方块说明文档 docs/integrations/block-integrations/exa/similar.md 为主线,结合其 源码实现 与配套测试,完整讲解它的输入输出、内容获取选项、成本追踪与典型 Agent 工作流,帮助你在搭建 Agent 时直接落地"内容发现 / 竞品分析 / 研究扩展"等场景。
Exa Find Similar 是什么、底层如何工作
在 AutoGPT Platform 中,Exa Find Similar 是一个标准块(Block),其能力定位与同目录下的 Exa Search 块(见 search.md)互补:Search 从关键词/语义出发搜索全网,而 Find Similar 以一个已知 URL 为锚点出发。从源码注释看,该块的核心声明为:
description="Finds similar links using Exa's findSimilar API"
categories={BlockCategory.SEARCH}
它的工作原理可概括为三步:
- 语义分析锚点页面:块把输入的
url发送给 Exa 的 findSimilar API,Exa 会分析该页面的内容与上下文语义(而非仅做关键词匹配)。 - 全网语义匹配:基于语义向量寻找与之相关的网页并返回结果列表。
- 可选的结果精化与内容获取:可以通过域名白名单/黑名单、抓取时间(crawl date)与发布时间(published date)范围、正文包含/排除文本等过滤条件缩小结果集;也可以开启
contents内容获取与moderation内容安全过滤。
在实现层面,AutoGPT Platform 的 backend 使用官方 Python SDK exa_py 的异步客户端,并根据是否请求正文内容自动选择不同的 API 端点。查看 similar.py 中的核心逻辑:
aexa = AsyncExa(api_key=credentials.api_key.get_secret_value())
if content_settings:
# Use find_similar_and_contents when contents are requested
sdk_kwargs["text"] = content_settings.get("text", False)
if "highlights" in content_settings:
sdk_kwargs["highlights"] = content_settings["highlights"]
if "summary" in content_settings:
sdk_kwargs["summary"] = content_settings["summary"]
response = await aexa.find_similar_and_contents(**sdk_kwargs)
else:
response = await aexa.find_similar(**sdk_kwargs)
也就是说:当 contents 配置了任何内容获取选项时,块调用 find_similar_and_contents 一步拿到正文/高亮/摘要;否则调用轻量的 find_similar 只取链接与元数据。该块在 backend 中对应的完整类名为 ExaFindSimilarBlock,注册 ID 为 5e7315d1-af61-4a0c-9350-7c868fa7438a,代码位于 similar.py。
在画布中接入 Exa Find Similar 与凭据配置
Exa 系列块共享同一套 Provider 凭据配置,定义在 _config.py 中:
exa = (
ProviderBuilder("exa")
.with_description("Neural web search")
.with_api_key("EXA_API_KEY", "Exa API Key")
.with_webhook_manager(ExaWebhookManager)
.with_base_cost(100, BlockCostType.COST_USD)
.build()
)
因此接入该块的先决条件是:
- 在 Exa 平台申请一个 API Key,把它配置为名为
EXA_API_KEY的环境变量,或在 AutoGPT Platform 的凭据管理中创建 provider 为exa的 API Key 凭据; - 在画布中添加 Exa Find Similar 块后,为它的
credentials字段选择该 Exa 凭据(块的输入 schema 中该字段描述为 "The Exa integration requires an API Key.")。
配套的测试基准则定义在 _test.py 中,用 provider="exa"、类型为 API Key 的测试凭据覆盖块的运行路径,可作为你在本地调用该块时的最小凭据样例参考。
输入参数详解
必填输入:锚点 URL
url(str,必填)是本次"相似发现"的唯一锚点,Exa 会围绕这个页面的语义内容展开全网匹配。该字段在块的 schema 中被标记为常规(非高级)字段,是画布上的第一配置项。
返回数量与域名过滤
number_of_results(int,可选)控制返回结果条数。需要特别留意的是,尽管文档表格未标注默认值,源码中为它显式设置了 default=10 并标记为高级字段(similar.py),同时该值在调用 SDK 时被映射为 num_results 关键字。如果你不填写,默认只返回 10 条。
域名过滤由 include_domains 与 exclude_domains(均为 List[str],可选)控制,语义在源码注释中写得很清楚:
include_domains:若指定,结果只会来自这些域名,是一个白名单;exclude_domains:从结果中剔除这些域名,是一个黑名单。
两个字段都带 default_factory=list 并被标为高级字段,即不填时默认空列表、不做限制。代码中只在列表非空时才把对应 key 传给 SDK(similar.py),避免向 Exa API 发送无意义的空过滤参数。
日期范围过滤:抓取时间与发布时间
块提供两对日期过滤参数,类型均为 str (date-time):
| 输入 | 语义 |
|---|---|
start_crawl_date / end_crawl_date |
针对 Exa 抓取(crawl)内容的时间范围 |
start_published_date / end_published_date |
针对网页原始发布时间的时间范围 |
两者差异对应底层数据结构:crawl 时间反映的是内容被 Exa 收录/抓取的时点,published 时间反映的是作者发布内容的时点。在源码中这四个字段都是 Optional[datetime] 的高级字段,默认 None(不限制);只有当值非空时,才以 datetime.isoformat() 的格式序列化后传入 SDK(similar.py)。例如在画布中给 end_published_date 设成最近 30 天的日期,就可以实现"只找与锚点相似且近期发布的内容"。
文本模式过滤
include_text 与 exclude_text(List[str],可选)用于按正文出现的文本模式做二次过滤。需要注意 Exa 对这两个参数有约束:最多 1 个字符串,且长度不超过 5 个单词。这不同于搜索引擎的自由关键字习惯,在设计 Agent 输入时应把短语压缩到 5 词以内,例如把 "latest artificial intelligence news" 这类查询收敛为 "AI news" 之类的短模式。
内容获取设置与安全过滤
contents(ContentSettings,可选)与 moderation(bool,可选)是该块两个"进阶能力开关",默认值在源码中分别为 ContentSettings()(空内容设置)与 False。contents 的完整子配置见下一节;moderation 置为 True 时,块会把 moderation 参数传给 Exa,由其过滤搜索结果中的不安全内容(源码中仅在为真时才写入 sdk_kwargs,见 similar.py)。
下面把 12 个输入参数汇总成可速查表格(含源码级默认值标注):
| 输入 | 说明 | 类型 | 必填 | 默认值(来自源码) |
|---|---|---|---|---|
url |
需要查找相似链接的锚点 URL | str | 是 | 无 |
number_of_results |
返回结果条数(映射为 SDK 的 num_results) |
int | 否 | 10(高级) |
include_domains |
域名白名单,指定后结果仅来自这些域名 | List[str] | 否 | [](高级) |
exclude_domains |
域名黑名单 | List[str] | 否 | [](高级) |
start_crawl_date |
抓取内容起始时间 | str (date-time) | 否 | None(高级) |
end_crawl_date |
抓取内容结束时间 | str (date-time) | 否 | None(高级) |
start_published_date |
发布内容起始时间 | str (date-time) | 否 | None(高级) |
end_published_date |
发布内容结束时间 | str (date-time) | 否 | None(高级) |
include_text |
需要匹配的正文文本模式(最多 1 个、≤5 词) | List[str] | 否 | [](高级) |
exclude_text |
需要排除的正文文本模式(最多 1 个、≤5 词) | List[str] | 否 | [](高级) |
contents |
内容获取设置(正文/高亮/摘要等) | ContentSettings | 否 | ContentSettings()(高级) |
moderation |
是否启用内容安全过滤 | bool | 否 | False(高级) |
输出与数据结构
该块声明了 6 个输出(见 similar.py 中的 Output schema):
| 输出 | 说明 | 类型 |
|---|---|---|
error |
请求失败时的错误信息 | str |
results |
相似文档列表(含元数据与可选内容) | List[ExaSearchResults] |
result |
单条相似文档结果(逐条发射) | ExaSearchResults |
context |
已格式化、可直接交给 LLM 的结果字符串 | str |
request_id |
请求唯一标识 | str |
cost_dollars |
本次请求的成本明细 | CostDollars |
其中 error 与 request_id 是 schema 中为失败语义与请求追踪预留的输出字段。在成功路径下,similar.py 的实际发射行为是:先一次性输出整个 results 列表,随后逐条输出 result(方便下游循环处理);当响应携带 Exa 生成的 context 时输出它;当响应携带 cost_dollars 时也一并输出。
results / result 使用共享模型 ExaSearchResults(定义在 helpers.py),该模型把 SDK 返回结果统一转换成平台侧字段,结构如下:
| 字段 | 说明 |
|---|---|
id |
Exa 文档 ID |
url |
网页地址 |
title |
页面标题 |
author |
作者 |
publishedDate |
发布时间 |
text |
提取的网页正文(请求 contents 时才有) |
highlights / highlightScores |
相关性最高片段及其分数 |
summary |
LLM 生成的摘要 |
subpages |
子页面列表(配置 subpages 抓取时才有) |
image / favicon |
图片/站点图标资源 |
extras |
附加链接与图片链接(links、imageLinks) |
深入 contents:内容获取设置
contents 是决定"拿元数据还是拿正文"的关键高级输入,它背后是定义在 helpers.py 的 ContentSettings 模型,包含以下子配置:
text:正文获取。支持布尔值与对象两种形态:- 布尔值:
true/false简单开关; - 高级对象(
TextAdvanced):max_characters限制返回字符数(占位提示1000)、include_html_tags决定是否保留 HTML 标签(源码注释说明保留标签有助于 LLM 理解文本结构); - 历史版本还兼容 "enabled/disabled" 枚举写法,处理逻辑见
process_text_field。
- 布尔值:
highlights:从每个页面提取最相关片段,用于生成 RAG 检索增强的引用。子参数包括num_sentences(每个高亮包含的句子数,默认 1、ge=1)、highlights_per_url(每个 URL 返回的高亮条数,默认 1)、query(自定义引导 LLM 挑选高亮的问题,占位如Key advancements)。summary:针对网页的 LLM 摘要。query可自定义摘要关注点(占位如Main developments);output_schema可传 JSON Schema,让摘要以结构化 JSON 输出,便于下游 Agent 做字段级消费。livecrawl:实时抓取策略枚举never/fallback/always/preferred,并配套毫秒级livecrawl_timeout(占位10000)与subpages、subpage_target(子页面抓取数量与定位关键词)。extras:附加内容,links(每个结果页内返回的链接数)与image_links(每个结果返回的图片数),默认 0 表示不请求。context:是否把结果格式化为可直接喂给 LLM 的 context 字符串,高级模式可用max_characters限制长度(占位10000)。
当这些配置被提交时,process_contents_settings(helpers.py)会把下划线风格的模型字段转换为 Exa API 的驼峰格式负载(例如 highlights_per_url → highlightsPerUrl、max_characters → maxCharacters、include_html_tags → includeHtmlTags)。有一个值得注意的实现细节:虽然 ContentSettings 模型本身支持 livecrawl、subpages、extras、context 等更广的配置项,但就 Exa Find Similar 这个块当前版本的 run() 而言,开启内容获取后只会把 text、highlights、summary 三个键转发给 find_similar_and_contents,其余高级子项主要由同目录下 Exa Contents / Exa Search 等兄弟块消费。因此,当你在画布中只想让相似结果附带正文与摘要用于喂给 LLM 时,配置 contents.text=true(或带 max_characters 的高级对象)并配合 highlights/summary 即可,无需依赖被该块忽略的配置项。
成本跟踪与配额换算
Exa 的每个响应都会携带 cost_dollars,其中 total 表示本次请求的美元成本,模型结构见 helpers.py(包含 total 总成本、breakDown 按操作类型的明细、以及 perRequestPrices / perPagePrices 两组标准价目参考)。块拿到响应后调用 merge_exa_cost(similar.py),把 cost_dollars.total 合并进 NodeExecutionStats.provider_cost,实现 Agent 执行粒度的成本统计。
在平台计费层面,_config.py 的注释解释了换算口径:with_base_cost(100, BlockCostType.COST_USD) 表示每 1 美元按 100 平台积分(约 $0.01/credit)计费,便宜的搜索通常消耗 1–2 积分,昂贵的深度检索(约 $0.20)则消耗约 20 积分,与真实 Provider 花费一致。需要注意的是这属于平台配置注释中给出的换算示意,实际计费以平台当前策略为准。
配套测试 cost_tracking_test.py 中专门覆盖了 TestExaSimilarCostTracking:
- 当 mock 的
find_similar响应携带cost_dollars = CostDollars(total=0.015)时,merge_stats会被调用且provider_cost精确等于0.015; - 当响应没有
cost_dollars时,merge_stats不会被调用(成本统计跳过,不产生副作用)。
这两条用例直接印证了 Exa Find Similar 块在成功请求下的成本合并行为,可作为你在集成或做成本可视化时对照的验收标准。
典型应用场景与工作流编排建议
文档明确给出三个典型场景,它们在 AutoGPT Platform 画布上都可以直接组合成可复用的 Agent:
- 内容发现(Content Discovery):给定一篇你喜欢的文章 URL,让该块找出语义相似的文章、博客与资源,形成持续的内容素材池。
- 竞品分析(Competitor Analysis):把已知竞品公司的官网/产品页 URL 作为锚点,借助
include_domains限定行业媒体域名、配合end_published_date过滤最新动态,发现同类公司或可对比的产品信息。 - 研究扩展(Research Expansion):把关键参考文献 URL 传入,借助
include_text/exclude_text收紧语义方向,扩展出更多可引用的原始资料。
在这类工作流里,Exa Find Similar 通常不是终点而是上游检索节点,推荐的下游编排思路是:
- 让块输出
results/result给数据清洗节点,用url、title、publishedDate建结构化清单; - 需要给 LLM 阅读时,让块直接输出
context(Exa 已格式化的上下文串)作为后续 LLM 节点的输入; - 开启
contents让每条结果自带正文text、highlights与summary,即可配合 LLM 节点实现"发现 → 摘要 → 决策"的自动化研究管线; - 把
cost_dollars接到平台统计或日志节点,监控每次相似检索的成本。
实现上,该块与同目录 Exa 家族其他块(search.py、contents.py、answers.py 等)共享 helpers 中的模型与成本合并逻辑,你可以直接在 backend/blocks/exa 目录下横向阅读这些兄弟块,理解"相似发现"与"关键词/语义搜索""正文获取"的协同边界,从而把 Exa 的能力作为一个完整的搜索工具族嵌入 Agent。
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 StartedRust0627
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