Autores: @hi_im_isaac_, @learnwdaniel, @gaozenghao
nota: a versão interativa do blog técnico completo está disponível em: https://www.cerebras.ai/blog/how-we-built-our-knowledge-base
Os funcionários fazem mais de 15.000 perguntas por dia à nossa base de conhecimento interna. Ela se tornou uma das ferramentas internas mais adotadas na empresa desde o lançamento, há 3 meses. Usada por humanos, automações e agentes.
Na Cerebras, nossas equipes trabalham com operações de datacenter, design de chips, hardware, treinamento, inferência, plataforma em nuvem e muito mais. Com centenas de novos funcionários entrando a cada ano, nossos canais de comunicação estavam se enchendo das mesmas perguntas:
- "Onde encontro X?"
- "Quem é o especialista em Y?"
- "O que é Z?"

Criamos o Cerebras Knowledge para ajudar as pessoas a conectar pessoas e sistemas a informações úteis.
Encontrando os dados onde eles estão
Encontrar informações dentro de uma organização é difícil. Os dados estão espalhados por ferramentas, e a cada trimestre alguém propõe a mesma solução brilhante: vamos registrar tudo em uma plataforma para que toda a informação esteja em um único lugar. O sonho de uma fonte única da verdade, claro, raramente funciona na prática.
As informações são geradas onde é mais conveniente e ergonômico: edições sugeridas em um documento, threads no Slack, referências de código no GitHub e metadados de status no Jira. Essas plataformas são feitas sob medida para seus domínios específicos, otimizadas por anos de engenharia de produto e análise de dados. Discutir um pull request no Google Docs seria uma experiência terrível.
Então, projetamos um sistema que exigia mudanças mínimas no comportamento existente. No lado da coleta de dados, isso significava extrair dados de cada plataforma diretamente.
Anatomia de uma base de conhecimento
Nossa base de conhecimento oferece três coisas:
- Uma plataforma para coletar e armazenar dados internos.
- Uma plataforma para consultar esses dados.
- Uma camada que aplica autenticação e autorização, com auditoria e análise.
No centro, há uma única tabela Postgres que armazena embeddings, resumos brutos e metadados de várias fontes. O sistema ingere continuamente dados de toda a empresa e mantém um armazenamento de dados pronto para consulta.
Queríamos uma interface de dados simples, mas que funcionasse com a maioria dos formatos de dados. Também queríamos que outros desenvolvedores da Cerebras pudessem criar conectores personalizados. O resultado é deliberadamente simples: cada fonte, desde threads do Slack até netlists, chega na mesma tabela de embeddings, e qualquer coisa nessa tabela é imediatamente consultável através da mesma interface:

Cada fonte de dados define o que são os dados, como se conectar a eles e com que frequência devem ser buscados. Cada linha de embedding resultante segue a mesma interface, independentemente de ter vindo do Slack, de um repositório de código, de um sistema de documentos ou de um banco de dados personalizado.
Como processamos conversas não estruturadas do Slack
O Slack foi a fonte de dados mais importante que precisávamos projetar. É onde as discussões de engenharia mais atualizadas acontecem em toda a empresa.

Inicialmente, testamos se embeddings simples sobre texto bruto teriam desempenho suficiente. Rapidamente percebemos que a busca vetorial sozinha era insuficiente para encontrar todos os dados relevantes.
As mensagens do Slack apresentam vários desafios:
- A densidade de informação varia enormemente: "ei, sim, claro, mike" e uma explicação detalhada de kernel são ambas mensagens.
- Os comprimentos das mensagens variam, e mensagens mais curtas frequentemente superam mensagens mais longas e detalhadas em similaridade de cosseno.
- O significado de uma mensagem geralmente depende da conversa ao redor.
Precisávamos de uma abordagem híbrida. Construímos a ingestão do Slack para que cada thread seja recuperável por várias técnicas de busca ao mesmo tempo, onde cada técnica compensa as fraquezas das outras:
- Busca de texto completo captura os tokens exatos que os embeddings borram: strings de erro, nomes de flags, nomes de host. Quando um engenheiro cola uma mensagem de erro literal, uma correspondência lexical exata é quase sempre a melhor evidência, e nenhuma quantidade de similaridade semântica deve superá-la.
- Busca de embeddings captura paráfrases. A pessoa que pergunta "restauração trava após carregar manifesto" e a pessoa que respondeu "checkpoint trava no NFS mount" podem nunca compartilhar vocabulário. A similaridade vetorial é o que conecta uma pergunta a uma resposta escrita em palavras diferentes.(1)
- Frequência inversa de documentos separa sinal de ruído. Uma mensagem curta construída em torno de tokens raros, como uma flag de configuração obscura, merece ser ranqueada. "Parece bom, obrigado!" fica perto de muitas consultas no espaço de embeddings, mas pontua perto de zero uma vez que a raridade do termo é levada em conta.
- Decaimento temporal codifica que respostas do Slack expiram. Duas threads podem responder à mesma pergunta, e a de seis meses atrás pode descrever infraestrutura que não existe mais. Quando a relevância é igual, a thread mais nova vence.

