idea_world_labDEV JOURNAL
dimanche 28 juin 2026

Conclusion dès le départ

Exactement. J'ai mal compris l'intention.

Si je le reformule correctement, c’est cela.

1. Dans le zip, il y a le markdown original, le schéma, le débogueur et le document de conception.  
2. Sur votre machine locale, il y a déjà la version convertie markdown → JSONL → DB.  
3. Ce que je peux voir, c’est le « markdown avant conversion et le schéma DB / flux de recherche ».  
4. Ainsi, le PoC doit découper le markdown du zip en morceaux similaires à des chunks JSONL, afin de simuler les cibles de recherche qui auraient pu être insérées dans votre base de données locale.  
5. Ce que vous voulez, ce n’est pas le squelette du code, mais suivre le flux montrant comment chaque méthode de recherche génère un score et pourquoi elle réussit ou échoue lorsqu’un chunk est inséré.

Et une modification importante.

Dans le projet actuel, /api/retrieve n'est pas BM25. Le code actuel est :

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

Ceci est. Le ts_rank_cd de PostgreSQL est un classement de texte intégral, et ce n’est pas le BM25 lui‑même. La documentation de PostgreSQL indique également que ts_rank/ts_rank_cd sont des fonctions de classement qui acceptent une option de normalisation de la longueur du document. plainto_tsquery tokenise la phrase d’entrée, ignore la ponctuation et crée une requête à partir des mots qui ne sont pas des stop‑words. (PostgreSQL)


Conclusion dès le départ

La meilleure solution qui satisfait vos conditions est celle‑ci.

Entrée:
  raw chunkText tel quel

À l'intérieur du Retriever:
  1. Recherche de candidats BM25 avec le raw chunk tel quel
  2. Recherche de candidats d'embedding de code avec le raw chunk tel quel
  3. Union des candidats
  4. Réordonnancement avec le reranker
  5. Vérification directe des preuves JSONL par Qwen ou le validateur

Version où l'extraction du signal API de Godot n'est pas obligatoire.

En d'autres termes, le « extracteur de signal API de Godot » que j'ai mentionné précédemment ne doit pas être rendu obligatoire. Comme vous le dites, cela donne une impression de codage en dur. On peut l'utiliser, mais le placer au cœur de la conception de base rend la maintenance plus difficile.

Recommandation finale :

Premier candidat : BM25  
Deuxième candidat : code embedding  
Tri final : reranker  
Validation : Qwen direct-evidence validator

Vérification de la compréhension du projet

Le véritable structure observée dans le zip est la suivante.

Conception JSONL

Le document définit les livrables comme suit.

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

Le schéma de la base de données n’est pas structuré de façon à insérer une ligne complète de JSONL dans une colonne payload jsonb.
Le champ JSONL est déployé en tant que colonnes de la table.

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

En d’autres termes, votre base de données locale contiendra cela.

{"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"]}

Entrée de quatre morceaux

Supposons que l'entrée soit toujours exactement ceci.

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)

Si l’on regarde ce chunk avec le tokenizer de code, les tokens uniques apparaissent ainsi.

_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

Il y a un total de 43 jetons, dont 25 uniques.

Le jeton important ici est celui-ci.

Tokens forts:
  animatedsprite2d
  is_action_pressed
  move_left
  move_right
  normalized
  clamp
  screen_size
  vector2
  zero

Tokens faibles:
  func
  var
  if
  else
  velocity
  position
  delta
  x
  0
  1

BM25 accorde un score élevé aux tokens forts.
C’est parce que plus un mot apparaît rarement dans l’ensemble du document, plus son IDF est grand.


Comment BM25 fonctionne et produit des résultats

BM25 calcule grossièrement « dans quelle mesure le mot de requête est bien présent dans le document ».

Officiellement, trois éléments sont essentiels.

TF: combien de fois le mot apparaît dans ce document  
IDF: à quel point le mot est rare dans l'ensemble du corpus  
Length normalization: correction pour que les documents longs ne soient pas automatiquement avantagés

