結論から
そうです。私の意図を間違えて捉えてしまいました。
正確にもう一度捉えると、これです。
1. zip には markdown 原本、スキーマ、デバッガ、設計文書が含まれています。
2. あなたのローカルにはすでに markdown → JSONL → DB 変換版が入っています。
3. 私が見ることができるのは「変換前の markdown と DB スキーマ/検索フロー」です。
4. したがって PoC は zip 内の markdown を JSONL チャンクのように分割し、あなたのローカル DB に入っていたであろう検索対象をシミュレートする必要があります。
5. あなたが求めているのはコードの骨格ではなく、チャンクが入ったときに各検索方式がどのようにスコアを生成し、なぜ成功/失敗するのかというフローを追跡することです。そして重要な修正があります。
現在のプロジェクトの /api/retrieve は BM25 ではありません。
現在のコードは:
chunkText
-> plainto_tsquery('simple', chunkText)
-> search_tsv @@ query
-> ts_rank_cd(...)です。PostgreSQL の ts_rank_cd は full‑text ranking であり、BM25 自体ではありません。PostgreSQL ドキュメントも ts_rank/ts_rank_cd が文書長の正規化オプションを受け取る ranking 関数であると説明しています。plainto_tsquery は入力文をトークン化して句読点を無視し、ストップワードでないものをクエリにします。(PostgreSQL)
結論から
あなたの条件を満たす最善はこれです。
入力:
raw chunkText そのまま
Retriever 内部:
1. raw chunk そのまま BM25 候補検索
2. raw chunk そのまま code embedding 候補検索
3. 候補 union
4. reranker で再排序
5. Qwen または validator が JSONL 直接根拠検証Godot APIシグナル抽出を必須としないバージョンです。
つまり、私が以前言った「Godot API signal extractor」は必須にすべきではありません。あなたの言う通り、それはハードコーディングの臭いがします。使えるかもしれませんが、基本設計の核心として置くと保守性が悪くなります。
最終的な推奨は:
1次候補: BM25
2次候補: code embedding
最終ソート: reranker
検証: Qwen direct-evidence validatorプロジェクト理解検証
zipで確認した実際の構造はこのようになっています。
JSONL 設計
文書には成果物がこのように定義されています。
work/godot_rag/jsonl/docs_chunks.jsonl
work/godot_rag/jsonl/api_mapping.jsonl
work/godot_rag/jsonl/label_prototypes.jsonl
work/godot_rag/jsonl/ingest_report.jsonlDB スキーマは JSONL の1行を丸ごと payload jsonb に入れる構造ではありません。
JSONL フィールドがテーブル列として展開されます。
docs_chunks:
chunk_id
doc_version
source_url
source_file
source_sha256
doc_type
symbol
section_path
heading
content
code_blocks
language_tags
godot_version_tags
api_symbols
token_count
metadata
embedding
search_tsv
api_mapping:
mapping_id
source_api
target_api
change_type
godot_from
godot_to
confidence
evidence_chunk_ids
match_terms
notes
negative_patterns
label_prototypes:
prototype_id
label
task_type
input_pattern
expected_finding
recommended_action
evidence_mapping_ids
evidence_chunk_ids
severity
validator_rules
embedding
search_tsvつまり、あなたのローカルDBにはこのように入っているでしょう。
{"chunk_id":"...","source_file":"outputs/godot_docs_full/pages/getting_started__first_2d_game__03.coding_the_player__161d377b.md","doc_type":"tutorial","heading":"Coding the player","content":"...","code_blocks":["func _process(delta): ..."],"api_symbols":["Input.is_action_pressed","Vector2.ZERO","AnimatedSprite2D","position.clamp"]}ネ chunk 入力
入力は必ずこれそのままだと仮定します。
func _process(delta):
var velocity = Vector2.ZERO
if Input.is_action_pressed(&"move_right"):
velocity.x += 1
if Input.is_action_pressed(&"move_left"):
velocity.x -= 1
if velocity.length() > 0:
velocity = velocity.normalized() * speed
$AnimatedSprite2D.play()
else:
$AnimatedSprite2D.stop()
position += velocity * delta
position = position.clamp(Vector2.ZERO, screen_size)このチャンクをコードトークナイザーで見ると、ユニークトークンはこのようになります。
_process
animatedsprite2d
clamp
delta
else
func
if
input
is_action_pressed
length
move_left
move_right
normalized
play
position
screen_size
speed
stop
var
vector2
velocity
x
zero
0
1合計43個のトークン、ユニークは25個です。
ここで重要なトークンはこれです。
強いトークン:
animatedsprite2d
is_action_pressed
move_left
move_right
normalized
clamp
screen_size
vector2
zero
弱いトークン:
func
var
if
else
velocity
position
delta
x
0
1BM25は強いトークンに高いスコアを付けます。
なぜなら、文書全体で稀に出現する単語ほどIDFが大きくなるからです。
BM25はどのように動作して結果が出るのか
BM25は大まかに「クエリ単語がドキュメントにどれだけうまく埋め込まれているか」を計算します。
公式的には三つが核心です。
TF: このドキュメント内で単語が何回出現したか
IDF: 全体コーパスでこの単語がどれほど稀であるか
Length normalization: 長いドキュメントが必ず有利にならないように補正ElasticsearchもBM25をデフォルトの関連性アルゴリズムとして使用し、用語頻度、逆文書頻度、フィールド長正規化を主要な要素として説明します。 (Elastic)
式はこのような感じです。
score(query, doc)
= Σ over query terms [
IDF(term)
*
TF_boost(term frequency in doc, doc length)
]つまり animatedsprite2d のような珍しい単語がドキュメントに複数回出現すると、スコアが大幅に上がります。
実際にシミュレーションした結果
zip の中の実際の Godot markdown 原本:
outputs/godot_docs_full/pages/これを JSONL docs_chunks のように 1800文字前後のチャンクに分割し、元の raw チャンクをそのままクエリとして BM25 を実行しました。
候補 1位
Input.is_action_pressed("move_right")
Input.is_action_pressed("move_left")
velocity.length()
velocity.normalized() * speed
$AnimatedSprite2D.play()
$AnimatedSprite2D.stop()なぜ1位かというと、これらのトークンが直接当たったからです。
animatedsprite2d
is_action_pressed
move_left
move_right
normalized
play
stop
input
speed
length
velocity
vector2
zeroスコアを大きくした上位貢献トークンは次のとおりです。
animatedsprite2d df=18 tf=7 contribution=11.975
is_action_pressed df=27 tf=2 contribution=8.609
move_left df=13 tf=1 contribution=7.479
velocity df=180 tf=18 contribution=7.467
move_right df=15 tf=1 contribution=7.299
normalized df=67 tf=2 contribution=7.069
stop df=133 tf=2 contribution=5.900
play df=144 tf=2 contribution=5.764
input df=323 tf=7 contribution=5.650
speed df=219 tf=3 contribution=5.637ここで df は、全体の chunk 4165 個のうち、その単語が含まれる chunk の数です。
すなわち:
animatedsprite2dは全体 4165 個のチャンクのうち 18 個にだけ出現します
move_leftは 13 個にだけ出現します
move_rightは 15 個にだけ出現します
is_action_pressedは 27 個にだけ出現しますそれでこの単語が含まれる文書が強く上がってきます。
これがBM25が「どのように」1位を作った過程です。
候補 2位
AnimatedSprite2D の説明
$AnimatedSprite2D.play()
get_node("AnimatedSprite2D").play()
position += velocity * delta
position = position.clamp(Vector2.ZERO, screen_size)一致したトークン:
animatedsprite2d
clamp
screen_size
play
delta
_process
velocity
position
stop
vector2
zeroこのチャンクが2位である理由は、入力処理部分はありませんが、clamp、screen_size、AnimatedSprite2Dが強く合致しているからです。
特に:
screen_size df=3
clamp df=32
animatedsprite2d df=18screen_sizeはほとんど出てこない単語なので、一度だけ出てきてもスコアが大きいです。
候補 3位: 失敗候補
3D player movement
Input.is_action_pressed("move_right")
Input.is_action_pressed("move_left")
Vector3.ZERO
direction.normalized()一致したトークン:
is_action_pressed
move_left
move_right
normalized
speed
velocity
delta
input
x
var
func
zeroこれは関連があるように見えますが、正確な答えではありません。
なぜアップされたかというと:
Input.is_action_pressed
move_right
move_left
normalized
speedこのような移動コードの共通トークンが強く重複しているためです。
しかしこの文書は3Dです。chunkは2Dで、AnimatedSprite2D、Vector2、position.clamp、screen_sizeが核心です。
つまり BM25 だけを使うとこのような false positive が発生します。
現在の方式が失敗する理由
現在 /api/retrieve に近い方式は plainto_tsquery(raw_chunk) です。
この方式は大まかにこのようなクエリになります。
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このようにすると、1つの JSONL チャンクで要求されるトークンが多すぎます。
私のシミュレーションでは:
strict raw AND hits: 0つまり、あるチャンクがすべてのユニークトークンを同時に満たさない場合は0件です。
なぜなら、実際の公式ドキュメントもこのように分かれています。
chunk A:
Input.is_action_pressed
velocity.normalized
AnimatedSprite2D.play/stop
chunk B:
AnimatedSprite2D の説明
position.clamp
screen_size両方とも関連していますが、1つの行にすべてが入っているわけではありません。
なので、現在の方式はやり直すのが正しいです。
方法別代替案
代替案 A. 現在の方式: raw chunk + PostgreSQL plainto_tsquery
流れ:
raw chunkText
-> plainto_tsquery
-> search_tsv @@ query
-> ts_rank_cdはい、チャンク結果:
失敗の可能性が高い
チャンクの分割が少し変わるだけでも0件利点:
実装はすでに完了しています
PostgreSQLだけで可能です
インフラはシンプルです欠点:
長いコードチャンクに弱い
AND 条件が過剰
コードトークンのノイズが多い
BM25ではない
意味検索ができない判定:
廃棄代替案 B. raw chunk + BM25 only
流れ:
raw chunkText
-> tokenizer
-> BM25
-> top JSONL 返却はい、チャンク結果:
1位: first_2d_game / coding_the_player
2位: first_2d_game / coding_the_player / clamp 部分
3位: first_3d_game / player_movement_codeなぜ成功したのか:
AnimatedSprite2D
move_right
move_left
is_action_pressed
screen_size
clampこれらの単語は Godot のドキュメント全体で希少であるため、高得点を得ました。
長所:
原理が透明
デバッグが容易
モデルコストなし
raw chunk をそのまま使用可能
コード/API は正確な文字列に強い欠点:
同義語/説明文が弱い
API名が直接出てこないと見つけられない
3D movementのように似たコードが混ざる
文書が異なる表現になると見逃す判定:
必ず書くべきです
しかし単独では不十分代替案 C. raw chunk + embedding only
流れ:
raw chunkText
-> 埋め込みベクトル
-> docs_chunks/api_mapping/label_prototypes 埋め込みとコサイン検索
-> 上位 JSONL を返すモデルを使用する理由:
文字列が完全に一致しなくても意味が近い文書を探すために例えば、クエリには:
position.clamp(Vector2.ZERO, screen_size)があり、文書には:
prevent the player from leaving the screen
clamping a value means restricting it to a given rangeこのように説明されている場合、BM25は弱くなる可能性があります。embeddingはこのような意味的つながりを捉えます。
利点:
表現が違っても見つける
文書説明型コンテンツに強い
raw chunk そのまま入れやすい
長い chunk も処理可能欠点:
正確なAPI判定が弱い
3D/2D、Godot3/Godot4 のような微妙な差異を混在させることができる
なぜこの結果が出たのか説明が難しい
api_mappingでは false positive のリスクが大きいはい、チャンク予測:
成功:
first_2d_game / coding_the_player 見つかる可能性が高い
失敗:
first_3d_game movementも同様に出る可能性が高い
“player movement” の意味が似ているため判定:
単独使用禁止
BM25候補の補完用として使用代替 D. raw chunk + BM25 + embedding 並列
流れ:
raw chunkText
-> BM25 top 50
-> embedding top 50
-> 結合
-> スコアを混合
-> top JSONL を返すこの方法はかなり良いです。
はい、チャンクの流れ:
BM25が捕らえるもの:
Input.is_action_pressed
AnimatedSprite2D
position.clamp
screen_size
embeddingが捕らえるもの:
player movement
2D movement tutorial
moving inside screen
animation based on movement利点:
文字列検索と意味検索の弱点を相互に補完
Qwen がなくても一次品質が向上
raw chunk 条件を維持欠点:
スコア結合のチューニングが必要です
BM25スコアとベクトルスコアのスケールが異なる
false positive を完全に防げない判定:
実戦最小推奨線代替案 E. raw chunk + Qwen query JSON 生成 + 検索
ハードコーディングを避けるためにこの方法も可能です。
フロー:
-> Qwenに検索用JSON生成をリクエスト
-> 生成されたJSONでBM25/vector検索
-> JSONL候補を返す例えば Qwen の出力:
{
"search_intent": "Godot 4 2D player movement tutorial",
"important_literals": [
"Input.is_action_pressed",
"Vector2.ZERO",
"AnimatedSprite2D",
"position.clamp",
"screen_size"
],
"likely_doc_topics": [
"first 2D game",
"coding the player",
"player movement",
"clamp position to screen",
"play and stop AnimatedSprite2D"
],
"migration_signals": []
}これは Godot API シグナルエクストラクタを直接ハードコーディングしない方法です。
代わりに Qwen がクエリプロファイルを作成します。
利点:
ハードコーディングが少ない
複雑なチャンクで意図の要約が可能
検索語を人が読みやすく作ることができる
Godot 特化の判断を Qwen に任せることができる欠点:
遅さ
コスト発生
Qwenがない手がかりを作り出すことができる
検索前段階から幻覚が可能
必ず raw chunk の直接根拠検証が必要はい、チャンク成功:
Qwenが AnimatedSprite2D / Input.is_action_pressed / position.clamp を取得すると成功チャンク失敗:
Qwenが「migration from Godot 3 to 4」のような存在しない意図を作り出すと、api_mappingを誤って引っ張ってきます判定:
検索品質の実験用としては良い
本番第一検索エンジンとしては慎重代替案 F. raw chunk + BM25 + embedding + reranker
これが最も優れています。
流れ:
raw chunkText
-> BM25 top 80
-> embedding top 80
-> 候補 union
-> rerankerが raw chunk と各 JSONL 候補を直接比較
-> top JSONL を返却
-> Qwen validatorが直接根拠検証rerankerは query と候補ドキュメントを一緒に読み取り、関連度を再評価します。Voyage 文書も reranker を embedding/BM25 のような一次検索結果の候補を受け取り、relevance score で再順位付けするモデルとして説明します。rerank-2.5 は 32K コンテキストを持つ品質最適化 reranker です。(Voyage AI)
ネ chunk で reranker が行うこと:
候補 1:
first_2d_game / coding_the_player
raw chunkと直接コードの流れがほぼ同じ
=> 非常に高い
候補 2:
same page / clamp section
position.clampとAnimatedSprite2Dの説明がある
=> 高い
候補 3:
first_3d_game / player_movement
Input.is_action_pressedは同じだがVector3/3Dの文脈
AnimatedSprite2Dなし
screen_size clampなし
=> 低い利点:
最も品質が高い
BM25 の偽陽性を大幅に削減
埋め込みの偽陽性も削減
生のチャンク条件を維持
ハードコーディングへの依存が低い欠点:
費用あり
レイテンシあり
候補を入れすぎると遅くなる
rerankerも根拠検証器ではないので最後のバリデータが必要判定:
最終推奨なぜモデルを使用し、なぜ使用しないのか
BM25にはモデルがない
BM25は統計的検索です。
この単語がクエリにある。
この単語がドキュメントにもある。
この単語がコーパスで希少である。
それならスコアを上げる。だから、あなたのチャンクのようなコード検索に強いです。
AnimatedSprite2D
Input.is_action_pressed
Vector2.ZERO
position.clampこのようなものは意味より文字列が重要です。
しかし BM25 は「clamp が画面外に出ないようにする」などの意味的なつながりは弱いです。
embedding モデルを使う理由
embedding は文をベクトルに変換します。
raw chunk
-> [0.12, -0.03, ...]
docs_chunk
-> [0.11, -0.02, ...]
cosine similarityそのため、文字列が正確に重ならなくても、近い内容を探します。
このプロジェクトで埋め込みが必要な場合:
1. ドキュメントが説明的で、コードと単語が異なるとき
2. API名は重複しないが同じ概念のとき
3. チュートリアルチャンクが複数の表現に分散しているとき
4. BM25が0件のときフォールバックが必要なとき埋め込みを除外しなければならない場合:
1. api_mapping source_api 正確なマッチング
2. Godot3/Godot4 バージョン判定
3. target_apiだけが一致する false positive の除去
4. migration rule 確定つまり:
embedding = recall 拡張
BM25/exact = 直接根拠
reranker/Qwen validator = 候補の整理と検証モデル別の特性
voyage-code-3
このプロジェクトに最も適しています。
理由:
queryが自然言語の質問ではなく、GDScriptコードチャンクです。
検索対象のJSONLにもcode_blocks、API名、ドキュメント説明が混在しています。Voyage 公式ドキュメント基準 voyage-code-3 は code retrieval 最適化モデルであり、32K コンテキスト、基本 1024 次元、256/512/1024/2048 次元をサポートします。Voyage 発表でも 32 個の code retrieval データセットで OpenAI text-embedding-3-large と CodeSage-large に対して高い平均性能を示したと説明しています。(Voyage AI)
特性:
長所:
code queryに強い
大きなチャンクの処理が可能
1024/2048 を選択可能
code -> docs の検索に適している
短所:
外部 API への依存
コストが発生
Godot 特化モデルではないおすすめの使用:
voyage-code-3 1024 float品質だけを見ると 2048 も可能ですが、1024 + reranker の方が現実的です。
OpenAI text-embedding-3-large
汎用の高品質埋め込みです。
OpenAI 公式ドキュメントによると text-embedding-3-large はデフォルトで 3072 次元、text-embedding-3-small はデフォルトで 1536 次元です。(OpenAI 開発者)
特性:
長所:
- 汎用的なセマンティック検索が強力
- 文書説明検索が安定的
- OpenAI エコシステムが優れている
短所:
- コード検索専用ではない
- 3072 次元で保存/インデックスコストが大きい
- このプロジェクトの “GDScript chunk -> JSONL” は voyage‑code‑3 より直接的ではない推奨度:
第2優先順位Gemini Embedding
Google Gemini embeddingは基本で3072次元であり、output_dimensionalityで768/1536/3072のようなサイズを選択できます。Googleのドキュメントでも、低次元を使用すると保存領域と計算コストを削減しながら、品質の損失を最小限に抑えられると説明しています。(Google AI for Developers)
特性:
長所:
汎用/多言語セマンティック検索が強い
文書説明型検索に適している
次元削減が可能
短所:
コード検索専用の選択肢ではない
APIコードの正確性より意味的類似性重視
Godot移行の正確な判断には単独使用は危険推奨度:
ドキュメント QA が中心だと良いです
コードチャンク検索が中心なら voyage-code-3 の下Jina embeddings v4
Jina embeddings v4は、複雑な文書検索、多言語、マルチモーダル、表/チャート/画像のようなビジュアルリッチなドキュメント検索に強みを持つモデルです。Jinaの説明に基づき、長い入力とマルチモーダル文書検索を強調しています。(jina.ai)
特性:
長所:
文書検索の範囲が広い
マルチモーダル/複合文書に強い
コードアダプタ系もある
短所:
あなたのプロジェクトは現在markdown/code中心
画像/表のリトリーバルが核心ではない
過剰な選択になる可能性がある推奨度:
今は優先度が低いQwenを検索に使う場合
Qwenはあなたの言う通り、現在はJSONL生成/検証側です。
検索にも使うことはできます。
使用方法は二つあります。
1. Qwen クエリプロファイル生成器
2. Qwen バリデータ / リランカーQwen query profile ジェネレータ
raw chunk
-> Qwenが検索用JSONを生成
-> BM25/ベクトル検索利点:
ハードコーディングの削減
Godot の文脈推論が可能
複雑なコードチャンクを要約可能欠点:
hallucination 可能
検索前段階で誤った意図注入可能
遅い
コストありQwen validator
raw chunk
+ retrieved JSONL
-> このJSONLが直接的な根拠かどうかを判定これは強力です。
プロジェクト観察ドキュメントにも「広いテーマの類似性ではなく、JSONLフィールドとチャンク文字列が直接一致する」必要があるという基準がすでに設定されています。
推奨:
Qwenは検索前のクエリ生成より、検索後に直接エビデンスバリデータとして使用する方が安全です。ネ chunk 基準 成功/失敗 フロー
成功フロー: BM25 + embedding + reranker
入力:
そのままの生データ第1段階のBM25がノイズ:
first_2d_game / coding_the_player
根拠:
Input.is_action_pressed
move_right
move_left
velocity.normalized
AnimatedSprite2D.play
AnimatedSprite2D.stop第2段階のBM25がノイズ:
same page / clamp section
根拠:
AnimatedSprite2D
position.clamp
Vector2.ZERO
screen_sizeembeddingが補完:
player movement
2D movement
animation based on movement
screen boundsrerankerが整理:
1位:
first_2d_game / coding_the_player
2位:
same page / clamp and AnimatedSprite2D explanation
下位:
first_3d_game / player_movement_code最終 JSONL:
{"table":"docs_chunks","payload":{"source_file":"outputs/godot_docs_full/pages/getting_started__first_2d_game__03.coding_the_player__161d377b.md","doc_type":"tutorial","heading":"Coding the player","content":"...Input.is_action_pressed...velocity.normalized...$AnimatedSprite2D.play()...$AnimatedSprite2D.stop()...","api_symbols":["Input.is_action_pressed","Vector2.ZERO","AnimatedSprite2D"]}}{"table":"docs_chunks","payload":{"source_file":"outputs/godot_docs_full/pages/getting_started__first_2d_game__03.coding_the_player__161d377b.md","doc_type":"tutorial","heading":"Coding the player","content":"...position += velocity * delta...position = position.clamp(Vector2.ZERO, screen_size)...","api_symbols":["Vector2.ZERO","position.clamp","AnimatedSprite2D"]}}これが成功です。
失敗フロー 1: 現在の raw tsquery
入力:
そのままの生データ内部 query:
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結果:
0件可能失敗の理由:
JSONLチャンク内にすべての単語がない場合は失敗これを覆い直す必要があります。
失敗フロー 2: BM25 only
結果:
1位 first_2d_game / coding_the_player
2位 same page / clamp section
3位 first_3d_game / player_movement_code失敗の理由:
first_3d_gameも Input.is_action_pressed、move_left、move_right、normalized を共有しますBM25は「2Dか3Dか」を完全に理解していません。
ただ単語スコアを計算します。
そのため、BM25 onlyだけでは不十分です。
失敗フロー 3: embedding only
結果予想:
2D player movement
3D player movement
scripting player input
vector math
animation player docs失敗の理由:
意味上すべて player movement で近いembeddingは意味が近いものをうまく見つけますが、API の精度は低いです。
失敗フロー 4: Qwen クエリプロファイルのみ
Qwen がこのようにうまく抽出すれば成功です。
{"important_terms":["Input.is_action_pressed","AnimatedSprite2D","position.clamp","Vector2.ZERO"]}しかし、このように抽出すると失敗します。
{"migration_intent":"Godot 3 to Godot 4 migration","important_terms":["AnimatedSprite","AnimatedSprite2D"]}失敗の理由:
raw chunkには AnimatedSprite がなく、AnimatedSprite2D だけです
Godot 4 の正常なコードですが、migration mapping を持ち込むことができますなので、Qwenを検索前のクエリ生成器としてだけ使用すると危険です。
検索後のバリデータの方が安全です。
最終提案 3つ
案1:最も低い複雑度
raw chunk
-> BM25
-> top JSONL使用:
PoC 第1段階
検索品質を素早く目で確認利点:
簡潔さ
速さ
透明性
モデルコストなし欠点:
false positive が存在する
意味検索ができない
文書表現が異なると見逃すはい、チャンクでは:
成功はある
しかし3Dムーブメントが混ざっている2案:バランス型
- BM25 上位 50
- 埋め込み 上位 50
- 結合
- 重み付けスコア
- JSONL 返却
- Qwen direct-evidence 検証使用:
現在のプロジェクトで現実的に最も良い中間段階利点:
BM25 と embedding の補完
Qwen で無関係な JSONL を削除
ハードコーディングが少ない欠点:
スコアのチューニングが必要です
reranker がないと ranking がやや不安定になりますはい、チャンクでは:
first_2d_gameが安定して上位
3D movementはQwen検証で低下案3:品質最優先
- BM25 top 80
- voyage-code-3 embedding top 80
- union
- rerank-2.5
- Qwen direct-evidence validator
- 最終 JSONL使用:
最終推奨利点:
最高の品質
ハードコーディングの最小化
rawチャンク条件を維持
BM25/embeddingの偽陽性をリランカーが削減
Qwenバリデータが直接根拠のないJSONLを除去欠点:
コスト
latency
実装段階が多いはい、チャンクでは:
最終受け入れ:
docs_chunks / first_2d_game / coding_the_player
最終拒否または下位ランク:
first_3d_game / player_movement_code
unrelated api_mapping
migration label_prototypes最終判断
もし今このプロジェクトを根本的に変えるなら、こうします。
入力は引き続き chunkText のみを保持する。
1次検索は BM25 で行う。
理由:コード/API 文字列検索に最も透明で強力だから。
2次検索は voyage‑code‑3 埋め込みで行う。
理由:クエリがコードチャンクであるため、コード検索モデルが適しているから。
3次ソートは rerank‑2.5 で行う。
理由:BM25 と埋め込みが取得した類似候補の中から、実際のチャンクに合致する JSONL を上位に持ってくるため。
最終検証は Qwen direct‑evidence validator で行う。
理由:JSONL 内にチャンクと直接合致する文字列/パターンの根拠がなければ破棄しなければならないから。一行でまとめると:
raw chunk そのまま入力 → BM25 + code embedding 並列候補生成 → reranker 再配置 → Qwen 直接根拠検証が正解です。
Godot API シグナル抽出器は必ず入れない方が良いです。
入れる場合でも「スコアボーナス」程度に留めてください。基本検索は raw chunk ベースの BM25/vector にするのがあなたの意図に合致します。