idea_world_labDEV JOURNAL
domingo, 28 de junio de 2026

Desde la conclusión

¡Correcto! Interpreté mal la intención.

Si lo expresamos con precisión, es esto.

1. Dentro del zip hay el markdown original, el esquema, el depurador y la documentación de diseño.  
2. En tu máquina local ya está la conversión markdown → JSONL → DB.  
3. Lo que puedo ver es el “markdown antes de la conversión y el esquema/flujo de búsqueda de la DB”.  
4. Por lo tanto, el PoC debe dividir el markdown del zip en fragmentos tipo JSONL y simular los objetivos de búsqueda que podrían haber ingresado en tu DB local.  
5. Lo que deseas no es la estructura del código, sino rastrear el flujo de cómo cada método de búsqueda genera puntuaciones y por qué tiene éxito o falla cuando se inserta un fragmento.

Y una corrección importante.

En el proyecto actual, /api/retrieve no es BM25. El código actual es:

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

Esto es. ts_rank_cd de PostgreSQL es un ranking de texto completo, no es BM25. La documentación de PostgreSQL también explica que ts_rank/ts_rank_cd son funciones de ranking que reciben una opción de normalización de la longitud del documento. plainto_tsquery tokeniza la oración de entrada, ignora la puntuación y crea una consulta con las palabras que no son stopwords. (PostgreSQL)


Desde la conclusión

La mejor solución que satisface tus condiciones es esta.

Entrada:
  raw chunkText tal cual

Dentro del Retriever:
  1. Búsqueda de candidatos BM25 con el raw chunk tal cual
  2. Búsqueda de candidatos de embedding de código con el raw chunk tal cual
  3. Unión de candidatos
  4. Reordenar con reranker
  5. Qwen o validator verifica directamente la evidencia JSONL

Versión que no hace obligatorio extraer la señal de la API de Godot.

Es decir, el “extractor de señales de la API de Godot” que mencioné antes no debe considerarse obligatorio. Como dices, eso huele a codificación rígida. Puede usarse, pero si se convierte en el núcleo del diseño básico, el mantenimiento empeorará.

La recomendación final es:

Candidato de primera fase: BM25  
Candidato de segunda fase: code embedding  
Ordenación final: reranker  
Validación: Qwen direct-evidence validator

Verificación de comprensión del proyecto

La estructura real observada en el zip es la siguiente.

Diseño JSONL

En el documento, los entregables están definidos de esta manera.

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

El esquema de la base de datos no está estructurado para insertar una línea completa de JSONL en payload jsonb.
Los campos de JSONL se expanden como columnas de la tabla.

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

Entonces, en su base de datos local, estará de esta manera.

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

Entrada de cuatro fragmentos

Supongamos que la entrada es siempre exactamente esto.

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 vemos este fragmento con un tokenizador de código, los tokens únicos aparecen así.

_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

Hay un total de 43 tokens, 25 únicos.

El token importante aquí es este.

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

Tokens débiles:
  func
  var
  if
  else
  velocity
  position
  delta
  x
  0
  1

BM25 da una puntuación alta a los tokens fuertes.
Esto se debe a que cuanto más rara es una palabra en todo el documento, mayor es su IDF.


¿Cómo funciona BM25 para producir resultados?

BM25 calcula, de manera aproximada, “qué tan bien está incrustada la palabra de la consulta en el documento”.

Formalmente, hay tres aspectos clave.

TF: cuántas veces aparece la palabra en este documento  
IDF: cuán rara es la palabra en todo el corpus  
Length normalization: corrección para que los documentos largos no tengan ventaja automática

Elasticsearch también utiliza BM25 como algoritmo de relevancia predeterminado, y describe la frecuencia de término, la frecuencia inversa de documento y la normalización de longitud de campo como elementos clave. (Elastic)

La fórmula tiene este aspecto.

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

Es decir, si una palabra rara como animatedsprite2d aparece varias veces en el documento, la puntuación aumenta significativamente.


Resultado de la simulación como si fuera real

El original de markdown de Godot real dentro del zip:

outputs/godot_docs_full/pages/

Dividí esto en fragmentos de aproximadamente 1800 caracteres como docs_chunks en JSONL, y ejecuté BM25 insertando cada fragmento sin procesar directamente como consulta.

