idea_world_labDEV JOURNAL
2026年6月28日日曜日

Retriever 検索 代替 分解 文書

作成日: 2026年 6月 28日

このディレクトリは Retriever 検索 代替 ChatGPT 原文 メモ を見やすく再分割した文書束です。

原文メモはそのまま保存します。この README には共通コンテキスト、現在の方式、モデル候補、選択ガイドをまとめました。下位文書は代替別の文書だけを置きます。

総合比較表

これらの表はまだ最終選択肢ではなく、6月 28日 の比較対象となる検索代替を一画面で判断するための観察表です。基準は「コードチャンクをそのまま Retriever に入れたときにどの JSONL 候補が上がり、その候補が Qwen 検証段階で直接根拠として認められるか」です。

一目で分かる結論

代替 場所 期待役割 すぐに使える程度 主な利点 主なリスク 現在の判断
A. PostgreSQL full-text 維持 現在 /api/retrieve 基準線 今実装されている検索がどこまでできるか確認 高い すでに組み込まれておりログをすぐに確認できる 長いコードチャンクがクエリになると関連文書が抜け落ちる可能性あり 基準線として残すが、最終検索方式と断定しない
B. BM25 only 第1次検索候補 コード/API 文字列が直接入った文書を探す 中程度 KinematicBody2D, move_and_slide, AnimatedSprite2D のような直接文字列に強い 2D/3D のように広い語が混ざると false positive が残る 最初の PoC として最も観察しやすい
C. embedding only 意味ベース recall 拡張 文字列が完全に一致しなくても関連説明文書を探す 中程度 文の意味、チュートリアル説明、類似使用コンテキストを広く取得 バージョン/API の正確さ判定には単独使用のリスクが大きい 単独使用より補助 recall として位置付ける
D. BM25 + embedding 現実的な中間案 文字列根拠と意味根拠を同時に集める 中程度 BM25 の精度と embedding の recall を併用 スコア結合、重複除去、上位候補基準が必要 実用最小案に近い
E. Qwen query profile 検索補助実験 コードチャンクから検索意図を JSON に変換 低い 人が見る query profile は読みやすい 検索前に LLM が存在しない API/意図を作り出す可能性 第1次検索の核心として使うより実験/補助に置く
F. BM25 + embedding + reranker + validator 最終候補構造 候補生成、再順位付け、直接根拠検証まで接続 低い 品質と観察性が最も高い コスト、レイテンシ、段階数が多い 最終目標候補とする

PoC シミュレーション結果比較

代替 入力チャンク 検索候補が出る方式 関連 JSONL が上がる可能性 関連のない JSONL が混ざる可能性 Qwen 検証前の期待状態 Qwen 検証後の期待状態
A chunkText 全体 plainto_tsquery('simple', chunkText) で長いクエリ生成 低いまたは不安定 低いこともあるが、0件なら観察候補なし 候補がないか一部だけ返る 直接根拠検証に至る前に抜け落ちる可能性
B chunkText 全体 トークン別 BM25 スコアで文書順位付け 高い 中程度 Input.is_action_pressed, AnimatedSprite2D, clamp 文書が上がる可能性 直接文字列がなければ除外
C chunkText 全体 code/document embedding 類似度 中程度 高い “movement”, “2D”, “player” 説明文書が広く上がる JSONL 内に直接文字列がなければ除外
D chunkText 全体 BM25 候補と embedding 候補を合成 高い 中程度 文字列候補と意味候補が共に表示 直接根拠のある候補だけが残る
E chunkText 全体を Qwen が解釈 Qwen が作った検索 profile で検索 profile が合致すれば中程度 高い profile にない API が混ざる可能性 profile 自体が実コードと合っているか検証必要
F chunkText 全体 BM25 + embedding 候補を reranker が再順位付け 高い 低い 候補が多くても上位が整理される Qwen が “はい/いいえ” で直接根拠だけ残す

デモ判定で明らかになった差異