Nenhum pontuador individual é confiável por si só. Cada técnica produz sua própria visão ranqueada do mesmo corpus, e essas visões são fundidas no momento da consulta (veja Re-ranqueamento).
Modo Socket
Para coletar dados em tempo real, instalamos um bot do Slack em nosso workspace e o executamos em Modo Socket. O Slack envia cada evento de mensagem para nós através de um WebSocket persistente, então recebemos atualizações em tempo real sem precisar consultar a Web API e queimar seus limites de taxa.
Quando um evento chega, imediatamente o reconhecemos, deduplicamos usando o ID de evento estável e marcamos a mensagem para o consumidor de ingestão.
O consumidor de ingestão não salva uma nova mensagem isoladamente. Ele resolve a thread à qual a mensagem pertence e busca novamente toda a conversa, incluindo o pai e cada resposta, da API do Slack. Em seguida, escreve a thread inteira de volta como uma linha. Uma resposta a uma thread existente, portanto, puxa novamente o pai e todos os irmãos, para que o conteúdo armazenado, a lista de participantes e o timestamp da última atividade sempre reflitam a conversa completa.
Cada canal do Slack em nosso sistema tem sua própria fonte de dados. Isso fornece ajuste fino para a frescura dos dados. Uma equipe pode optar por ingerir um canal de incidentes movimentado com mais frequência, por exemplo.
Threads e mensagens
O texto bruto do Slack é pesquisável por palavras-chave assim que chega, porque mantemos um índice de texto completo (GIN) do Postgres sobre o conteúdo bruto. Para permitir uma busca vetorial útil, no entanto, fazemos algum processamento adicional.(8)
Durante a destilação, um LLM extrai dados estruturados da thread completa:
- Uma pergunta de uma linha que um engenheiro realmente pesquisaria.
- Um breve resumo.
- A resolução.
- Os sistemas e referências de código mencionados.

Incorporamos esses pontos de dados e os escrevemos na tabela de embeddings compartilhada. A transcrição original não é incorporada diretamente. Em nossos experimentos, a precisão aumentou significativamente quando a thread foi normalizada em um formato consistente.(7,9) Os metadados adicionais também fornecem mais sinal útil para a correspondência semântica.
Bursting
Neste ponto, a busca no Slack era boa, mas continuávamos encontrando o mesmo problema: mensagens importantes dentro de threads longas nem sempre eram representadas no resumo no nível da thread.
Para aumentar o sinal de mensagens individuais, usamos bursting. Um burst é uma sequência de mensagens consecutivas do mesmo autor. Incorporamos bursts individuais com o tópico da thread anexado como contexto(2) porque às vezes a resposta está em uma mensagem tangente cujo vocabulário nunca aparece no resumo da thread. Os embeddings de burst tornam essa mensagem encontrável por conta própria.
Para evitar que dados de baixo sinal cheguem ao banco de dados, cada burst é pontuado contra uma combinação ponderada de sinais e deve ultrapassar um limite antes de ser incorporado:
- Contém um token relativamente raro no corpus, com IDF de pelo menos 4,0.
- O burst combinado tem pelo menos 200 caracteres.
- Uma ou mais mensagens no burst contêm reações, fornecendo um impulso social.

Após a destilação, os bursts qualificados são incorporados e armazenados na tabela de embeddings junto com o registro no nível da thread.
Repositórios de código
Inicialmente, debatemos se era necessário incorporar repositórios de código. Com o aumento do Claude Code e outras ferramentas de linha de comando, criar embeddings de código parecia contraintuitivo quando parecia que "grep é tudo que você precisa". Depois de conversar com outros no setor e ler as descobertas da Cursor sobre busca semântica em grandes bases de código, decidimos tentar.
Temos muitos repositórios internos, alguns maiores que 40 GB. Nossa principal preocupação era como mantê-los atualizados de forma eficiente.
Usando @cocoindex_io para manter embeddings de código
Após vários experimentos, chegamos ao CocoIndex, uma estrutura de código aberto para incorporação de documentos especializada em vetorizar bases de código.
Para cada repositório, dividimos o código usando limites de regex específicos da linguagem, ordenados do mais grosseiro ao mais fino. O divisor tenta limites de nível superior, como classes, primeiro. Se um bloco resultante ainda for muito grande, ele recorre a limites de método e, em seguida, blocos menores. Incorporamos os blocos resultantes e escrevemos os vetores no Postgres. Um único arquivo pode gerar vários embeddings em diferentes níveis de especificidade, como registros no nível de arquivo e no nível de função.

