0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OpenClaw の web 検索を Brave に移した — 「DuckDuckGo がブロックされている」は自分で作った証拠だった

0
Posted at

発端: 1台だけ「web_search が無効です」と言い出した

自宅のマシンで OpenClaw のインスタンスを6台動かしています。用途ごとに人格を分けていて、Discord から使い分けています。

そのうちの1台が、Discord でこう返してきました。

web_search が無効です。別ルートで取ります。

調べると、その1台の openclaw.json には tools セクションが丸ごと存在しませんでした。プラグインを有効化しても tools.web.search を明示しないと web_search は生えません。それだけの話……のはずでした。

「ほかの台と同じ設定にすればいい」と思って参照元を見にいったら、参照元のほうが壊れていました。 ここから調査が始まって、最終的に艦隊全体を Brave Search API に移すところまで行きました。

この記事は、その経緯です。途中で私(を動かしている側)が測定を間違えて誤診したので、そこも含めて書きます。

誤診: 「DuckDuckGo は完全にブロックされている」

残りの台は全部 provider: "duckduckgo" でした。ホストから直接叩いてみます。

$ curl -s -o /tmp/ddg.html -w "http=%{http_code}\n" \
    'https://html.duckduckgo.com/html/?q=test'
http=202

HTTP 202 が返り、中身は bot チャレンジの画面でした。

Unfortunately, bots use DuckDuckGo too. Please complete the following challenge to confirm this search was made by a human. Select all squares containing a duck:

念のため10クエリ連続で叩いたところ、10/10 が 202 + チャレンジ画面。続けて実機のエージェントに検索させると、こちらも失敗しました。

$ openclaw --profile opuu agent -m 'web_search で「電子帳簿保存法 改正 国税庁」を検索して'
  → 検索失敗 / DuckDuckGo returned a bot-detection challenge.

ここで「このホストからの DuckDuckGo は完全にブロックされている。4台とも実質壊れている」と結論を出しました。Issue にもそう書きました。

これが間違いでした。

訂正: 自分でブロックを作って、それを観測していた

「これブロックされてるの?」と聞かれて測り直したとき、実運用ログを先に見ました。OpenClaw は audit_events にツール呼び出しの記録を残しているので、直近30日を集計します。

profile web_search 呼出 成功 失敗
A 185 167 18
B 142 100 42
C 101 84 17
D 23 12 11
E 6 1 5
合計 457 364(80%) 93

8割は通っていました。

決定的だったのは時刻です。C は当日の 14:35〜14:39 に web_search を11回連続で成功させていて、家電の型番13機種を実売価格つきで回答していました。私が10連打の curl を撃ったのは 15:14。エージェントのテストを走らせたのは 15:17 です。

順番が逆でした。私がブロックを発生させ、その3分後に「ブロックされている」と観測していたわけです。

集計するときの落とし穴も1つありました。audit_events は1回の呼び出しにつき started と succeeded/failed の2行が入るので、started を除外しないと件数が倍になります。

回復も測ろうとして、また同じ失敗をしかけた

「ではどれくらいで回復するのか」を測るため、3分間隔で8回叩きました。結果は全部 202 で、10連打から43分経っても解けていませんでした。

ただこの測定にも問題があります。3分間隔で叩き続けたこと自体が、ブロックを維持している可能性があるからです。「バーストが43分のブロックを作った」のか「自動アクセスを続ける限り解けない」のかを、この測り方では区別できません。同じ穴に落ちかけたので、プローブは止めました。

結局わかったのはこれだけです。

✅ 確実 通常運用のペース(30日457件・間隔もまばら)なら8割通る
✅ 確実 短時間に10連打すると確実に折れる。しかも数十分単位で尾を引く
❓ 未確定 完全放置したときの回復時間。プローブ自体が影響しうるので測れていない

教訓は「合成テストより実運用ログを先に見る」です。 手元で叩いた10回より、30日分の実績のほうが実態を表していました。

それでも移した理由

「壊れていない」のに移すのは変に聞こえますが、理由が変わっただけです。

1. 公式が本番利用を否定している

OpenClaw の DuckDuckGo search のドキュメントに、こう書いてあります1。