Primer candidato

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

Porque es el número uno, estos tokens coincidieron directamente.

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

Los tokens de contribución principales que aumentaron significativamente la puntuación son los siguientes.

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

Aquí df es el número de chunks que contienen la palabra entre los 4165 chunks totales.

Es decir:

animatedsprite2d aparece solo en 18 de los 4165 chunks totales  
move_left aparece solo en 13  
move_right aparece solo en 15  
is_action_pressed aparece solo en 27

Así que los documentos que contienen estas palabras aparecen con fuerza.

Este es el proceso por el cual BM25 crea el número 1.


Candidato 2.º

Descripción de AnimatedSprite2D
$AnimatedSprite2D.play()
get_node("AnimatedSprite2D").play()
position += velocity * delta
position = position.clamp(Vector2.ZERO, screen_size)

Token coincidente:

animatedsprite2d
clamp
screen_size
play
delta
_process
velocity
position
stop
vector2
zero

Esta chunk ocupa el segundo lugar porque, aunque no tiene una parte de procesamiento de entrada, clamp, screen_size y AnimatedSprite2D encajan fuertemente.

¡Especialmente:!

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

screen_size es una palabra que casi nunca aparece, por lo que incluso si aparece una sola vez, la puntuación es alta.


Candidato 3.º: Candidato fallido

fragmento contenido:
Movimiento de jugador 3D
Input.is_action_pressed("move_right")
Input.is_action_pressed("move_left")
Vector3.ZERO
direction.normalized()

Token coincidente:

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

Esto parece estar relacionado, pero no es la respuesta exacta.

La razón es que:

Input.is_action_pressed
move_right
move_left
normalized
speed

Este código de movimiento tiene tokens comunes que se superponen fuertemente.

Sin embargo, este documento es 3D. Los chunks son 2D y AnimatedSprite2D, Vector2, position.clamp, screen_size son los elementos clave.

Es decir, si solo se usa BM25 pueden aparecer estos falsos positivos.


Por qué falla el método actual

El método cercano a /api/retrieve actualmente es plainto_tsquery(raw_chunk).

Este método genera aproximadamente la siguiente consulta.

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

Esto requiere demasiados tokens en un solo fragmento JSONL.

En mi simulación:

strict raw AND hits: 0

Es decir, si un chunk no satisface simultáneamente todos los tokens únicos, es 0 casos.

Porque la documentación oficial también está dividida de esta manera.

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

chunk B:
  Descripción de AnimatedSprite2D
  position.clamp
  screen_size

Ambos están relacionados, pero no todo está contenido en una sola fila.

Por lo tanto, el método actual debe ser reemplazado.


Alternativas por método

Alternativa A. Método actual: raw chunk + PostgreSQL plainto_tsquery

Flujo:

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

Resultado de tu chunk:

Alta probabilidad de falla  
Incluso si la división del chunk cambia un poco, 0 casos

Ventajas:

La implementación ya está hecha  
Posible solo con PostgreSQL  
Infraestructura simple

Desventajas:

Débil ante fragmentos de código largos  
Las condiciones AND son excesivas  
Mucho ruido de tokens de código  
No es BM25  
Búsqueda semántica no disponible

Veredicto:

Eliminación

Alternativa B. fragmento crudo + solo BM25

Flujo:

raw chunkText
-> tokenizer
-> BM25
-> top JSONL devolución

Resultado de tu chunk:

1.º: first_2d_game / coding_the_player  
2.º: first_2d_game / coding_the_player / sección clamp  
3.º: first_3d_game / player_movement_code

¿Por qué tuvo éxito?:

AnimatedSprite2D
move_right
move_left
is_action_pressed
screen_size
clamp

Estas palabras son raras en toda la documentación de Godot, por lo que recibieron una puntuación alta.

Ventajas:

Principio transparente  
Depuración fácil  
Sin costo de modelo  
Se puede usar raw chunk tal cual  
Código/API fuerte en coincidencia exacta de cadenas

Desventajas:

**Sinónimos/explicaciones débiles**  
No se puede encontrar si el nombre de la API no aparece directamente  
Se mezcla con código similar, como *movimiento 3D*  
Se pasa por alto si la documentación está expresada de manera diferente