観察状況 最初の単純プロンプト 改善した直接根拠プロンプト 検索構造への意味
Godot 3 コード + 関連 JSONL “はい” が出ることがある “はい” が出るべき 関連根拠が実際の文字列/API と合致すれば合格
Godot 3 コード + 無関係 JSONL 広い Godot 用語のため “はい” が出ることがある “いいえ” が出るべき 検索候補が上がっても直接根拠検証がなければ危険
KinematicBody2D コード + 3D Spatial マッピング Godot migration という広い意味で関連と見なせる “いいえ” が正しい embedding only や緩い LLM 判定は false positive を生む
yield(...) コード + await プロトタイプ 直接パターンがあれば “はい” “はい” label_prototypes は全体使用パターン変更の根拠になる
AnimatedSprite2D.play() コード + 一般 animation docs 意味的に関連しているように見える JSONL に直接文字列/パターンがなければ “いいえ” docs_chunks も最終判定では直接根拠確認が必要

候補返却傾向比較

代替 上位に良く上がる候補 見逃しやすい候補 誤って混ざりやすい候補 目で確認すべきポイント
A 1 行内にクエリトークンが多い文書 関連トークンが複数文書に分散した場合 クエリが厳しすぎるとほぼなし なぜ 0 件か、どのトークンでマッチが崩れたか
B API 名、関数名、ノード名が直接入った行 表現が変わった説明文書 同じ単語を持つ別システム文書 match_terms と実際の source chunk 文字列の一致有無
C 意味が似たチュートリアル/説明文書 短い exact API マッピング Godot バージョンや 2D/3D が異なる文書 cosine スコアより直接文字列根拠があるか
D exact 候補 + semantic 候補 スコア結合で落ちた候補 両検索が弱く合致した候補 候補が BM25 由来か embedding 由来か
E Qwen が抽出した意図と合う候補 Qwen が profile に入れなかった実コード手がかり Qwen が想像した API/意図関連候補 profile の各フィールドが実際の chunk に存在するか
F reranker が chunk と合っていると判断した候補 reranker モデルが知らない Godot 特殊ケース reranker スコアは高いが JSONL 直接根拠がない候補 rerank スコアと validator 判定が分かれる点

False Positive / False Negative の比較

代替案 false positive の例 false negative の例 減らす方法
A 稀だが query が広く展開されると一般的な movement 文書が混入 関連文書が複数の row に分かれると 0 件 query 生成方式を変えるか BM25 候補を追加
B movement, player, 2d のため別の movement 文書が混入 API 名が文書に説明的にしか記載されていないと抜け落ちる ストップワード/弱いトークン分離、直接 API トークンに重み付け
C 意味が似通った 3D/別バージョン文書が混入 短い API rename row を捕捉できない BM25 と並行して使用し validator を付ける
D BM25 と embedding の両方で弱くマッチした候補が混入 fusion 基準が誤っていると片方の候補が埋もれる union source フラグと direct evidence ログを残す
E Qwen がない migration_intent を作成し候補を汚染 Qwen が実際のコード手がかりを profile に抜け落とす profile を検索前に信頼せず補助的にのみ使用
F reranker が妥当な説明を過大評価 候補生成段階で全く取得できなかった文書 候補数を十分確保し Qwen 検証を最後に置く

テーブル別適合度

代替案 docs_chunks api_mapping label_prototypes 備考
A 長い説明文書で不安定 exact mapping 行が短いと見逃す可能性あり pattern 行が短いと見逃す可能性あり 現行方式の限界を確認するベースライン
B API/heading/token 検索に適する source/target API の exact 検索に適する source/target pattern 検索に適する 3 テーブルすべてが初回検索候補として観察しやすい
C 説明型文書の recall に適する API rename のような短い行には弱い 使用パターンの意味が似通えば補助可能 docs_chunks の補助として最も自然
D 説明・文字列候補をすべて確保 exact mapping と semantic 候補を同時に確保 全体的な使用パターン候補を広く確保 3 テーブル共通構造で最も無難
E query profile がうまく出れば説明文書検索可能 幻想的な API が入ると危険 prototype の意図は作れるが汚染の可能性あり 検索の核というより観察・補助実験に近い
F 最終説明根拠の精錬に適する migration 根拠の精錬に適する 使用方式変更根拠の精錬に適する 3 テーブルを同一パイプラインで扱いやすい

