RH
RouterHub AI API 中转站导航与平台推荐
博客

Cloudflare Web Search API 接入 Worker

Cloudflare Web Search API 把搜索结果统一成结构化响应,Worker 可直接调用并把结果作为工具回传给模型,但密钥、网关和结果语义仍需单独治理。

muchacha 2026-10-06 02:22:27

正文

先看结论

Cloudflare 在 2026 年 10 月 2 日发布了处于开放测试阶段的 Web Search API。它做的不是把任意模型变成“会浏览网页”的模型,而是提供一个独立的搜索调用:Worker 通过 env.AI.websearch() 发出查询,拿到包含标题、链接和描述的结构化结果,再把这些结果放回模型上下文。对开发者而言,最重要的变化是可以把“搜索供应商选择”和“模型生成”拆成两个可替换的步骤,同时继续沿用 AI Gateway 的日志、访问控制和网关配置。

这条边界决定了接入方式:如果应用只需要搜索结果,就直接调用 websearch();如果应用需要让模型自己决定何时搜索,就先给模型注册一个 web_search 函数工具,再由 Worker 执行搜索并进行第二次模型调用。两条路径都不应把搜索结果当作已经完成事实核验的答案,应用仍要保留来源链接,并根据任务类型设计引用、过滤和失败处理。

API 到底新增了什么

Cloudflare 的官方 changelog 将 Web Search API 描述为一项独立的搜索接口。它允许 AI Agent 或普通后端应用提交查询,并获得可以直接放进模型上下文的结构化结果,而不是让模型猜 URL 或只依赖训练截止时间。官方文档列出的统一结果字段包括 items 和 metadata;每个结果至少有 url、title 与 description,元数据可以包含原始查询、请求 ID 和延迟信息。

这和“在提示词中写请搜索网页”是两个不同层次的问题。提示词只能要求模型采取某种行为,不能凭空提供搜索能力;Web Search API 则把网络检索变成一个明确的后端依赖。你的代码可以记录查询、请求 ID 和返回来源,也可以在结果为空、上游不可用或查询超限时执行确定性的降级逻辑。对于需要审计的应用,这种显式边界比把一段不透明的搜索上下文塞进提示词更容易排查。

API 目前支持通过 provider 参数选择 Ceramic.ai、Exa 或 Linkup。Cloudflare 对三者使用统一的调用形状,因此更换 provider 不需要改动业务层的结果读取代码。不过,统一的是外层响应,不是搜索语义:Cloudflare 文档说明,Exa 的描述字段来自与查询相关的 highlights,Linkup 在该集成中使用 fast 搜索深度并返回原始搜索结果。也就是说,切换 provider 后仍要重新确认描述字段是否适合直接交给模型、是否需要二次抓取,以及你的引用展示是否会发生变化。

这里有一个容易忽略的限制:查询字符串最多 1,024 个字符,单次最多返回 10 条结果。limit 不是“尽可能多拿一些”的开关,而是一个明确的上限。长任务 Agent 不应把所有中间查询无限累积在上下文里,更稳妥的做法是限制查询长度和结果数,先筛选域名、标题或描述,再把少量候选内容交给模型。

Worker 的最小接入方式

先在 Wrangler 配置中加入 AI binding。Cloudflare 的官方示例要求配置 Worker 名称、入口文件、兼容日期和 ai.binding。下面只保留与 Web Search API 相关的最小结构:

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "web-search-worker",
  "main": "src/index.ts",
  "compatibility_date": "2026-10-06",
  "ai": {
    "binding": "AI"
  }
}

Worker 代码可以先把搜索能力做成一个普通 HTTP 接口。websearch() 返回标准 Response,因此调用方需要显式执行 response.json():