Elasticsearch utilise également BM25 comme algorithme de pertinence par défaut, en expliquant la fréquence des termes, la fréquence inverse des documents et la normalisation de la longueur du champ comme éléments clés. (Elastic)

La formule ressemble à ceci.

score(query, doc)
= Σ over query terms [
    IDF(term)
    *
    TF_boost(term frequency in doc, doc length)
  ]

En d'autres termes, si un mot rare comme animatedsprite2d apparaît plusieurs fois dans le document, le score augmente considérablement.


Résultat de la simulation réaliste

Le markdown original Godot réel dans le zip :

outputs/godot_docs_full/pages/

Nous l'avons découpé en morceaux d'environ 1800 caractères comme le JSONL docs_chunks, puis nous avons inséré les morceaux bruts tels quels dans la requête et exécuté BM25.

Candidat n°1

Input.is_action_pressed("move_right")
Input.is_action_pressed("move_left")
velocity.length()
velocity.normalized() * speed
$AnimatedSprite2D.play()
$AnimatedSprite2D.stop()

Pourquoi c’est la première place ? Parce que ces jetons ont été directement corrects.

animatedsprite2d
is_action_pressed
move_left
move_right
normalized
play
stop
input
speed
length
velocity
vector2
zero

Les principaux jetons contributeurs qui ont fortement augmenté le score sont les suivants.

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

Ici, df représente le nombre de chunks contenant le mot parmi les 4165 chunks au total.

C’est‑à‑dire :

animatedsprite2d n'apparaît que dans 18 des 4165 chunks au total  
move_left n'apparaît que dans 13  
move_right n'apparaît que dans 15  
is_action_pressed n'apparaît que dans 27

Ainsi, les documents contenant ces mots remontent fortement.

C’est le processus par lequel BM25 a créé la première place.


Candidat 2e place

Description d'AnimatedSprite2D
$AnimatedSprite2D.play()
get_node("AnimatedSprite2D").play()
position += velocity * delta
position = position.clamp(Vector2.ZERO, screen_size)

Jeton correspondant :

animatedsprite2d
clamp
screen_size
play
delta
_process
velocity
position
stop
vector2
zero

La raison pour laquelle ce chunk est en deuxième position est qu'il n'y a pas de partie de traitement d'entrée, mais que clamp, screen_size et AnimatedSprite2D correspondent fortement.

En particulier :

screen_size df=3
clamp df=32
animatedsprite2d df=18

screen_size est un mot qui apparaît rarement, donc même s'il n'apparaît qu'une fois, le score est élevé.


3e candidat : candidat échoué

3D player movement
Input.is_action_pressed("move_right")
Input.is_action_pressed("move_left")
Vector3.ZERO
direction.normalized()
contenu du fragment:

Jeton correspondant :

is_action_pressed
move_left
move_right
normalized
speed
velocity
delta
input
x
var
func
zero

Cela semble lié, mais ce n’est pas la réponse exacte.

Pourquoi cela a‑t‑il été publié :

Input.is_action_pressed
move_right
move_left
normalized
speed

Cette raison provient du fait que les jetons communs du code de déplacement se chevauchent fortement.

Cependant, ce document concerne la 3D. Le chunk est en 2D et AnimatedSprite2D, Vector2, position.clamp, screen_size sont essentiels.

En d’autres termes, l’utilisation exclusive de BM25 entraîne ce type de faux positif.


Pourquoi la méthode actuelle échoue

La méthode proche de /api/retrieve actuelle est plainto_tsquery(raw_chunk).

Cette méthode se traduit approximativement par la requête suivante.

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

Dans ce cas, un seul morceau JSONL exige trop de jetons.

Dans ma simulation :

strict raw AND hits: 0

En d’autres termes, si un chunk ne satisfait pas simultanément tous les tokens uniques, il n’y a aucun résultat.

En effet, la documentation officielle est également structurée de cette manière.

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

chunk B:
  Description d'AnimatedSprite2D
  position.clamp
  screen_size

