GrooveSeek

Semantic search over a Markdown knowledge base, served over MCP.

View the Project on GitHub alphabet-h/grooveseek

15. 定義は短くてよい

背景と課題

quality filter は各チャンクを 3 つのシグナルから採点し、既定では 0.3 未満を隠す: 長さ (30 文字未満で -0.6)、定型語のみ (-0.5)、弱い構造 (80 文字未満の 1 行で -0.3)。

このうち 2 つは短さを「中身が薄いこと」の代理として読む。バイナリ形式由来の チャンクは既に両方を免除されていた — ページ / シート / スライドが短いのは形式の構造で あって薄い節ではないから。ソースコードの定義チャンクは免除されていなかった。免除が Parser::is_binary() に載っていて、code parser はバイナリ parser ではないからである。

ADR-0012 は その結果を「結果と代償」に記録した: 1 行の宣言は閾値を割って filter される、 「名前以外の情報を持たず、その名前が指すものは別に index される」から。公開している behavior doc も同じことを書き、spec は再検討の条件を「実 KB で意味のある短い定義が 落ちる報告」と定めた。

この主張には根拠があったし、正しかった — 本リポジトリの Rust ソースに対しては。 閾値を割ったのは pub mod x; と unit struct だけだった (母集団と件数は ADR-0012 の 同じ節、出典は feature-56-spike-2026-08-27.md §5)。 見たのは Rust だけである。 v1.3.0 は 2 番目の言語の grammar を初めて配った版であり、 しかも再検討の条件を満たす報告は原理的に届かない — 作者が常用している production KB は コード解析を無効にしているからである。

母集団を 1 コーパス 435 定義から 3 コーパス 11,002 定義へ広げて測り直した (via: python census.py <db> <rows-out>、記録は av-07-short-definition-census-2026-09-04.md §5):

corpus 定義数 閾値割れ 名前だけ 値を持つ
本リポジトリ src (Rust) 3362 70 70 0
本リポジトリ tests (Rust) 814 19 19 0
CPython Lib/*.py (Python) 6826 723 2 721

Rust は 8 倍以上の母集団で元の結論をそのまま再現した。Python は再現しなかった。 Python の index から落ちているのは MAXYEAR = 9999 / HAVE_ARGUMENT = 44 / SF_NOUNLINK = 0x00100000 — pickle の opcode、token の ID、stat のフラグ、バージョン 文字列である。値こそが情報で、その値はどこにも別途 index されていない。 docs が 書いた理由はこれらに対して不十分なのではなく、端的に偽である。

つまりこの制限は 1 言語で観測したことを全言語へ一般化したもので、それが到達可能に なった版を出荷した直後だった。

判断の軸

検討した選択肢

  1. そのままにする。 Rust の数字は制限の記述が正確だと言っている。
  2. 2 減点のうち片方だけ免除する。 type ShardId = u64; は返り、pub mod x; は 隠れたままにする。
  3. 定義の中身を読むシグナルを足す — 右辺の有無、本体の有無 — で名前だけの宣言を 減点する。
  4. バイナリチャンクと同じく、定義を長さ由来の 2 減点から免除し、公開している 制限の記述を書き換える。

決定

選択肢 4 を採る。

選択肢 2 を採らない理由 — そもそも成立しない。 既定閾値で計算すると、減点 1 個では 決して隠れない (長さのみ 0.4 / 構造のみ 0.7 / 定型語のみ 0.5)。さらに 「30 文字未満」は必ず「80 文字未満」なので、長さ由来の 2 減点は必ず同時に立つか 同時に立たないかである。したがってチャンクが落ちるのは 2 つとも立った時だけで、 どちらか一方を免除すれば影響を受けるチャンクは全部戻る。pub mod x; は 10 文字 (via: echo -n 'pub mod x;' | wc -m)、type ShardId = u64; は 19 文字で、まったく 同じ 2 減点を取る。減点の粒度では区別できない。選択肢 2 は「docs が嘘のままの選択肢 4」であり、この項目を再提起する人が最初に手を伸ばす形でもある。

選択肢 3 を採らない理由。 2 つを分けるには定義の中身を読むか、symbol_kind の 値ごとの言語別テーブルを持つかになる。前者は一度落とした第 3 の軸そのもので、後者は 「grammar をデータのままにする」方針が避けているものである。得られるものは上限が 分かっている: Rust 2 コーパスの名前だけの宣言 89 件 (上表の 70 + 19) を再び隠す代わりに、 この変更が取り戻す 721 件 (同表) の値を捨てることになる。

選択肢 1 を採らない理由。 全言語が Rust と同じように振る舞う限りは妥当な答えだが、 それは 1 リリース前に成り立たなくなった。

免除は既存の bool を広げるのではなく新しい QualityProfile (Text / Binary / Definition) に載せる。「このチャンクにどの減点が当たるか」という問いの答えを 1 か所に 保ち、size cap も決めているフラグに相乗りさせないため。

結果と代償

参考

採点は grooveseek/src/quality.rs、既存 index を移行させる backfill は grooveseek/src/db/meta.rs の backfill_quality。この決定を決めた census — 母集団と 判定基準はどちらも数える前に固定した — は av-07-short-definition-census-2026-09-04.md で、本リポジトリではなく開発用ノート側にある。