Crawl4AI AdaptiveCrawler API 深度解析:三层评分、digest() 主循环与知识基持久化
【免费下载链接】crawl4ai🚀🤖 Crawl4AI: Open-source LLM Friendly Web Crawler & Scraper. Don't be shy, join here: https://discord.gg/jP8KfhDhyN项目地址: https://gitcode.com/GitHub_Trending/craw/crawl4ai
本文基于 Crawl4AI(当前仓库版本 0.9.0,见 crawl4ai/version.py)的官方 API 参考文档 docs/md_v2/api/adaptive-crawler.md,完整讲解AdaptiveCrawler类及其核心方法digest()的参数、属性、配置项与完整示例,并结合 crawl4ai/adaptive_crawler.py 源码剖析其三层置信度评分、链接排序与停止判定的底层实现,帮助读者既能正确调用该 API,也能理解每一次“何时停止爬取”决策背后的计算逻辑。
概述:让爬虫自己判断“信息够了没有”
传统爬虫要么按 BFS/DFS 机械地遍历整站,要么依赖人工预设规则决定爬多少页。Crawl4AI 的AdaptiveCrawler实现了自适应信息采集(adaptive information foraging):它围绕一个自然语言查询(query)从起始 URL 出发,边爬取边评估“已收集的信息是否足以回答该查询”,在置信度达到阈值、内容趋于饱和或资源用尽时自动停止。
该模块的核心文件为 crawl4ai/adaptive_crawler.py,其中定义了四个关键构件:
| 构件 | 源码位置 | 职责 |
|---|---|---|
CrawlState | L26-L150 | 数据类,追踪已爬 URL、知识库、待处理链接与全部统计指标,支持 JSON 持久化 |
AdaptiveConfig | L153-L274 | 数据类,控制阈值、页面数上限、链接权重、嵌入策略等全部行为参数 |
CrawlStrategy(抽象基类) | L277-L298 | 定义calculate_confidence/rank_links/should_stop/update_state四个抽象接口 |
AdaptiveCrawler | L1292 | 主类,编排初始爬取、链接排序、批量抓取与停止判定循环 |
AdaptiveCrawler与AdaptiveConfig均在包顶层导出(见 crawl4ai/init.py 第 84-85 行),因此可以直接from crawl4ai import AsyncWebCrawler, AdaptiveCrawler, AdaptiveConfig使用。
构造函数
API 文档给出的构造函数签名为:
AdaptiveCrawler( crawler: AsyncWebCrawler, config: Optional[AdaptiveConfig] = None )参数说明:
- crawler(
AsyncWebCrawler):底层网页爬虫实例,负责实际抓取页面。它继承了 Crawl4AI 的全部能力,包括 JavaScript 渲染、链接预览、Cookie 会话等。 - config(
Optional[AdaptiveConfig]):自适应爬取行为配置。不传时使用默认配置。
从源码结构看(L1295-L1313),构造函数实际有几点额外细节值得注意:
def __init__(self, crawler: Optional[AsyncWebCrawler] = None, config: Optional[AdaptiveConfig] = None, strategy: Optional[CrawlStrategy] = None): self.crawler = crawler self.config = config or AdaptiveConfig() self.config.validate() # ... self._owns_crawler = crawler is None- crawler 可以为 None:若不传入,
digest()内部会自动创建AsyncWebCrawler并在结束后负责关闭(_owns_crawler标记控制清理逻辑,见 L1469-L1472)。但最佳实践仍是显式传入并自己管理async with生命周期。 - config 会立即校验:
AdaptiveConfig.validate()(L229-L256)用断言检查阈值范围(如0 <= confidence_threshold <= 1)、权重和(coverage/consistency/saturation 三权重之和必须为 1,relevance/novelty/authority 三权重之和必须为 1),非法配置会在构造阶段直接抛错。 - strategy 参数可注入自定义策略:不传时根据
config.strategy字段创建StatisticalStrategy(默认)或EmbeddingStrategy(见 L1315-L1328),这也为高级用户实现了文档 FAQ 中提到的“可定制评分算法”的扩展点。
主方法 digest()
digest()是执行自适应爬取的主入口(源码 L1330-L1472):
async def digest( start_url: str, query: str, resume_from: Optional[Union[str, Path]] = None ) -> CrawlState参数
- start_url(
str):爬取的起始 URL,应为有效的 HTTP/HTTPS 地址,建议选导航结构清晰的文档索引页; - query(
str):引导爬取过程的查询语句,爬虫用它评估链接相关性并判断信息是否充分; - resume_from(
Optional[Union[str, Path]]):可选,之前保存的状态文件路径,传入后从中断点恢复而非重新爬取。
返回
返回CrawlState对象(定义见 L26-L43),包含:
crawled_urls(Set[str]):所有已爬取 URL;knowledge_base(List[CrawlResult]):已爬取页面的完整结果(含 markdown 正文与链接);pending_links(List[Link]):发现但尚未爬取的链接队列;query:本次查询;metrics(Dict[str, float]):性能与质量指标(confidence、coverage、consistency、saturation、pages_crawled、depth_reached 等);- 统计跟踪字段:
term_frequencies(词频 TF)、document_frequencies(文档频 DF)、new_terms_history(每页新增词数历史,用于饱和度计算)等。
更多细节可参考 digest() 方法参考。
最小示例
async with AsyncWebCrawler() as crawler: adaptive = AdaptiveCrawler(crawler) state = await adaptive.digest( start_url="https://docs.python.org", query="async context managers" )主循环的工作流程
digest()内部实现了一个“抓取—评分—决策”循环,可以概括为以下步骤(对应源码 L1366-L1465):
初始化或恢复状态:若给了
resume_from,用CrawlState.load()反序列化 JSON 状态(L53-L110),并刷新 query;否则新建空状态。初始爬取:调用
_crawl_with_preview()(L1474-L1504)爬取 start_url。该方法内部构造了CrawlerRunConfig:config = CrawlerRunConfig( link_preview_config=LinkPreviewConfig( include_internal=True, include_external=False, query=query, # 用于 BM25 上下文评分 concurrency=5, timeout=self.config.link_preview_timeout, # 默认 5 秒 max_links=50, verbose=False ), score_links=True # 启用链接内在质量评分 )也就是说,每个待处理链接都会附带“预览”元数据(锚文本、title、meta description/keywords),供后续 BM25 相关性打分使用。
link_preview_timeout由 AdaptiveConfig 中的link_preview_timeout: float = 5.0控制。嵌入策略的查询扩展(仅
strategy="embedding"且非恢复模式):调用map_query_semantic_space()(L726-L802)通过 LLM 生成n_query_variations个查询变体,并按 80/20 划分训练/验证集,训练集嵌入后作为“查询语义空间点云”。深度展开循环(上限
max_depth,默认 5):每一轮依次执行:strategy.calculate_confidence(state)计算当前置信度并写入metrics['confidence'];strategy.should_stop(state, config)判定是否停止(详见下文“停止条件”);strategy.rank_links(state, config)对pending_links排序,得到[(Link, score), ...];- 若最高分低于
min_gain_threshold,认为继续爬取收益不足,直接退出; - 否则取 top-
top_k_links个未爬链接,经_crawl_batch()(L1506-L1527)用asyncio.gather并行抓取,失败结果被自动过滤; - 新页面的内部链接补充进
pending_links(仅保留带head_data预览数据的链接),随后strategy.update_state()更新词频/嵌入等统计。
落盘与收尾:若
save_state=True且配置了state_path,每完成一轮深度就调用self.state.save()增量保存;循环结束后再计算最终置信度并写入pages_crawled、depth_reached指标。
停止条件
综合两个策略的实现,爬取会在满足以下任一条件时停止(统计策略的should_stop见 L527-L546):
- 置信度阈值:
confidence >= confidence_threshold; - 页面上限:
len(crawled_urls) >= max_pages; - 收益递减:候选链接最高分低于
min_gain_threshold,或饱和度saturation >= saturation_threshold(默认 0.8); - 无候选链接:
pending_links为空。
嵌入策略另有“收敛+验证”机制:当连续迭代置信度改进量低于embedding_min_relative_improvement * confidence时,先用保留查询验证覆盖率,验证分超过embedding_validation_min_score才以converged_validated停止;若置信度低于embedding_min_confidence_threshold(默认 0.1)则判定查询与内容完全无关(metrics['is_irrelevant'] = True)并提前终止(L1155-L1204)。
三层评分体系:Coverage / Consistency / Saturation
coverage_stats属性返回的四个指标中,前三项由默认的StatisticalStrategy计算(L301-L612),它们共同构成判断“信息是否充分”的依据。
1. Coverage(覆盖率)
衡量查询词在知识库中的分布情况(_calculate_coverage, L328-L367)。对每个查询词 term:
doc_coverage = df / total_documents # 文档覆盖:含该词页面占比 freq_signal = log(1+tf) / log(1+max_tf) # 归一化对数频率信号 term_score = doc_coverage * (1 + 0.5 * freq_signal)最终coverage = min(1.0, sqrt(平均 term_score))。开方曲线让“部分覆盖”与“良好覆盖”之间的区分更明显。
2. Consistency(一致性)
衡量各页面之间信息是否连贯(_calculate_consistency, L369-L394):对知识库中所有页面对计算词集合的 Jaccard 相似度并取平均。页面数少于 2 时返回 1.0。高一致性说明爬到的都是同一主题集群下的内容,而非跑题的页面。
3. Saturation(饱和度)
检测“ diminishing returns ”(_calculate_saturation, L396-L411):
saturation = 1 - (recent_rate / initial_rate) # 近期新词发现速率 vs 初始速率new_terms_history记录每页新增了多少此前未见的词。当后期页面几乎不带来新词时,recent_rate趋近 0,饱和度趋近 1——即“继续爬也学不到新东西了”。
置信度合成与链接排序
三项指标加权合成总置信度:
confidence = 0.4 * coverage + 0.3 * consistency + 0.3 * saturation注意源码此处使用了与配置默认值一致的 0.4/0.3/0.3 固定权重(L324)。AdaptiveConfig中确实定义了coverage_weight/consistency_weight/saturation_weight三个可调字段(默认 0.4/0.3/0.3,见 L164-L168),但从当前源码看,统计策略的置信度合成是硬编码的这三个默认值,若你修改权重,validate()会校验其和为 1,但对置信度的实际影响需要以你所用版本的实现为准。
链接排序(rank_links, L413-L438)对每个候选链接计算:
score = relevance_weight * relevance # 0.5 + novelty_weight * novelty # 0.3 + authority_weight * authority # 0.2- Relevance(相关性):优先复用爬取阶段 BM25 得到的
contextual_score,否则用查询词与链接预览文本(锚文本+title+meta description/keywords)的词重叠率(L440-L470); - Novelty(新颖性):链接预览文本中“未出现过的新词”占比,估计该链接能带来多少增量信息(L472-L496);
- Authority(权威性):当前版本中该分量被固定为 1.0,基于 URL 结构的权威度评分函数
_calculate_authority()已定义但未启用(L498-L525 与 L425-L426 可见其为占位/注释状态)。
分词器_tokenize()(L598-L607)采用“去标点 + 过滤长度 ≤2 的词”的简单方案,因此查询词过短或全为虚词时评分会退化,这也是官方建议 query 使用 3-8 个具体技术词的原因。
属性(Properties)
confidence
当前置信度(0-1),表示信息充分程度:
@property def confidence(self) -> float实现上直接读取state.metrics['confidence'](L1530-L1535)。官方文档给出的解读区间(见 docs/md_v2/core/adaptive-crawling.md):
| 区间 | 含义 |
|---|---|
| 0.0 - 0.3 | 信息不足,需要更多爬取 |
| 0.3 - 0.6 | 部分信息,可能回答基础问题 |
| 0.6 - 0.7 | 良好覆盖,可回答多数问题 |
| 0.7 - 1.0 | 优秀覆盖,信息全面 |
coverage_stats
返回详细覆盖统计字典(L1537-L1558),包含:
coverage:查询词覆盖率得分;consistency:信息一致性得分;saturation:内容饱和度得分;confidence:总置信度;- 以及
pages_crawled、unique_terms、total_terms、total_content_length、pending_links等辅助统计。
is_sufficient
布尔值,判断信息是否已足够(L1560-L1568)。注意它对两种策略采用了不同判据:
- 统计策略:
confidence >= confidence_threshold; - 嵌入策略:返回
strategy._validation_passed,即是否通过了保留查询的验证,而非简单阈值比较。
state
@property state -> CrawlState,直接访问当前爬取状态,可读取crawled_urls、new_terms_history、metrics等字段做进度监控:
print(f"Pages crawled: {len(state.crawled_urls)}") print(f"New terms per page: {state.new_terms_history}")方法详解
get_relevant_content(top_k=5)
从知识库中检索与查询最相关的内容(L1897-L1923):
def get_relevant_content(self, top_k: int = 5) -> List[Dict[str, Any]]参数top_k(默认 5)为返回的相关文档数量。实现是对每个页面计算查询词与页面正文的词集合重叠率作为score,按分数降序返回 top-K。源码返回的字典字段为url、score、content、index(内容取自result.markdown.raw_markdown),可直接用于构造 RAG 上下文或做摘要:
for page in adaptive.get_relevant_content(top_k=3): print(f"- {page['url']} (score: {page['score']:.2f})")print_stats(detailed=False)
以格式化方式打印爬取统计(L1570-L1771):
- detailed=False:若环境装有
rich,输出彩色汇总表(爬取页数、词汇量、内容长度、confidence/coverage/consistency/saturation、是否充分); - detailed=True:输出完整指标,包括每个查询词在多少页面中出现(
'term': found in df/N docs)、Top 20 高频词、按爬取顺序列出每个 URL 及其新增词数、文档频率分布;嵌入策略下还会输出语义覆盖分析(平均最小距离、邻居数、验证分、查询变体样例等)。
rich缺失时自动降级为纯文本输出,不会报错。
export_knowledge_base(path)
把收集的知识库导出为 JSONL 文件(L1781-L1843):
def export_knowledge_base(self, path: Union[str, Path]) -> None每行一个 JSON 对象,字段包括url、content(raw markdown)、metadata、links、query,以及crawl_metadata(该 URL 的爬取序号、爬取时置信度、当时文档总数)。导出结果适合直接喂给 RAG 管线或做离线分析:
adaptive.export_knowledge_base("my_knowledge.jsonl")import_knowledge_base(path)
导入之前导出的知识库(L1845-L1878),为async方法:
async def import_knowledge_base(self, path: Union[str, Path]) -> None它逐行解析 JSONL,重建带markdown.raw_markdown的结果对象,加入state.knowledge_base,并调用strategy.update_state()同步更新词频/嵌入统计——也就是说,导入后置信度、覆盖率等指标会基于合并后的知识库重新计算,可以在新会话中无缝接续分析。文件不存在时抛FileNotFoundError。
AdaptiveConfig 配置项全解
AdaptiveConfig(L153-L274)控制自适应爬取的全部行为。API 文档中给出的核心字段如下:
@dataclass class AdaptiveConfig: confidence_threshold: float = 0.8 # Stop when confidence reaches this max_pages: int = 50 # Maximum pages to crawl top_k_links: int = 5 # Links to follow per page min_gain_threshold: float = 0.1 # Minimum expected gain to continue save_state: bool = False # Auto-save crawl state state_path: Optional[str] = None # Path for state persistence需要注意:以当前仓库源码为准,这些字段的实际默认值为confidence_threshold=0.7、max_pages=20、top_k_links=3,并且源码还额外提供了max_depth: int = 5(展开循环深度上限)和strategy: str = "statistical"(策略选择)。文档中列出的 0.8/50/5 应视为推荐的更宽松配置。完整参数可分三组理解:
核心控制参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
confidence_threshold | float | 0.7 | 置信度达到该值即停止(0-1) |
max_depth | int | 5 | 展开循环最大轮数 |
max_pages | int | 20 | 累计最大爬取页面数 |
top_k_links | int | 3 | 每轮选取排序最高的几个链接去爬 |
min_gain_threshold | float | 0.1 | 最高候选分低于该值则停止(信息增益不足) |
strategy | str | "statistical" | 可选 "statistical" / "embedding" |
评分权重参数(validate()要求各组权重和为 1)
| 参数 | 默认值 | 说明 |
|---|---|---|
saturation_threshold | 0.8 | 饱和度达到该值即停止 |
consistency_threshold | 0.7 | 一致性阈值 |
coverage_weight/consistency_weight/saturation_weight | 0.4 / 0.3 / 0.3 | 三层指标的置信度合成权重 |
relevance_weight/novelty_weight/authority_weight | 0.5 / 0.3 / 0.2 | 链接排序三分量权重 |
嵌入策略专用参数(strategy="embedding"时生效)
| 参数 | 默认值 | 说明 |
|---|---|---|
embedding_model | "sentence-transformers/all-MiniLM-L6-v2" | 本地嵌入模型 |
embedding_llm_config | None | 嵌入 API 配置(LLMConfig或 dict),用于文本向量化调用 |
query_llm_config | None | 查询扩展所用的聊天模型配置;未设置时回退到embedding_llm_config(向后兼容) |
n_query_variations | 10 | LLM 生成的查询变体数量(实际会多生成 30% 用于验证集划分) |
embedding_coverage_radius | 0.2 | 余弦距离小于该半径视为“已覆盖” |
embedding_k_exp | 1.0 | 距离→分数指数衰减因子score = exp(-k*distance),越大越苛刻 |
embedding_nearest_weight/embedding_top_k_weight | 0.7 / 0.3 | 混合打分:最近邻分数 vs top-k 平均分权重 |
embedding_overlap_threshold | 0.85 | 链接与知识库相似度超过该值则被降权(去冗余) |
link_preview_timeout | 5.0 | 链接预览超时(秒) |
embedding_min_confidence_threshold | 0.1 | 置信度低于该值判定查询与内容无关,立即停止 |
embedding_min_relative_improvement | 0.1 | 相对改进低于该比例视为收敛 |
embedding_validation_min_score | 0.3 | 收敛后保留查询验证分须超过该值才允许停止 |
embedding_quality_min_confidence/embedding_quality_max_confidence/embedding_quality_scale_factor | 0.7 / 0.95 / 0.833 | 将内部 learning score 映射为展示给用户的质量置信度 |
save_state/state_path | False / None | 每轮深度后自动保存状态到指定 JSON 文件 |
自定义配置示例:
config = AdaptiveConfig( confidence_threshold=0.7, max_pages=20, top_k_links=3 ) adaptive = AdaptiveCrawler(crawler, config=config)两种策略:Statistical 与 Embedding
AdaptiveConfig.strategy决定实例化哪种策略(L1315-L1328)。
Statistical(默认):纯词频/文档频率统计,无 LLM、无模型加载、可离线运行,适合术语明确的文档型站点。即前文详述的三层评分体系。
Embedding:基于语义空间的覆盖度学习(EmbeddingStrategy, L615-L1289),适合概念性、模糊性查询。其工作链路为:
- 查询扩展:LLM 生成查询变体点云,训练/验证 80/20 划分(原始查询恒定保留在训练集);
- 覆盖度评分:对每个查询点计算知识库嵌入的最近邻余弦相似度,
confidence取平均最佳相似度(L988-L1014); - 缺口驱动选链:
find_coverage_gaps()找出距离最远的查询点,select_links_for_expansion()(L871-L986)给“最能缩小这些缺口”的链接打分,并对与知识库相似度超过embedding_overlap_threshold的链接施加重叠惩罚,避免爬入冗余页面;链接嵌入带 MD5 键缓存以节省 API 调用; - 去重入库:
update_state()对新页面文本截断至 5000 字符后嵌入,与现有知识库最大相似度 ≥0.95 的页面直接丢弃; - 验证式停止:收敛时先用验证集查询确认覆盖真实达标,通过后展示置信度由
get_quality_confidence()(L1206-L1231)映射到 0.7-0.95 区间。
两种策略的选型对比与嵌入策略的详细配置(含embedding_llm_config/query_llm_config的分离使用方式)可参考 Adaptive Crawling Guide 与 Advanced Adaptive Strategies,仓库中也有对应示例脚本:docs/examples/adaptive_crawling/ 下的embedding_strategy.py、embedding_vs_statistical.py、llm_config_example.py等。
持久化与恢复
自动保存状态
config = AdaptiveConfig( save_state=True, state_path="my_crawl.json" )配置后每完成一轮深度展开都会调用CrawlState.save()(L53-L80)将全部状态(已爬 URL、知识库 markdown、词频表、待处理链接、嵌入向量等)序列化为 JSON 落盘,循环结束时再保存一次。
断点恢复
state2 = await adaptive.digest( start_url="https://example.com", query="authentication oauth2 jwt", resume_from="my_crawl.json" )CrawlState.load()(L82-L110)会还原词频表、待处理链接队列和嵌入矩阵(列表还原为 numpy 数组),恢复模式下跳过 start_url 的重复爬取(若其不在crawled_urls中则仍会补爬),并跳过查询扩展步骤,直接续爬。
知识库导出/导入
# 导出为 JSONL adaptive.export_knowledge_base("auth_knowledge.jsonl") # 在新会话中导入并合并统计 new_adaptive = AdaptiveCrawler(crawler) await new_adaptive.import_knowledge_base("auth_knowledge.jsonl")完整的两阶段(构建→导出→分析→导入)演示见 docs/examples/adaptive_crawling/export_import_kb.py。
完整实战示例
综合 API 文档的完整示例,配合源码中已确认的默认值与调用方式:
import asyncio from crawl4ai import AsyncWebCrawler, AdaptiveCrawler, AdaptiveConfig async def main(): # 配置自适应爬取 config = AdaptiveConfig( confidence_threshold=0.75, max_pages=15, save_state=True, state_path="my_crawl.json" ) async with AsyncWebCrawler() as crawler: adaptive = AdaptiveCrawler(crawler, config) # 启动自适应爬取 state = await adaptive.digest( start_url="https://example.com/docs", query="authentication oauth2 jwt" ) # 检查结果 print(f"Confidence achieved: {adaptive.confidence:.0%}") adaptive.print_stats() # 获取最相关的页面 for page in adaptive.get_relevant_content(top_k=3): print(f"- {page['url']} (score: {page['score']:.2f})") # 导出知识库供后续使用 adaptive.export_knowledge_base("auth_knowledge.jsonl") if __name__ == "__main__": asyncio.run(main())运行时的行为可以从源码直接预期:从 start_url 出发并做链接预览(内部链接、BM25 上下文评分、5 并发、50 链接上限);随后每轮对候选链接按 0.5×relevance + 0.3×novelty + 0.2×authority 排序,取前 3 个并行爬取;当置信度 ≥0.75、页面数 ≥15、饱和度 ≥0.8 或无有效候选时停止;每个状态都会自动写入my_crawl.json。
查询构造上,官方建议(见 docs/md_v2/api/digest.md):使用 3-8 个具体技术词(如"oauth2 jwt refresh tokens authorization"),避免过宽泛的查询;起始 URL 选择导航良好的文档索引页效率最高。
延伸阅读与测试验证
- digest() 方法参考:停止条件、错误处理与进度监控示例;
- Adaptive Crawling Guide:三层评分概念、策略对比表、最佳实践与 FAQ;
- Advanced Adaptive Strategies:自定义
CrawlStrategy的实现指引; - 示例脚本目录:docs/examples/adaptive_crawling/(含
basic_usage.py、advanced_configuration.py、custom_strategies.py、embedding_strategy.py、export_import_kb.py等); - 回归与单元测试:tests/adaptive/test_adaptive_crawler.py、tests/adaptive/test_embedding_strategy.py、tests/adaptive/test_query_llm_config.py,可用于验证不同配置下的行为是否符合预期。
需要说明的适用前提:digest()是网络爬取操作,embedding 策略依赖本地 sentence-transformers 或外部 LLM API;print_stats()的彩色输出依赖可选的rich库(缺失时自动降级);import_knowledge_base()必须await调用。以上均以当前仓库 0.9.0 版本的源码实现为准。
【免费下载链接】crawl4ai🚀🤖 Crawl4AI: Open-source LLM Friendly Web Crawler & Scraper. Don't be shy, join here: https://discord.gg/jP8KfhDhyN项目地址: https://gitcode.com/GitHub_Trending/craw/crawl4ai
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考