首页
/ Crawl4AI 自适应爬取(Adaptive Crawling)实战:按查询生长、在信息饱和时停止的"知识电容器"

Crawl4AI 自适应爬取(Adaptive Crawling)实战:按查询生长、在信息饱和时停止的"知识电容器"

2026-09-06 12:40:09作者:凤尚柏Louis

Adaptive Crawling(自适应爬取)是 Crawl4AI 中一种与传统深度爬取截然不同的思路:它不追求"爬遍全站",而是围绕一个具体查询(query)动态生长知识库,并通过覆盖率、一致性、饱和度三类统计信号判断"何时该停止"。本文基于仓库中的官方博文 adaptive-crawling-revolution.md 展开,并结合 crawl4ai/adaptive_crawler.py 的源码实现,带你完整掌握其信息论基础、两种策略(statistical / embedding)的原理与全部配置参数,以及 digest 主循环的实际工作方式。

一、什么是 Adaptive Crawling:一个"知识电容器"

Crawl4AI 的作者将 Adaptive Crawling 类比为电容器——储存信息,并在需要时精准释放。它的核心范式转变可以概括为一句话:从 "crawl everything, hope for the best"(爬取一切、听天由命)变为 "crawl intelligently, know when to stop"(智能爬取、知道何时停止)。

原文档给出的动机非常直接:许多团队误以为"用了 LLM 就等于高效",但 LLM 只是让事情"变得可能",而非"变得聪明"。将蛮力深度爬取与 LLM 处理叠加,浪费的不仅是时间,更是 token 与算力开销。原博文报告的对比数据是(引自 原文):

方式 页面数 有效内容 token 成本 耗时
传统深度爬取 500 页 50 页有效 $15 约 2 小时
自适应爬取 15 页 14 页有效 $2 约 10 分钟

另一个更具代表性的场景是构建客服知识库:传统做法爬整个文档站(max_depth=5)得到 1,200 页、约 100 页有用;自适应做法按真实用户查询 payment processing errors refund policies 生长,45 页中 42 页全部相关。原文强调,关键不是"爬得更少",而是"爬得对"(crawling right)。

在 Crawl4AI 中,这套能力由 crawl4ai 包直接导出,导入方式见 crawl4ai/init.py

from crawl4ai import AsyncWebCrawler, AdaptiveCrawler, AdaptiveConfig

二、信息论基础:三个智能支柱

原博文的第一原则是"纯统计,没有魔法"——先用经典统计方法,不依赖 embedding 或 LLM。仓库源码中这一原则落在 StatisticalStrategy 类上,它维护词频(term frequency)、文档频率(document frequency)两张表,并用三个指标构成置信度。这三个指标即原文所称的"Three Pillars of Intelligence"。

2.1 Coverage(覆盖率):广度传感器

回答的不是"有没有页面",而是"有没有的页面"。源码实现位于 adaptive_crawler.py_calculate_coverage

  • 对查询分词,统计每个查询词在知识库中的文档覆盖率 doc_coverage = df / total_documents
  • 叠加归一化对数频率信号 freq_signal = log(1+tf) / log(1+max_tf)
  • 合成 term_score = doc_coverage * (1 + 0.5 * freq_signal),对所有查询词取平均后开平方根(平方根曲线使"部分覆盖"与"良好覆盖"更易区分),最终截断在 [0, 1]

2.2 Consistency(一致性):连贯性检测器

多来源的信息应当相互印证。源码(adaptive_crawler.py)用成对文档的 Jaccard 相似度衡量:

overlap = len(terms_i & terms_j) / len(terms_i | terms_j)
consistency = sum(overlaps) / len(overlaps)

页面之间共识越多,置信度越高;若彼此冲突(重叠低),系统会推断还需要更多数据。

2.3 Saturation(饱和度):效率守护者

原文称之为"最关键的指标":当新页面不再贡献新信息时就停止爬取。源码(adaptive_crawler.py)通过 new_terms_history(每页新增词数)计算:

saturation = 1 - (recent_rate / initial_rate)

初期每页带来大量新词,近期速率下降则饱和度逼近 1,触发停止。

2.4 加权置信度与停止条件

calculate_confidenceadaptive_crawler.py)将三者加权合成总置信度(源码中硬编码了默认权重):

confidence = 0.4 * coverage + 0.3 * consistency + 0.3 * saturation

注意 AdaptiveConfig 中同时提供了 coverage_weight=0.4consistency_weight=0.3saturation_weight=0.3 三个可配置权重(见 AdaptiveConfig),但当前 calculate_confidence 使用的是固定权重——从源码结构看,配置权重主要起声明与校验作用(validate() 会断言三者之和为 1),实际调用仍以硬编码值计算。