DuckDuckGo is an experimental, unofficial integration that scrapes DuckDuckGo's non-JavaScript HTML search pages -- not an official API.

Bot-challenge risk -- DuckDuckGo may serve CAPTCHAs or block requests under heavy or automated use.

For production use, consider Brave Search (free tier available) or another API-backed provider.

つまり、今回起きたことは公式が事前に警告していたとおりの故障です。カタログ上のラベルも DuckDuckGo Search (experimental) になっています。

2. プラグインがブラウザを偽装している

プラグインの中身を読みました。

// @openclaw/duckduckgo-plugin
"https://html.duckduckgo.com/html"
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 ... Chrome/122.0.0.0 Safari/537.36"
// → 返ってきた HTML から result__a / result__snippet を正規表現で抜く

API ではなく、人間向けの検索結果ページを、Chrome のフリをして取得して HTML を切り出しています。 DuckDuckGo にはウェブ検索結果を返す公式 API が存在しないので(api.duckduckgo.com は Instant Answer 専用でリンクは返りません)、こうするしかない、という構図です。

CAPTCHA は、DuckDuckGo がまさにこの使い方に対して出しているものです。

規約も読みましたが、「スクレイピング禁止」と名指しした条項は見つかりませんでした。そこは断定できません。ただし Acceptable Use Policy にはこうあります。

…We reserve the right to suspend or terminate access for any users who we deem in violation of the spirit of these conditions.

明文の禁止条項が無くても、ブラウザ偽装 + bot 検知の発動という事実の組み合わせは、歓迎されていない以外に読みようがないです。ここから UA を回したりプロキシを挟んだりして回避しにいくのは、アクセス制御の回避になるのでやりません。

「連打で折れて、いつ戻るか分からないものを本番に置く理由がない」 というのが、移行の理由になりました。

プロバイダを比較する

OpenClaw 2026.8.1 には15のプロバイダがあります。整理するとこうです。

区分 プロバイダ 備考
キー不要 parallel-free Parallel のホスト型 Search MCP
firecrawl-free Firecrawl の無料 starter tier
ollama Ollama のクラウド検索 API
duckduckgo 非公式スクレイパー
要キー brave 公式推奨。独立インデックス
tavily / exa エージェント向けに構造化。リサーチ用途
perplexity / firecrawl / parallel
gemini / grok / kimi / minimax モデル側の grounding 経由
自前ホスト searxng メタ検索を自分で立てる

選ぶときに効いた事実が3つありました。

① キー不要プロバイダは自動選択されない

Web search のドキュメントによると、auto-detect は使える資格情報を持つプロバイダしか候補にしません2。DuckDuckGo・Parallel free・Firecrawl free・Ollama・Codex Hosted Search は、内部の order 値を持っていてもauto-detect では絶対に選ばれず、明示的に指定したときだけ使われます。

API 系の優先順位では Brave が order 10 で最上位です。つまり「キーを入れて何も指定しない」と Brave が選ばれる設計になっています。

② 検索プロバイダにフォールバックは無い

ここは訂正が要った点です。ネット上の比較記事には「Parallel を primary、Brave を fallback に」と書いてあるものがありますが、スキーマを読むと tools.web.search.provider は単一の文字列で、fallbacks はモデル側にしか存在しません。複数プロバイダのキーを設定しておくことはできますが、自動切替はしません。プロバイダが落ちたら検索も落ちます。

③ web_fetch は別物

web_search は外部プロバイダ経由ですが、web_fetch は常にローカル実行です(tools.web.fetch.provider が別にあります)。検索が死んでも fetch は生きます。今回、DDG が折れていた間もエージェントが「別ルートで取ります」と言えたのはこれのおかげでした。

個別に検討したもの

parallel-free — Parallel Search はキー不要で、Parallel 社が公開しているホスト型 Search MCP に JSON-RPC で正面から接続します。名乗りも openclaw-parallel と正直です。ベンチマークでは Artificial Analysis Search Index (2026年8月) で1位という数字がありますが、出典がほぼ Parallel 自身です。そして無料枠のレート制限・SLA・打ち切り条件が非公表なので、「いつ終わってもおかしくない厚意」と見るのが安全だと判断しました。移行のつなぎには使えます。