Veredicto:

Debe escribirse  
Pero por sí solo es insuficiente

Alternativa C. raw chunk + embedding only

Flujo:

raw chunkText
-> embedding vector
-> docs_chunks/api_mapping/label_prototypes embedding y búsqueda de coseno
-> devolver top JSONL

Razón para usar el modelo:

Para encontrar documentos con significado cercano aunque las cadenas no coincidan exactamente.

Por ejemplo, la consulta contiene:

position.clamp(Vector2.ZERO, screen_size)

Hay y en el documento:

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

Si se describe de esta manera, BM25 puede debilitarse. El embedding captura esta conexión semántica.

Ventajas:

Se encuentra aunque la expresión sea diferente  
Fuerte en contenido de tipo descripción de documento  
Fácil de insertar el chunk crudo tal cual  
Capaz de procesar chunks largos

Desventajas:

La determinación precisa de la API es débil  
Se pueden mezclar diferencias sutiles como 3D/2D, Godot3/Godot4  
Es difícil explicar por qué se obtuvo este resultado  
En api_mapping hay un gran riesgo de falsos positivos

Predicción de chunk:

Éxito:
  first_2d_game / coding_the_player alta probabilidad de encontrar

Fracaso:
  first_3d_game movement también alta probabilidad de aparecer
  “player movement” significado similar

Veredicto:

Prohibido el uso exclusivo  
Usado para complementar candidatos BM25

Alternativa D. raw chunk + BM25 + embedding paralelo

Flujo:

-> BM25 top 50  
-> embedding top 50  
-> combinar  
-> mezclar puntuaciones  
-> devolver top JSONL

Este método es bastante bueno.

Flujo de chunk:

Lo que captura BM25:
  Input.is_action_pressed
  AnimatedSprite2D
  position.clamp
  screen_size

Lo que captura embedding:
  player movement
  2D movement tutorial
  moving inside screen
  animation based on movement

Ventajas:

## Debilidades de la búsqueda de cadenas y la búsqueda semántica se complementan entre sí  
Qwen sin necesidad de mejorar la calidad de primera fase  
Mantener la condición de fragmento sin procesar

Desventajas:

## Necesita ajuste de combinación de puntuaciones
- La escala de la puntuación BM25 y la puntuación vectorial es diferente
- No se pueden bloquear completamente los falsos positivos

Veredicto:

Línea mínima recomendada práctica

Alternativa E. generación de raw chunk + consulta Qwen JSON + búsqueda

Para evitar el hardcoding, este método también es posible.

Flujo:

-> Solicitar a Qwen la generación de JSON para búsqueda  
-> Buscar con BM25/vector usando el JSON generado  
-> Devolver candidatos JSONL

Por ejemplo, salida de 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": []
}

Esto es un método que no codifica directamente el extractor de señales de la API de Godot.
En su lugar, Qwen crea un perfil de consulta.

Ventajas:

Poco código hardcoded  
Posible resumir la intención en chunks complejos  
Se puede hacer que los términos de búsqueda sean legibles para las personas  
Se puede delegar el juicio especializado de Godot a Qwen

Desventajas:

Lentitud  
Generación de costos  
Puede crear pistas sin Qwen  
Posibilidad de alucinación desde la etapa previa a la búsqueda  
Es necesario verificar directamente la evidencia del fragmento sin procesar

Éxito del chunk:

Si Qwen extrae AnimatedSprite2D / Input.is_action_pressed / position.clamp, éxito

Falló el chunk:

Qwen crea una intención inexistente como “migration from Godot 3 to 4”, lo que hace que el api_mapping se arrastre incorrectamente.

Veredicto:

Es bueno para experimentos de calidad de búsqueda  
Se debe ser cauteloso como motor de búsqueda de primera fase en producción

Alternativa F. raw chunk + BM25 + embedding + reranker

Esto es lo mejor.

Flujo:

-> BM25 top 80
-> embedding top 80
-> unión de candidatos
-> El reranker compara directamente el raw chunk con cada candidato JSONL
-> devuelve el JSONL superior
-> El validador Qwen verifica la evidencia directa