统计策略的停止判定 should_stopadaptive_crawler.py)有四个条件,满足任一即停:

  1. confidence >= confidence_threshold(默认 0.7);
  2. crawled_urls >= max_pages(默认 20);
  3. 待爬队列 pending_links 为空;
  4. saturation >= saturation_threshold(默认 0.8)。

三、"网络爬取的 A*":信息气味与链接评分

原文把这套机制称为 "information scenting"——像 A* 寻路一样,不随机追随每个链接,而是按"对当前及未来查询贡献有意义信息的概率"排序。这正是博文中的信息增益思想在源码里的落地:

# 信息增益计算——自适应爬取的核心(原文示意)
def calculate_information_gain(new_page, knowledge_base):
    new_terms = extract_terms(new_page) - existing_terms(knowledge_base)
    overlap = calculate_overlap(new_page, knowledge_base)
    gain = len(new_terms) / (1 + overlap)  # 新词多、重叠少 → 增益高
    return gain

源码中,StatisticalStrategy.rank_linksadaptive_crawler.py)对每个候选链接计算三个分量:

score = (config.relevance_weight * relevance +   # 默认 0.5
         config.novelty_weight * novelty +        # 默认 0.3
         config.authority_weight * authority)     # 默认 0.2
  • relevance:链接预览文本(text/title/meta 的 title、description、keywords)与查询的匹配度;若链接在爬取阶段已做过 BM25 上下文打分(link.contextual_score),则直接采用;否则退化为查询词重叠率;
  • novelty:链接文本中有多少比例是新词(link_terms - existing_terms),未知时给 0.5;
  • authority:权重存在但当前恒取 1.0(_calculate_authority 已注释停用,adaptive_crawler.py)。

链接预览数据来自 _crawl_with_previewadaptive_crawler.py),它在每次 arun 时挂载 LinkPreviewConfig(include_internal=True, query=query, concurrency=5, timeout=link_preview_timeout, max_links=50) 并开启 score_links=True,让 Crawl4AI 的链接预览机制为每个内链抓取 head 数据并用查询词做 BM25 打分——这就是"气味"的来源。

四、两种策略:统计基线与嵌入语义增强

4.1 Statistical 策略:精确词、快而字面

strategy="statistical" 是默认策略,纯统计、零模型依赖,适合查询词与页面用词高度一致的场景。StatisticalStrategy 还内置了 BM25 参数(k1=1.2b=0.75,见 adaptive_crawler.py)作为相关性打分的备选路径。

4.2 Embedding 策略:语义空间中的覆盖

EmbeddingStrategyadaptive_crawler.py)在统计基线之上叠加语义理解,其关键步骤是 map_query_semantic_spaceadaptive_crawler.py):

  1. 用对话模型把查询扩成 n_query_variations 个语义变体(多生成 30% 用于留出验证集,80/20 划分,原查询始终留在训练侧);
  2. embedding_model(默认 sentence-transformers/all-MiniLM-L6-v2)嵌入这些变体,得到查询的"语义邻域点云";
  3. 用带缓存的向量化余弦距离矩阵(_compute_distance_matrix)衡量知识库文档对查询点的覆盖。

由此实现博文所说的"自动扩展查询、映射覆盖空间、智能识别缺口":对 authentication oauth 这类查询,它能理解 "auth"、"login"、"SSO" 属于同一语义区域,而不是字面匹配。

4.3 无关检测:知道何时"认输"

原文强调的最实用特性是:embedding 策略知道何时放弃。源码中它对应 EmbeddingStrategy.should_stopadaptive_crawler.py)的最低相关性阈值检查:

# 检查置信度是否低于最低阈值(完全不相关)
if confidence < min_confidence_threshold and len(state.crawled_urls) > 0:
    state.metrics['stopped_reason'] = 'below_minimum_relevance_threshold'
    state.metrics['is_irrelevant'] = True
    return True

这就是博文示例中"用意大利面查询爬 Python 官方文档,置信度 5%(低于阈值),仅爬 2 页即停,stopped_reasonbelow_minimum_relevance_threshold"的底层实现。阈值由 embedding_min_confidence_threshold(默认 0.1)控制,可通过 state.metrics 读取停止原因。

五、digest 主循环:一次完整的自适应爬取