Les deux sont liés, mais tout n'est pas contenu dans une seule ligne.

Donc, il est approprié de réviser complètement la méthode actuelle.


Alternatives par méthode

Alternative A. Méthode actuelle : raw chunk + PostgreSQL plainto_tsquery

Flux:

raw chunkText
-> plainto_tsquery
-> search_tsv @@ query
-> ts_rank_cd

Voici le résultat du chunk :

Probabilité d'échec élevée  
Même un léger changement dans la division du chunk donne 0 cas

Avantages:

L'implémentation est déjà faite  
Possible uniquement avec PostgreSQL  
Infrastructure simple

Inconvénients:

Faible avec les gros morceaux de code  
Les conditions AND sont excessives  
Il y a beaucoup de bruit de jetons de code  
Ce n'est pas BM25  
Recherche sémantique impossible

Jugement:

Élimination

Alternative B. raw chunk + BM25 only

Flux :

raw chunkText
-> tokenizer
-> BM25
-> top JSONL retour

Voici le résultat du chunk :

1er: first_2d_game / coding_the_player  
2e: first_2d_game / coding_the_player / clamp section  
3e: first_3d_game / player_movement_code

Pourquoi a-t-il réussi ?

AnimatedSprite2D
move_right
move_left
is_action_pressed
screen_size
clamp

Ces mots sont rares dans l’ensemble de la documentation Godot, ce qui leur a valu un score élevé.

Avantages :

Principe transparent  
Débogage facile  
Aucun coût de modèle  
Utilisation directe du raw chunk possible  
Code/API robuste aux chaînes exactes

Inconvénients:

Faible avec les synonymes/phrases explicatives  
Impossible de le trouver si le nom de l'API n'apparaît pas directement  
Du code similaire se mélange, comme 3D movement  
Manqué si le document est exprimé différemment

Jugement:

Il faut absolument l'écrire  
Mais seul, ce n'est pas suffisant

Alternative C. raw chunk + embedding only

Flux:

raw chunkText  
-> embedding vector  
-> docs_chunks/api_mapping/label_prototypes embedding et recherche cosinus  
-> retour du top JSONL

Raison d’utiliser le modèle :

Pour trouver des documents dont le sens est proche même si les chaînes ne se chevauchent pas exactement.

Par exemple, la requête contient :

position.clamp(Vector2.ZERO, screen_size)

Il y a, et le document contient :

prevent the player from leaving the screen
clamping a value means restricting it to a given range

Si cela est expliqué ainsi, BM25 peut être affaibli. L'embedding capture ce type de connexion sémantique.

Avantages:

Même si l'expression diffère, il trouve  
Fort avec le contenu de type description de document  
Facile à insérer le chunk brut tel quel  
Capable de traiter même les gros chunks

Inconvénients:

Le jugement précis de l'API est faible  
Il peut mélanger des différences subtiles comme 3D/2D, Godot3/Godot4  
Il est difficile d'expliquer pourquoi ce résultat est apparu  
Il y a un grand risque de faux positifs dans api_mapping

Votre prévision de chunk :

Succès:
  first_2d_game / coding_the_player probabilité élevée de le trouver

Échec:
  first_3d_game movement aussi susceptible d’apparaître avec une probabilité élevée
  parce que le sens de « player movement » est similaire

Jugement:

**Interdiction d’utilisation exclusive**  
Utilisé pour compléter les candidats BM25

Alternative D. raw chunk + BM25 + embedding parallèle

Flux :

raw chunkText
-> BM25 top 50
-> embedding top 50
-> Combiner
-> Mélanger les scores
-> Retourner le top JSONL

Cette méthode est assez bonne.

Votre flux de chunk :

BM25 qui capture :
  Input.is_action_pressed
  AnimatedSprite2D
  position.clamp
  screen_size

embedding qui capture :
  player movement
  2D movement tutorial
  moving inside screen
  animation based on movement

Avantages:

Complémenter mutuellement les faiblesses de la recherche de chaînes et de la recherche sémantique  
La qualité initiale s'améliore même sans Qwen  
Conserver les conditions du raw chunk

Inconvénients:

Score combiné nécessite un réglage  
Le score BM25 et l'échelle du score vectoriel sont différents  
Impossible de bloquer complètement les faux positifs

Jugement:

Ligne minimale recommandée en pratique

Alternative E. génération de raw chunk + requête JSON Qwen + recherche

Pour éviter le codage en dur, cette méthode est également possible.

Flux :

-> Demander à Qwen de créer un JSON pour la recherche  
-> Recherche BM25/vector avec le JSON généré  
-> Retourner les candidats JSONL

Par exemple, sortie 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": []
}

Ceci est une méthode qui n'implique pas le codage en dur de l'extracteur de signaux de l'API Godot.
À la place, Qwen crée un profil de requête.

Avantages :

Peu de codage en dur  
Possibilité de résumer l'intention dans des chunks complexes  
Il est possible de rendre les mots-clés plus lisibles pour les humains  
On peut confier le jugement spécialisé pour Godot à Qwen

Inconvénients:

Lenteur  
Coût engendré  
Qwen peut créer des indices inexistants  
Hallucination possible dès la phase de recherche  
Il est indispensable de vérifier directement les preuves brutes du fragment

Oui chunk succès:

Qwen obtient le succès lorsqu'il extrait AnimatedSprite2D / Input.is_action_pressed / position.clamp.

Oui échec du chunk:

Si Qwen crée une intention inexistante comme « migration de Godot 3 vers 4 », il entraîne une mauvaise manipulation d'api_mapping.

Jugement:

Bon pour les expériences de qualité de recherche  
À utiliser avec prudence comme moteur de recherche principal en production

Alternative F. raw chunk + BM25 + embedding + reranker

C’est le meilleur.

Flux :

-> BM25 top 80  
-> embedding top 80  
-> union des candidats  
-> le reranker compare directement le raw chunk avec chaque candidat JSONL  
-> retourne le JSONL top  
-> le validateur Qwen vérifie les preuves directes

reranker lit la requête et le document candidat ensemble et réévalue leur pertinence. Le document Voyage est également décrit comme un modèle de réordonnancement qui reçoit les candidats du résultat de recherche de premier niveau tels que embedding/BM25 et les réarrange selon un score de pertinence. rerank-2.5 est un réordonnanceur optimisé pour la qualité avec un contexte de 32 K. (Voyage AI)

Ce que fait le réordonnanceur dans chaque chunk :

**Candidat 1**  
- `first_2d_game / coding_the_player`  
- Le flux de code brut est presque identique au *raw chunk*  
- => **Très élevé**

**Candidat 2**  
- `same page / clamp section`  
- Contient une explication de `position.clamp` et d'`AnimatedSprite2D`  
- => **Élevé**

**Candidat 3**  
- `first_3d_game / player_movement`  
- `Input.is_action_pressed` est le même, mais le contexte est Vector3/3D  
- Aucun `AnimatedSprite2D`  
- Aucun clamp de `screen_size`  
- => **Réduit**

Avantages:

Meilleure qualité  
Réduit considérablement les faux positifs BM25  
Réduit également les faux positifs d'embedding  
Maintient les conditions du chunk brut  
Faible dépendance au codage en dur

Inconvénients:

Coût présent  
Latence présente  
Si vous mettez trop de candidats, c’est lent  
Le reranker n’est pas non plus un vérificateur de preuves, donc un validateur final est nécessaire

Jugement:

Recommandation finale

Pourquoi utiliser le modèle et pourquoi ne pas l'utiliser

BM25 n'a pas de modèle

BM25 est une recherche statistique.

Ce mot est dans la requête.  
Ce mot est aussi dans le document.  
Ce mot est rare dans le corpus.  
Alors on augmente le score.

Ainsi, il est performant pour la recherche de code comme votre chunk.

AnimatedSprite2D
Input.is_action_pressed
Vector2.ZERO
position.clamp