interface Env {
  AI: Ai;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== "POST") {
      return new Response("Method Not Allowed", { status: 405 });
    }

    const body = await request.json<{ query?: string }>();
    const query = body.query?.trim();
    if (!query) {
      return Response.json({ error: "query is required" }, { status: 400 });
    }
    if (query.length > 1024) {
      return Response.json({ error: "query is too long" }, { status: 400 });
    }

    const upstream = await env.AI.websearch({
      gatewayId: "default",
      query,
      provider: "exa",
      limit: 5,
    });

    if (!upstream.ok) {
      return Response.json(
        { error: "web search failed", status: upstream.status },
        { status: 502 },
      );
    }

    const results = await upstream.json();
    return Response.json(results);
  },
} satisfies ExportedHandler<Env>;

这段代码有三个值得保留的工程判断。第一,查询在进入上游之前做长度和空值校验,避免把明显不合法的请求交给网关。第二,返回给客户端的是结构化结果,而不是在 Worker 里先拼成一段自然语言;这样前端、模型编排层和日志系统仍能看到来源字段。第三,上游失败被映射成明确的 502,而不是把错误响应伪装成空搜索结果。空结果和搜索服务失败会导致完全不同的重试与提示策略。

REST API 的后端调用形状也很简单:向 Cloudflare 账户下的 /ai/websearch/ 发送 POST,请求体包含 query、provider、limit 和网关选项。无论采用 Worker binding 还是 REST,真正需要配置的是 AI Gateway 网关和凭证。官方文档说明,账户可以使用 AI Gateway credits,也可以在网关中保存搜索供应商自己的 API key;如果请求明确指定了不存在的 BYOK 别名,服务会返回 400,而不是静默退回 credits。生产环境应当把这个行为纳入启动检查和告警,不要等到第一次用户请求才发现凭证别名写错。

让模型按需调用搜索

如果每个请求都先搜索,系统会增加不必要的检索次数,也会让不需要实时信息的任务变慢。更适合 Agent 的编排方式是把搜索声明成模型可调用的工具。Cloudflare 的官方示例分成三步:先用 env.AI.run() 把 web_search 函数工具交给模型;模型返回工具调用后,Worker 读取参数并调用 env.AI.websearch();最后把搜索结果作为 tool 消息发回模型,获取最终回答。

核心代码可以写成下面的结构:

const messages = [
  { role: "user", content: "查找 Cloudflare Web Search API 的官方文档并总结接入限制。" },
];

const first = await env.AI.run("@cf/google/gemma-4-26b-a4b-it", {
  messages,
  tools: [
    {
      type: "function",
      function: {
        name: "web_search",
        description: "Search the web for current information.",
        parameters: {
          type: "object",
          properties: { query: { type: "string" } },
          required: ["query"],
        },
      },
    },
  ],
}, { gateway: { id: "default" } });

const call = first.tool_calls?.[0];
if (call?.name === "web_search") {
  const searchResponse = await env.AI.websearch({
    gatewayId: "default",
    query: call.arguments.query,
    provider: "linkup",
    limit: 5,
  });
  const searchResults = await searchResponse.json();

  const final = await env.AI.run("@cf/google/gemma-4-26b-a4b-it", {
    messages: [
      ...messages,
      {
        role: "tool",
        name: "web_search",
        content: JSON.stringify(searchResults),
      },
    ],
  }, { gateway: { id: "default" } });
}

示例中的 if 只是为了展示调用链,生产代码还要处理模型没有调用工具、一次返回多个工具调用、工具参数不是合法 JSON、搜索响应非 2xx,以及第二次模型调用失败的情况。尤其不要默认 tool_calls[0] 一定存在。一个稳妥的编排器应该为每次工具调用生成关联 ID,记录原始查询和搜索返回状态,并在最终回答中要求模型保留来源链接。

模型工具调用和搜索 API 是两个独立失败点。模型可能正确决定“需要搜索”,但搜索供应商随后超时;搜索可能成功返回结果,但第二次模型调用因为上下文过长失败。因此重试策略要按阶段拆分:搜索请求可以在满足幂等条件时重试,模型生成请求则要避免把同一工具结果重复追加多次。若应用无法判断某个结果是否已经写入上下文,宁可结束本轮并返回可诊断错误,也不要让 Agent 进入重复搜索循环。

供应商抽象不等于结果完全相同