ログで必ず見るべきこと

代替案 検索前ログ 検索中ログ 検索後ログ 失敗原因確認ポイント
A raw chunkText、生成された tsquery search_tsv @@ query のヒット数、ts_rank_cd 返却 row、スコア query が長くなりすぎたか、どのトークンが row に無いか
B raw chunkText、トークンリスト term frequency、matched terms、BM25 スコア テーブル別 top‑k 強いトークンと弱いトークンが区別できているか
C raw chunkText、embedding model ベクトル距離/コサインスコア semantic top‑k 実際の文字列なしで意味だけ合う候補か
D BM25 top‑k、embedding top‑k union、duplicate merge、スコア融合 source フラグ付き候補リスト 片方の検索だけで取得した候補がなぜ残ったか
E Qwen に送った raw chunk Qwen query profile JSON profile 基盤検索結果 profile フィールドが実際の source に存在するか
F BM25/embedding 候補全体 rerank スコア、rerank 順序 Qwen はい/いいえ、reject reason reranker 通過候補が直接根拠検証でなぜ落ちたか

実装複雑度と運用コスト

代替案 実装量 DB 変更 外部モデルコスト latency デバッグ難易度 失敗時の修正容易度
A 低い ほぼなし なし 低い 低い 低い。だが性能限界が構造的になる可能性あり
B 中程度 BM25 検索エンジン/インデックス選択が必要 なしまたは低い 低い 低い 高い。トークン/重み調整が可能
C 中程度 embedding カラム/インデックスが必要 あり 中程度 中程度 中程度。モデル・次元・threshold の調整が必要
D 中〜高 BM25 とベクトルの両方が必要 あり 中程度 中程度 高い。どの検索経路が失敗したか分離可能
E 中程度 profile 保存は任意 あり 高い 高い 低い。LLM profile がなぜ誤っているか再現が難しいことがある
F 高い BM25 + vector + rerank ログが必要 あり 高い 高い 高い。段階別ログがあれば修正箇所が見える

Qwen をどこに置くか比較

位置 長所 リスク 現在より適した使用方法
検索前 query 生成 ハードコーディングなしで検索意図を作成できる 手がかりが無いものを作り検索を汚染する可能性 実験用にのみ置く
検索候補後直接根拠検証 検索結果が実際のコードと合致しているかフィルタできる 呼び出しコストがかかる 最も重要視する
最終回答生成 コード説明・マイグレーション応答を作成できる 根拠が間違っていれば回答も間違う 検証済み JSONL 候補の後に置く
全体判断統合 人が読みやすい結論を作成できる 中間失敗を隠す可能性 ログが十分蓄積された後に検討する

選択優先順位

優先順位 選択肢 理由 この段階で確認すべき質問
1 A をベースラインとして測定 現行実装がどこで破綻しているか把握する必要がある 現在の方式は実際に何件返しているか
2 B を別途 PoC で付加 コード/API 文字列検索が最も説明的にできる KinematicBody2D, yield, move_and_slide が正しい row を見つけられるか
3 C を補助 recall として比較 docs_chunks の説明型候補を広げられる 意味候補が直接根拠検証でどれだけ落ちるか
4 D で並列候補生成 現実的な最小構造 BM25 と embedding 候補を合成したとき品質が向上するか
5 F で reranker/validator を追加 最終品質の確認 Qwen の直接根拠判定が無関係な JSONL を安定して除外できるか
保留 E 検索前 LLM は汚染リスクが大きい profile が実際のコード文字列のみで作られているか

PoC チェックリスト