ollama — 名前から誤解しやすいのですが、ローカルのインデックスではなく Ollama のクラウド検索 API です。ローカル Ollama 経由ならキー不要ですが ollama signin(アカウント)が必須で、ホストに Ollama を常駐させる必要もあります。旨味が薄いので外しました。

gemini — Gemini search は Google 検索の grounding 経由です。返るのはリンク一覧ではなく合成回答+引用URL。Gemini 3.x なら月5,000 grounding が無料で、うちの月460回なら完全に無料枠内です。ただし AI Studio の無料枠はデータが学習に使われ得るので、会計・経費まわりを扱う台の検索クエリを流すのは別途判断が要ります。

素の Google 検索 — 課金しても、もう事実上選べません。Custom Search JSON API は新規受付が終了していて、廃止も告知されています。SerpAPI / Serper のような SERP 代行は OpenClaw が公式サポートしておらず、性質は DDG と同じ(他人が代わりにスクレイピングしているだけ)です。正規に Google の結果を使う道は Gemini grounding だけになります。

searxng — 外部の ToS に一切依存しない唯一の構成です。ただしメタ検索なので、上流(Google 等)から弾かれるリスクを自分で背負うことになり、DDG と同じ問題を自前で抱え直す可能性があります。

決め手は「実測」対「無料枠」

最後は数字でした。

実測 457件/月(全台合計・直近30日)
ピーク 85件/日(8/30の単発の山)
Brave の無料枠 約1,000クエリ/月
余裕 2.2倍

Brave Search API の料金は Search プランが $5 / 1,000リクエストで、毎月 $5 のクレジットが自動付与されます。割り算すると実質1,000クエリ/月が無料です。

ここは言い方を正確にしておきます。Brave のページに「1,000件まで無料」とは書いていません。書いてあるのは「$5/1,000」と「毎月$5のクレジット」の2つで、1,000という数字は割り算して出したものです。単価かクレジット額が変われば動きます。課金形態も Prepaid(前払い)で、クレジットを先に積む方式です。

超えても $5/1,000 なので、仮に倍使っても月$5以下。桁を間違えるような話ではないので、これで決めました。

なお、不正防止でクレジットカードの登録は必要です(無料枠では請求されません)。

移行でハマったところ

手順自体は単純です。

P=<profile>
cp -p ~/.openclaw-$P/openclaw.json ~/.openclaw-$P/_backup-brave-$(date +%Y%m%d-%H%M%S)-openclaw.json
openclaw --profile $P plugins install @openclaw/brave-plugin --accept-capabilities
openclaw --profile $P config set plugins.entries.brave.config.webSearch.apiKey '<KEY>'
openclaw --profile $P config set tools.web.search.provider brave
openclaw --profile $P config validate

設定はこうなります3。

{
  plugins: {
    entries: {
      brave: {
        config: {
          webSearch: {
            apiKey: "BRAVE_API_KEY_HERE",
            mode: "web",
          },
        },
      },
    },
  },
  tools: {
    web: {
      search: { provider: "brave", maxResults: 5, timeoutSeconds: 30 },
    },
  },
}

以下、実際に踏んだ罠です。

罠1: provider はインストール済みプラグインからしか解決されない

プラグインを入れる前に provider を設定すると弾かれます。

[secrets] [WEB_SEARCH_PROVIDER_INVALID_AUTODETECT]
  tools.web.search.provider is "..." . No provider will be selected.

provider の有効値は、インストール済みのプラグインが contracts.webSearchProviders で宣言した id から動的に解決されます。カタログに載っていても、未インストールなら無効です。プラグインを入れてから provider を設定する(または入れた後に再起動する)のが正しい順序です。

罠2: drain — kill してもすぐには死なない

gateway に SIGTERM を送ると、いきなり死ぬのではなく drain(新規受付を止めて、走っている処理が終わるのを待つ)に入ります。

[admission] closed: restart drain
[reload] channel reload still deferred after 90318ms with
         1 reply(ies), 1 embedded run(s), 1 gateway request(s),
         1 background task run(s) active

1台では claude --model claude-opus-5 --effort max の子プロセスが2本(11分・9分経過)走っていて、その2本が終わるまで gateway が終了しませんでした。実際に約6分かかりました。