reranker lee la consulta y los documentos candidatos juntos y vuelve a medir la relevancia. También se describe el documento de Voyage como un modelo de reranker que recibe candidatos de resultados de búsqueda de primera fase como embedding/BM25 y los reordena según una puntuación de relevancia. rerank-2.5 es un reranker optimizado para calidad con contexto de 32 K. (Voyage AI)

Lo que hace el reranker en cada fragmento:

candidato 1:
  first_2d_game / coding_the_player
  raw chunk y el flujo de código son casi idénticos
  => muy alto

candidato 2:
  same page / clamp section
  incluye explicación de position.clamp y AnimatedSprite2D
  => alto

candidato 3:
  first_3d_game / player_movement
  Input.is_action_pressed es igual pero en contexto Vector3/3D
  No hay AnimatedSprite2D
  No hay clamp de screen_size
  => bajo

Ventajas:

La mejor calidad  
Reduce mucho los falsos positivos de BM25  
También reduce los falsos positivos de embedding  
Mantiene la condición de raw chunk  
Baja dependencia de hardcoding

Desventajas:

Hay costo  
Hay latencia  
Si se ponen demasiados candidatos, se vuelve lento  
El reranker tampoco es un verificador de evidencia, por lo que se necesita un validador final

Veredicto:

Recomendación final

¿Por qué usar el modelo y por qué no usarlo?

BM25 no tiene modelo

BM25 es una búsqueda estadística.

Esta palabra está en la consulta.  
Esta palabra también está en el documento.  
Esta palabra es rara en el corpus.  
Entonces se aumenta la puntuación.

Así que es fuerte en la búsqueda de código como tu fragmento.

AnimatedSprite2D
Input.is_action_pressed
Vector2.ZERO
position.clamp

Esto es más importante la cadena que el significado.

Sin embargo, BM25 tiene una conexión de significado débil, como “evita que el clamp salga de la pantalla”.


Razón para usar modelos de embedding

El embedding convierte una oración en un vector.

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

Por lo tanto, incluso si las cadenas no se superponen exactamente, se busca el contenido más cercano.

En caso de que se necesite embedding en este proyecto:

1. Cuando el documento es descriptivo y el código y las palabras difieren  
2. Cuando los nombres de API no se superponen pero son el mismo concepto  
3. Cuando el fragmento tutorial está distribuido en varias expresiones  
4. Cuando BM25 tiene 0 resultados y se necesita un método de respaldo

Casos en los que se debe excluir el embedding:

1. api_mapping source_api coincidencia exacta  
2. Determinación de versión Godot3/Godot4  
3. Eliminar falsos positivos que solo coinciden con target_api  
4. Confirmación de regla de migración

Es decir:

embedding = expansión del recall  
BM25/exact = evidencia directa  
reranker/Qwen validator = organización y verificación de candidatos

Características por modelo

voyage-code-3

Este proyecto es el que mejor se adapta.

Razón:

La consulta no es una pregunta en lenguaje natural, sino un fragmento de código GDScript.  
En el JSONL de búsqueda también están mezclados bloques de código, nombres de API y descripciones de documentación.

Según la documentación oficial de Voyage, voyage-code-3 es un modelo optimizado para recuperación de código, con contexto de 32K, dimensión base de 1024, y soporta dimensiones de 256/512/1024/2048. En la presentación de Voyage también se explica que obtuvo un rendimiento promedio superior al de OpenAI text-embedding-3-large y CodeSage-large en 32 conjuntos de datos de recuperación de código. (Voyage AI)

Características:

Ventajas:
  Fuerte en consultas de código
  Capaz de manejar fragmentos largos
  Posibilidad de elegir 1024/2048
  Adecuado para búsqueda de código -> documentos

Desventajas:
  Dependencia de API externas
  Genera costos
  No es un modelo especializado para Godot

Uso recomendado:

voyage-code-3 1024 float

Si solo consideramos la calidad, 2048 también es posible, pero 1024 + reranker es más realista.


OpenAI text-embedding-3-large

Es un embedding de alta calidad y uso general.

Según la documentación oficial de OpenAI, text-embedding-3-large tiene 3072 dimensiones por defecto, y text-embedding-3-small tiene 1536 dimensiones por defecto. (OpenAI desarrollador)

Características:

Ventajas:
  Recuperación semántica universal es fuerte
  Es estable para la búsqueda de descripciones de documentos
  El ecosistema de OpenAI es bueno

Desventajas:
  No es exclusivamente para recuperación de código
  Al ser de 3072 dimensiones, el costo de almacenamiento/índice es alto
  En este proyecto “GDScript chunk -> JSONL” es menos directo que voyage-code-3

Recomendación:

Segunda prioridad

Gemini Embedding

Google Gemini embedding es de 3072 dimensiones por defecto, y se puede elegir tamaños como 768/1536/3072 mediante output_dimensionality. La documentación de Google también explica que al usar dimensiones más pequeñas se puede reducir el espacio de almacenamiento y el costo computacional, manteniendo una pérdida de calidad mínima. (Google AI for Developers)

Características:

Ventajas:
  rango amplio/multilingüe en recuperación semántica es fuerte
  es bueno para recuperación basada en descripción de documentos
  permite reducción de dimensionalidad

Desventajas:
  no es una opción dedicada exclusivamente a recuperación de código
  prioriza la similitud semántica sobre la precisión del código API
  usarlo solo conlleva riesgos al juzgar la migración exacta de Godot

Recomendación:

Sería bueno que se centre en QA de documentos  
Si se centra en la búsqueda de fragmentos de código, debajo de voyage-code-3

Jina embeddings v4

Jina embeddings v4 es un modelo que destaca en la recuperación de documentos visualmente ricos, como tablas, gráficos e imágenes, así como en la búsqueda compleja de documentos multilingües y multimodales. Según la descripción de Jina, enfatiza la recuperación de documentos con entradas largas y multimodales. (jina.ai)

Características:

Ventajas:
  Amplio alcance de búsqueda de documentos
  Fuerte en documentos multimodales/complejos
  También hay una familia de adaptadores de código

Desventajas:
  Tu proyecto está actualmente centrado en markdown/código
  La recuperación de imágenes/tablas no es el núcleo
  Puede ser una elección excesiva

Recomendación:

Por ahora es de baja prioridad

Caso de uso de Qwen en la búsqueda

Qwen está, como dices, actualmente en el área de generación/validación de JSONL.
También se puede usar en la búsqueda.

Hay dos formas de usarlo.

1. Generador de perfil de consulta Qwen  
2. Validador / reordenador Qwen

Generador de perfil de consulta Qwen

-> Qwen genera JSON para búsqueda  
-> Búsqueda BM25/vector

Ventajas:

Reducción del hardcoding  
Posibilidad de inferencia de contexto en Godot  
Posibilidad de resumir fragmentos de código complejos

Desventajas:

Posible alucinación  
Posible inyección de intención incorrecta en la etapa previa a la búsqueda  
Lentitud  
Hay costo

Validador Qwen

raw chunk
+ retrieved JSONL
-> Determinar si este JSONL es evidencia directa

Esto es fuerte.
En el documento de observación del proyecto también ya se ha establecido el criterio de que “no la similitud de tema amplio, sino que los campos JSONL y la cadena chunk deben coincidir directamente”.

Recomendación:

Es más seguro usar Qwen como validador de evidencia directa después de la búsqueda que generar la consulta antes de la búsqueda.

Flujo de éxito/fallo basado en chunks

Flujo de éxito: BM25 + incrustación + reordenador

Entrada:

trozo sin procesar tal cual

El BM25 de primera fase es ruido:

first_2d_game / coding_the_player

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

El BM25 de segunda fase es ruido:

same page / clamp section

Referencia:
  AnimatedSprite2D
  position.clamp
  Vector2.ZERO
  screen_size

Se complementa el embedding:

player movement
2D movement
animation based on movement
screen bounds

Resumen del reranker:

1.º:
  first_2d_game / coding_the_player

2.º:
  same page / clamp and AnimatedSprite2D explanation

Descenso:
  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"]}}

Esto es un éxito.


Flujo de error 1: raw tsquery actual

Entrada:

trozo sin procesar tal cual

consulta interna:

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

Resultado:

0 casos posibles

Razón del fracaso:

Falla si no hay todas las palabras dentro de un fragmento JSONL.

Esto debe ser reescrito.


Flujo de error 2: solo BM25

