@dbx-tools/appkit-web-search

中文文档
0.9.206发布于 2026/11/11Apache-2.0TypeScriptES Module构建来源已验证近 30 天下载 8743

Server-side web search runtime, Mastra tools, and AppKit plugin

原始使用文档
文档目录10 节
  1. Quick start
  2. Choose a web provider
  3. Configure the AppKit plugin
  4. Choose the web-search model
  5. Scrape fallback
  6. Search and fetch without an agent
  7. Restrict reachable URLs
  8. Gate calls for approval
  9. Configuration
  10. Modules

@dbx-tools/appkit-web-search

Add web_search and web_fetch to an AppKit or Mastra backend. web_search runs on the Databricks Model Serving native web-search tool: the model searches the web server-side and returns a synthesized answer plus the sources it used without a third-party search API key. web_fetch reads a single page through got-scraping.

Quick start

Register the plugin and give its tools to an agent:

import { createApp, server } from "@databricks/appkit";
import { webSearch } from "@dbx-tools/appkit-web-search";
import { agents, mastra } from "@dbx-tools/appkit-mastra";

const researcher = agents.createAgent({
  instructions: "Search the web, then read the most relevant sources.",
  async tools(plugins) {
    return { ...(await plugins["web-search"].toolkit()) };
  },
});

await createApp({
  plugins: [server(), webSearch(), mastra({ agents: { researcher } })],
});

Registration provides:

  • AppKit plugin registration that resolves and logs the effective policy at boot.
  • Two Mastra tools: web_search (answer + citations) and web_fetch (page contents as readable text or raw HTML), plus the same pair as AppKit agent tools through the plugin's ToolProvider.
  • web_search resolves a dedicated web-search-capable model (Gemini by default, then GPT) independently of the calling agent's chat model (which may not support web search). Loose names ("gemini", "gpt") fuzzy-match the live catalogue through @dbx-tools/model.
  • A built-in provider -> request-fragment map (OpenAI Responses API {"tools":[{"type":"web_search"}]}, Gemini Chat Completions {"google_search":{}}), overridable per provider with the WEB_SEARCH_TOOLS setting.
  • A named URL policy (allowlist or unrestricted) over a glob allowlist built on @dbx-tools/path's match matcher: web_search silently filters disallowed citations, web_fetch refuses a disallowed URL, and the mode in force is logged at boot.
  • Per-tool approval gating: gate every call, or (for web_fetch) only calls whose URL matches a pattern, mapped onto Mastra's requireApproval.
  • Every outbound call runs through AppKit's execute() chain (per-user cache, retry with jittered backoff, timeout, and telemetry) and unwinds on an AbortSignal. Each plugin instance keeps its policy and executor separate, so multiple apps in one process remain isolated.
  • Page and fallback-result HTML use parser-backed text conversion and selectors, including complete HTML entity decoding and malformed-markup recovery.

Choose a web provider

AppKit has no first-party web-search or page-fetch surface. Use this package when an agent needs to look things up on the open web or read a URL the user pasted, with a policy layer (allowlist + optional approval) around it so a deployment controls which sites are reachable and which calls pause for a human. The web-search tool resolves its own web-search-capable model, so an agent running on any chat model (including one without web search) can still search. It provides a Mastra tool pair and an AppKit plugin, like @dbx-tools/email.

Use native Databricks web search when a supported GPT or Gemini endpoint is deployed. Use the DuckDuckGo scrape fallback only when no capable endpoint is available and unsynthesized search results are acceptable. Use web_fetch for a specific public HTTPS page. For indexed application data, use @dbx-tools/search instead.

Configure the AppKit plugin

import { createApp, lakebase, server } from "@databricks/appkit";
import { webSearch } from "@dbx-tools/appkit-web-search";
import { agents, mastra } from "@dbx-tools/appkit-mastra";

const researcher = agents.createAgent({
  instructions: "Research questions using web_search, then read sources with web_fetch.",
  async tools(plugins) {
    return { ...(await plugins["web-search"].toolkit()) };
  },
});

await createApp({
  plugins: [
    server(),
    lakebase(),
    webSearch({
      model: "gemini", // defaults to Gemini, then GPT, when omitted
      allowedUrls: ["*.databricks.com", "docs.example.com"],
    }),
    mastra({ agents: researcher, storage: true }),
  ],
});