Cette chose accorde plus d'importance à la chaîne de caractères qu'au sens.

Cependant, BM25 a une connexion sémantique faible, comme « empêcher le clamp de sortir de l'écran ».


Pourquoi utiliser un modèle d'embedding

L'embedding transforme les phrases en vecteurs.

raw chunk
-> [0.12, -0.03, ...]
docs_chunk
-> [0.11, -0.02, ...]
cosine similarity

Ainsi, même si les chaînes ne se chevauchent pas exactement, nous recherchons le contenu le plus proche.

Dans le cas où l'embedding est nécessaire pour ce projet :

1. Lorsque le document est descriptif et que le code et le mot diffèrent  
2. Lorsque les noms d'API ne se chevauchent pas mais représentent le même concept  
3. Lorsque le fragment de tutoriel est réparti en plusieurs expressions  
4. Lorsque BM25 renvoie 0 résultat et qu'un repli est nécessaire

Cas où l'embedding doit être exclu :

1. Correspondance précise de l'api_mapping source_api  
2. Détermination de la version Godot3/Godot4  
3. Suppression des faux positifs ne correspondant qu'à target_api  
4. Confirmation de la règle de migration

En d'autres termes :

embedding = extension du rappel  
BM25/exact = preuve directe  
reranker/Qwen validator = organisation et vérification des candidats

Caractéristiques par modèle

voyage-code-3

Ce projet correspond le mieux.

Raison :

La requête n'est pas une question en langage naturel, mais un bloc de code GDScript.  
Le JSONL cible de recherche contient également des blocs de code, des noms d'API et des descriptions de documentation mélangés.

Selon la documentation officielle de Voyage, voyage-code-3 est un modèle optimisé pour la récupération de code, avec un contexte de 32 K, une dimension de base de 1024, et il prend en charge les dimensions 256/512/1024/2048. Dans la présentation de Voyage, il est indiqué qu'il a obtenu des performances moyennes supérieures à celles d'OpenAI text-embedding-3-large et de CodeSage-large sur 32 jeux de données de récupération de code. (Voyage AI)

Caractéristiques :

Avantages :
  Fort pour les requêtes de code
  Capable de traiter de gros chunks
  Possibilité de choisir 1024/2048
  Adapté à la recherche code → docs

Inconvénients :
  Dépendance aux API externes
  Coûts engendrés
  Pas un modèle spécialisé pour Godot

Recommandations d'utilisation:

voyage-code-3 1024 float

Si l’on ne considère que la qualité, 2048 est également possible, mais 1024 + reranker est plus réaliste.


OpenAI text-embedding-3-large

C’est un embedding général de haute qualité.

Selon la documentation officielle d’OpenAI, text-embedding-3-large a par défaut 3072 dimensions, et text-embedding-3-small a par défaut 1536 dimensions. (OpenAI développeur)

Caractéristiques:

Avantages :
  - récupération sémantique polyvalente puissante
  - recherche de description de documents stable
  - bon écosystème OpenAI

Inconvénients :
  - pas dédié à la récupération de code
  - 3072 dimensions entraînent des coûts de stockage/indice élevés
  - le “GDScript chunk -> JSONL” de ce projet est moins direct que voyage-code-3

Recommandation :

2e priorité

Gemini Embedding

Google Gemini embedding est de 3072 dimensions par défaut, et vous pouvez choisir une taille comme 768/1536/3072 avec output_dimensionality. La documentation Google explique également que l’utilisation de dimensions plus petites permet de réduire l’espace de stockage et le coût de calcul tout en limitant la perte de qualité. (Google AI for Developers)

Caractéristiques :

Avantages:
  - Polyvalent / récupération sémantique multilingue puissante
  - Idéal pour la récupération de type description de documents
  - Réduction de dimension possible

Inconvénients:
  - Pas une option dédiée uniquement à la récupération de code
  - Priorise la similarité sémantique plutôt que la précision du code API
  - Risque d’utilisation isolée pour juger précisément la migration de Godot

Recommandation :