O CocoIndex rastreia metadados de sincronização no Postgres. Em cada commit, ele reincorpora e reexporta apenas os blocos de código alterados, em vez de recomputar todo o repositório. Isso funcionou especialmente bem para nós porque o estado de sincronização e o armazenamento de embeddings estão no mesmo banco de dados.
À medida que o número de bases de código crescia, movemos a integração de repositórios para arquivos de configuração que as próprias equipes podem enviar, incluindo listas de permissão e bloqueio no nível de caminho de arquivo.
Fontes de dados personalizadas
Algumas equipes já tinham seus próprios bancos de dados e não queriam mover dados para o Slack ou um sistema de documentos apenas para participar da base de conhecimento. Elas queriam a mesma superfície de consulta sobre suas tabelas existentes.
Para suportar isso, tratamos fontes personalizadas como scripts de plugin. Uma equipe abre um pull request com um pequeno módulo Python que sabe como ler de seu sistema e emitir linhas no formato de nossa tabela de embeddings, além de uma entrada de fonte de dados correspondente.
Enquanto o script escrever no banco de dados compartilhado usando o mesmo esquema que qualquer outra linha de embedding, o resto da pilha funciona inalterado. Os dados se tornam consultáveis junto com Slack, código e documentos, sem tratamento especial em nenhum outro lugar do sistema.
Planejamento e distribuição de ferramentas
Para cada consulta, primeiro executamos uma passagem curta de planejamento onde um LLM decide quais ferramentas e fontes de dados provavelmente são relevantes. As principais ferramentas:
- subsystem_index: resumos LLM por arquivo.
- search: o pipeline de vetor unificado em Slack, wiki, código e outras fontes indexadas, mesclado e re-ranqueado internamente.
- search_slack: recuperação direta do Slack.
- search_code: ripgrep sobre repositórios de origem.
- recent_prs: pull requests recentes relevantes para a pergunta.
- who_knows: pessoas com experiência demonstrada em um tópico.
O planejador trabalha com uma descrição compacta do que indexamos: quais projetos existem, quais fontes estão disponíveis em cada projeto e para que cada fonte é boa em responder. Dada a consulta do usuário e o escopo ativo, ele emite seleções de ferramentas que o executor distribui em paralelo, normaliza em um formato de evidência comum e passa para um LLM de síntese final.(4)

Re-ranqueamento
Um documento pode aparecer no topo simplesmente porque compartilha vocabulário com a consulta enquanto responde a uma pergunta diferente. Antes do re-ranqueamento, combinamos as listas de resultados incompatíveis dos recuperadores com fusão de rank recíproco, ou RRF. Para cada documento, adicionamos peso / (60 + rank) para cada lista em que aparece, com um peso padrão de 1,0 e uma constante de suavização de 60.