所有策略最终由 AdaptiveCrawleradaptive_crawler.py)编排。构造函数会调用 config.validate() 校验参数合法性,再按 config.strategy 创建对应策略。核心入口 digest(start_url, query, resume_from=None) 的循环结构(adaptive_crawler.py)如下:

  1. 初始化/恢复状态:若传 resume_from,从 JSON 文件反序列化 CrawlState(支持断点续爬),否则新建;
  2. 查询空间扩展(仅 embedding 策略且非恢复时):执行 map_query_semantic_space,把查询变体与嵌入存入 state.query_embeddings
  3. 初始爬取:用 LinkPreviewConfig 爬取 start_url,把成功的 CrawlResult 加入知识库,内链(dict 或 Links 对象两种形态均兼容)进入 pending_links
  4. 循环(depth < max_depth)
    • calculate_confidence 计算当前置信度;
    • should_stop 判定停止(含上面各类阈值);
    • rank_links 对候选链接评分,若最高分低于 min_gain_threshold(默认 0.1)则停止——"增益太低不值得再爬";
    • 取 top top_k_links(默认 3)个链接,_crawl_batchasyncio.gather 并行爬取,失败的 URL 被过滤并打印;
    • 新知识并入知识库,新内链去重后追加到待爬队列,update_state 更新词频/嵌入等统计量;
    • 若配置 save_state,每轮落盘一次 state_path
  5. 收尾:计算最终置信度(embedding 策略额外走 get_quality_confidence 映射为 0.7–0.95 区间的"质量置信度"),写入 pages_crawleddepth_reached 指标后返回 CrawlState;若 AdaptiveCrawler 自己创建的 AsyncWebCrawler,会在 finally 中确保释放浏览器资源。

CrawlStateadaptive_crawler.py)承载全部运行态:已爬 URL 集合、知识库、待爬链接、TF/DF 表、new_terms_history,以及 embedding 策略专用的 kb_embeddingsquery_embeddingsexpanded_queriessemantic_gaps。其 save/load 以 JSON 持久化(numpy 数组转 list 存储),这为知识库的跨会话生长与断点恢复提供了基础。

六、AdaptiveConfig 全参数速查

以下默认值直接来自 AdaptiveConfig 的源码注释,validate()adaptive_crawler.py)会对全部参数做断言校验。

核心控制

参数 默认值 说明
confidence_threshold 0.7 置信度达到该值即停止
max_depth 5 扩展轮数上限(每轮爬一批 top-k 链接)
max_pages 20 最大爬取页面数
top_k_links 3 每轮选取的最高分链接数
min_gain_threshold 0.1 候选链接最高分低于此值则停止
strategy "statistical" statistical / embedding(其他取值抛 ValueError

三支柱权重与链接评分权重(两组权重各自必须和为 1)

参数 默认值 说明
coverage_weight 0.4 覆盖率权重
consistency_weight 0.3 一致性权重
saturation_weight 0.3 饱和度权重
relevance_weight 0.5 链接相关性权重
novelty_weight 0.3 链接新信息权重
authority_weight 0.2 链接权威性权重
saturation_threshold 0.8 饱和度停止阈值
consistency_threshold 0.7 一致性阈值

Embedding 策略专属

参数 默认值 说明
embedding_model sentence-transformers/all-MiniLM-L6-v2 本地嵌入模型(可经 embedding_llm_config 改用 LLM 嵌入 API)
n_query_variations 10 查询语义变体数量(另多生成 30% 做验证)
coverage_threshold 0.85 覆盖目标
alpha_shape_alpha 0.5 覆盖形状参数(高维下退化为质心+半径统计模型)
embedding_min_confidence_threshold 0.1 低于该值判定"完全不相关"并立即停止
embedding_coverage_radius 0.2 查询点被视为"已覆盖"的余弦距离半径,越小要求越严
embedding_k_exp 1.0 距离→得分的指数衰减系数,score = exp(-k * distance)
embedding_nearest_weight 0.7 最近邻在混合得分中的权重(与 top-k 权重之和须为 1)
embedding_top_k_weight 0.3 top-k 平均得分权重
embedding_overlap_threshold 0.85 与知识库相似度超此值的冗余链接会被降权
embedding_min_relative_improvement 0.1 每批次的最小相对提升,低于则停
embedding_validation_min_score 0.3 验证分低于此值不信任收敛,防止过早停止
link_preview_timeout 5.0 链接预览超时(秒)

持久化与 LLM 配置

参数 默认值 说明
save_state / state_path False / None 每轮把 CrawlState 写入 JSON,配合 digest(resume_from=path) 断点续爬
embedding_llm_config None 嵌入 API 配置(LLMConfig 或 dict);为 None 时用本地 sentence-transformers
query_llm_config None 查询扩展(对话补全)配置;缺省时回退到 embedding_llm_config,再缺省用内置默认 provider

七、实战:按需求生长的知识库

7.1 最小可运行示例

这是原文 "Try It Yourself" 的完整代码,参数与源码默认值一一对应:

from crawl4ai import AsyncWebCrawler, AdaptiveCrawler, AdaptiveConfig

async with AsyncWebCrawler() as crawler:
    # 选择策略
    config = AdaptiveConfig(
        strategy="embedding",              # 或 "statistical"
        embedding_min_confidence_threshold=0.1  # 低于此值判定无关并停止
    )

    adaptive = AdaptiveCrawler(crawler, config)

    result = await adaptive.digest(
        start_url="https://your-docs.com",
        query="your users' actual questions"
    )

    adaptive.print_stats()
    print(f"Found {adaptive.confidence:.0%} of needed information")
    print(f"In just {len(result.crawled_urls)} pages")

注意两个策略的典型表现差异(原文给出的对比示例):查询 authentication oauth 时,统计策略按精确词搜索、爬 12 页、置信度 78%,"快但字面";embedding 策略理解 "auth/login/SSO" 的同义关系,爬 8 页、置信度 92%。

7.2 观察"动态生长"

原文用了一个漂亮的比喻:知识库像过饱和溶液中的晶体——加一个查询(种子),相关信息围绕它结晶;换查询,知识结构随之调整。用两个不同查询连续 digest 同一站点:

# 周一:客户问认证
auth_knowledge = await adaptive.digest(
    "https://docs.api.com",
    "oauth jwt authentication"
)

# 周二:客户问限流——爬虫在已有知识上继续生长
rate_limit_knowledge = await adaptive.digest(
    "https://docs.api.com",
    "rate limiting throttling quotas"
)

配合 save_state=True, state_path="./kb_state.json",第二次可以用 resume_from 直接恢复第一次的状态,而不是从零爬起。

7.3 查看统计与导出知识库

  • adaptive.print_stats(detailed=False):基于 rich 表格输出页数、唯一词数、内容长度、置信度、coverage/consistency/saturation(统计策略)或 validation score/Is Sufficient(embedding 策略);detailed=True 还会打印 top 20 词、逐 URL 的新增词数、DF 分布与查询空间样本(adaptive_crawler.py);
  • adaptive.coverage_stats 属性返回同一指标的 dict 形式,便于程序化消费;
  • adaptive.is_sufficient:统计策略比较 confidence >= confidence_threshold,embedding 策略以"验证集是否通过"为准;
  • export_knowledge_base(filepath, format="jsonl") / import_knowledge_base(filepath)adaptive_crawler.py):以 JSONL 导出/导入知识库,每条记录含 url、markdown 内容、链接与爬取元数据(爬取顺序、爬取时置信度),方便把知识库喂给下游 LLM 或 RAG 管线。