チェック項目 見るべき画面/ログ 成功基準
同じ chunkText を複数の代替案に入れる ウェブデバッガの chunk 入力領域 入力が代替案ごとに変わらない
テーブル別検索ボタン docs_chunks, api_mapping, label_prototypes ボタン 同じ chunk でテーブル別の候補差が見える
raw 候補確認 返された JSONL ペイロード source chunk 内の実際の文字列と一致するフィールドが見える
Qwen 検証確認 prompt + chunk + jsonl デバッグ領域 関連候補は「はい」、無関係候補は「いいえ」となる
reject reason 確認 validator 結果ログ Godot, migration, 2D など広い単語だけが一致した候補が除外される
false positive 収集 除外された候補リスト 後で query/token/validator ルール改善に使用できる
代替案別比較保存 観察日誌または結果 JSON 同じ入力に対する A‑F の差分が残る

代替案ドキュメント

現在の前提

現在の基準は次のとおりである。

1. zip の中には markdown の原本、スキーマ、デバッガ、設計文書があります。  
2. ローカルにはすでに markdown → JSONL → DB 変換版が入っています。  
3. 今見ることができるのは変換前の markdown と DB スキーマ/検索フローです。  
4. PoC は zip 内の markdown を JSONL チャンクのように分割し、ローカル DB に入っていたであろう検索対象をシミュレートする必要があります。  
5. 必要なのはコードの骨格ではなく、チャンクが入ったときに各検索方式がどのようにスコアを作り、なぜ成功/失敗するかを追跡することです。

JSONL/DB 基準

文書基準の成果物は以下の通りです。

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.jsonl

DBは 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

基準チャンク

代替比較で基準としたチャンクは次の Godot コードです。

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)

強いトークン:

animatedsprite2d
is_action_pressed
move_left
move_right
normalized
clamp
screen_size
vector2
zero

弱いトークン:

func
var
if
else
velocity
position
delta
x
0
1

検索入力はそのまま chunkText を保持する。検索の前に Godot API シグナルを必ず抽出して別のクエリに変換する方式は、基本設計の核心とはしない。必要であれば後で補助スコア程度に検討する。

現在の /api/retrieve 方法

現在のプロジェクトの /api/retrieve は BM25 ではない。

流れは次に近い。

chunkText
  -> plainto_tsquery('simple', chunkText)
  -> search_tsv @@ query
  -> ts_rank_cd(...)

PostgreSQLの ts_rank_cd は full-text ランキング関数です。BM25 のように term frequency、inverse document frequency、field length normalization を組み合わせた検索エンジン式 BM25 そのものとは異なるものとして考える必要があります。

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行にあまりにも多くのトークンを要求する可能性があります。

公式ドキュメントのチャンクが次のように分割されていると問題が生じます。

chunk A:
  Input.is_action_pressed
  velocity.normalized
  AnimatedSprite2D.play/stop

chunk B:
  AnimatedSprite2D の説明
  position.clamp
  screen_size

両方とも関連していますが、1つの行にすべてのトークンが同時に含まれていない可能性があります。

原文メモでは、strict raw AND 性質の検索が以下のように失敗する可能性があると考えられています。

strict raw AND hits: 0

現在の方式は PoC の基準線としては見ることができますが、最終的な検索戦略として維持するには弱いです。次のステップでは現在の方式と BM25 を分離して比較する必要があります。

モデル候補

voyage-code-3

現在のプロジェクトに最も適した埋め込み候補として言及されました。

理由:

queryが自然言語の質問ではなく、GDScriptコードチャンクです。  
検索対象のJSONLにもcode_blocks、API名、ドキュメント説明が混在しています。

利点:

  • code retrieval に強い。
  • 長いチャンクの処理に有利。
  • code → docs 検索に適している。

欠点:

  • 外部 API への依存がある。
  • コストがかかる。
  • Godot 特化モデルではない。

メモ基準の推奨:

voyage-code-3 1024 float

OpenAI text-embedding-3-large

汎用高品質埋め込み候補。

