YaCy の実験的なフォーク: 順位付けの改善・署名付きの結果・NAT の内側のピア
YaCy のフォーク。公開網で測って見つけた弱点を直し、偽の結果やスパムを除ける信頼の仕組みを足し、NAT の内側のピアもリレー経由で参加できるようにした。このページの主張はすべて、docker compose で誰でも組み直せる閉じた P2P 網で測ったもの。
位置づけ: 実験。YaCy プロジェクトのリリースではなく、YaCy プロジェクトとは関係がない。公開の YaCy 網との互換性(ピア ID、seed、日本語・中国語の単語 hash)は意図して捨てている。変更の効果をデータ付きで示して議論の材料にし、合うものは小さな単位で本家に提案するために公開している。
なぜ
公開の YaCy 網(freeworld)で 16 クエリを測ったところ、上位 10 件のうちクエリの全語を含む結果は 11% だった。原因を調べると:
- 2 語以上のクエリを Solr に minimum match 1(OR 検索)で送っていた。自ピアの索引にも、すべての他ピアにも。
- Solr の点数はピアごとに最高点で割って正規化される。1 語しか一致しない頁しか持たないピアでも、その 1 位が他ピアの完全一致と同じ点になる。
- 日本語・中国語は空白と句読点でしか区切られず、文全体が 1 語になるので、単語索引(RWI)で引けない。
- 新しいピアだけの網では、3 日未満のピアは単語索引の検索先にならない。小さな網の定義では remote Solr 検索の送り先が 0 台になる。
- 結果に出所の証明が無い。ピアはどのクエリにもどんな URL・タイトルでも返せ、seed は第三者に再送・改ざんされうる。
- NAT の内側のピアは「junior」にしかなれない。外から届かないので、そのピアが索引した頁は他から見えない。
何を変えたか
順位付け
厳しめの minimum match、ピアごとの正規化の後に掛ける語の被覆率の重み、本文の薄い頁の減点、小さな網での全ピアへの問い合わせ。
日本語・中国語
CJK の文字列を重なりのある 2 文字(bigram)として、Solr でも単語索引でも索引・検索する。
信頼
Ed25519 のピアの鍵、署名付きの seed、宣言タグ付きでコーディネータが署名する信頼の一覧、全文書への作者の署名。
NAT 越え
go-libp2p の小さな sidecar が circuit relay に予約を取り、NAT の内側のピアも検索に応答できる。
検索品質
| 症状 | 原因 | 変更 |
|---|---|---|
| 1 語だけ一致する頁(キーワードの詰め込み)が上位に来る | 2 語以上のクエリに Solr の mm=1 | minimum match 2<-1 5<80%: 2 語は両方必須、3〜5 語は 1 語の欠けを許す(search.ranking.solr.mm, .mm.cjk) |
| あるピアの部分一致の 1 位が、他ピアの完全一致と同じ順位になる | ピアごとの点数の正規化 | 正規化後の点数に(見つかった語 / クエリの語)² を掛ける(search.ranking.coverage.exponent) |
| 日本語・中国語が単語索引で引けない | CJK の分かち書きが無い | 単語索引とクエリで重なりのある bigram に分ける。Solr の schema に CJKWidthFilter + CJKBigramFilter |
| クエリ全体をタイトルに持つ本文の薄い頁(タグ一覧)が上位に来る | qf が title^15・h1^11 に対して本文 ^1 | 本文が 100 語未満の結果に 語数 / 100 の重みを掛ける(search.ranking.thin.words)。CJK の語数の数え方を修正(空白の数を数えていた) |
| 新しいピアだけの網で、他ピアの単語索引を検索しない | DHT 検索は 3 日以上のピアだけ | 設定可能に(remotesearch.dht.minage、既定 3) |
| 小さな網で remote Solr 検索がどこにも飛ばない、DHT 検索先を外す | 送り先数の式が 0 になる。DHT 検索先は Solr の問い合わせ先から外していた | 32 ピア以下の網では、DHT 検索先も含めて接続中の全ピアに問い合わせる |
信頼の仕組み
- ピアの身元。各ピアは Ed25519 の鍵を持つ。12 文字のピア hash は公開鍵から導かれ、seed の中核(名前・ポート・鍵・到達方法・宣言タグ)に署名する。署名の無い seed は既定で受け入れない。hello のチャレンジで、その住所に鍵の持ち主が居ることを確かめる。
- 信頼の一覧。利用者はコーディネータの鍵を設定する。コーディネータはオペレータに委任し、オペレータは信頼するピアの一覧に署名する。一覧の各ピアには優先度と、
ads・proxy:<engine>などの宣言タグが付く。一覧は有効期限ではなく版で管理し、ピアからピアへ広がる。 - 作者の署名。頁を crawl したピアが、その URL・タイトル・単語の Bloom フィルタに署名する。既定では、署名が正しく、作者が信頼集合にいる結果だけを出す。DHT で文書を預かるピアは文書を偽造できず、できるのは返さないことだけ。
- 開放モード。署名の無い結果や信頼集合外の結果も「未検証」の印付きで出せる。未検証の結果は必ず検証済みより下に並ぶ。署名が偽造された結果は出さない。
NAT 越え
YaCy の横で同じ鍵を使う sidecar(Go、go-libp2p)を動かす。NAT の内側では circuit relay v2 に予約を取り、回線のアドレスを署名付きの seed で知らせる(Reach=relay)。他のピアはそのピアへのローカルのトンネルのポートを開いて普通の HTTP を使うので、YaCy の既存のクライアントはそのまま動く。このピアは既定では検索に応答するだけで、本人が申し出ない限り DHT のデータを預からない。
詳しくは信頼と NAT 越えの設計。
結果
yacy-lab の 2 つの実験。どちらも docker compose の閉じた網で、コーパスとクエリは決定的に作る。
検索品質: 本家とフォーク、各 3 ピア
各ピアは 1 サイトずつ crawl する。クエリは 1 番ピアに resource=global で投げ、正解の大半は他のピアにある。罠頁は 2 種類ある。クエリの 1 語を詰め込んだ頁と、クエリ全体をタイトルに持つ中身の薄い「タグ一覧」頁。11 クエリ(英語 6・日本語 4・中国語 1)の平均。2 回測り、範囲を示したもの以外は同じ値。
| クラスタ / 経路 | R-precision ↑ | recall@10 ↑ | 上位 R 件の罠 ↓ | 上位 10 件で全語を含む ↑ |
|---|---|---|---|---|
| 本家、既定 | 0.52 | 0.96 | 0.48 | 0.42 |
| フォーク、既定 | 0.84–0.88 | 1.00 | 0.12–0.16 | 0.75 |
| 本家、単語索引のみ | 0.02 | 0.02 | 0.00 | 0.09 |
| フォーク、単語索引のみ | 0.93 | 0.95 | 0.07 | 0.77 |
- 上位 5 件に入った詰め込み頁(11 クエリの合計): 本家 14、フォーク 0。
- タグ一覧頁は下がるが上位 5 件には残る。平均順位は既定の検索で 4.5 位、単語索引のみで 5.0 位。Solr のみでは、薄さの重みなしの 2.0 位が重みありで 3.1 位になる。タイトルにクエリ語を持たない正解頁よりはまだ上。重みを強めればもっと下がるが、正当な短い頁まで下げてしまうので、既定は控えめにしている。
- 本家は新しいピアだけの網で他ピアの単語索引を使えず、日本語・中国語の語はそもそも引けない。
- minimum match の既定値はこのコーパスを見て選んだ。公開網で妥当かは、公開網で測って確かめる必要がある。
信頼と NAT 越え: フォーク 6 ピア、リレー、NAT
信頼集合のピア 3 台、ads を宣言した信頼集合のピア、スパムを crawl して署名を流用した文書を仕込む「署名はあるが信頼集合外」のピア、MASQUERADE するルーターの内側のピア。26 の検査がすべて通る。主なもの:
- ピア ID は鍵から導かれる。信頼集合外のピアのスパムは既定では出ない。信頼ピアがその複製を持っていても出ない。
- 開放モードではスパムが「未検証」の印付きで、すべての検証済みの結果の下に出る。署名を流用した文書は開放モードでも出ない。
adsのピアの結果にはタグが付き、excludeTags=adsで消える。- NAT の内側のピアには直接届かないが、その頁はリレー経由で見つかり、接続も保たれる。
- 一覧の新しい版はピア間の交換だけで広がる。オペレータの委任を失効させると、そのピアたちの結果が消える。
動かしてみる
デモは 1 台のマシンで両方の網(本家 3 ピアと、フォークの信頼と NAT 越えの構成)を立て、その前に検索画面を置く。Docker とメモリ 7GB ほどが要る。
git clone https://github.com/pad01g/yacy_search_server.git yacy
git clone https://github.com/pad01g/yacy-lab.git
cd yacy
git checkout baseline && docker build -t yacy-lab/upstream:baseline -f docker/Dockerfile .
git checkout improved-search && docker build -t yacy-lab/fork:latest -f docker/Dockerfile .
docker build -t yacy-lab/sidecar:latest sidecar/
cd ../yacy-lab
docker compose -f compose.demo.yaml -p yacydemo up -d
# http://localhost:8800 を開く(準備に 10 分ほどかかり、進み具合も画面に出る)
画面では信頼の設定も変えられる。問い合わせ元が信頼するコーディネータを選ぶ(もう 1 つのコーディネータの一覧はスパムのピアだけで、それも信頼するとスパムが「検証済み」になる)。信頼の一覧を編集して署名し直す。新しい版を 1 つのピアにだけ渡して広がるのを見る。オペレータへの委任を失効させる、し直す。
実験そのもの: docker compose -p yacylab up -d && docker compose -p yacylab run --rm runner(検索品質)、docker compose -f compose.trust.yaml -p yacytrust up -d && docker compose -f compose.trust.yaml -p yacytrust run --rm runner(信頼と NAT 越え)。詳しくは yacy-lab の README。
AI エージェント向け
エージェントは自分のピアを動かし、検索 API も API キーも無しに検索の道具として使える。
- MCP サーバー(
io.github.pad01g/yacy-search):docker run -i --rm -e YACY_URL=http://host.docker.internal:8090 -e YACY_ADMIN_PASSWORD=yacy ghcr.io/pad01g/yacy-search-mcp:0.1.0。tool はsearch、crawl、index_status、peers、get_ranking_settings、set_ranking_setting、evaluate_ranking、trust_status。エージェントの横で動き、エージェント自身のピアに問い合わせる。中央のサービスは無い。 - スキル:
npx skills add pad01g/yacy-labで yacy-p2p-search が入る(ピアの起動、crawl、検索、順位の評価と調整)。 - 信頼の登録簿: pad01g/yacy-trust。pull request がマージされると、ピアが一覧に載る、またはピアを保証するオペレータになれる。
- 機械向けの要約: llms.txt。
限界
- 既定では公開の YaCy 網と互換性がない(署名の無い seed を拒否し、日本語・中国語の単語 hash も変わる)。開放モードなら旧ピアも検索できるが、結果は未検証の扱いになる。
- コーディネータを設定しないピアは、自分の文書しか信頼しない。閉じた網なら
trust.signedOnly=trueが使える。公開の網では誰かがコーディネータを務める必要がある。 - コーパスは人工的で小さい(160 頁)。公開網で見つけた弱点を再現するが、公開網での改善幅を予測するものではない。
- NAT の試験は MASQUERADE するルーター 1 台で、実際の家庭用ルーターではない。穴あけ(DCUtR)は有効にしてあるが、成否は測っていない。
- 信頼された作者が虚偽の内容に署名することは防げない(タグと監査で扱う)。預かったピアが結果を返さないことも防げない。
リンク
- コード: pad01g/yacy_search_server の branch
improved-search(変更の一覧は FORK.md) - 実験とデモ: pad01g/yacy-lab
- 設計書の全文: docs/trust-and-nat.md
- 本家: yacy/yacy_search_server · YaCy フォーラム