Voltar as noticias
A pesquisa do seu servidor MCP está ruim: classificação, embeddings e o que cada um corrige
MCP ProtocolAltaEN

A pesquisa do seu servidor MCP está ruim: classificação, embeddings e o que cada um corrige

Dev.to - MCP·27 de julho de 2026

No final de Parte Um, deixei o servidor de notas com uma busca que descrevi como burra, e prometi voltar para isso. Aqui está como está hoje, na mesma consulta:

search_notes("firewall")
build-your-first-mcp-server-python: Construa seu primeiro servidor MCP em Python: dê a Claude suas próprias notas
caddy-reverse-proxy-docker-compose-ubuntu-26-04: Proxy Reverso Seus Contêineres com Caddy e Docker Compose no Ubuntu 26.04
create-sudo-user-ubuntu-26-04: Crie um Usuário Sudo no Ubuntu 26.04
enable-ssh-on-ubuntu-desktop: Ative o SSH em um desktop Ubuntu
extend-azure-windows-disk-run-command: Estendendo C: em VMs Azure Windows bloqueadas sem RDP

Ficou pior. O primeiro resultado agora é a própria Parte Um, o post reclamando que essa busca é ruim, que menciona "firewall" apenas porque estava citando essa saída exata. O blog tem 51 posts, 17 deles mencionam firewalls em algum lugar, e UFW Firewall Basics ainda não está na lista.

Este post corrige isso em duas etapas, porque existem dois problemas diferentes aqui e apenas um deles é o que todo mundo busca. O primeiro é um bug de classificação e custa cerca de vinte linhas. O segundo é uma consulta que nenhuma quantidade de classificação jamais responderá, e esse custa um modelo de incorporação.

TL;DR Classifique cada resultado e ordene, em vez de pegar os cinco primeiros em ordem alfabética. Isso sozinho coloca o post certo no topo. Em seguida, adicione incorporações locais com ollama pull nomic-embed-text, divididas em cabeçalhos ## e armazenadas em um .npz indexado por caminho, mtime e nome do modelo. Prefixe documentos com search_document: e consultas com search_query:, o que dobrou a frequência com que meu índice colocou o post certo primeiro. Construa o índice a partir de um comando, não na primeira busca, porque 496 partes levaram quatro minutos na CPU. E mantenha ambas as buscas, porque falham em lugares diferentes: a correspondência exata perde brute force sobre um hífen, e as incorporações perdem porque duas palavras não têm significado suficiente para trabalhar.

Antes de começar

  • O servidor final da Parte Um. Tudo abaixo edita esse server.py.
  • Ollama rodando localmente. Esse post é sobre alimentar uma GPU para modelos de chat, e nada disso se aplica aqui: modelos de incorporação são pequenos e uma CPU é suficiente para esse trabalho.
  • Duas dependências: uv add ollama numpy

1. O bug era classificação, não correspondência

A Parte Um já nomeou isso, e vale a pena citar porque o diagnóstico é a totalidade da Etapa Um:

A correspondência de substring não falhou. Encontrou o post do UFW bem. O problema é que não há classificação alguma.

Veja o que o loop original faz:

hits = []
for path in _posts():
    text = path.read_text(encoding="utf-8")
    if query.lower() in text.lower():
        hits.append(f"{{path.stem}}: {{_title(text, path.stem)}}")
    if len(hits) == 5:
        break

_posts() agrupa os arquivos .md e os arquivos .mdx separadamente e concatena as duas listas ordenadas, então a ordem é alfabética dentro de cada extensão e qualquer arquivo .mdx vai parar no final, não importa como é chamado. O loop anexa nessa ordem e quebra em cinco. Assim, search_notes não está retornando os cinco melhores resultados para "firewall", está retornando os primeiros cinco arquivos nessa ordem que contêm a string em qualquer lugar, incluindo blocos de código e links. u é ordenado após b, c e e, então o post que é inteiramente sobre firewalls perde para quatro posts que os mencionam de passagem.

Isso é pior do que retornar nada, porque cinco resultados confiantes e plausíveis errados são uma resposta sobre a qual o modelo se baseará felizmente.

A correção é parar de tratar uma correspondência como um booleano. Colete cada resultado, classifique-o, ordene e, em seguida, trunque:

def _frontmatter(text: str) -> str:
    parts = text.split("---", 2)
    return parts[1] if len(parts) > 2 else ""


def _fm_line(fm: str, key: str) -> str:
    return next((l for l in fm.
Contexto Triplo Up

Empresas brasileiras que utilizam servidores MCP podem enfrentar desafios na busca de informações. Melhorar a classificação e a utilização de embeddings pode otimizar a experiência do usuário e a eficiência na recuperação de dados.

Noticias relacionadas

Gostou do conteudo?

Receba toda semana as principais novidades sobre WebMCP.