Ce serait bien de se concentrer sur la QA des documents  
Si c'est centré sur la recherche de morceaux de code, sous voyage-code-3

Jina embeddings v4

Jina embeddings v4 est un modèle qui excelle dans la recherche de documents complexes, le multilingue, le multimodal, ainsi que la récupération de documents visuellement riches tels que les tableaux, graphiques et images. Selon la description de Jina, il met l’accent sur la prise en charge de longues entrées et la récupération de documents multimodaux. (jina.ai)

Caractéristiques :

Avantages:
  La portée de recherche des documents est large
  Fort pour les documents multimodaux/combinés
  Il existe également une série d'adaptateurs de code

Inconvénients:
  Votre projet est actuellement centré sur markdown/code
  La récupération d'images/tableaux n'est pas essentielle
  Cela peut être un choix excessif

Recommandation :

Pour le moment, priorité basse

Cas d’utilisation de Qwen pour la recherche

Qwen, comme vous l’avez indiqué, concerne actuellement la génération/validation de JSONL.
Il peut également être utilisé pour la recherche.

Il existe deux manières de l’utiliser.

1. Générateur de profil de requête Qwen  
2. Validateur / réordonnanceur Qwen

Générateur de profil de requête Qwen

raw chunk
-> Qwen génère un JSON pour la recherche
-> Recherche BM25/vector

Avantages:

Réduction du codage en dur  
Possibilité d'inférence de contexte Godot  
Possibilité de résumer les blocs de code complexes

Inconvénients:

hallucination possible  
Injection d'intention incorrecte possible avant la recherche  
Lenteur  
Coût présent

Validateur Qwen

raw chunk
+ retrieved JSONL
-> Déterminer si ce JSONL est une preuve directe

Ceci est puissant.
Dans le document d'observation du projet, le critère « il faut que le champ JSONL et la chaîne de chunk correspondent directement, plutôt que la similarité de sujet large » est déjà établi.

Recommandation:

Il est plus sûr d’utiliser Qwen comme validateur de preuves directes après la recherche plutôt que de générer des requêtes avant la recherche.

Flux de succès/échec basé sur le chunk

Flux de succès : BM25 + embedding + reranker

Entrée:

raw chunk tel quel

Le bruit du BM25 de première étape :

first_2d_game / coding_the_player

Justification :
  Input.is_action_pressed
  move_right
  move_left
  velocity.normalized
  AnimatedSprite2D.play
  AnimatedSprite2D.stop

Le BM25 de deuxième phase est du bruit:

same page / clamp section

Base:
  AnimatedSprite2D
  position.clamp
  Vector2.ZERO
  screen_size

L'embedding est complété :

player movement
2D movement
animation based on movement
screen bounds

Résumé du reranker :

1er:
  first_2d_game / coding_the_player

2e:
  same page / clamp and AnimatedSprite2D explanation

Inférieur:
  first_3d_game / player_movement_code

JSONL final :

{"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"]}}

Ce n’est pas un succès.


Flux d’échec 1 : tsquery brut actuel

Entrée:

raw chunk tel quel

Requête interne :

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

Résultat:

0 cas possible

Raison de l'échec:

Échouer si tous les mots ne sont pas présents dans un chunk JSONL

Ce texte doit être réécrit.


Flux d'échec 2 : BM25 uniquement

Résultat :

1ère first_2d_game / coding_the_player  
2ème same page / clamp section  
3ème first_3d_game / player_movement_code

Raison de l'échec:

first_3d_game partage également Input.is_action_pressed, move_left, move_right, normalized.

BM25 ne comprend pas parfaitement si c’est “2D ou 3D”.
Il calcule simplement le score des mots.

Ainsi, BM25 only n’est pas suffisant.


Flux d'échec 3 : embedding only

Résultat attendu:

2D player movement
3D player movement
scripting player input
vector math
animation player docs

Raison de l'échec:

En termes de sens, tout est un mouvement du joueur, proche

Les embeddings trouvent bien les éléments sémantiquement proches, mais la précision de l'API est faible.