更多可运行的示例脚本可参考仓库的 docs/examples/adaptive_crawling/ 目录(basic_usage.pycustom_strategies.pyembedding_configuration.py 等),配套 API 文档见 docs/md_v2/core/adaptive-crawling.md

八、渐进式路线图:统计 → 嵌入 → LLM

原文给出了 Adaptive Crawling 的三阶段路线,与源码现状可以互相印证:

  • Phase 1(已实现):统计基础——纯信息论,不依赖昂贵模型,StatisticalStrategy 即其落点;
  • Phase 2(当前可用):嵌入增强——在统计基线上叠加语义理解,可选(strategy="embedding"),默认模型为本地小模型 all-MiniLM-L6-v2,不引入 LLM 成本即可工作;
  • Phase 3(规划中):LLM 集成——LLM 只用于复杂推理、"外科手术式"使用,且始终架在统计基础之上。从源码结构看,query_llm_config 让 LLM 目前仅参与查询扩展(对话补全),链接选择与停止判定仍由统计/向量信号完成——这与"LLM 不浪费在批量处理上"的理念一致。

仓库根目录还有一篇数学框架长文 PROGRESSIVE_CRAWLING.md,是对同一思想的更完整推导,适合作延伸阅读。

九、总结:处理"对"的数据,而不是处理更多数据

Adaptive Crawling 的设计哲学可以浓缩为原博文结尾的那句话:Grow knowledge on demand. Stop when you have enough.(按需生长知识,够了就停。)落到工程上,它是三件事的组合:

  1. 用 TF/DF、Jaccard、词发现速率等统计信号量化"够不够"(coverage / consistency / saturation);
  2. 用查询感知的链接预览 + 信息增益排序决定"先爬谁"(relevance / novelty 加权评分);
  3. digest 循环把两者闭环起来,并让 embedding 策略在语义空间里进一步识别覆盖缺口、检测无关查询。

对维护文档站知识库、API 文档 RAG、竞品资料监测这类"查询驱动"的采集场景,这套机制把"爬满 1,000 页"的问题转化为"回答一个查询需要多少页"的问题——这也正是 Crawl4AI 中 AdaptiveCrawler 区别于 crawl_bfs/dfs 等既有深度爬取策略的核心价值。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

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