Resultado:

1.º first_2d_game / coding_the_player  
2.º same page / clamp section  
3.º first_3d_game / player_movement_code

Razón del fracaso:

first_3d_game también comparte Input.is_action_pressed, move_left, move_right, normalized.

BM25 no comprende perfectamente si es “2D o 3D”. Simplemente calcula la puntuación de las palabras.

Por lo tanto, BM25 solo no es suficiente.


Flujo de falla 3: solo incrustación

Resultado esperado:

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

Razón del fracaso:

En sentido, todo es movimiento del jugador, cercano

embedding es bueno para encontrar cosas con significado cercano, pero la precisión de la API es baja.


Flujo de falla 4: Solo perfil de consulta Qwen

Si Qwen extrae tan bien como esto, tendrá éxito.

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

¡Pero si lo haces así, fallará!

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

Razón del fracaso:

En el raw chunk no hay AnimatedSprite, solo AnimatedSprite2D.  
Es código correcto de Godot 4, pero se puede traer el mapeo de migración.

Por lo tanto, usar Qwen solo como generador de consultas antes de la búsqueda es peligroso.
Un validador después de la búsqueda es más seguro.


3 recomendaciones finales

Opción 1: la complejidad más baja

raw chunk
-> BM25
-> top JSONL

Uso:

PoC 1ª fase  
Verificar rápidamente la calidad de búsqueda a simple vista

Ventajas:

Simplicidad  
Rápido  
Transparencia  
Sin costo de modelo

Desventajas:

Hay falsos positivos  
No se puede buscar el significado  
Se pierde si la representación del documento es diferente

En el chunk:

El éxito es  
Pero se mezcla con movimiento 3D

Opción 2: Tipo equilibrado

- BM25 top 50  
- embedding top 50  
- unión  
- puntuación ponderada  
- devolución JSONL  
- verificación Qwen direct‑evidence

Uso:

La fase intermedia más realista y buena para el proyecto actual

Ventajas:

## BM25 y complementación con embedding
- Eliminar JSONL sin relación con Qwen
- Reducir el uso de hardcoding

Desventajas:

Se necesita ajuste de puntuación  
Sin reranker, el ranking se tambalea ligeramente

En el chunk:

first_2d_game está estable en la parte superior  
El movimiento 3D disminuye en la validación de Qwen

Opción 3: Calidad ante todo

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

Uso:

Recomendación final

Ventajas:

La mejor calidad  
Minimizar el hardcoding  
Mantener la condición de raw chunk  
El reranker reduce los falsos positivos de BM25/embedding  
El validador Qwen elimina JSONL sin evidencia directa

Desventajas:

## Costo
latencia
Muchas etapas de implementación

En el chunk:

**Aceptación final:**  
  docs_chunks / first_2d_game / coding_the_player  

**Rechazo final o rango inferior:**  
  first_3d_game / player_movement_code  
  unrelated api_mapping  
  migration label_prototypes

Juicio final

Si ahora reviro este proyecto, así lo haré.

La entrada mantiene solo un chunkText continuamente.

La primera búsqueda se realiza con BM25.  
  Razón: es la más transparente y potente para la búsqueda de cadenas de código/API.

La segunda búsqueda se realiza con el embedding voyage-code-3.  
  Razón: la consulta es un fragmento de código, por lo que un modelo de recuperación de código es el adecuado.

El tercer ordenamiento se hace con rerank-2.5.  
  Razón: para elevar al principio los JSONL que realmente coinciden con el fragmento entre los candidatos similares obtenidos por BM25 y el embedding.

La validación final se lleva a cabo con el validador Qwen direct-evidence.  
  Razón: si no hay evidencia directa en forma de cadena/patrón dentro del JSONL que coincida con el fragmento, debe descartarse.

Una línea resumida:

raw chunk 그대로 입력 → BM25 + code embedding 병렬 후보 생성 → reranker 재정렬 → Qwen 직접근거 검증 es la respuesta correcta.

No es necesario incluir el extractor de señales de la API de Godot.
Incluso si se incluye, solo debe usarse como un “bono de puntuación”. La búsqueda básica debe basarse en BM25/vector a partir del raw chunk, que es lo que corresponde a tu intención.