Cloudflare AI Search 正式 GA:原生图片检索、OCR,以及 11 月 1 日开始计费意味着什么
Cloudflare 的 AI Search 在 2026 年 10 月 1 日正式 GA,值得注意的不是「正式」两个字,而是这条检索流水线不再把图片当成文本问题来处理。在此之前,一张图能搜到,只取决于描述句恰好提到了什么:先做目标检测,写一句话,把这句话向量化。产品质感、截图状态、图表之间的关系、版面结构,全都在那次压缩里消失了。GA 版本直接对图片像素做向量化,同时保留描述用于文本理解,并用 Matryoshka 表示学习把向量规模压到可存。计费从 2026 年 11 月 1 日开始,所有 Workers 套餐继续保留免费额度,所以实际问题是:你的语料要花多少钱,哪个限额先顶到天花板。这是算术题,不是感觉题。
托管检索:失败模式很无聊,而基础设施就该无聊。
GA 到底改了什么
随正式发布一起上线的是三件事:原生图片向量、PDF 的光学字符识别(OCR),以及更大的文件支持。服务本身还是那套托管索引与检索管线,由 Workers AI、Vectorize、R2 和 Browser Run 组成;Cloudflare 自己的博客和开发者文档搜索就跑在它上面。
计费是排期而不是立即生效。面向用量的计费从 2026 年 11 月 1 日开始,包含每月的摄取、存储、语义查询与全文查询额度,同时所有 Workers 套餐继续有免费额度。如果你已经在跑实例,接下来这几周正是用新计量方式量出真实查询量与摄取量的窗口,而不是靠猜。
# Before you design around AI Search, check which limit binds first for your corpus.
FREE = {"instances": 100, "files_per_instance": 100_000, "queries_per_month": 20_000,
"pages_crawled_per_day": 500, "max_file_mb": 4}
PAID = {"instances": 5_000, "files_per_instance": 1_000_000, "queries_per_month": None,
"pages_crawled_per_day": None, "max_file_mb": 4}
def headroom(plan, docs, queries, pages, largest_file_mb):
limits = FREE if plan == "free" else PAID
months = [
("files", docs / limits["files_per_instance"]),
("pages/day", pages / (limits["pages_crawled_per_day"] or 1e9)),
("file size", largest_file_mb / limits["max_file_mb"]),
("queries", (queries / limits["queries_per_month"]) if limits["queries_per_month"] else 0),
]
return sorted(((name, round(ratio, 2)) for name, ratio in months), key=lambda kv: -kv[1])
print(headroom("free", docs=40_000, queries=12_000, pages=180, largest_file_mb=3.2))
# [('queries', 0.6), ('pages/day', 0.36), ('files', 0.4), ('file size', 0.8)]
# Highest ratio is what you plan around. Free tier: file size and query volume usually win.
多模态那块才是真工程
旧的实现对自身的粗糙很坦诚:目标检测、生成描述、把描述向量化。图片因此可搜,但只通过描述捕捉到的细节。新路径两件事都做——既保留基于描述的理解,也做原生图片检索——而且描述不再需要预判「未来的查询会在意哪些细节」。
原生检索跑在 Qwen3-VL-Embedding 上。查询时 AI Search 会检查你这个实例的向量模型是否支持图片。支持,就把查询图片直接嵌进与被索引图片、文本相同的向量空间;不支持,就用 ToMarkdown 把图片转成文本再按描述检索——这样每个模型都有基本的多模态能力,而原生模型能拿到完整的视觉信号。
能保持可负担的原因是 Matryoshka 表示学习:它让更小的向量仍然保留有用信息,于是存储和检索延迟不会随着表示变丰富而线性膨胀。
# Crawl budgets interact: pages per crawl, files per instance, pages per day.
def crawl_days(pages, plan="free"):
per_day = FREE["pages_crawled_per_day"] if plan == "free" else float("inf")
days = pages / per_day if per_day != float("inf") else 1
return {"pages": pages, "plan": plan, "days_to_ingest": days,
"files_limit_hit": pages > (FREE if plan == "free" else PAID)["files_per_instance"]}
print(crawl_days(12_000)) # free tier: 24 days at 500 pages/day
print(crawl_days(12_000, "paid")) # paid: single pass, unlimited pages/day
# A discover crawl accepts up to 100,000 pages, but the lowest of the three limits binds.
跟一遍查询链路,再决定信哪儿
一次查询走的是一条固定链路:可选的改写,然后向量化,然后向量检索与关键词检索并行,再融合,可选重排,最后返回 top chunks,或把它交给生成模型写成答案。知道顺序很重要,因为它告诉你该在哪里动手。如果结果语义上接近但漏掉错误码、SKU 这类精确词,是关键词那一路权重不够;如果结果关键词精确但概念跑偏,是向量那一路在起作用。
融合是大多数混合检索系统悄悄失败的地方。倒数排名融合(RRF)是不错的默认值,因为两个原本没打算互相比较的检索系统之间,它不需要做分数校准。下面的代码说明:想搞清自己的融合逻辑,其实只需要这么几行,不必接受一个黑箱。
# Hybrid retrieval fuses vector and keyword results. Reciprocal rank fusion is 4 lines.
def rrf(vector_hits, keyword_hits, k=60):
'''vector_hits / keyword_hits: ordered lists of chunk ids'''
scores = {}
for weight, hits in ((1.0, vector_hits), (1.0, keyword_hits)):
for rank, doc in enumerate(hits, start=1):
scores[doc] = scores.get(doc, 0.0) + weight / (k + rank)
return sorted(scores.items(), key=lambda kv: -kv[1])
VEC = ["chunk_9", "chunk_3", "chunk_14", "chunk_1"]
BM25 = ["chunk_3", "chunk_1", "chunk_22", "chunk_9"]
for doc, score in rrf(VEC, BM25)[:5]:
print(f"{doc:10}{score:.4f}")
# AI Search runs vector and keyword search in parallel, fuses, optionally reranks,
# then returns chunks or hands the top ones to a generation model.
图片像素直接向量化,描述同时保留。两个信号,而不是一个。
决定架构的是这些限额
Workers 免费额度:每账号 100 个 AI Search 实例、100 个命名空间、每实例 10 万个文件、单文件最大 4 MB、每月 2 万次查询、跨实例搜索每次最多 10 个实例、每天最多抓取 500 页。Workers 付费版:5,000 个实例、每实例 100 万文件(混合检索为 50 万)、查询不限量、每日抓取不限量。
有两个细节容易踩。4 MB 的文件上限在免费和付费完全一样,所以大 PDF 语料不管付不付钱都得先做预处理。而 discover 抓取虽然接受最多 10 万页,但同时受「每实例文件数」和「每日抓取页数」约束,真正的天花板是这三者里最低的那个。免费版那个是每天 500 页,也就是说 1.2 万页的站点要 24 天才能摄取完。
# Image queries: route by whether the embedding model can take images natively.
def route_query(query_image, text_query, supports_image_embeddings):
if query_image and supports_image_embeddings:
return {"mode": "native_image", "note": "embedded by Qwen3-VL-Embedding, same vector space as indexed images"}
if query_image:
return {"mode": "caption_fallback", "note": "image converted to text with ToMarkdown before search"}
if text_query:
return {"mode": "text", "note": "standard hybrid retrieval"}
return {"mode": "invalid", "note": "need an image or a query"}
print(route_query(True, None, True))
print(route_query(True, None, False))
print(route_query(None, "bird perched on a leafy branch", True))
# Text-only models still work with image queries, they just lose the fine visual signal.
价格里包含什么,不包含什么
存储、向量索引,以及网站抓取所消耗的 Browser Run 用量,都包含在 AI Search 里。Workers AI 和 AI Gateway 仍然单独计费。这个切分才是要建模的部分,因为到规模上,生成通常才是主导的可变成本,而检索相对平稳。
还有一个迁移细节。在 AI Search 迁到托管基础设施之前创建的实例,用的是你自己账号里的各项服务,所以旧账单里可能包含单独的 R2、Vectorize、Workers AI、AI Gateway 费用。迁移之后,抓取到的页面存在内置存储里,AI Search 最初创建的那个专用 R2 桶不再被写入。它还在你的账号里,里面残留的对象可能仍然计入 R2 存储,所以删之前先看清里面有什么。
# Migration: after GA, AI Search stores pages itself. Old R2 buckets are no longer written.
import boto3
def legacy_bucket_report(bucket, prefix="ai-search/", sample=1000):
s3 = boto3.client("s3", endpoint_url="https://<accountid>.r2.cloudflarestorage.com")
listing = s3.list_objects_v2(Bucket=bucket, Prefix=prefix, MaxKeys=sample)
objects = listing.get("Contents", [])
size = sum(o["Size"] for o in objects)
return {"sampled_objects": len(objects), "sampled_mb": round(size / 1e6, 1),
"truncated": listing.get("IsTruncated", False)}
print(legacy_bucket_report("legacy-ai-search-bucket"))
# Check the sample before deleting anything: R2 storage for that bucket may still bill,
# and only you know whether anything else points at those objects.
真正顶到天花板的通常是文件大小或查询量,而不是实例数量。
自建还是托管,说点实在的
当你的语料是文档和网页、团队很小、检索质量要求是「能拿自己的内容回答问题就够了」,托管索引就是正确答案。免费额度每月 2 万次查询,撑一个内部工具或文档站没问题,而且它的失败模式很无聊——基础设施就该无聊。
什么时候自建?当排序必须编码领域判断、当你要托管的服务不会暴露的 chunk 级控制、或者语料根本不全是文本的时候。多模态的补强确实让图片类目更近了,但没有把差距抹平:索引你还是看不进去,而且托管服务永远比你上周读到的那个检索技巧慢一步。
这个月我会做一件不体面但有用的事:导出用户真正会敲的一百条查询,同时打到现有方案和一个 AI Search 实例上,人工给前五条结果打分;然后按新计量把需要的摄取量算成钱。六周的测量,胜过六个月的架构讨论。
📌 常见问题 FAQ
Cloudflare AI Search 什么时候正式可用的?
2026 年 10 月 1 日进入 GA。这次 GA 新增了原生图片向量、PDF 的 OCR 与更大的文件支持。
计费从什么时候开始?
面向用量的计费从 2026 年 11 月 1 日开始,包含每月额度内的摄取、存储、语义查询与全文查询;所有 Workers 套餐仍保留免费额度。
原生图片检索和描述检索差在哪?
描述检索向量化的是生成的描述,描述没提到的内容就搜不到。原生检索用 Qwen3-VL-Embedding 直接向量化图片像素,保留质感、版面、色彩与空间关系——这些是简短描述承载不了的。
免费额度的限额是多少?
每账号 100 个实例、100 个命名空间、每实例 10 万个文件、单文件 4 MB、每月 2 万次查询、每日抓取 500 页;discover 抓取单次最多 10 万页,但实际受其中最低的那个限额约束。
存储和抓取包含在价格里吗?
存储、向量索引,以及网站抓取消耗的 Browser Run 用量都包含在 AI Search 里。Workers AI 与 AI Gateway 的用量单独计费。