openclaw websearch修改brave search 为百度搜索API接口
使用方法:
1.百度开发平台注册搜索接口APIKEY
2.替换dist/agents/tools/web-search.js JS文件里内容为:

import { Type } from "@sinclair/typebox";
import { formatCliCommand } from "../../cli/command-format.js";
import { jsonResult, readNumberParam, readStringParam } from "./common.js";
import {
  DEFAULT_CACHE_TTL_MINUTES,
  DEFAULT_TIMEOUT_SECONDS,
  normalizeCacheKey,
  readCache,
  readResponseText,
  resolveCacheTtlMs,
  resolveTimeoutSeconds,
  withTimeout,
  writeCache,
} from "./web-shared.js";

const SEARCH_PROVIDERS = ["baidu"];
const DEFAULT_SEARCH_COUNT = 10;
const MAX_SEARCH_COUNT = 50;
const BAIDU_SEARCH_ENDPOINT = "https://qianfan.baidubce.com/v2/ai_search/web_search";

// 百度搜索缓存
const SEARCH_CACHE = new Map();

// 百度搜索参数定义
const WebSearchSchema = Type.Object({
  query: Type.String({ 
    description: "Search query string (max 72 characters)." 
  }),
  count: Type.Optional(Type.Number({
    description: `Number of results to return (1-${MAX_SEARCH_COUNT}).`,
    minimum: 1,
    maximum: MAX_SEARCH_COUNT,
  })),
  freshness: Type.Optional(Type.String({
    description: "Filter results by publish time. Values: 'week' (last 7 days), 'month' (last 30 days), 'semiyear' (last 180 days), 'year' (last 365 days).",
  })),
  site: Type.Optional(Type.String({
    description: "Specify a site to search within (e.g., 'example.com').",
  })),
  edition: Type.Optional(Type.String({
    description: "Search edition. 'standard' (full version) or 'lite' (standard version with better latency).",
    enum: ["standard", "lite"],
  })),
  safe_search: Type.Optional(Type.Boolean({
    description: "Enable safe search to filter sensitive content. Default: false.",
  })),
});

// 解析搜索配置
function resolveSearchConfig(cfg) {
  const search = cfg?.tools?.web?.search;
  if (!search || typeof search !== "object") {
    return undefined;
  }
  return search;
}

// 解析搜索是否启用
function resolveSearchEnabled(params) {
  if (typeof params.search?.enabled === "boolean") {
    return params.search.enabled;
  }
  if (params.sandboxed) {
    return true;
  }
  return true;
}

// 解析百度API Key
function resolveSearchApiKey(search) {
  const fromConfig = search && "apiKey" in search && typeof search.apiKey === "string" 
    ? search.apiKey.trim() 
    : "";
  const fromEnv = (process.env.BAIDU_API_KEY || process.env.QIANFAN_API_KEY || "").trim();
  return fromConfig || fromEnv || undefined;
}

// API Key缺失的错误信息
function missingSearchKeyPayload() {
  return {
    error: "missing_baidu_api_key",
    message: `web_search needs a Baidu Qianfan API key. Run \`${formatCliCommand("openclaw configure --section web")}\` to store it, or set BAIDU_API_KEY in the Gateway environment.`,
    docs: "https://docs.openclaw.ai/tools/web",
  };
}

// 解析搜索提供商(仅百度)
function resolveSearchProvider(search) {
  return "baidu"; // 目前只支持百度
}

// 解析搜索数量
function resolveSearchCount(value, fallback) {
  const parsed = typeof value === "number" && Number.isFinite(value) ? value : fallback;
  const clamped = Math.max(1, Math.min(MAX_SEARCH_COUNT, Math.floor(parsed)));
  return clamped;
}

// 规范化新鲜度参数
function normalizeFreshness(value) {
  if (!value) {
    return undefined;
  }
  
  const trimmed = value.trim().toLowerCase();
  const freshnessMap = {
    "week": "week",
    "month": "month",
    "semiyear": "semiyear",
    "year": "year",
    "w": "week",
    "m": "month",
    "s": "semiyear",
    "y": "year",
    "7d": "week",
    "30d": "month",
    "180d": "semiyear",
    "365d": "year",
  };
  
  return freshnessMap[trimmed] || undefined;
}

