Local Search Skill
自動エンジンフォールバックによるローカルウェブ検索。 DuckDuckGo / Bing / Googleの直接HTMLスクレイピング · レート制限に耐性 · 標準化された結果スキーマ。
このスキルは、あなたのマシンから直接公開検索エンジンを呼び出します。APIキー不要、SDK不要、ネットワークホップ不要。 あるエンジンがレート制限をかけている場合、オーケストレーターは自動的に次のエンジンにフォールバックします。そのため、GoogleがブロックされているデータセンターIPからの場合でも、多くの場合、最初の試行で成功します。
各結果は、標準の7つのフィールド(url、name、snippet、host_name、rank、date、favicon)と、3つのオプションの拡張フィールド(source_engine、raw_html、score)を持つ SearchFunctionResultItem です。同様の形状のウェブ検索スキールに対して書かれたコンシューマーコードは、変更なしでここでも動作します。
✨ ハイライト
- クラウドSDKへの依存ゼロ —
cheerio(HTMLパーサー)とNodeのビルトインfetchのみ。 - 3つのエンジン、自動フォールバック —
engine: "auto"(デフォルト)の場合、DuckDuckGo → Bing → Google。 - ロケール対応 —
--locale en-US/zh-CN/ja-JP/ 任意のBCP-47タグ。英語のクエリに対してもローカライズされた結果を返す可能性のあるBingで、非US IPにとって重要。 - 新しさフィルター —
--recency-days 7で過去1週間の結果に制限。DDGは正確な日数をサポート。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 — Node 18+で
tsxを使用、またはbunでゼロコンフィグ実行。
🚀 クイックスタート
# 1. インストール(ランタイム依存は cheerio のみ)
cd skills/local-search && npm install
# 2. 検索(DDG → Bing → Google の自動フォールバック)
tsx bin/web-search.ts "人工知能"
# 3. 特定のエンジンとロケールを指定
tsx bin/web-search.ts "機械学習" --engine bing --locale en-US --num 5
# 4. 最新ニュースをJSONで出力
tsx bin/web-search.ts "AIのブレークスルー" --recency-days 7 --json -o ai_news.json
プログラムでの使用例:
import { search } from "local-search";
const outcome = await search("フランスの首都は?", { num: 5 });
if (outcome.success) {
console.log(`${outcome.engine} が ${outcome.elapsedMs}ms で回答`);
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|w1|m1(バケット) |
www.google.com/search | 不要 | ⚠️ 中 | ❌ 低(enablejs 壁) | tbs=qdr:d|w|m|y(バケット) |
engine: "auto" は DuckDuckGo → Bing → Google の順で試行し、最初の空でない結果セットを返します。データセンターIPからの実効チェーンは DDG → Bing(Googleは通常ブロックされます)です。レジデンシャルIPからは3つすべてが有効です。優先順位を変更するには、src/engines/index.ts の AUTO_ENGINE_ORDER を編集してください。
📦 インストールパス
推奨される場所: {project_path}/skills/local-search
このスキルパッケージをプロジェクトの上記パスに展開してください。
前提条件
- Node.js >= 18(ビルトインのグローバル
fetchを使用)。 - 1つのnpm依存:
cheerio(HTMLパーサー)。 - 動作するインターネット接続 — スキルは公開検索エンジンを直接呼び出します。
スキルディレクトリで一度だけインストールします:
cd {project_path}/skills/local-search
npm install # または: bun install / pnpm install
スキルはピュアTypeScript ESMです。
tsx(推奨、開発者向け)またはbun(ゼロコンフィグ)で実行できます。ランタイムに依存しないnode --experimental-strip-types bin/web-search.tsもNode 22+で動作します。
なぜローカルなのか?
| 側面 | クラウド検索スキル | このスキル (local-search) |
|---|---|---|
| 検索バックエンド | クラウド関数 / API | DuckDuckGo / Bing / Googleへの直接HTTP |
| 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 / パースヘルパー
│ ├── 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 "人工知能"
# 結果数を制限
tsx bin/web-search.ts "機械学習" --num 5
# (短いオプションも使用可能)
tsx bin/web-search.ts "機械学習" -n 5
特定のエンジンを指定
# DuckDuckGoのみを強制
tsx bin/web-search.ts "最新テクノロジーニュース" --engine duckduckgo
# Bingのみを強制
tsx bin/web-search.ts "最新テクノロジーニュース" --engine bing
# Googleのみを強制(最高品質だが、レート制限されやすい)
tsx bin/web-search.ts "最新テクノロジーニュース" --engine google
# デフォルト — DDG → Bing → Google を試行し、最初の空でない結果を返す
tsx bin/web-search.ts "最新テクノロジーニュース" --engine auto
新しさフィルター
# 過去7日間の結果
tsx bin/web-search.ts "暗号通貨ニュース" --num 10 --recency-days 7
# (短いオプション)
tsx bin/web-search.ts "暗号通貨ニュース" -r 7
結果をJSONに保存
# ファイルにJSONを書き込む(人間可読なバナーは抑制されます)
tsx bin/web-search.ts "気候変動研究" --num 5 --json -o search_results.json
# またはJSONを標準出力にパイプ
tsx bin/web-search.ts "AIのブレークスルー" --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> | — | エンジンごとのタイムアウト(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("フランスの首都は?", { num: 5 });
if (outcome.success) {
console.log(`使用エンジン: ${outcome.engine}`);
console.log(`試行順: ${outcome.enginesTried.join(" → ")}`);
console.log(`経過時間: ${outcome.elapsedMs}ms`);
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フレームワーク", { 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("量子コンピューティング応用", {
num: 8,
engine: "duckduckgo",
});
新しさフィルター
const outcome = await search("ゲノミクス研究", {
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フィルターはありません)。 - 最適な用途: 2番目のフォールバック。優れた英語の結果。時折ロケール特有の問題(
ensearch=1で軽減)。
Google (google)
- エンドポイント:
https://www.google.com/search?q=...&num=... - APIキー不要、しかしGoogleはスクレイパーを最も積極的にブロックします。データセンター / クラウドIP からは、Googleは通常「JavaScriptを有効にしてください」というリダイレクトページ(
/httpservice/retry/enablejs)を返し、解析可能な結果はゼロになります — エンジンはこれをソフト障害として表面化し、自動チェーンは次に進みます。レジデンシャルIP からは、Google HTMLスクレイピングは多くの場合正常に動作します。 - 新しさフィルター:
tbs=qdr:d|w|m|yバケット。 - 最適な用途: レジデンシャルIPでの最後の手段のフォールバック。クラウドIPでは通常失敗します(DDG+Bingでニーズをカバーできます)。
自動フォールバック戦略
engine: "auto"(デフォルト)の場合、オーケストレーターは以下の順でエンジンを試行します:
- DuckDuckGo — 最もブロックが少なく、最も細かい日付フィルター
- Bing — 通常、任意のIP(データセンターを含む)から安定
- Google — 到達可能な場合の最高の結果品質。データセンターIPではブロックされることが多い
≥ 1件の結果を得た最初のエンジンを返します。3つすべてが失敗した場合、結果は { success: false, errors: [...] } で、すべてのエンジンのエラーが添付されます。または、searchOrThrow モードでは AllEnginesFailedError がスローされます。
実用的な信頼性テーブル(テスト中に観察):
| 呼び出し元IPタイプ | DDG | Bing | 実効的な自動チェーン | |
|---|---|---|---|---|
| レジデンシャル | 高 | 高 | 中 | DDG → Bing → Google(3つとも有効) |
| データセンター / クラウド | 中(負荷時にレート制限) | 高 | 低(enablejs 壁) | 実質的に DDG → Bing |
順序は src/engines/index.ts(AUTO_ENGINE_ORDER)で定義されています。異なる優先順位が必要な場合は、その配列を編集してください。
一般的なユースケース
- リアルタイム情報: 最新ニュース、株価、天気、スポーツスコア
- 最新ドキュメント: フレームワークのリリースノート、API変更、最近公開された論文
- ファクトチェック: ライブのウェブソースに対して主張を検証
- リサーチ: トピックに関する複数のソースを収集し、視点を比較
- コンテンツ発見: チュートリアル、ブログ記事、最近の講演を検索
- 競合・市場分析: 競合他社の発表、業界トレンド
- 学術調査: 論文、引用、著者ページを検索
ライセンス
MIT