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 JSONLVersió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 validatorVerificació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.jsonlEl 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_tsvEntonces, 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
1Hay 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
1BM25 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áticaElasticsearch 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
zeroLos 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.637Aquí 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 27Así 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
zeroEsta 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=18screen_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
zeroEsto parece estar relacionado, pero no es la respuesta exacta.
La razón es que:
Input.is_action_pressed
move_right
move_left
normalized
speedEste 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_sizeEsto requiere demasiados tokens en un solo fragmento JSONL.
En mi simulación:
strict raw AND hits: 0Es 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_sizeAmbos 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_cdResultado de tu chunk:
Alta probabilidad de falla
Incluso si la división del chunk cambia un poco, 0 casosVentajas:
La implementación ya está hecha
Posible solo con PostgreSQL
Infraestructura simpleDesventajas:
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 disponibleVeredicto:
EliminaciónAlternativa B. fragmento crudo + solo BM25
Flujo:
raw chunkText
-> tokenizer
-> BM25
-> top JSONL devoluciónResultado 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
clampEstas 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 cadenasDesventajas:
**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 diferenteVeredicto:
Debe escribirse
Pero por sí solo es insuficienteAlternativa C. raw chunk + embedding only
Flujo:
raw chunkText
-> embedding vector
-> docs_chunks/api_mapping/label_prototypes embedding y búsqueda de coseno
-> devolver top JSONLRazó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 rangeSi 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 largosDesventajas:
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 positivosPredicció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 similarVeredicto:
Prohibido el uso exclusivo
Usado para complementar candidatos BM25Alternativa D. raw chunk + BM25 + embedding paralelo
Flujo:
-> BM25 top 50
-> embedding top 50
-> combinar
-> mezclar puntuaciones
-> devolver top JSONLEste 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 movementVentajas:
## 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 procesarDesventajas:
## 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 positivosVeredicto:
Línea mínima recomendada prácticaAlternativa 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 JSONLPor 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 QwenDesventajas:
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, éxitoFalló 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ónAlternativa 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 directareranker 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
=> bajoVentajas:
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 hardcodingDesventajas:
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 finalVeredicto:
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.clampEsto 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 similarityPor 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 respaldoCasos 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ónEs decir:
embedding = expansión del recall
BM25/exact = evidencia directa
reranker/Qwen validator = organización y verificación de candidatosCaracterí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 GodotUso recomendado:
voyage-code-3 1024 floatSi 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-3Recomendación:
Segunda prioridadGemini 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 GodotRecomendació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-3Jina 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 excesivaRecomendación:
Por ahora es de baja prioridadCaso 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 QwenGenerador de perfil de consulta Qwen
-> Qwen genera JSON para búsqueda
-> Búsqueda BM25/vectorVentajas:
Reducción del hardcoding
Posibilidad de inferencia de contexto en Godot
Posibilidad de resumir fragmentos de código complejosDesventajas:
Posible alucinación
Posible inyección de intención incorrecta en la etapa previa a la búsqueda
Lentitud
Hay costoValidador Qwen
raw chunk
+ retrieved JSONL
-> Determinar si este JSONL es evidencia directaEsto 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 cualEl 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.stopEl BM25 de segunda fase es ruido:
same page / clamp section
Referencia:
AnimatedSprite2D
position.clamp
Vector2.ZERO
screen_sizeSe complementa el embedding:
player movement
2D movement
animation based on movement
screen boundsResumen del reranker:
1.º:
first_2d_game / coding_the_player
2.º:
same page / clamp and AnimatedSprite2D explanation
Descenso:
first_3d_game / player_movement_codeJSONL 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 cualconsulta 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_sizeResultado:
0 casos posiblesRazó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_codeRazó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 docsRazón del fracaso:
En sentido, todo es movimiento del jugador, cercanoembedding 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 JSONLUso:
PoC 1ª fase
Verificar rápidamente la calidad de búsqueda a simple vistaVentajas:
Simplicidad
Rápido
Transparencia
Sin costo de modeloDesventajas:
Hay falsos positivos
No se puede buscar el significado
Se pierde si la representación del documento es diferenteEn el chunk:
El éxito es
Pero se mezcla con movimiento 3DOpción 2: Tipo equilibrado
- BM25 top 50
- embedding top 50
- unión
- puntuación ponderada
- devolución JSONL
- verificación Qwen direct‑evidenceUso:
La fase intermedia más realista y buena para el proyecto actualVentajas:
## BM25 y complementación con embedding
- Eliminar JSONL sin relación con Qwen
- Reducir el uso de hardcodingDesventajas:
Se necesita ajuste de puntuación
Sin reranker, el ranking se tambalea ligeramenteEn el chunk:
first_2d_game está estable en la parte superior
El movimiento 3D disminuye en la validación de QwenOpció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 finalUso:
Recomendación finalVentajas:
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 directaDesventajas:
## Costo
latencia
Muchas etapas de implementaciónEn 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_prototypesJuicio 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.