plugin.webSearch() resolves config (over env), compiles the URL policy, and provides the executor used by its tools. App-integrated Mastra agents can consume the native plugin toolkit as shown above, which preserves that plugin's policy, OBO scope, and AppKit execution chain. Approval, when enabled, requires Mastra storage, so register lakebase() or configure storage in the Mastra plugin.

The plugin is also an AppKit ToolProvider, so a native code agent can take the same two tools without Mastra. Put the definition at server/agents/researcher/agent.ts so AppKit discovers it:

import { createAgent } from "@databricks/appkit/beta";

export default createAgent({
  name: "Researcher",
  default: true,
  model: "databricks-gpt-6-1-sol",
  instructions: "Research questions with web_search, then read sources with web_fetch.",
  tools: (plugins) => ({ ...plugins["web-search"].toolkit() }),
  skills: ["research-policy"],
});

Place the curated skill at server/agents/skills/research-policy/SKILL.md, register native agents() with no deprecated agent map, and build a compiled native-agent server with:

import { appkitServerConfig } from "@databricks/appkit/tsdown";

export default appkitServerConfig();

Both tools are annotated effect: "read" and require user context, and neither is auto-inheritable: an agent has to ask for them, because web_fetch reaches whatever URL the model produces.

Native volume skills run as the service principal. The 0.81 skillCredentialMode: "obo" option is not wired. Keep request-scoped writable skills, remote sources, asynchronous Graphiti discovery, Sandbox/Monty, Mastra memory, and durable approvals on @dbx-tools/appkit-mastra.

Choose the web-search model

The native web-search tool only runs on certain models, and it is provider- specific. This package resolves deployed endpoints through @dbx-tools/model and applies this order:

  1. Highest-ranked web-search-capable Gemini.
  2. Without Gemini, a capable GPT matching the calling/requested model.
  3. Highest-ranked capable GPT.
  4. Any additional modelFallbacks searches in order.

When Databricks reports that Gemini web search is unavailable because cross-region processing is disabled, the plugin suppresses Gemini for 24 hours in that runtime and transparently retries the same tool call with the next GPT candidate.

DATABRICKS_SERVING_ENDPOINT_NAME is AppKit's standard name for a Model Serving binding, and it is the field the plugin declares in its manifest, so app.yaml / bundle wiring reaches this plugin the way it reaches any other. The request's model, plugin model, and environment model values are used to match the calling GPT after Gemini availability is checked. When the tool runs through @dbx-tools/appkit-mastra, the host injects the resolved chat model, overriding any different model argument generated by the LLM.

Model resolution runs against the live workspace catalogue through @dbx-tools/model, so it only picks an endpoint that is actually deployed. A configured id that doesn't exist is never called.

Scrape fallback

