発端: 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つ困りました。
- ポートが空かない。 旧プロセスがポートを握ったままなので、直後に起こした新プロセスは bind に失敗して即死します。「kill したのに古い設定のまま動いている」という状態になります
- 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%通っていることは最初にわかりました。
チャットで作業を進めていると、エージェントは自信を持って断言してきます。今回それを止めたのは「これブロックされてるの?」という一言でした。断言に対して一度立ち止まるのは、人間の側がやる価値のある仕事だと思います。
移行の判断自体は変わりませんでしたが、理由は「壊れているから」から「公式が非推奨の非公式スクレイパーで、負荷がかかると折れるから」に変わりました。同じ結論でも、理由が正しくないと次に応用が効きません。
参考リンク
- OpenClaw 公式サイト / ドキュメント
- Web search(プロバイダ一覧と auto-detect の順序)
- DuckDuckGo search
- Brave search
- Parallel search
- Gemini search
- Ollama search
- Web fetch
- Brave Search API 料金
- DuckDuckGo Acceptable Use Policy
- Google Custom Search JSON API overview
- 拙記事: 「ドメイン移管が失敗した理由を調べて」をどこに投げるか — ChatGPT・Claude Code と比べて OpenClaw を選んでいる理由
-
出典: 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 同梱ドキュメントで確認) ↩
-
出典: 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)。 ↩
-
出典: Brave search · OpenClaw。キーは
plugins.entries.brave.config.webSearch.apiKeyか、Gateway 環境変数BRAVE_API_KEYに置きます。ダッシュボードで選ぶプランは Search です。 ↩