idea_world_labDEV JOURNAL
domingo, 28 de junho de 2026

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 JSONL

Versã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 validator

Verificaçã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.jsonl

O 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_tsv

Entã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
1

Total 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
  1

BM25 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ática

Elasticsearch 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
zero

Os 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.637

Aqui, 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 27

Portanto, 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
zero

O 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=18

screen_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
zero

Isso parece estar relacionado, mas não é a resposta correta.

Por que foi publicado:

Input.is_action_pressed
move_right
move_left
normalized
speed

Esses 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_size

Isso faria com que um chunk JSONL exigisse tokens demais.

Na minha simulação:

strict raw AND hits: 0

Ou 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_size

Ambos 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_cd

Resultado do chunk:

Alta probabilidade de falha  
Mesmo que a divisão de chunk mude um pouco, 0 casos

Vantagens:

A implementação já está feita  
Possível apenas com PostgreSQL  
Infraestrutura simples

Desvantagens:

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 suportada

Julgamento:

Descarte

Alternativa B. raw chunk + BM25 only

Fluxo:

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

Resultado 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_code

Por que teve sucesso:

AnimatedSprite2D
move_right
move_left
is_action_pressed
screen_size
clamp

Essas 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 strings

Desvantagens:

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 diferente

Julgamento:

É necessário escrever  
Mas sozinho não é suficiente

Alternativa C. raw chunk + embedding only

Fluxo:

raw chunkText  
-> vetor de embedding  
-> docs_chunks/api_mapping/label_prototypes embedding e busca por cosseno  
-> retornar JSONL superior

Razão para usar o modelo:

Para encontrar documentos com significado próximo mesmo que as strings não coincidam exatamente

Por 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 range

Se 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ém

Desvantagens:

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 positivos

Previsã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” é semelhante

Julgamento:

Uso exclusivo proibido  
Usado para complementar candidatos BM25

Alternativa D. raw chunk + BM25 + incorporação paralela

Fluxo:

raw chunkText
-> BM25 top 50
-> embedding top 50
-> Combinar
-> Misturar pontuações
-> retornar top JSONL

Este 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 movimento

Vantagens:

**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 positivos

Julgamento:

Linha mínima recomendada na prática

Alternativa 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 JSONL

Por 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 Qwen

Desvantagens:

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 bruto

Sim, 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 cauteloso

Alternativa 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 direta

O 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
  => Reduzido

Vantagens:

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 hardcoding

Desvantagens:

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ário

Julgamento:

Recomendação final

Por 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.clamp

Isso é 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 similarity

Entã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 fallback

Caso 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ção

Ou seja:

embedding = recall expansão
BM25/exact = evidência direta
reranker/Qwen validator = organização e verificação de candidatos

Caracterí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 Godot

Uso recomendado:

voyage-code-3 1024 float

Se 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-3

Recomendação:

2ª prioridade

Gemini 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 Godot

Recomendação:

Seria bom se fosse focado em QA de documentos  
Se for focado na busca de trechos de código, abaixo de voyage-code-3

Jina 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 excessiva

Recomendaçã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 Qwen

Gerador de perfil de consulta Qwen

raw chunk
-> Qwen gera JSON para pesquisa
-> Pesquisa BM25/vetor

Vantagens:

Redução de hardcoding  
Possível inferência de contexto no Godot  
Possível resumir trechos de código complexos

Desvantagens:

possível alucinação  
possível injeção de intenção errada na fase pré-pesquisa  
lento  
existe custo

Qwen validator

raw chunk
+ retrieved JSONL
-> Determinar se este JSONL é evidência direta

Isso é 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.stop

2ª BM25 é ruído:

same page / clamp section

Base:
  AnimatedSprite2D
  position.clamp
  Vector2.ZERO
  screen_size

embedding está aprimorado:

player movement
2D movement
animation based on movement
screen bounds

Resumo do reranker:

1º:
  first_2d_game / coding_the_player

2º:
  same page / clamp and AnimatedSprite2D explanation

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

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_size

Resultado:

0 casos possíveis

Razã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_code

Razã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 docs

Razã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 JSONL

Uso:

PoC 1ª fase  
Verificar rapidamente a qualidade da pesquisa visualmente

Vantagens:

Simplicidade  
Rápido  
Transparência  
Sem custo de modelo

Desvantagens:

Existe false positive  
Busca de significado não funciona  
Se a representação do documento for diferente, perde‑se

No chunk:

Tem sucesso  
Mas há mistura de movimento 3D

Opçã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 Qwen

Uso:

A fase intermediária mais realista e boa para o projeto atual

Vantagens:

BM25 e complementação de embedding  
Remover JSONL não relacionado ao Qwen

Hardcoding escrito

Desvantagens:

É necessário ajuste de pontuação  
Sem reranker, o ranking fica um pouco instável

No chunk:

first_2d_game está estável na parte superior  
3D movement diminuiu na validação Qwen

Opção 3: Qualidade em Primeiro Lugar

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

Uso:

Recomendação final

Vantagens:

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 direta

Desvantagens:

Custo  
latência  
Muitas etapas de implementação

No 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_prototypes

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