最終的に、私はリンクを3層に分割しました。Hermes はローカルの SearXNG にのみアクセスし、SearXNG は設定内で HTTP/HTTPS のアウトバウンドリクエストを明示的に Mihomo に委ね、Mihomo が単独でサブスクリプション、ヘルスチェック、ノードの自動選択を担当します。この方法のメリットは「設定がかっこいい」ことではなく、問題発生時に階層ごとに確認できる点です。Hermes がローカル検索にリクエストを送ったか、SearXNG が JSON を返したか、Mihomo に利用可能なノードがあるか、そしてサブスクリプション自体の解析に失敗していないか。
全体のフローは次のとおりです。
Hermes Agent
-> http://localhost:8888
-> SearXNG
-> http://mihomo:7890
-> Mihomo が利用可能なノードを自動選択
-> 海外の検索エンジン
ホストマシンでローカルポートのみを公開する:
127.0.0.1:8888 -> SearXNG
127.0.0.1:7897 -> Mihomo 混合プロキシ、デバッグ用
127.0.0.1:9097 -> Mihomo コントローラー、ローカル管理用
SearXNG と Mihomo は同じ Docker ネットワーク内にいるため、SearXNG はホストマシンの 127.0.0.1:7897 にアクセスせず、コンテナ名でアクセスします。
http://mihomo:7890
SearXNG で最も見落とされやすい 2 箇所
Hermes が SearXNG を呼び出す際に JSON をリクエストします。SearXNG 公式 Search API ドキュメントにも明記されている通り、format パラメータを使用できるかどうかは settings.yml の search.formats で有効化されているかどうかに依存します。有効化されていないフォーマットをリクエストすると 403 が返されます。したがって、デフォルトの HTML だけを残しておくわけにはいきません。
search:
safe_search: 0
autocomplete: ""
formats:
- html
- json
2つ目はプロキシです。Docker の環境変数やシステムレベルの http_proxy がアプリケーション層で安定的に継承されると過信しないでください。SearXNG のリクエストは独自に行われるため、最も直接的な方法は outgoing.proxies に明示的に記述することです。
outgoing:
request_timeout: 15.0
max_request_timeout: 40.0
extra_proxy_timeout: 10
proxies:
"http://": "http://mihomo:7890"
"https://": "http://mihomo:7890"
SearXNG のデフォルト設定例では all://: という書き方が見られるため、自然に間違った設定項目ではないことが分かります。ただし、私が今回使用した環境では、all://: は期待通りにマッチせず、最終的に落ち着いたのは http:// と https:// を分けて記述する方法でした。記事内でこの境界を保持しているのは、ある一度の実測結果をすべてのバージョンにおける確定的な結論のように書いてしまわないためです。
完全な SearXNG の主要な設定は以下のように圧縮できます。
use_default_settings: true
general:
instance_name: "Hermes SearXNG"
search:
safe_search: 0
autocomplete: ""
formats:
- html
- json
server:
secret_key: "请替换成随机密钥"
limiter: false
image_proxy: true
bind_address: "0.0.0.0"
valkey:
url: valkey://valkey:6379/0
outgoing:
request_timeout: 15.0
max_request_timeout: 40.0
extra_proxy_timeout: 10
proxies:
"http://": "http://mihomo:7890"
"https://": "http://mihomo:7890"
engines:
- name: bing
disabled: false
shortcut: bi
- name: bing news
disabled: false
shortcut: bin
- name: google
disabled: false
shortcut: go
timeout: 8.0
- name: brave
disabled: false
shortcut: br
timeout: 8.0
- name: duckduckgo
disabled: false
shortcut: ddg
timeout: 8.0
- name: startpage
disabled: false
shortcut: sp
timeout: 8.0
- name: wikipedia
disabled: false
shortcut: wp
- name: arxiv
disabled: false
shortcut: arx
- name: sogou
disabled: true
- name: 360search
disabled: true
ui:
static_use_hash: true
ここでちょっとした落とし穴があります:SearXNG のデフォルトエンジン設定では、Bing ニュースの name は bing news であり、engine が bing_news です。use_default_settings: true の場合、engines は name でマージおよび上書きされるため、ここでは name: bing news と記述し、name: bing_news とは記述しないでください。
Mihoma がやることはただ1つ:SearXNG に安定した出口を与えること
Mihomo はサーバー全体を引き継ぐ必要はなく、Docker をグローバルにプロキシ経由にする必要もありません。mixed port を 1 つだけ開いて、同じ Docker ネットワーク内の SearXNG がアクセスできるようにします:
mixed-port: 7890
allow-lan: true
bind-address: '*'
mode: rule
log-level: info
ipv6: false
external-controller: 0.0.0.0:9090
secret: "请替换成随机密钥"
profile:
store-selected: true
store-fake-ip: true
proxy-providers:
sub:
type: http
url: "你的订阅地址"
interval: 3600
path: ./proxy_providers/sub.yaml
health-check:
enable: true
url: https://www.gstatic.com/generate_204
interval: 300
timeout: 5000
lazy: false
expected-status: 204
proxy-groups:
- name: AUTO
type: url-test
use:
- sub
url: https://www.gstatic.com/generate_204
interval: 300
timeout: 5000
tolerance: 50
lazy: false
expected-status: 204
- name: PROXY
type: select
proxies:
- AUTO
- DIRECT
use:
- sub
rules:
- MATCH,PROXY
この設定の意味はとても限定的です:購読は 3600 秒ごとに更新され、ノードは 300 秒ごとにヘルスチェックを行い、AUTO は url-test でレイテンシに基づいてノードを選択し、すべてのトラフィックは最終的に PROXY にマッチします。新しい設定が最初に起動されたとき、PROXY リストの最初にあるのは AUTO です。今後、コントロールパネルでノードを手動で切り替えた場合、store-selected: true が選択を保持するため、「デフォルトで AUTO を通る」とは限りなくなります。
また、サブスクリプション形式にも注意が必要です。proxy-providers は provider 形式、あるいは Mihomo が provider として解析できるサブスクリプションに適しています。サービス事業者によっては、完全な Clash 設定を返すものもあれば、ノードリストを返すものもあります。ログに解析失敗のエラーが出た場合、SearXNG を疑うのではなく、まずサービス事業者の管理画面で Clash Meta、Mihomo、Proxy Provider 形式に切り替えてください。
MOD スクリプト
このスクリプトは、SearXNG を /opt/searxng に配置済みのマシンを対象としています。既存の docker-compose.yml と searxng/settings.yml をバックアップし、本機にあらかじめ用意されている metacubex/mihomo イメージをそのまま利用し、SearXNG、Valkey、Mihomo を同じ compose プロジェクトにまとめます。
このスクリプトはMihomoのイメージを自動的にプルしません。SearXNGとValkeyについても、ローカルに対応するイメージがない場合、docker compose upが起動できるかどうかは、ローカルのイメージとネットワーク環境によって異なります。完全にオフラインの環境では、3つのイメージを事前にすべて用意しておいてください。
sudo bash -s <<'EOF'
set -euo pipefail
APP_DIR="/opt/searxng"
COMPOSE_FILE="$APP_DIR/docker-compose.yml"
SETTINGS_FILE="$APP_DIR/searxng/settings.yml"
MIHOMO_DIR="$APP_DIR/mihomo"
if [ ! -f "$COMPOSE_FILE" ]; then
echo "$COMPOSE_FILE が見つかりません。SearXNG が /opt/searxng にデプロイされているか確認してください"
exit 1
fi
if [ ! -f "$SETTINGS_FILE" ]; then
echo "$SETTINGS_FILE が見つかりません。SearXNG settings.yml のパスを確認してください"
exit 1
fi
if docker image inspect metacubex/mihomo:latest >/dev/null 2>&1; then
MIHOMO_IMAGE="metacubex/mihomo:latest"
else
MIHOMO_IMAGE="$(docker images --format '{{.Repository}}:{{.Tag}}' \
| awk -F: '$1=="metacubex/mihomo" && $2!="<none>" {print; exit}')"
fi
if [ -z "${MIHOMO_IMAGE:-}" ]; then
echo "利用可能なローカル metacubex/mihomo イメージが検出されませんでした。"
echo "先に確認してください:docker images | grep -i mihomo"
exit 1
fi
echo "ローカル Mihomo イメージを検出しました:$MIHOMO_IMAGE"
LOCAL_MIHOMO_IMAGE="searxng-mihomo-local:latest"
docker tag "$MIHOMO_IMAGE" "$LOCAL_MIHOMO_IMAGE"
printf "Clash/Mihomo の購読 URL を入力してください: " > /dev/tty
IFS= read -r -s SUB_URL < /dev/tty
printf "\n" > /dev/tty
if [ -z "$SUB_URL" ]; then
echo "購読 URL が空のため、終了します。"
exit 1
fi
mkdir -p "$MIHOMO_DIR/proxy_providers"
chmod 700 "$MIHOMO_DIR"
TS="$(date +%Y%m%d-%H%M%S)"
cp -a "$COMPOSE_FILE" "$COMPOSE_FILE.bak.$TS"
cp -a "$SETTINGS_FILE" "$SETTINGS_FILE.bak.$TS"
echo "バックアップを作成しました:"
echo " $COMPOSE_FILE.bak.$TS"
echo " $SETTINGS_FILE.bak.$TS"
MIHOMO_SECRET="$(openssl rand -hex 16)"
cat > "$MIHOMO_DIR/config.yaml" <<YAML
mixed-port: 7890
allow-lan: true
bind-address: '*'
mode: rule
log-level: info
ipv6: false
external-controller: 0.0.0.0:9090
secret: "$MIHOMO_SECRET"
profile:
store-selected: true
store-fake-ip: true
proxy-providers:
sub:
type: http
url: "$SUB_URL"
interval: 3600
path: ./proxy_providers/sub.yaml
health-check:
enable: true
url: https://www.gstatic.com/generate_204
interval: 300
timeout: 5000
lazy: false
expected-status: 204
proxy-groups:
- name: AUTO
type: url-test
use:
- sub
url: https://www.gstatic.com/generate_204
interval: 300
timeout: 5000
tolerance: 50
lazy: false
expected-status: 204
- name: PROXY
type: select
proxies:
- AUTO
- DIRECT
use:
- sub
rules:
- MATCH,PROXY
YAML
chmod 600 "$MIHOMO_DIR/config.yaml"
OLD_SECRET="$(grep -E '^[[:space:]]*secret_key:' "$SETTINGS_FILE" | head -n1 | sed -E "s/.*secret_key:[[:space:]]*['\"]?([^'\"]+)['\"]?.*/\1/" || true)"
if [ -z "$OLD_SECRET" ] || [ "$OLD_SECRET" = "secret_key:" ]; then
OLD_SECRET="$(openssl rand -hex 32)"
fi
cat > "$SETTINGS_FILE" <<YAML
use_default_settings: true
general:
instance_name: "Hermes SearXNG"
search:
safe_search: 0
autocomplete: ""
formats:
- html
- json
server:
secret_key: "$OLD_SECRET"
limiter: yml:ro
- searxng-cache:/var/cache/searxng:rw
depends_on:
- valkey
- mihomo
networks:
- searxng-net
logging:
driver: json-file
options:
max-size: "2m"
max-file: "3"
valkey:
image: docker.io/valkey/valkey:8-alpine
container_name: searxng-valkey
restart: unless-stopped
command: valkey-server --save 30 1 --loglevel warning
volumes:
- valkey-data:/data
networks:
- searxng-net
logging:
driver: json-file
options:
max-size: "2m"
max-file: "3"
mihomo:
image: $LOCAL_MIHOMO_IMAGE
container_name: searxng-mihomo
restart: unless-stopped
command: ["-d", "/root/.config/mihomo"]
ports:
- "127.0.0.1:7897:7890"
- "127.0.0.1:9097:9090"
volumes:
- ./mihomo:/root/.config/mihomo
networks:
- searxng-net
logging:
driver: json-file
options:
max-size: "2m"
max-file: "3"
networks:
searxng-net:
volumes:
searxng-cache:
valkey-data:
YAML
cd "$APP_DIR"
if docker compose version >/dev/null 2>&1; then
DC="docker compose"
elif command -v docker-compose >/dev/null 2>&1; then
DC="docker-compose"
else
echo "docker compose / docker-compose が見つかりません"
exit 1
fi
echo
echo "サービスを開始しています..."
$DC up -d --no-build
echo
echo "サービスの起動を待機しています..."
sleep 12
echo
echo "コンテナーの状態:"
$DC ps
echo
echo "Mihomo の最新ログ:"
$DC logs --tail=80 mihomo || true
echo
echo "Mihomo ローカルプロキシーポート 127.0.0.1:7897 をテストしています:"
curl -I -sS --max-time 25 --proxy http://127.0.0.1:7897 https://www.gstatic.com/generate_204 || true
echo
echo
echo "SearXNG JSON 検索をテストしています:"
curl -sS --max-time 50 "http://127.0.0.1:8888/search?q=openai%20gpt&format=json" \
| python3 -c 'import sys,json; d=json.load(sys.stdin); print("OK:", len(d.get("results", [])), "results")' || true
echo
echo "完了しました。"
echo "Hermes は引き続き次の設定を使用します: SEARXNG_URL=http://localhost:8888"
echo "SearXNG 送信プロキシー: searxng -> http://mihomo:7890"
echo "Mihomo ローカルデバッグプロキシー: http://127.0.0.1:7897"
echo "Mihomo 制御ポート: http://127.0.0.1:9097"
echo "Mihomo 設定ファイル: $MIHOMO_DIR/config.yaml"
EOF
Hermes は検索エントリのみを保持する
Hermes 側は Mihomo について知る必要はありません。必要なのは、ローカルマシンに SearXNG があるということだけです。
nano ~/.hermes/.env
書き込み:
SEARXNG_URL=http://localhost:8888
次に、~/.hermes/config.yaml で検索バックエンドを指定します:
web:
search_backend: "searxng"
Hermes 公式の SearXNG skill をインストールしている場合は、引き続き使用できます。
hermes skills install official/research/searxng-search
もし Hermes がユーザーサービスであれば:
systemctl --user restart hermes
systemctl --user status hermes --no-pager
ここにも境界があります:SearXNG は検索を担当し、ウェブページ本文の抽出は担当しません。Hermes のドキュメントでも search backend と extract backend は分けられています。後で Hermes に検索結果ページの本文を読み込ませたい場合は、web.extract_backend に Firecrawl、Tavily、Exa、Parallel などの抽出ソリューションを別途設定する必要があります。
検証:階層をスキップしないこと
まず Mihomo をテストしてください。すぐに Hermes に問い合わせないでください。
curl -I --proxy http://127.0.0.1:7897 https://www.gstatic.com/generate_204
HTTP ステータスを返せる場合、ホストのデバッグプロキシポートが利用可能であることを示します。次に SearXNG の JSON をテストします。
curl -s --max-time 50 \
"http://127.0.0.1:8888/search?q=openai%20gpt&format=json" \
| python3 -c 'import sys,json; d=json.load(sys.stdin); print(len(d.get("results", [])), "results")'
ここで 403 が発生した場合は、まず search.formats に json が含まれているか確認してください。ここでタイムアウトが発生したり結果が空の場合は、SearXNG のログを確認してください。
cd /opt/searxng
docker compose logs -f searxng
次に、Hermes で検索をトリガーするリクエストを送信します。
OpenAI GPT の最新情報をインターネットで検索し、ソースリンクを列挙してください。
ログに /search?...format=json が表示されている場合、Hermes がローカルの SearXNG を呼び出していることを示しています。次に Mihomo を確認します。
cd /opt/searxng
docker compose logs -f mihomo
サブスクリプションの更新、provider、health check、AUTO といった情報に注目してください。 Mihomo がサブスクリプションを解析できていなければ、SearXNG をどれだけ正しく設定しても意味がありません。
省いてはいけない境界線いくつか
まず、ポートをパブリックネットワークに公開しないでください。本記事の compose はローカルホストのみにバインドしています:
ports:
- "127.0.0.1:8888:8080"
- "127.0.0.1:7897:7890"
- "127.0.0.1:9097:9090"
「変更しないでください:」のように変更しないでください。
0.0.0.0:8888:8080
0.0.0.0:7897:7890
0.0.0.0:9097:9090
そうでない場合、検索サービス、プロキシポート、および Mihomo のコントロールポートは、いずれもパブリックネットワークから悪用されるリスクがあります。external-controller: 0.0.0.0:9090 はコンテナ内でのリッスンであり、外部からアクセスできるかどうかを実際に決定するのは compose のポートバインディングです。
第二に、Google や Startpage が頻繁にタイムアウトする場合、すぐにリンク構成全体を否定的に見直さないでください。まず Bing、Brave、DuckDuckGo、Wikipedia、arXiv などをそのまま残しておき、Mihomo のサブスクリプションとヘルスチェックが安定してから、验证码やタイムアウトを引き起こしやすいエンジンを有効にしてください。
第三に、スクリプトのロールバックポイントが明確である点です。
cd /opt/searxng
cp docker-compose.yml.bak.あなたのタイムスタンプ docker-compose.yml
cp searxng/settings.yml.bak.あなたのタイムスタンプ searxng/settings.yml
docker compose up -d
私はサーバー全体にグローバルプロキシを適用するよりも、このように役割を分割するアプローチを好みます。グローバルプロキシの問題は影響範囲が広すぎることであり、障害が発生した際に、それがシステム環境、Docker、アプリケーション設定、それともプロキシノード自体が原因なのかを判断するのが難しくなります。Hermes、SearXNG、Mihomo はそれぞれ一つの役割だけを担うため、トラブルシューティング時にはむしろ楽になります。
参考資料
- Hermes Agent:Web Search & Extract
- Hermes Agent:Free meta-search via SearXNG
- SearXNG Search API
- SearXNG settings.yml
- SearXNG outgoing settings
- SearXNG engines settings
- SearXNG デフォルト settings.yml
- Mihomo proxy-providers configuration
- Mihomo url-test proxy group
写作附记
この文章では、元の素材の中で最も実用的な部分を保持しています。具体的には、階層化アーキテクチャ、SearXNG JSON、明示的な outgoing proxy、Mihomo の provider とヘルスチェック、Docker Compose、Hermes 設定、検証コマンド、よくある質問、そしてロールバックです。削減したのは繰り返しの説明や、誤解を招きやすい絶対的な表現です。たとえば all://: は汎用的に無効な設定ではなく、この環境では期待どおりにマッチしなかっただけです。
また、設定名を1か所修正しました:SearXNG の Bing ニュースエンジンは name: bing news と記述する必要があります。name: bing_news と書くと、デフォルトのエンジン名と一致しないため、通常のスペルミスよりもリスクが高くなります。
元のプロンプト
ユーザーは、「中国国内サーバーへの Hermes + SearXNG + Mihomo デプロイ:Hermes 検索をプロキシ経由にし、利用可能なノードを自動選択させる」と題した完全なデプロイ資料を提供し、整理・分析して記事に誤りがないことを確認した上で、ブログ執筆スキルを呼び出して記事にまとめるよう求めています。
資料には、背景、目標アーキテクチャ、なぜシステムプロキシを使用しないのか、SearXNG settings.yml、Mihomo config.yaml、Docker Compose、改造スクリプト、Hermes 設定、検証フロー、よくある質問、ロールバック方法、最終的な効果が含まれています。
この位置までスクロールするとコメントを読み込みます。