本地搜索技能
具有自动引擎回退的本地网页搜索。 直接 HTML 抓取 DuckDuckGo / Bing / Google · 对抗速率限制 · 标准结果模式。
此技能直接从您的机器调用公共搜索引擎。无需 API 密钥,无需 SDK,无需网络跳转。 如果某个引擎对您进行速率限制,协调器会静默地回退到下一个引擎——因此,即使在 Google 被屏蔽的数据中心 IP 上,调用通常也能在首次尝试时成功。
每个结果都是一个 SearchFunctionResultItem,包含标准的 7 个字段(url、name、snippet、host_name、rank、date、favicon)以及三个可选扩展字段:source_engine、raw_html、score。针对其他任何形状相似的网页搜索技能编写的消费代码可以在此处无需修改地运行。
✨ 亮点
- 零云 SDK 依赖——仅需
cheerio(HTML 解析器)和 Node 内置的fetch。 - 三个引擎,自动回退——当
engine: "auto"(默认)时,DuckDuckGo → Bing → Google。 - 区域感知——
--locale en-US/zh-CN/ja-JP/ 任意 BCP-47 标签。对于非美国 IP 尤为重要,否则 Bing 会为英文查询提供本地化结果。 - 时效性过滤——
--recency-days 7限制为过去一周的结果。DDG 支持精确 N 天;Bing/Google 使用天/周/月桶。 - 标准结果模式——
url/name/snippet/host_name/rank/date/favicon字段,形状与 ClawHub 注册表中的其他网页搜索技能相同。 - CLI + SDK——一次性使用
tsx bin/web-search.ts <query>,或在代码中使用import { search } from "local-search"。 - 纯 TypeScript ESM——通过
tsx在 Node 18+ 上运行,或通过bun零配置运行。
🚀 快速开始
# 1. 安装(一个运行时依赖:cheerio)
cd skills/local-search && npm install
# 2. 搜索(跨 DDG → Bing → Google 自动回退)
tsx bin/web-search.ts "artificial intelligence"
# 3. 指定引擎和区域
tsx bin/web-search.ts "machine learning" --engine bing --locale en-US --num 5
# 4. 最近新闻(JSON 格式)
tsx bin/web-search.ts "AI breakthroughs" --recency-days 7 --json -o ai_news.json
程序化使用:
import { search } from "local-search";
const outcome = await search("What is the capital of France?", { num: 5 });
if (outcome.success) {
console.log(`由 ${outcome.engine} 回答,耗时 ${outcome.elapsedMs} 毫秒`);
outcome.results.forEach(r => console.log(`- ${r.name}\n ${r.url}`));
}
🎯 何时使用此技能
当用户需要存在于公共网络上的信息时,请使用 local-search——超出模型训练数据范围的信息:
- 实时信息:当前新闻、股票价格、天气、体育比分
- 最新文档:框架发布说明、API 变更、最近发表的论文
- 事实核查:对照实时网络来源验证声明
- 研究:收集关于某个主题的多个来源,比较观点
- 内容发现:查找教程、博客文章、近期演讲
- 竞争/市场分析:竞争对手公告、行业趋势
- 学术查阅:查找论文、引文、作者页面
🔄 引擎对比
| 引擎 | 端点 | API 密钥 | 住宅 IP | 数据中心 IP | 时效性 |
|---|---|---|---|---|---|
| DuckDuckGo | html.duckduckgo.com/html/ (GET) | 无 | ✅ 高 | ⚠️ 负载下有限制 | df=d<N>(精确天数) |
| Bing | www.bing.com/search | 无 | ✅ 高 | ✅ 高 | `freshness=d1 |
www.google.com/search | 无 | ⚠️ 中 | ❌ 低(enablejs 墙) | `tbs=qdr:d |
engine: "auto" 按顺序尝试它们:DuckDuckGo → Bing → Google,返回第一个非空结果集。从数据中心 IP 出发,有效链是 DDG → Bing(Google 通常被屏蔽);从住宅 IP 出发,三个引擎都可用。编辑 src/engines/index.ts 中的 AUTO_ENGINE_ORDER 以更改优先级。
📦 安装路径
推荐路径:{project_path}/skills/local-search
将此技能包解压到项目中的上述路径。
先决条件
- Node.js >= 18(使用内置的全局
fetch)。 - 一个 npm 依赖:
cheerio(HTML 解析器)。 - 可用的互联网连接——技能直接调用公共搜索引擎。
在技能目录中安装一次:
cd {project_path}/skills/local-search
npm install # 或:bun install / pnpm install
此技能是纯 TypeScript ESM。您可以通过
tsx(推荐,对开发者友好)或bun(零配置)运行。在 Node 22+ 上,node --experimental-strip-types bin/web-search.ts也能以运行时无关的方式工作。
为何选择本地?
| 方面 | 云搜索技能 | 此技能 (local-search) |
|---|---|---|
| 搜索后端 | 云函数 / API | 直接 HTTP 到 DuckDuckGo / Bing / Google |
| API 密钥 / SDK | 必需 | 无 |
| 网络路径 | 客户端 → 云 → 搜索引擎 | 客户端 → 搜索引擎 |
| 故障处理 | 取决于 SDK | 跨 3 个引擎自动回退 |
| 结果模式 | SearchFunctionResultItem(7 个字段) | 相同 7 个字段 + 3 个可选扩展字段 |
| CLI | 特定 SDK | tsx bin/web-search.ts |
架构
local-search/
├── SKILL.md ← 您在这里
├── LICENSE.txt
├── package.json ← 声明 `cheerio` 依赖
├── tsconfig.json
├── bin/
│ └── web-search.ts ← CLI 入口
├── src/
│ ├── index.ts ← 公共 SDK 导出
│ ├── search.ts ← 带自动回退的协调器
│ ├── types.ts ← SearchFunctionResultItem + 选项
│ └── engines/
│ ├── _shared.ts ← fetch / parse 辅助函数
│ ├── duckduckgo.ts ← POST https://html.duckduckgo.com/html/
│ ├── bing.ts ← GET https://www.bing.com/search
│ ├── google.ts ← GET https://www.google.com/search
│ └── index.ts ← 引擎注册表 + AUTO_ENGINE_ORDER
└── scripts/
└── web_search.ts ← 快速入门示例
唯一的运行时依赖是 cheerio(HTML 解析器),在 package.json 中声明。Node 18+ 的内置 fetch 负责网络层——此包中未导入任何其他 SDK。
CLI 用法
CLI 位于 bin/web-search.ts。通过 tsx、bun 或任何 TS 感知的运行器运行。
基本搜索
# 默认:自动引擎,10 个结果,人类可读输出
tsx bin/web-search.ts "artificial intelligence"
# 限制结果数量
tsx bin/web-search.ts "machine learning" --num 5
# (短选项也有效)
tsx bin/web-search.ts "machine learning" -n 5
指定引擎
# 强制仅使用 DuckDuckGo
tsx bin/web-search.ts "latest tech news" --engine duckduckgo
# 强制仅使用 Bing
tsx bin/web-search.ts "latest tech news" --engine bing
# 强制仅使用 Google(质量最高,最可能被限速)
tsx bin/web-search.ts "latest tech news" --engine google
# 默认——尝试 DDG → Bing → Google,返回第一个非空
tsx bin/web-search.ts "latest tech news" --engine auto
时效性过滤
# 过去 7 天的结果
tsx bin/web-search.ts "cryptocurrency news" --num 10 --recency-days 7
# (短选项)
tsx bin/web-search.ts "cryptocurrency news" -r 7
保存结果为 JSON
# 将 JSON 写入文件(抑制人类可读横幅)
tsx bin/web-search.ts "climate change research" --num 5 --json -o search_results.json
# 或将 JSON 通过管道输出到标准输出
tsx bin/web-search.ts "AI breakthroughs" --num 3 --recency-days 1 --json > ai_news.json
静默模式(脚本 / 管道)
# 抑制“引擎/计时”横幅——仅打印结果
tsx bin/web-search.ts "react hooks" --quiet
CLI 参数
| 标志 | 短选项 | 描述 |
|---|---|---|
--num <N> | -n | 结果数量(默认:10,最大:50) |
--engine <id> | -e | duckduckgo | bing | google | auto(默认:auto) |
--recency-days <N> | -r | 限制为过去 N 天的结果(默认:0 = 无过滤) |
--timeout <ms> | — | 每个引擎的超时时间(毫秒)(默认:8000) |
--json | — | 输出 JSON 而非人类可读输出 |
--output <path> | -o | 将 JSON 写入文件而非标准输出 |
--pretty | — | 美化 JSON(默认开启;传递 --no-pretty 以禁用——尚未支持,改为省略 --json) |
--quiet | -q | 抑制引擎信息横幅 |
--help | -h | 显示帮助 |
搜索结果结构
每个结果项都是一个 SearchFunctionResultItem:
interface SearchFunctionResultItem {
// ----- 标准字段(在 ClawHub 网页搜索技能中共享的形状) -----
url: string; // 结果的完整 URL
name: string; // 页面标题
snippet: string; // 预览文本 / 描述
host_name: string; // 域名(例如 "en.wikipedia.org")
rank: number; // 在引擎结果集中从 1 开始的排名
date: string; // 发布日期 / 更新日期;未知时为 "N/A"
favicon: string; // 图标 URL(Google S2)
// ----- 扩展字段(local-search 新增) -----
source_engine: "duckduckgo" | "bing" | "google";
raw_html?: string; // 摘要元素的原始 HTML(可选)
score?: number; // 启发式相关性分数 [0, 100](可选)
}
SDK 用法
如果您正在编写 TypeScript/JavaScript 代码(而非运行 CLI),请从 src/index.ts 导入:
简单搜索
import { search } from "local-search";
const outcome = await search("What is the capital of France?", { num: 5 });
if (outcome.success) {
console.log(`使用的引擎:${outcome.engine}`);
console.log(`尝试顺序:${outcome.enginesTried.join(" → ")}`);
console.log(`耗时:${outcome.elapsedMs}毫秒`);
for (const item of outcome.results) {
console.log(`- ${item.name} [${item.source_engine}]`);
console.log(` ${item.url}`);
console.log(` ${item.snippet}`);
}
} else {
console.error("搜索失败:", outcome.error);
if (outcome.errors) {
// 自动模式:查看哪些引擎尝试过及其报告
for (const e of outcome.errors) {
console.error(` ${e.engine}:`, e.error);
}
}
}
失败时抛出异常的变体
import { searchOrThrow, AllEnginesFailedError } from "local-search";
try {
const results = await searchOrThrow("JavaScript frameworks", { num: 10 });
console.log(results);
} catch (err) {
if (err instanceof AllEnginesFailedError) {
console.error("所有引擎均失败:", err.errors);
} else {
console.error("单引擎错误:", err);
}
}
指定引擎
import { search } from "local-search";
// 跳过回退链——仅使用 DuckDuckGo。
const outcome = await search("quantum computing applications", {
num: 8,
engine: "duckduckgo",
});
时效性过滤
const outcome = await search("genomics research", {
num: 5,
recency_days: 30, // 过去 30 天
});
自定义 fetch(测试 / 边缘运行时)
const outcome = await search("hello world", {
fetchImpl: myMockFetch, // 任何符合 WhatWG-fetch 标准的函数
userAgent: "my-bot/1.0",
timeoutMs: 5000,
});
引擎后端
DuckDuckGo (duckduckgo)
- 端点:
https://html.duckduckgo.com/html/(POST 表单q=...) - 无需 API 密钥。受到粗略的速率限制——持续高负载会返回空结果集。
- 时效性:
df=d<N>表单字段。 - 最佳用途:默认首次尝试;很少被彻底屏蔽;尊重隐私。
Bing (bing)
- 端点:
https://www.bing.com/search?q=...&count=... - 无需 API 密钥。HTML 布局稳定且易于解析。
- 时效性:
freshness=d1/w1/m1桶(Bing 没有任意 N 天 URL 过滤器)。 - 最佳用途:第二个回退引擎;英文结果良好;偶尔的区域怪癖(通过
ensearch=1缓解)。
Google (google)
- 端点:
https://www.google.com/search?q=...&num=... - 无需 API 密钥,但 Google 在阻止抓取方面最为激进。从 数据中心 / 云 IP 出发,Google 通常会返回一个“请启用 JS”的重定向页面(
/httpservice/retry/enablejs),且没有可解析的结果——引擎将其视为软失败,自动链会回退。从 住宅 IP 出发,Google HTML 抓取通常工作正常。 - 时效性:
tbs=qdr:d|w|m|y桶。 - 最佳用途:在住宅 IP 上的最后回退引擎;在云 IP 上通常会失败(而 DDG+Bing 已满足需求)。
自动回退策略
当 engine: "auto"(默认)时,协调器按此顺序尝试引擎:
- DuckDuckGo——阻止最少,粒度最细的日期过滤器
- Bing——从任何 IP(包括数据中心)通常稳定
- Google——可达时结果质量最好;在数据中心 IP 上通常被屏蔽
它返回第一个产生 ≥ 1 个结果的引擎。如果所有三个都失败,则结果为 { success: false, errors: [...] },并附带每个引擎的错误——或在 searchOrThrow 模式下,抛出 AllEnginesFailedError。
实际可靠性表格(测试期间观察):
| 调用者 IP 类型 | DDG | Bing | 有效自动链 | |
|---|---|---|---|---|
| 住宅 | 高 | 高 | 中 | DDG → Bing → Google(3 个可行) |
| 数据中心 / 云 | 中(负载下有限制) | 高 | 低(enablejs 墙) | 实际为 DDG → Bing |
顺序在 src/engines/index.ts(AUTO_ENGINE_ORDER)中定义;如果您想要不同的优先级,请编辑该数组。
常见用例
- 实时信息——新闻、股票、天气、体育比分
- 最新文档——框架发布说明、API 变更
- 事实核查——对照实时来源验证声明
- 研究——收集多个来源
- 内容发现——查找教程和博客文章
- 竞争分析——竞争对手公告
- 学术查阅——查找论文和引文