Conclusão
Correto. Eu interpretei a intenção de forma equivocada.
Se eu a interpretar corretamente, é isso.
1. O zip contém o markdown original, o esquema, o depurador e a documentação de design.
2. Seu ambiente local já possui a versão convertida de markdown → JSONL → DB.
3. O que eu consigo ver são o “markdown antes da conversão e o esquema/fluxo de busca do DB”.
4. Portanto, o PoC deve dividir o markdown dentro do zip como chunks de JSONL e simular os alvos de busca que poderiam estar no seu DB local.
5. O que você deseja não é a estrutura do código, mas rastrear como cada método de busca gera pontuações e por que tem sucesso ou falha quando um chunk é inserido.E uma correção importante.
No projeto atual, /api/retrieve não usa BM25.
O código atual é:
chunkText
-> plainto_tsquery('simple', chunkText)
-> search_tsv @@ query
-> ts_rank_cd(...)É. O ts_rank_cd do PostgreSQL é um ranking de texto completo, não o próprio BM25. A documentação do PostgreSQL também descreve que ts_rank/ts_rank_cd são funções de ranking que recebem uma opção de normalização do tamanho do documento. plainto_tsquery tokeniza a frase de entrada, ignorando pontuação e cria a query a partir das palavras que não são stopwords. (PostgreSQL)
Conclusão
A melhor solução que satisfaz suas condições é esta.
raw chunkText como está
**Interno do Retriever:**
1. Busca de candidatos BM25 usando o raw chunk como está
2. Busca de candidatos de code embedding usando o raw chunk como está
3. união de candidatos
4. reordenar usando reranker
5. Qwen ou validador verifica diretamente a evidência JSONLVersão que não torna a extração de sinal da API do Godot obrigatória.
Ou seja, o “extrator de sinal da API do Godot” que mencionei antes não deve ser obrigatório. Como você disse, isso cheira a hard‑coding. Pode ser usado, mas se for colocado como núcleo do design básico, a manutenção se deteriora.
A recomendação final é:
Primeira opção: BM25
Segunda opção: code embedding
Ordenação final: reranker
Validação: Qwen direct-evidence validatorVerificação de compreensão do projeto
A estrutura real verificada no zip é a seguinte.
Design JSONL
O documento define a entrega da seguinte forma.
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.jsonlO esquema do banco de dados não é uma estrutura que insere uma linha inteira de JSONL em payload jsonb.
Os campos JSONL são expandidos como colunas da tabela.
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_tsvEntão, no seu banco de dados local, ele deve estar armazenado assim.
{"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 quatro blocos
Assuma que a entrada é sempre exatamente isso.
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)Se você olhar este chunk com um tokenizador de código, os tokens únicos são assim.
_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
1Total de 43 tokens, 25 únicos.
Aqui o token importante é este.
Tokens fortes:
animatedsprite2d
is_action_pressed
move_left
move_right
normalized
clamp
screen_size
vector2
zero
Tokens fracos:
func
var
if
else
velocity
position
delta
x
0
1BM25 dá pontuações altas a tokens fortes.
Isso ocorre porque quanto mais rara a palavra em todo o documento, maior é o IDF.
Como o BM25 funciona e gera resultados
BM25 calcula, de forma simplificada, “quão bem a palavra da consulta está inserida no documento”.
Formalmente, três pontos são essenciais.
TF: Quantas vezes a palavra aparece neste documento
IDF: Quão rara é essa palavra em todo o corpus
Length normalization: Correção para que documentos longos não tenham vantagem automáticaElasticsearch também usa BM25 como algoritmo de relevância padrão, explicando frequência de termo, frequência inversa de documento e normalização de comprimento de campo como elementos principais. (Elastic)
Isso tem essa aparência.
score(query, doc)
= Σ over query terms [
IDF(term)
*
TF_boost(term frequency in doc, doc length)
]Ou seja, se palavras raras como animatedsprite2d aparecem várias vezes no documento, a pontuação aumenta significativamente.
Resultado da simulação como se fosse real
Origem do markdown real do Godot dentro do zip:
outputs/godot_docs_full/pages/Dividi isso em chunks de aproximadamente 1800 caracteres, como o JSONL docs_chunks, e inseri o seu raw chunk diretamente como query para executar o BM25.
Candidato nº 1
Input.is_action_pressed("move_right")
Input.is_action_pressed("move_left")
velocity.length()
velocity.normalized() * speed
$AnimatedSprite2D.play()
$AnimatedSprite2D.stop()Por que é o número 1? Porque esses tokens combinaram diretamente.
animatedsprite2d
is_action_pressed
move_left
move_right
normalized
play
stop
input
speed
length
velocity
vector2
zeroOs tokens de contribuição superiores que aumentaram significativamente a pontuação são os seguintes.
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.637Aqui, df é o número de chunks que contêm a palavra entre os 4165 chunks totais.
Ou seja:
animatedsprite2d aparece apenas em 18 dos 4165 chunks totais
move_left aparece apenas em 13
move_right aparece apenas em 15
is_action_pressed aparece apenas em 27Portanto, documentos que contêm essas palavras sobem fortemente.
Este é o processo que fez o BM25 “como” chegar ao primeiro lugar.
Candidato 2º lugar
Descrição do AnimatedSprite2D
$AnimatedSprite2D.play()
get_node("AnimatedSprite2D").play()
position += velocity * delta
position = position.clamp(Vector2.ZERO, screen_size)Token correspondido:
animatedsprite2d
clamp
screen_size
play
delta
_process
velocity
position
stop
vector2
zeroO motivo de este chunk estar em segundo lugar é que, embora não haja parte de processamento de entrada, clamp, screen_size e AnimatedSprite2D combinam fortemente.
Especialmente:
screen_size df=3
clamp df=32
animatedsprite2d df=18screen_size é uma palavra que quase nunca aparece, então mesmo que apareça apenas uma vez, a pontuação é alta.
3º candidato: candidato falho
3D player movement
Input.is_action_pressed("move_right")
Input.is_action_pressed("move_left")
Vector3.ZERO
direction.normalized()
chunk conteúdo:Token correspondido:
is_action_pressed
move_left
move_right
normalized
speed
velocity
delta
input
x
var
func
zeroIsso parece estar relacionado, mas não é a resposta correta.
Por que foi publicado:
Input.is_action_pressed
move_right
move_left
normalized
speedEsses tokens de código de movimentação se sobrepõem fortemente.
No entanto, este documento é 3D. O chunk é 2D e AnimatedSprite2D, Vector2, position.clamp, screen_size são os elementos principais.
Ou seja, ao usar apenas BM25, podem ocorrer falsos positivos.
Por que o método atual falha
O método mais próximo de /api/retrieve atualmente é plainto_tsquery(raw_chunk).
Esse método gera aproximadamente a seguinte 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_sizeIsso faria com que um chunk JSONL exigisse tokens demais.
Na minha simulação:
strict raw AND hits: 0Ou seja, se um chunk não satisfizer simultaneamente todos os tokens únicos, será 0 registros.
Porque a documentação oficial real também é dividida assim.
chunk A:
Input.is_action_pressed
velocity.normalized
AnimatedSprite2D.play/stop
chunk B:
Descrição do AnimatedSprite2D
position.clamp
screen_sizeAmbos são relevantes, mas nem tudo está contido em uma única linha.
Portanto, é correto refazer o método atual.
Alternativas por método
Alternativa A. Método atual: raw chunk + PostgreSQL plainto_tsquery
Fluxo:
raw chunkText
-> plainto_tsquery
-> search_tsv @@ query
-> ts_rank_cdResultado do chunk:
Alta probabilidade de falha
Mesmo que a divisão de chunk mude um pouco, 0 casosVantagens:
A implementação já está feita
Possível apenas com PostgreSQL
Infraestrutura simplesDesvantagens:
Fraco em trechos de código longos
Condição AND é excessiva
Muitos ruídos de tokens de código
Não é BM25
Busca semântica não suportadaJulgamento:
DescarteAlternativa B. raw chunk + BM25 only
Fluxo:
raw chunkText
-> tokenizer
-> BM25
-> top JSONL retornadoResultado do chunk:
1º: first_2d_game / coding_the_player
2º: first_2d_game / coding_the_player / parte do clamp
3º: first_3d_game / player_movement_codePor que teve sucesso:
AnimatedSprite2D
move_right
move_left
is_action_pressed
screen_size
clampEssas palavras são raras em toda a documentação do Godot, por isso receberam uma pontuação alta.
Vantagens:
Princípio transparente
Depuração fácil
Sem custo de modelo
raw chunk pode ser usado como está
Código/API forte em correspondência exata de stringsDesvantagens:
Fraco em sinônimos/descrições
Não consegue encontrar se o nome da API não aparecer diretamente
Códigos semelhantes se misturam, como em 3D movement
Perde se a documentação for expressa de forma diferenteJulgamento:
É necessário escrever
Mas sozinho não é suficienteAlternativa C. raw chunk + embedding only
Fluxo:
raw chunkText
-> vetor de embedding
-> docs_chunks/api_mapping/label_prototypes embedding e busca por cosseno
-> retornar JSONL superiorRazão para usar o modelo:
Para encontrar documentos com significado próximo mesmo que as strings não coincidam exatamentePor exemplo, a consulta contém:
position.clamp(Vector2.ZERO, screen_size)Existe, e no documento:
prevent the player from leaving the screen
clamping a value means restricting it to a given rangeSe for explicado assim, o BM25 pode ficar fraco. O embedding captura essas conexões semânticas.
Vantagens:
Mesmo que a expressão seja diferente, encontra
É forte em conteúdo descritivo de documentos
É fácil inserir o chunk bruto como está
Pode processar chunks longos tambémDesvantagens:
A avaliação precisa da API é fraca
Pode misturar diferenças sutis como 3D/2D, Godot3/Godot4
É difícil explicar por que esse resultado ocorreu
No api_mapping há risco de falsos positivosPrevisão de chunk:
**Sucesso:**
first_2d_game / coding_the_player **alta probabilidade de encontrar**
**Falha:**
first_3d_game movement também pode aparecer com alta frequência
porque o significado de “player movement” é semelhanteJulgamento:
Uso exclusivo proibido
Usado para complementar candidatos BM25Alternativa D. raw chunk + BM25 + incorporação paralela
Fluxo:
raw chunkText
-> BM25 top 50
-> embedding top 50
-> Combinar
-> Misturar pontuações
-> retornar top JSONLEste método é bastante bom.
Fluxo de chunk:
BM25 que captura:
Input.is_action_pressed
AnimatedSprite2D
position.clamp
screen_size
embedding que captura:
movimento do jogador
tutorial de movimento 2D
movendo dentro da tela
animação baseada no movimentoVantagens:
**Fraquezas da busca por string e da busca semântica se complementam**
**A qualidade de primeira fase melhora mesmo sem o Qwen**
**Manter a condição de fragmento bruto**Desvantagens:
É necessário ajuste de combinação de pontuações
A escala da pontuação BM25 e da pontuação vetorial são diferentes
Não consegue impedir completamente falsos positivosJulgamento:
Linha mínima recomendada na práticaAlternativa E. geração de raw chunk + consulta JSON Qwen + pesquisa
Para evitar hard‑coding, essa abordagem também é possível.
Fluxo:
raw chunkText
-> Solicitar a criação de JSON para pesquisa ao Qwen
-> Pesquisar BM25/vetor com o JSON gerado
-> Retornar candidatos JSONLPor exemplo, saída do 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": []
}Esta é uma forma de não codificar diretamente o extrator de sinais da API do Godot.
Em vez disso, o Qwen cria um perfil de consulta.
Vantagens:
Pouco hardcoding
É possível resumir a intenção em um chunk complexo
É possível tornar a palavra‑chave legível para humanos
É possível delegar o julgamento especializado de Godot ao QwenDesvantagens:
Lentidão
Ocorrência de custos
Pode gerar pistas sem Qwen
Possibilidade de alucinação desde a etapa pré-pesquisa
É necessário validar diretamente a evidência do chunk brutoSim, chunk bem-sucedido:
Se Qwen extrair `AnimatedSprite2D` / `Input.is_action_pressed` / `position.clamp`, terá sucesso.Falha no chunk:
Se o Qwen gerar uma intenção inexistente como “migration from Godot 3 to 4”, ele puxará o api_mapping de forma incorreta.Julgamento:
Pesquisa de qualidade é boa para experimentos
Para o primeiro motor de busca em produção, seja cautelosoAlternativa F. raw chunk + BM25 + embedding + reranker
Esta é a melhor.
Fluxo:
-> BM25 top 80
-> embedding top 80
-> união de candidatos
-> O reranker compara diretamente o raw chunk com cada candidato JSONL
-> retorna JSONL superior
-> O validador Qwen verifica a evidência diretaO reranker lê a consulta e os documentos candidatos juntos e recalcula a relevância. Também descrevemos o documento Voyage como um modelo de reranker que recebe candidatos de resultados de busca de primeira fase, como embedding/BM25, e os reordena com base em uma pontuação de relevância. rerank-2.5 é um reranker otimizado para qualidade com contexto de 32 K. (Voyage AI)
O que o reranker faz no seu chunk:
Candidato 1:
first_2d_game / coding_the_player
O fluxo de código bruto é quase o mesmo
=> Muito alto
Candidato 2:
same page / clamp section
Contém explicação de position.clamp e AnimatedSprite2D
=> Alto
Candidato 3:
first_3d_game / player_movement
Input.is_action_pressed é o mesmo, mas no contexto Vector3/3D
Sem AnimatedSprite2D
Sem clamp de screen_size
=> ReduzidoVantagens:
Melhor qualidade
Reduz muito os falsos positivos do BM25
Também reduz os falsos positivos de embedding
Mantém as condições de raw chunk
Baixa dependência de hardcodingDesvantagens:
Existe custo
Existe latência
Se inserir muitos candidatos, fica lento
O reranker também não é um verificador de evidência, portanto um validador final é necessárioJulgamento:
Recomendação finalPor que usar o modelo e por que não usar
O BM25 não tem modelo
BM25 é uma busca estatística.
Esta palavra está na consulta.
Esta palavra também está no documento.
Esta palavra é rara no corpus.
Então aumenta a pontuação.Portanto, ele é forte na busca de código, como o seu chunk.
AnimatedSprite2D
Input.is_action_pressed
Vector2.ZERO
position.clampIsso é mais sobre a string do que o significado.
Mas o BM25 tem uma conexão de significado fraca, como “impedir que o clamp saia da tela”.
Por que usar modelos de embedding
embedding converte frases em vetores.
raw chunk
-> [0.12, -0.03, ...]
docs_chunk
-> [0.11, -0.02, ...]
cosine similarityEntão, mesmo que as strings não coincidam exatamente, encontraremos o conteúdo mais próximo.
Caso seja necessário embedding neste projeto:
1. Quando o documento é descritivo e o código difere das palavras
2. Quando os nomes da API não se sobrepõem, mas referem ao mesmo conceito
3. Quando o bloco de tutorial está distribuído em várias expressões
4. Quando o BM25 retorna 0 resultados e é necessário um fallbackCaso em que a incorporação deve ser excluída:
1. Correspondência exata do source_api no mapeamento da API
2. Determinação da versão Godot3/Godot4
3. Remover falsos positivos que correspondem apenas ao target_api
4. Definir regra de migraçãoOu seja:
embedding = recall expansão
BM25/exact = evidência direta
reranker/Qwen validator = organização e verificação de candidatosCaracterísticas por modelo
voyage-code-3
Este projeto é o mais adequado.
Razão:
A consulta não é uma pergunta em linguagem natural, mas um trecho de código GDScript.
O JSONL de destino da pesquisa também contém blocos de código, nomes de API e descrições de documentos.De acordo com a documentação oficial do Voyage, voyage-code-3 é um modelo otimizado para recuperação de código, com contexto de 32K, dimensão padrão 1024, suportando dimensões 256/512/1024/2048. Na apresentação do Voyage, também é explicado que, em 32 conjuntos de dados de recuperação de código, ele obteve desempenho médio superior ao OpenAI text-embedding-3-large e ao CodeSage-large. (Voyage AI)
Características:
Vantagens:
forte em consultas de código
capaz de processar chunks longos
possibilidade de escolher 1024/2048
adequado para busca code → docs
Desvantagens:
dependência de API externa
geração de custos
não é um modelo especializado para GodotUso recomendado:
voyage-code-3 1024 floatSe considerarmos apenas a qualidade, 2048 também é possível, mas 1024 + reranker é mais realista.
OpenAI text-embedding-3-large
É um embedding genérico de alta qualidade.
De acordo com a documentação oficial da OpenAI, text-embedding-3-large tem 3072 dimensões por padrão, e text-embedding-3-small tem 1536 dimensões por padrão. (OpenAI desenvolvedor)
Características:
Vantagens:
Recuperação semântica genérica forte
Estável na busca de descrições de documentos
Bom ecossistema OpenAI
Desvantagens:
Não é dedicado à recuperação de código
Como tem 3072 dimensões, o custo de armazenamento/índice é alto
No “GDScript chunk -> JSONL” deste projeto, é menos direto que o voyage-code-3Recomendação:
2ª prioridadeGemini Embedding
O embedding do Google Gemini tem 3072 dimensões por padrão, e é possível escolher tamanhos como 768/1536/3072 usando output_dimensionality. A documentação do Google também explica que, ao usar dimensões menores, é possível reduzir o espaço de armazenamento e o custo computacional, mantendo a perda de qualidade mínima. (Google AI for Developers)
Características:
Vantagens:
Recuperação semântica universal/multilíngue forte
Bom para recuperação baseada em descrição de documentos
Redução de dimensionalidade possível
Desvantagens:
Não é uma opção exclusiva para recuperação de código
Prioriza similaridade semântica em vez de exatidão do código da API
Uso isolado pode ser arriscado para julgamento exato da migração GodotRecomendação:
Seria bom se fosse focado em QA de documentos
Se for focado na busca de trechos de código, abaixo de voyage-code-3Jina embeddings v4
Jina embeddings v4 tem como ponto forte a busca em documentos complexos, multilíngue, multimodal e a recuperação de documentos visualmente ricos, como tabelas, gráficos e imagens. De acordo com a descrição da Jina, enfatiza entradas longas e a recuperação de documentos multimodais. (jina.ai)
Características:
## Vantagens
- A abrangência da pesquisa de documentos é ampla
- É forte em documentos multimodais/combinados
- Também há a linha de adaptadores de código
## Desvantagens
- Seu projeto atualmente está focado em markdown/código
- Recuperação de imagens/tabelas não é o foco principal
- Pode ser uma escolha excessivaRecomendação:
Agora, a prioridade é baixa.Caso de uso do Qwen na pesquisa
O Qwen, como você mencionou, está atualmente focado na geração/validação de JSONL.
Ele também pode ser usado na pesquisa.
Existem duas maneiras de utilizá‑lo.
1. Gerador de perfil de consulta Qwen
2. Validador / reordenador QwenGerador de perfil de consulta Qwen
raw chunk
-> Qwen gera JSON para pesquisa
-> Pesquisa BM25/vetorVantagens:
Redução de hardcoding
Possível inferência de contexto no Godot
Possível resumir trechos de código complexosDesvantagens:
possível alucinação
possível injeção de intenção errada na fase pré-pesquisa
lento
existe custoQwen validator
raw chunk
+ retrieved JSONL
-> Determinar se este JSONL é evidência diretaIsso é forte.
No documento de observação do projeto, já existe um critério de que “não basta a similaridade de tema amplo, mas os campos JSONL e a string do chunk devem corresponder diretamente”.
Recomendação:
É mais seguro usar o Qwen como validador de evidência direta após a pesquisa, em vez de gerar a consulta antes da pesquisa.Fluxo de sucesso/falha baseado em chunk
Fluxo de sucesso: BM25 + embedding + reranker
Entrada:
raw chunk como estáO primeiro BM25 é ruído:
first_2d_game / coding_the_player
Base:
Input.is_action_pressed
move_right
move_left
velocity.normalized
AnimatedSprite2D.play
AnimatedSprite2D.stop2ª BM25 é ruído:
same page / clamp section
Base:
AnimatedSprite2D
position.clamp
Vector2.ZERO
screen_sizeembedding está aprimorado:
player movement
2D movement
animation based on movement
screen boundsResumo do reranker:
1º:
first_2d_game / coding_the_player
2º:
same page / clamp and AnimatedSprite2D explanation
Baixo:
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"]}}Este é o sucesso.
Fluxo de Falha 1: tsquery bruto atual
Entrada:
raw chunk como está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_sizeResultado:
0 casos possíveisRazão da falha:
Se um chunk JSONL não contiver todas as palavras, falhará.Isso precisa ser reescrito.
Fluxo de Falha 2: BM25 only
Resultado:
1º lugar first_2d_game / coding_the_player
2º lugar same page / clamp section
3º lugar first_3d_game / player_movement_codeRazão da falha:
first_3d_game também compartilha Input.is_action_pressed, move_left, move_right, normalized.BM25 não entende perfeitamente se é “2D ou 3D”. Ele simplesmente calcula a pontuação das palavras.
Portanto, BM25 apenas não é suficiente.
Fluxo de falha 3: apenas embedding
Resultado esperado:
2D player movement
3D player movement
scripting player input
vector math
animation player docsRazão da falha:
Em termos de significado, todos são próximos ao movimento do jogador.embedding encontra bem itens semanticamente próximos, mas a precisão da API é baixa.
Fluxo de Falha 4: Apenas perfil de consulta Qwen
Se o Qwen selecionar tão bem assim, será bem‑sucedido.
{"important_terms":["Input.is_action_pressed","AnimatedSprite2D","position.clamp","Vector2.ZERO"]}Mas se fizer assim, falha.
{"migration_intent":"Godot 3 to Godot 4 migration","important_terms":["AnimatedSprite","AnimatedSprite2D"]}Razão da falha:
raw chunk não contém **AnimatedSprite**, apenas **AnimatedSprite2D**.
É um código válido do Godot 4, mas pode trazer o mapeamento de migração.Então, usar o Qwen apenas como gerador de consultas antes da pesquisa é arriscado.
Um validador após a pesquisa é mais seguro.
3 Recomendações Finais
1ª Opção: Complexidade mais baixa
raw chunk
-> BM25
-> top JSONLUso:
PoC 1ª fase
Verificar rapidamente a qualidade da pesquisa visualmenteVantagens:
Simplicidade
Rápido
Transparência
Sem custo de modeloDesvantagens:
Existe false positive
Busca de significado não funciona
Se a representação do documento for diferente, perde‑seNo chunk:
Tem sucesso
Mas há mistura de movimento 3DOpção 2: Tipo equilibrado
raw chunk
-> BM25 top 50
-> embedding top 50
-> união
-> pontuação ponderada
-> retorno JSONL
-> verificação de evidência direta QwenUso:
A fase intermediária mais realista e boa para o projeto atualVantagens:
BM25 e complementação de embedding
Remover JSONL não relacionado ao Qwen
Hardcoding escritoDesvantagens:
É necessário ajuste de pontuação
Sem reranker, o ranking fica um pouco instávelNo chunk:
first_2d_game está estável na parte superior
3D movement diminuiu na validação QwenOpção 3: Qualidade em Primeiro Lugar
-> BM25 top 80
-> voyage-code-3 embedding top 80
-> union
-> rerank-2.5
-> Qwen direct-evidence validator
-> JSONL finalUso:
Recomendação finalVantagens:
A melhor qualidade
Minimização de hardcoding
Manter as condições de raw chunk
O reranker reduz falsos positivos de BM25/embedding
O validador Qwen remove JSONL sem evidência diretaDesvantagens:
Custo
latência
Muitas etapas de implementaçãoNo chunk:
## Aceitar final:
docs_chunks / first_2d_game / coding_the_player
## Rejeitar final ou classificar como de nível inferior:
first_3d_game / player_movement_code
unrelated api_mapping
migration label_prototypesJulgamento Final
Se eu reformular este projeto agora, farei o seguinte.
Entrada mantém apenas um chunkText.
A primeira pesquisa é feita com BM25.
Razão: é a mais transparente e robusta para busca de strings de código/API.
A segunda pesquisa usa o embedding voyage-code-3.
Razão: como a consulta é um trecho de código, o modelo de recuperação de código é o mais adequado.
A terceira ordenação utiliza o rerank-2.5.
Razão: para elevar ao topo os candidatos semelhantes trazidos pelo BM25 e pelo embedding que realmente correspondem ao chunk e ao JSONL correto.
A validação final é feita com o validador Qwen direct‑evidence.
Razão: se não houver evidência direta de string/padrão no JSONL que corresponda ao chunk, ele deve ser descartado.Resumo em uma linha:
entrada raw chunk → geração paralela de candidatos com BM25 + code embedding → reordenação pelo reranker → verificação direta de evidência pelo Qwen é a resposta correta.
O extrator de sinais da API do Godot deve ser omitido.
Mesmo que seja incluído, deve ser usado apenas como um “bônus de pontuação”. A busca padrão deve seguir a sua intenção, baseada em raw chunk com BM25/vetor.