// 运行百度搜索
async function runBaiduSearch(params) {
  const requestBody = {
    messages: [
      {
        content: params.query.substring(0, 72), // 百度限制72字符
        role: "user",
      },
    ],
    search_source: "baidu_search_v2",
    resource_type_filter: [
      {
        type: "web",
        top_k: params.count,
      },
      {
        type: "video",
        top_k: 0,
      },
      {
        type: "image",
        top_k: 0,
      },
      {
        type: "aladdin",
        top_k: 0,
      },
    ],
  };

  // 可选参数
  if (params.edition) {
    requestBody.edition = params.edition;
  }

  if (params.freshness) {
    requestBody.search_recency_filter = params.freshness;
  }

  if (params.site) {
    requestBody.search_filter = {
      match: {
        site: [params.site],
      },
    };
  }

  if (typeof params.safe_search === "boolean") {
    requestBody.safe_search = params.safe_search;
  }

  const start = Date.now();
  const res = await fetch(BAIDU_SEARCH_ENDPOINT, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${params.apiKey}`,
      "X-Appbuilder-Authorization": `Bearer ${params.apiKey}`,
    },
    body: JSON.stringify(requestBody),
    signal: withTimeout(undefined, params.timeoutSeconds * 1000),
  });

  if (!res.ok) {
    const detail = await readResponseText(res);
    throw new Error(`Baidu Search API error (${res.status}): ${detail || res.statusText}`);
  }

  const data = await res.json();
  
  // 处理错误响应
  if (data.code) {
    throw new Error(`Baidu Search API error (${data.code}): ${data.message || "Unknown error"}`);
  }

  // 转换百度响应格式为通用格式
  const references = data.references || [];
  const results = references
    .filter(ref => ref.type === "web") // 只处理网页结果
    .map((ref, index) => ({
      title: ref.title || "",
      url: ref.url || "",
      description: ref.content || "",
      published: ref.date || undefined,
      siteName: ref.website || resolveSiteName(ref.url),
      relevanceScore: ref.rerank_score || 0,
      authorityScore: ref.authority_score || 0,
    }));

  return {
    query: params.query,
    provider: "baidu",
    count: results.length,
    totalCount: references.length,
    tookMs: Date.now() - start,
    requestId: data.request_id,
    results: results,
  };
}

// 从URL解析站点名称
function resolveSiteName(url) {
  if (!url) {
    return undefined;
  }
  try {
    return new URL(url).hostname;
  } catch {
    return undefined;
  }
}

// 运行Web搜索(主函数)
async function runWebSearch(params) {
  const cacheKey = normalizeCacheKey(
    `baidu:${params.query}:${params.count}:${params.freshness || "default"}:${params.site || "default"}:${params.edition || "standard"}:${params.safe_search || "false"}`
  );

  // 检查缓存
  const cached = readCache(SEARCH_CACHE, cacheKey);
  if (cached) {
    return { ...cached.value, cached: true };
  }

  let result;
  if (params.provider === "baidu") {
    result = await runBaiduSearch(params);
  } else {
    throw new Error(`Unsupported web search provider: ${params.provider}`);
  }

  // 写入缓存
  writeCache(SEARCH_CACHE, cacheKey, result, params.cacheTtlMs);
  return result;
}

// 创建Web搜索工具
export function createWebSearchTool(options) {
  const search = resolveSearchConfig(options?.config);
  
  if (!resolveSearchEnabled({ search, sandboxed: options?.sandboxed })) {
    return null;
  }

  const provider = resolveSearchProvider(search);
  
  return {
    label: "Web Search",
    name: "web_search",
    description: "Search the web using Baidu Qianfan Search API. Returns real-time web search results with titles, URLs, and content snippets. Supports site-specific search and freshness filtering.",
    parameters: WebSearchSchema,
    execute: async (_toolCallId, args) => {
      //const apiKey = resolveSearchApiKey(search);
      const   apiKey=替换为自己的百度搜索APIKEY
      if (!apiKey) {
        return jsonResult(missingSearchKeyPayload());
      }

      const params = args;
      const query = readStringParam(params, "query", { required: true });
      
      // 验证查询长度
      if (query.length > 72) {
        return jsonResult({
          error: "query_too_long",
          message: "Search query must be 72 characters or less for Baidu Search API.",
          docs: "https://docs.openclaw.ai/tools/web",
        });
      }

      const count = readNumberParam(params, "count", { integer: true }) ?? search?.maxResults ?? undefined;
      const rawFreshness = readStringParam(params, "freshness");
      const site = readStringParam(params, "site");
      const edition = readStringParam(params, "edition");
      const safeSearch = params.safe_search; // 已经是boolean类型
      
      // 验证新鲜度参数
      const freshness = rawFreshness ? normalizeFreshness(rawFreshness) : undefined;
      if (rawFreshness && !freshness) {
        return jsonResult({
          error: "invalid_freshness",
          message: "freshness must be one of: 'week', 'month', 'semiyear', 'year'.",
          docs: "https://docs.openclaw.ai/tools/web",
        });
      }

      const result = await runWebSearch({
        query,
        count: resolveSearchCount(count, DEFAULT_SEARCH_COUNT),
        apiKey,
        timeoutSeconds: resolveTimeoutSeconds(search?.timeoutSeconds, DEFAULT_TIMEOUT_SECONDS),
        cacheTtlMs: resolveCacheTtlMs(search?.cacheTtlMinutes, DEFAULT_CACHE_TTL_MINUTES),
        provider,
        freshness,
        site,
        edition,
        safe_search: safeSearch,
      });

      return jsonResult(result);
    },
  };
}

// 导出测试用函数
export const __testing = {
  normalizeFreshness,
  resolveSearchCount,
};

3.此段代码中apikey替换为百度apikey
return {
label: “Web Search”,
name: “web_search”,
description: “Search the web using Baidu Qianfan Search API. Returns real-time web search results with titles, URLs, and content snippets. Supports site-specific search and freshness filtering.”,
parameters: WebSearchSchema,
execute: async (_toolCallId, args) => {
//const apiKey = resolveSearchApiKey(search);
const apiKey=替换为百度apikey

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