これで2つ困りました。

  1. ポートが空かない。 旧プロセスがポートを握ったままなので、直後に起こした新プロセスは bind に失敗して即死します。「kill したのに古い設定のまま動いている」という状態になります
  2. drain 中は応答しない。 受付を閉じているので、待っている間そのインスタンスは実質停止です

kill → 待つ → 起こす、の順にすべきでした。落とす前に ps --ppid <gateway pid> -o pid,etime,args で in-flight を確認する、というのを手順に足しています。

ただし強制終了(SIGKILL)しなくて正解でした。再起動後のログに

[main-session-restart-recovery] started interrupted main session: agent:main:main

とあって、中断された実行が自動で再開されています。SIGKILL していたらその2本は失われていました。

罠3: 手動起動のインスタンスは自分で戻ってこない

/proc/<pid>/cgroup で確認したところ、6台中5台が systemd 管理下ではなく手動起動でした。

$ cat /proc/<pid>/cgroup
…/tmux-server.service      ← 手動
…/openclaw-gateway-<name>.service   ← systemd

ps の親プロセスでは判別できません。setsid した手動プロセスも pid 1(systemd)にぶら下がるためです。

これが効いてくるのはプラグイン導入で再起動が要るときです。systemd 管理下なら Restart=always で戻りますが、手動起動には supervisor がいないので、落としたら自分で上げ直すまで戻ってきません。実際、この作業中に1台を約20分停止させました。

検証は自己申告を信じない

一番効いたのはこれです。エージェントに「検索できた?」と聞くと、平気で「できました」と言います。なので、

  • agent -m で実際に検索させる
  • 返ってきた URL を curl で叩いて HTTP 200 と <title> の一致まで確認する
  • 起動ログの http server listening (N plugins: … brave …) を読む

というところまでやりました。

結果

6台すべて provider: "brave" に統一しました。duckduckgo プラグインは「revert 用に残す」のをやめて無効化しています。戻る先が偽装スクレイパーなら、残す意味がないからです。

キーは1本を全台で共有しました。使用量の内訳は見えなくなりますが、実測457件/月なら1本で足ります。保存先は各 openclaw.json(パーミッション 600・gitignore 済み)です。

この作業で学んだこと

測り方が結論を作ってしまうことがあります。 今回、10連打の curl は「事実」でしたが、その事実は私が作ったものでした。実運用ログを先に見ていれば、30日で80%通っていることは最初にわかりました。

チャットで作業を進めていると、エージェントは自信を持って断言してきます。今回それを止めたのは「これブロックされてるの?」という一言でした。断言に対して一度立ち止まるのは、人間の側がやる価値のある仕事だと思います。

移行の判断自体は変わりませんでしたが、理由は「壊れているから」から「公式が非推奨の非公式スクレイパーで、負荷がかかると折れるから」に変わりました。同じ結論でも、理由が正しくないと次に応用が効きません。

参考リンク

  1. 出典: DuckDuckGo search · OpenClaw。原文は「DuckDuckGo is an experimental, unofficial integration that scrapes DuckDuckGo's non-JavaScript HTML search pages -- not an official API. Expect occasional breakage from bot-challenge pages or HTML changes.」「Bot-challenge risk -- DuckDuckGo may serve CAPTCHAs or block requests under heavy or automated use.」「For production use, consider Brave Search (free tier available) or another API-backed provider.」(OpenClaw 2026.8.1 同梱ドキュメントで確認) ↩

  2. 出典: Web search · OpenClaw。auto-detect は「only picks a provider that needs a」資格情報を持つものだけを候補にし、DuckDuckGo・Parallel free・Firecrawl free・Ollama Web Search・Codex Hosted Search は「never win auto-detection, even though they have an internal order value」と明記されています。API 系の順序は Brave (order 10) → MiniMax (15) → Gemini (20) → Grok (30) → Kimi (40) → Perplexity (50) → Firecrawl (60) → Exa (65) → Tavily (70) → Parallel (75) → SearXNG (200)。 ↩

  3. 出典: Brave search · OpenClaw。キーは plugins.entries.brave.config.webSearch.apiKey か、Gateway 環境変数 BRAVE_API_KEY に置きます。ダッシュボードで選ぶプランは Search です。 ↩

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?