Flux d'échec 4 : Profil de requête Qwen uniquement

Qwen réussira s’il est sélectionné aussi bien.

{"important_terms":["Input.is_action_pressed","AnimatedSprite2D","position.clamp","Vector2.ZERO"]}

Cependant, si vous le choisissez ainsi, cela échoue.

{"migration_intent":"Godot 3 to Godot 4 migration","important_terms":["AnimatedSprite","AnimatedSprite2D"]}

Raison de l'échec:

Dans le raw chunk, il n’y a pas d’AnimatedSprite, seulement AnimatedSprite2D.  
C’est du code correct pour Godot 4, mais il est possible d’apporter le mapping de migration.

Ainsi, il est dangereux d'utiliser Qwen uniquement comme générateur de requêtes avant la recherche.
Un validateur après la recherche est plus sûr.


3 recommandations finales

Option 1 : la complexité la plus faible

raw chunk
-> BM25
-> top JSONL

Utilisation:

PoC 1ère phase  
Vérifier rapidement la qualité de recherche à l'œil

Avantages:

Simplicité  
Rapide  
Transparence  
Pas de coût de modèle

Inconvénients:

false positive présence  
signification non recherchée  
si la représentation du document diffère, omission

Dans le chunk :

Le succès est atteint  
Mais le mouvement 3D est mélangé

Option 2 : Type équilibré

-> BM25 top 50
-> embedding top 50
-> union
-> score pondéré
-> retour JSONL
-> validation de direct-evidence Qwen

Utilisation:

L'étape intermédiaire la plus réaliste pour le projet actuel

Avantages:

BM25 et amélioration par embedding  
Suppression du JSONL non lié à Qwen

Hardcoding écrit

Inconvénients:

Nécessité d'ajuster le score  
Sans reranker, le classement est légèrement instable

Dans le chunk :

first_2d_game est stable et supérieur  
Le mouvement 3D diminue lors de la validation Qwen

Option 3 : Qualité avant tout

- BM25 top 80
- voyage-code-3 embedding top 80
- union
- rerank-2.5
- Qwen direct-evidence validator
- JSONL final

Utilisation:

Recommandation finale

Avantages:

La meilleure qualité  
Minimisation du codage en dur  
Maintien des conditions de raw chunk  
Le reranker réduit les faux positifs BM25/embedding  
Le validateur Qwen supprime les JSONL sans preuve directe

Inconvénients:

Coût  
latence  
De nombreuses étapes de mise en œuvre

Dans le chunk :

Acceptation finale :
  docs_chunks / first_2d_game / coding_the_player

Rejet final ou rang inférieur :
  first_3d_game / player_movement_code
  unrelated api_mapping
  migration label_prototypes

Jugement final

Si je renverse ce projet maintenant, je procéderai ainsi.

Le texte d’entrée reste uniquement un chunkText.

1ᵉʳ recherche : BM25.  
    Raison : c’est la méthode la plus transparente et puissante pour la recherche de chaînes de code/API.

2ᵉ recherche : embedding voyage‑code‑3.  
    Raison : la requête est un chunk de code, donc un modèle de récupération de code est approprié.

3ᵉ tri : rerank‑2.5.  
    Raison : pour faire remonter en tête les JSONL réellement correspondants parmi les candidats similaires obtenus par BM25 et l’embedding.

Validation finale : validateur Qwen direct‑evidence.  
    Raison : si aucun texte ou motif correspondant directement au chunk n’est présent dans le JSONL, il faut le rejeter.

Résumé en une phrase :

Entrée du raw chunk tel quel → génération de candidats parallèles BM25 + code embedding → réordonnancement par le reranker → validation directe des preuves par Qwen est la réponse.

Il est préférable de ne pas inclure l’extracteur de signaux API de Godot.
Même si vous l’ajoutez, il ne doit servir qu’à un « bonus de score ». La recherche de base doit se faire via BM25/vector basé sur le raw chunk, conformément à votre intention.