Semantic search over a Markdown knowledge base, served over MCP.
これまでの parser はどれも散文を割ってきた。Markdown は見出しで、バイナリ形式は その形式が既に持っている単位 — ページ / シート / スライド — で割る。ソースコードには 見出しが無く、読み手が返してほしい単位は定義である。問いに答えるのは、関数であり、 struct であり、method だからだ。
散文と同じ割り方をすると、関数の途中で始まって次の関数の途中で終わる chunk ができる。
これはまさに本 feature が消そうとしている検索の失敗そのものだ。かといって「定義だけ」で
割ると穴が空く — ファイルは定義だけでできていない。import、トップレベルの文、impl
ブロックを囲む波括弧、そしていちばん効く場合として parser が解釈できなかった領域が、
どの定義の外にも残る。定義だけを出す chunker は、それらを黙って落とす。
本決定が答える問い: コード chunk の単位は何か。そして、その単位が覆わなかった バイトはどうなるのか。
TxtParser はファイル全体を 1 chunk にする。
波括弧 1 つが余計な 2,000 行のファイルが 1 chunk になり、検索の役に立たないうえ、
壊れた箇所の周囲で問題なく parse できていた 90 個の定義まで隠してしまう。選択肢 4 を採る。単位は grammar 自身の tags.scm の @definition.* capture。
どの定義も覆わないバイト範囲は、見出しを持たない行ベースの chunk になる。
そこから導かれること、そして各部がその形である理由:
定義の範囲は doc comment から始まる。 tags query が報告するのは定義ノードで、 その上に書かれた doc comment はノードの外にある。放っておくと comment は定義の chunk にもその上の gap chunkにも入る — 二重に index され、内容を合計するものは 二重に数える。そこで覆う範囲を comment ノードの連なりのぶん手前へ伸ばし、空行で止める。 空行で隔てられた comment は、その定義ではなくファイルへの注釈だからだ。
予算を超えた定義は入れ子の定義へ再帰し、入れ子が無ければ行で割る。 module や class には潜る先があるが、method には無い — つまり実際のコードで支配的な形では、 行で割るのが例外ではなく常道になる。割れた各片は元の定義の見出しと種別を保ち、 自分自身の行範囲を報告する。長い関数の後半に hit しても、それがどの関数かは分かる。
行番号は定義ではなく chunk を指す。 関数の上から取り込んだ doc comment は範囲の内側で、 3 つに割れた関数は各片が自分の範囲を持つ。「その行でファイルを開けば、この chunk の中身が そこにある」がコードファイルの全 chunk について真になる読み方は、これだけである。
種別は言語のキーワードではなく grammar の語。 Rust の tags query は struct も enum も
union も class と呼ぶ。キーワードを復元するにはノード種別から表示名への対応表を
言語ごとに groove 側へ持つことになり、それこそこの設計が避けている表だ。だから
symbol_kind は class を運び、値の集合は enum で固定せず、言語の追加とともに増える。
スコープは定義リストではなく木を歩いて得る。 Rust の tags query は impl ブロックを
参照としてしか capture せず、定義にはしない。よって impl Database は定義木の節にならない。
だが同じファイルにある 2 つの open method を区別するのは、まさにそれである。親を辿って
name / type / trait のいずれかの field を持つ最初の祖先を採る — この 3 つの field 名は
grammar をまたいで通用するので、言語中立のまま保てる。
短さの閾値に満たない gap 断片は捨てる。 ただし捨てるとファイルの chunk が 無くなる場合は残す。quality filter だけでは足りない: 閉じ波括弧 1 行は閾値を下回って filter されるが、閾値未満の 2 行断片は filter を上回ってしまい、中身の無い chunk として 生き残る。ここで再利用する閾値は quality filter が既に使っているものと同じで、 ずれ得る数を 2 つ持たないためである。
tags_any: ["code"]、先頭 ! 付きの path_globs) を使う。search に新しい parameter は
足していない。pub mod x; や unit struct — は quality の閾値を割って filter される。
本リポジトリ自身のソース 6 ファイル 435 定義のうち 12 件で実測。名前以外の情報を
持たず、その名前が指すものは別に index される。chunker は grooveseek/src/parser/code/ にある。どの grammar を同梱し、残りをどう届けるか
という対の決定は ADR-0013。