The native tool is preferred. When the workspace has no deployed GPT/Gemini endpoint (so native web search can't run at all), the tool falls back to a DuckDuckGo scrape using got-scraping so it still returns results instead of erroring. Fallback results set model to "scrape:duckduckgo" and carry the substance in citations (there is no model synthesizing an answer, so answer is a lead-in over the top results). It is enabled by default. Set scrapeFallback: false (or WEB_SEARCH_SCRAPE_FALLBACK=0) to require a native model and error when none is deployed.

The plugin selects the provider request fragment from the resolved model: OpenAI GPT uses the Responses API {"tools":[{"type":"web_search"}]}. Gemini uses the top-level Chat Completions field {"google_search":{}}. Override or extend that map per provider with the webSearchTools setting (env WEB_SEARCH_TOOLS, JSON) as the platform evolves:

plugin.webSearch({
  model: "databricks-gemini-3-pro",
  webSearchTools: { gemini: { request: { google_search: {} } } },
});

Search and fetch without an agent

import { search, fetch, runtime, tool } from "@dbx-tools/appkit-web-search";
import { getExecutionContext } from "@databricks/appkit";

const webRuntime = runtime.createWebSearchRuntime({
  model: "gemini",
  allowedUrls: ["*.databricks.com"],
});

// web_search needs the OBO client + host from the active execution context:
const ctx = getExecutionContext();
const host = (await ctx.client.config.getHost()).toString();

const result = await search.runWebSearch(
  { query: "unity catalog lineage best practices" },
  webRuntime,
  { client: ctx.client, host },
);
// result.answer, result.citations, result.model

const page = await fetch.runWebFetch({ url: result.citations[0]!.url, format: "text" }, webRuntime);

Use direct calls for operational lookups, tests, or admin flows where a model is not involved. Standalone Mastra tool factories likewise accept runtime or config. They never discover or attach to a process-global plugin:

const webRuntime = runtime.createWebSearchRuntime({
  allowedUrls: ["*.databricks.com"],
});

const tools = {
  web_search: tool.webSearchTool({ runtime: webRuntime }),
  web_fetch: tool.webFetchTool({ runtime: webRuntime }),
};

Pass the runtime explicitly or provide the executor to createWebSearchRuntime.

Restrict reachable URLs

import { allowlist } from "@dbx-tools/appkit-web-search";

const list = allowlist.toUrlAllowList(
  allowlist.parseAllowedUrls(["*.databricks.com", "docs.example.com/api/**"]),
);

list.allows("https://docs.databricks.com/aws/en/index.html"); // true
list.allows("https://evil.example.com/"); // false

Each entry is a glob compiled by @dbx-tools/path's match matcher. A host entry (no path, for example databricks.com or *.databricks.com) matches the URL's hostname, and a bare host also matches its subdomains. A path entry (docs.example.com/api/**) matches host + pathname. Enforcement is asymmetric by design: web_search citations are silently filtered to the permitted set (the model never surfaces a source it then can't fetch), while an explicit web_fetch of a disallowed URL is refused with an error (a visible, correctable mistake). web_fetch accepts only HTTPS targets that resolve to public addresses, and reapplies both checks before every redirect request.

Which of the two modes is in force is named by urlPolicy, and the effective mode is in the boot log:

urlPolicyEffect
"allowlist"Only allowedUrls entries are reachable
"unrestricted"Any public HTTPS URL is reachable

Leave urlPolicy unset and it follows allowedUrls: "allowlist" when there are entries, "unrestricted" when there are none. Set it to say which one you meant. The two contradictions fail at startup rather than quietly picking a side: "allowlist" with no entries, and "unrestricted" with entries.

// An open deployment, said out loud:
plugin.webSearch({ urlPolicy: "unrestricted" });

// A closed one:
plugin.webSearch({ urlPolicy: "allowlist", allowedUrls: ["*.databricks.com"] });

Gate calls for approval

Both tools run without approval by default. Pass approval to a tool (or set it plugin-wide) to require approval. true gates every call. A URL pattern (or list) gates only calls whose URL matches. A web_fetch is evaluated precisely against its target URL, while a web_search (whose result URLs are unknown before the call) treats a pattern gate as "gate every call".

import { tool } from "@dbx-tools/appkit-web-search";

// Every fetch requires approval:
tool.webFetchTool({ approval: true });

// Only fetches of an internal domain require approval:
tool.webFetchTool({ approval: "*.internal.example.com" });

// The same thing, with the mode named:
tool.webFetchTool({ approval: { mode: "urls", patterns: ["*.internal.example.com"] } });

// Plugin toolkit default:
plugin.webSearch({ approval: ["*.internal.example.com", "*.corp.example.com"] });

// Standalone tool config:
tool.webFetchTool({ config: { approval: ["*.internal.example.com"] } });

Every spelling normalizes to one of three modes ({ mode: "none" }, { mode: "always" }, or { mode: "urls", patterns }), which is what the resolved config carries and the boot log reports. Approval uses the same glob syntax as the allowlist, so the two policies read identically.

Configuration

Resolution order is explicit config first, then the environment variable, then a built-in default.

OptionTypeDefaultDescription
modelstringresolved from fallbacksWeb-search endpoint: an endpoint name, a loose name, or a capability class.
modelFallbacksstring | string[]Gemini, then GPTOrdered candidates tried when model is unset.
webSearchToolsRecord<string, unknown>built-in provider mapProvider -> request-fragment overrides, merged over the defaults.
modelFuzzyMatchbooleantrueFuzzy-match loose model names against the live catalogue.
modelFuzzyThresholdnumber0.4Fuse.js score below which a fuzzy match is accepted.
maxCitationsnumber10Hard cap on citations returned from one search.
fetchMaxLengthnumber50000Hard cap on web_fetch content length, in characters.
timeoutMsnumber60000Per-request network timeout.
scrapeFallbackbooleantrueScrape DuckDuckGo when no web-search-capable model is deployed.
urlPolicy"unrestricted" | "allowlist"follows allowedUrlsWhich URLs the tools may reach.
allowedUrlsstring | string[]noneAllow-list globs or bare hosts.
approvalboolean | string | string[] | ApprovalPolicy{ mode: "none" }Default approval gate for both tools.
Environment variableSets
WEB_SEARCH_MODELmodel (dedicated override, wins over the binding below)
DATABRICKS_SERVING_ENDPOINT_NAMEmodel (AppKit's Model Serving binding, treated as a preference)
WEB_SEARCH_MODEL_FALLBACKSmodelFallbacks (comma/space-separated)
WEB_SEARCH_TOOLSwebSearchTools (JSON object. Any other value fails at boot.)
WEB_SEARCH_FUZZYmodelFuzzyMatch
WEB_SEARCH_FUZZY_THRESHOLDmodelFuzzyThreshold
WEB_SEARCH_MAX_CITATIONSmaxCitations
WEB_SEARCH_FETCH_MAX_LENGTHfetchMaxLength
WEB_SEARCH_TIMEOUT_MStimeoutMs
WEB_SEARCH_SCRAPE_FALLBACKscrapeFallback
WEB_SEARCH_URL_POLICYurlPolicy
WEB_SEARCH_ALLOWED_URLSallowedUrls (comma/space-separated)

The plugin's maxCitations / fetchMaxLength are hard caps: a per-call request may narrow them but never exceed them.

Every outbound call, including the serving POST, a page fetch, and the scrape fallback, runs through the plugin's execute() chain, so all three are cached, retried with jittered backoff, timed out, and traced. The cache is namespaced per user, so an on-behalf-of result never crosses identities. See defaults.ts for the TTLs and retry budgets and why each was chosen.

OpenAI Responses web searches stream native search, page-read, and completion status through the Mastra tool writer. The common tool-session pill updates while the search runs. The completed Responses payload remains the tool result.

Requirements: the native web-search tool runs on pay-per-token GPT / Gemini serving endpoints with cross-region processing enabled. It is unavailable on provisioned throughput, for external models, or under HIPAA/BAA compliance. See the Databricks docs.

Modules

  • plugin - WebSearchPlugin, webSearch() AppKit plugin factory, and the AppKit ToolProvider implementation.
  • tool - webSearchTool() / webFetchTool() Mastra tools.
  • defaults - the cache / retry / timeout settings every outbound call runs with, and the reasoning behind each.
  • search - runWebSearch() over the Databricks native web-search tool.
  • provider - provider detection + the provider -> tool-spec map.
  • scrape - runScrapeSearch() DuckDuckGo fallback plus parser-backed parseDdgHtml() result extraction for workspaces with no native web-search model.
  • fetch - runWebFetch() over got-scraping.
  • html-text - htmlToText() / htmlFragmentToText() / decodeHtmlEntities(), shared by fetch and the DuckDuckGo scrape fallback.
  • runtime - isolated runtime construction and execution: createWebSearchRuntime(), createResolvedWebSearchRuntime(), and executeRead().
  • config - config types, JSON schema, resolveWebSearchConfig(), URL policy and approval helpers.
  • allowlist - URL allowlist parsing/compiling on top of @dbx-tools/path.
  • schema - zod tool contracts and inferred types.

Pair this package with @dbx-tools/appkit-mastra to add the tools to an agent and resolve its model through @dbx-tools/model. Use @dbx-tools/email for the sibling approval-gated tool pattern.

版本与发布记录

@dbx-tools/appkit-web-search 共发布 318 个版本,首次发布于 2026/07/26;过去 12 个月发布了 318 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.9.206latest2026/11/11今天
0.9.2052026/11/111 天前
0.9.2032026/11/074 天前
0.9.2022026/11/065 天前
0.9.2012026/11/056 天前
0.9.2002026/11/056 天前

关于 @dbx-tools/appkit-web-search 的常见问题

@dbx-tools/appkit-web-search 是什么?

Server-side web search runtime, Mastra tools, and AppKit plugin

如何安装 @dbx-tools/appkit-web-search?

在项目目录运行 npm install @dbx-tools/appkit-web-search,或使用 pnpm add / yarn add / bun add。

@dbx-tools/appkit-web-search 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@dbx-tools/appkit-web-search 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@dbx-tools/appkit-web-search 可以商用吗?

@dbx-tools/appkit-web-search 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@dbx-tools/appkit-web-search 的下载量有多少?

根据 npm 官方下载统计,@dbx-tools/appkit-web-search 在 2026/10/12 – 2026/11/10 这 30 天内被下载 8,743 次,日均约 291 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@dbx-tools/appkit-web-search 还在维护吗?

最新版本 0.9.206 发布于 2026/11/11(今天);至今共发布 318 个版本,其中过去 12 个月发布了 318 个;首次发布于 2026/07/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。