A constante de suavização faz com que o consenso importe mais do que um único voto forte: um documento que aparece perto do topo em vários recuperadores pode vencer aquele que está em primeiro lugar em apenas um deles. Em seguida, mesclamos blocos duplicados de volta a uma fonte, limitamos quantos resultados cada arquivo pode contribuir e terminamos com um top vinte mais diverso.
Enviamos a consulta original e esses candidatos para um pequeno modelo de re-ranqueamento. Ele dá a cada documento uma pontuação de zero a dez, e mantemos os dez primeiros.(6)
Uma vez que o ranking está finalizado, adicionamos contexto de volta aos vencedores. Por exemplo, se correspondermos a uma seção da wiki, puxamos as duas seções vizinhas para que o título, pré-condições e ressalvas que a divisão em blocos separou não sejam perdidos. Isso dá aos leitores um trecho completo em vez de um parágrafo solitário que está faltando contexto importante.
Portanto, a saída da busca é um pacote rico de evidências: resultados fundidos de diferentes recuperadores, deduplicados no nível da fonte, re-ranqueados contra a pergunta real e, só então, expandidos com o contexto circundante.
MCP
Na integração MCP, expomos blocos de construção de recuperação como ferramentas diretas, em vez de escondê-los atrás de um endpoint "responda a esta pergunta". Essas ferramentas são intencionalmente simples e o mais livres de LLM possível, para que os clientes possam consultá-las de forma rápida e barata.(5)
Cada ferramenta MCP corresponde a um primitivo de recuperação subjacente, como search_slack, search_code, search ou who_knows. As entradas e saídas das ferramentas são estreitas, estruturadas e estáveis, tornando-as fáceis de chamar de qualquer cliente ou agente sem incorporar lógica de orquestração adicional dentro da própria ferramenta.
A maioria das ferramentas executa um pipeline de consulta, como busca vetorial, busca lexical ou ripgrep, aplica heurísticas de pontuação leves e retorna linhas de evidência brutas.
O Claude Code, ou qualquer agente compatível com MCP, torna-se o motor de orquestração. Ele decide quais ferramentas chamar, em que ordem e como montar os resultados em uma resposta final ou edição de código. A camada de recuperação em si não depende dessas decisões de LLM para atender solicitações.
Interface Web
Na interface web, as mesmas ferramentas existem, mas estão conectadas a um pipeline de consulta completo que é executado de ponta a ponta para cada pergunta do usuário. O agente da interface possui as etapas de planejador e executor.
- Planejador: Uma passagem LLM leve inspeciona a consulta e o projeto ativo, depois escolhe quais ferramentas de recuperação invocar, como search, search_slack e subsystem_index.
- Executor: O sistema distribui essas chamadas de ferramenta em paralelo, coleta os resultados e os normaliza em um esquema de evidência compartilhado com pontuações, atualidade e dicas de fonte.
- Síntese: Uma passagem LLM final pega o pacote de evidências tipadas e a pergunta original, depois produz a resposta mostrada na interface, incluindo citações, ressalvas e síntese entre fontes.
Da perspectiva do usuário, a interface web é simplesmente "faça uma pergunta e obtenha uma resposta". Nos bastidores, executa o mesmo padrão planejador → executor → sintetizador que os clientes MCP podem recriar explicitamente.

Organização
À medida que o corpus crescia, "buscar tudo em todos os lugares" rapidamente deixou de ser útil. Engenheiros de times de compiladores não queriam manuais de infraestrutura em seus resultados, e vice-versa. Projetos são como tornamos a busca relevante por padrão.
Projetos e busca com escopo
Introduzimos projetos como a principal forma de organizar o espaço de trabalho sobre o qual uma consulta é executada. Um projeto é um pacote nomeado de fontes de dados: canais específicos do Slack, repositórios de código, bancos de dados internos e espaços de documentos relevantes para uma equipe ou iniciativa.
Os projetos são intencionalmente leves. A mesma fonte de dados, como um canal de incidentes compartilhado ou um repositório de plataforma central, pode ser referenciada por vários projetos em vez de ser duplicada.

Integração e padrões
Durante a integração, os usuários são solicitados a selecionar ou criar um projeto padrão que corresponda à forma como trabalham, como infraestrutura de treinamento ML, Compilador ou Operações de Datacenter.
Esse projeto padrão é armazenado no perfil do usuário e define o escopo das consultas automaticamente. Um novo engenheiro obtém respostas de alto sinal sem primeiro precisar aprender quais canais do Slack, repositórios ou espaços de documento são importantes.
Considerações finais
No final, a base de conhecimento funciona porque encontra as pessoas onde a informação já está, em vez de forçar tudo em um sistema rígido. Ao combinar várias técnicas de busca, podemos apresentar evidências rapidamente. O resultado é uma experiência de busca que permanece flexível o suficiente para dados reais da empresa, mas estruturada o suficiente para permanecer útil à medida que a Cerebras continua crescendo.
Se você leu até aqui e isso é interessante para você, o time de ai/growth está contratando – entre em contato se tiver interesse @learnwdaniel
Referências
- Malkov e Yashunin, Busca Aproximada de Vizinhos Mais Próximos Eficiente e Robusta Usando Grafos Navigable Small World Hierárquicos, arXiv:1603.09320 / IEEE TPAMI 2018.Apresentando a Recuperação Contextual, 2024.
- Cormack, Clarke e Büttcher, Fusão de Classificação Recíproca Supera Métodos de Aprendizagem de Classificação Condorcet e Individuais, SIGIR 2009.
- Li et al., Search-o1: Modelos de Raciocínio Aprimorados por Busca Agentic, arXiv:2501.05366, 2025.
- Anthropic, Execução de Código com MCP, 2025.
- Liu et al., Perdidos no Meio: Como os Modelos de Linguagem Usam Contextos Longos, arXiv:2307.03172, 2023.





