代替案 A: 現在の PostgreSQL フルテキスト維持
フロー
raw chunkText
-> plainto_tsquery
-> search_tsv @@ query
-> ts_rank_cd
-> top JSONL を返す利点
- すでに実装されている。
- PostgreSQLだけで可能である。
- インフラがシンプルである。
search_tsvをすでに持っているテーブルとすぐに合う。
欠点
- BM25ではない。
- 長いコードチャンクに弱い。
plainto_tsqueryが作る条件が過度に狭くなる可能性がある。- コードトークンのノイズが多い。
- 意味検索ができない。
- チャンク分割が少しでも変わると0件が出ることがある。
基準チャンクで予想される問題
基準チャンクには次のトークンが一度に入っている。
func
process
delta
velocity
vector2
zero
input
is_action_pressed
move_right
move_left
normalized
animatedsprite2d
play
stop
position
clamp
screen_sizeこのすべてのトークンが 1 つの JSONL 行に同時に含まれていない場合、検索結果が抜け落ちる可能性があります。
PoC シミュレーション
基準チャンクがそのまま /api/retrieve に入ると仮定します。
入力:
chunkText = 全体 _process(delta) 関数 本文現在の方式はこの入力を plainto_tsquery('simple', chunkText) に変える。
想定されるクエリの性質:
func & process & delta & var & velocity & vector2 & zero
& input & is_action_pressed & move_right & move_left
& length & normalized & speed & animatedsprite2d
& play & stop & position & clamp & screen_sizeこのとき、実際の関連文書が次のように2つのチャンクに分かれていると問題が生じます。
docs chunk A:
Input.is_action_pressed("move_right")
Input.is_action_pressed("move_left")
velocity.normalized() * speed
$AnimatedSprite2D.play()
$AnimatedSprite2D.stop()
docs chunk B:
position += velocity * delta
position = position.clamp(Vector2.ZERO, screen_size)人が見ると、AとBは両方とも関係している。
しかし、full-text query が同時にあまりにも多くのトークンを要求すると、次のようになる可能性があります。
docs chunk A:
animatedsprite2d = あり
is_action_pressed = あり
move_left/move_right = あり
clamp = なし
screen_size = なし
position.clamp = なし
=> クエリ全体を満たさない
docs chunk B:
clamp = あり
screen_size = あり
position = あり
animatedsprite2d = なしまたは弱い
move_left/move_right = なし
is_action_pressed = なし
=> クエリ全体を満たさない目に見える失敗の形:
検索結果 0件
または非常に狭いチャンクのみ返すこの方式では検索失敗が「文書に根拠がなくて失敗」なのか、「クエリ条件が過剰で失敗」なのかを区別することが難しい。
PoCで確認すべきログ
この代替案をテストする際は結果だけを見るべきではない。
次の値も併せて示す必要がある。
1. raw chunkText
2. plainto_tsqueryで作成された query
3. queryに入ったトークン一覧
4. 返却される行数
5. 0件の場合、どのトークンが原因で絞り込みすぎになったか予想観察:
raw chunkが長くなるほどクエリが長くなり、関連文書が複数のチャンクに分かれていると結果が抜け落ちます。判定
PoC の基準線としては使用可能です
最終検索戦略としては廃棄候補原文メモの判断は「覆すのが正しい」に近いです。