Cloudflare 的统一 provider 参数适合把供应商选择放在配置层,但不能替代应用层的语义测试。至少要验证四件事。

第一,验证结果字段。统一响应给出了 URL、标题和描述,但不同供应商对描述的生成方式不同。Exa 的描述更接近与查询相关的页面 highlights;Linkup 的集成返回原始搜索结果。若你的 UI 把描述直接显示给用户,或者要求模型把描述当作证据,切换供应商时必须重新检查截断、引用位置和内容长度。

第二,验证来源策略。对于官方文档问答,可以在应用侧限制 includeDomains 一类的能力,或者在工具描述中要求模型优先选择官方域名;但 Web Search API 的统一接口本身不等于自动完成可信度排序。应用需要检查 URL 是否存在,必要时对关键页面再发起抓取或让用户打开原文。搜索结果是候选证据,不是你的业务数据库。

第三,验证上下文预算。搜索结果的描述越长,模型第二次调用的输入就越大。不要因为 limit 允许 10 条,就无条件把 10 条完整结果交给模型。可以先按域名、标题和重复 URL 去重,再设置最大描述字符数;如果任务需要全文,应把“搜索”和“读取页面”拆成两个明确工具,而不是偷偷把所有内容塞进一次提示词。

第四,验证数据边界。Cloudflare 文档说明,搜索请求经过 AI Gateway,因此可以在同一套网关体系中看到日志和分析数据;同时也支持 BYOK。开发者应在发送查询前决定哪些用户内容允许进入第三方搜索服务,必要时先做脱敏。不要把访问令牌、私有仓库路径、客户姓名或完整故障日志原样拼入搜索词。网关有日志不代表应用自动完成了隐私分级。

如果你还在选择 API relay 或模型渠道,可以先查看 RouterHub 的/平台目录,再把搜索供应商、模型端点和工具调用分开验收。对于已经接入网关的项目,也可以参考站内的AI Gateway 生产控制面文章,把日志、限流和降级纳入同一套请求观测,而不是只测“能不能返回一段答案”。

上线前的验收顺序

建议按由外到内的顺序验收,而不是一上来就让 Agent 自由搜索。

第一步,先用固定查询调用 websearch(),确认网关 ID、凭证、provider、返回状态和结果结构都正确。固定查询应包含一个你能打开原文核对的页面,这样可以区分“请求成功”和“结果真的可用”。

第二步,再验证错误输入:空查询、超过 1,024 个字符的查询、无效 provider、缺失 BYOK 别名和上游非 2xx。每类错误都应有可搜索的日志字段,至少包含请求 ID、业务会话 ID、provider 和阶段名。不要在客户端只显示“搜索失败”,否则后续无法判断是配置、权限还是上游问题。

第三步,加入模型工具调用,分别测试模型不调用工具、调用一次工具、重复调用工具和工具参数异常。为 Agent 设置最大搜索轮数;这个上限是防止循环的最后一道保险,不应只依赖提示词中的“不要重复搜索”。

第四步,验证来源呈现。最终回答要能让用户打开原始 URL,而不是只显示模型改写后的结论。对于政策、版本、错误码和 API 参数这类容易变化的事实,应用应优先展示官方来源,并在必要时在界面中显示检索时间。

第五步,做一次供应商切换测试。把 provider 从 Exa 换成 Linkup 或 Ceramic.ai,确认业务代码仍能解析统一字段,同时检查描述长度、来源排序和模型回答是否改变。切换成功只说明接口适配层工作,不说明三个供应商对同一问题给出的证据完全等价。

Cloudflare Web Search API 的实际价值,不在于替开发者选出一个“最好的搜索引擎”,而在于把搜索调用变成了可配置、可观测、可替换的基础能力。Worker 只负责明确的编排:校验查询、调用搜索、保留来源、把结果交回模型,并对每个失败阶段做不同处理。只要把这些边界写进代码,开放测试阶段的 API 也能以较小范围接入真实 Agent;如果把统一响应误解成统一质量,后续的引用、上下文和故障排查都会变得模糊。

参考来源