長所:

  • 汎用セマンティック検索に強い。
  • 文書説明検索が安定している。
  • OpenAI エコシステムと結合しやすい。

短所:

  • コード検索専用ではない。
  • 基本次元が大きく、保存/インデックスコストが増加する可能性がある。
  • GDScript チャンク → JSONL 検索は voyage-code-3 より直接的でない場合がある。

メモ基準の推奨度:

第2優先順位

Gemini Embedding

汎用/多言語セマンティック検索候補。

長所:

  • 文書説明型検索に適している。
  • 次元削減の選択肢がある。
  • 多言語文書検索に強みがある可能性がある。

短所:

  • コード検索専用の選択肢ではない。
  • APIコードの正確性より意味的類似性に近い。
  • Godot 移行の正確な判断には単独使用は危険がある。

Jina embeddings v4

複雑な文書、マルチモーダル、ビジュアルリッチな文書検索に強みがある候補。

長所:

  • 文書検索の幅が広い。
  • マルチモーダル/複合文書に強い。
  • コードアダプタ系も検討可能。

短所:

  • 現在のプロジェクトは markdown/コード中心である。
  • 画像/表の検索が主目的ではない。
  • 現段階では過剰な選択になる可能性がある。

メモ基準の推奨度:

今は優先度が低い

rerank-2.5

reranker 候補として言及された。

役割:

BM25とembeddingが取得した候補をraw chunkと共に再比較する。  
関連度スコアで候補を再順位付けする。

このプロジェクトで期待される効果:

  • 2D movement と 3D movement の false positive を低減する。
  • AnimatedSprite2DVector2screen_sizeposition.clamp のような直接的文脈をよりよく反映させる。
  • BM25 と embedding がそれぞれもたらした候補を最終候補として整理する。

注意:

  • reranker は validator ではない。
  • 直接根拠の検証は Qwen direct‑evidence validator が再度行う必要がある。

選択肢要約

代替案 流れ 長所 リスク
A PostgreSQL フルテキストを維持 すでに実装済み 長いチャンクで 0 件になる可能性、BM25 ではない
B BM25 のみ 透明性が高く、文字列/API に強い 3D movement のような false positive
C embedding のみ 意味検索が可能 API/バージョンの精度が低い
D BM25 + embedding 現実的なバランス スコア結合のチューニングが必要
E Qwen クエリプロファイル ハードコーディングの削減、意図の要約が可能 幻覚、コスト、検索前の汚染
F BM25 + embedding + reranker + validator 品質最優先 コスト、レイテンシ、工程が多い

現在の暫定判断

原文メモの結論は次に近い。

入力は chunkText を一つだけ保持する。

1次検索は BM25 で行う。  
  理由:コード/API 文字列検索に対して透明で強力であるため。

2次検索は voyage-code-3 埋め込みで行う。  
  理由:クエリがコードのチャンクであるため、コード検索モデルが適しているから。

3次ソートは rerank-2.5 で行う。  
  理由:BM25 と埋め込みがもたらした類似候補の中から、実際のチャンクに合致する JSONL を上位に持ってくるためである。

最後の検証は Qwen direct-evidence バリデータで行う。  
  理由:JSONL 内にチャンクと直接合致する文字列/パターンの根拠がなければ破棄しなければならないから。

一行要約:

raw chunk をそのまま入力
  -> BM25 + code embedding 並列候補生成
  -> reranker 再配置
  -> Qwen 直接根拠検証

次に実際に比較すること

  • 現在の PostgreSQL フルテキスト方式が基準チャンクで何件返すか
  • BM25 が first_2d_game を実際に上位に上げるか
  • BM25 only で 3D movement の偽陽性がどれだけ混ざるか
  • embedding only がどのような説明文書を追加で取得するか
  • BM25 + embedding union で重複とスコアをどう合成するか
  • reranker が 2D/3D 候補を適切に再順位付けするか
  • Qwen direct-evidence validator が関係ない JSONL を実際に 아니오(いいえ)として捨てるか