Saltar para o conteúdo
megapromotingVamos falar

Expertise · Bases de conhecimento e pesquisa semântica

Construímos o corpus a partir do qual um assistente tem permissão para responder, com regras sobre o que entra, como se divide, como se pesquisa e o que acontece quando a fonte muda.

Um assistente que responde aos clientes é exatamente tão bom quanto o corpus de onde lê. Nós tratamos do corpus: extração de documentos e do site, divisão em fragmentos, indexação, recuperação, o gate de aprovação humana e a atualização. Inclui a resposta à pergunta de quando NÃO vale a pena a pesquisa semântica.

Já construídoExistem implementações próprias para cada etapa, mas o seu estado difere e deve ser dito de forma direta. O que corre com vetores próprios: a memória de um quadro interno usado diariamente, com coluna `vector(1536)`, índice `ivfflat` em distância de cosseno, limiar de similaridade 0,35 e um loop que reindexa a cada 30 minutos apenas o que mudou. O que corre com vetores no fornecedor: a base de conhecimento dos agentes vocais, sincronizada para o motor de recuperação do fornecedor com fragmentos de 500 e sobreposição de 100. O que corre em produção **sem** vetores: a base de conhecimento da plataforma de assistentes de texto — extração, divisão, configuração por assistente e recuperação híbrida estão escritas e funcionam, mas o caminho de indexação escreve a coluna de vetor com `null`, portanto a pontuação híbrida degrada para lexical, e o próprio código diz isso através da etiqueta `keyword-only`. O que está escrito e ainda não está ligado em produção: o pipeline próprio em `pgvector` da plataforma vocal, cuja migração traz no cabeçalho a menção “LOCAL ONLY — do not push to prod”. Um serviço vende-se com este mapa em cima da mesa, não sem ele.

Um assistente que fala com clientes não “sabe” nada. A cada pergunta, alguém pesquisa antecipadamente alguns fragmentos de texto e coloca-os à sua frente, e ele formula a resposta a partir deles. A qualidade da resposta decide-se quase integralmente nessa etapa de pesquisa, não na escolha do modelo. Por isso, este serviço é sobre corpus e sobre recuperação, não sobre modelo.

O trabalho tem quatro partes que se estragam independentemente umas das outras. A extração: de que ficheiros e de que páginas retiramos texto utilizável, e o que fazemos quando não conseguimos. A divisão: quão grandes são os fragmentos, quanto se sobrepõem, onde se corta uma frase. A recuperação: como se escolhem os fragmentos para uma pergunta específica, quantos, e com que critério. A atualização: o que acontece quando o documento-fonte se modifica. Um sistema pode ser impecável em três delas e inútil por causa da quarta.

Há uma etapa adicional, que muitos saltam e que muda completamente o resultado: o gate de aprovação. Um corpus que cresce sozinho, a partir de conversas, passa rapidamente a conter respostas que ninguém validou. O ciclo que construímos funciona ao contrário: quando o agente diz “não tenho esta informação”, a pergunta é registada com o estado “em espera”, uma pessoa recebe uma notificação e responde, e a sua resposta passa para o estado “aprovado” e só então entra no corpus. O conhecimento cresce a partir de lacunas reais, com uma pessoa na cadeia.

E a parte impopular: a pesquisa semântica nem sempre é a escolha correta. Para uma plataforma pública própria, recusámo-la deliberadamente. O corpus é o próprio texto da plataforma, dividido em passagens curtas que trazem cada uma a página de onde vêm, pontuadas por sobreposição de palavras. Sem vetores, sem armazenamento vetorial, sem um pedido adicional na rede — e, mais importante, sem nenhuma fonte de fora do repositório. O assistente só pode repetir o que já está escrito no site, e qualquer resposta é verificada numa página escrita por uma pessoa. Este é todo o mecanismo anti-invenção.

Qué incluye

El trabajo, por componentes

Extraímos texto e dizemos claramente quando não conseguimos

A partir de texto simples, markdown, registos, CSV, JSON, DOCX e PDF. O DOCX é desempacotado como arquivo e o documento XML no interior é lido; o PDF passa pelo extrator padrão com preservação da disposição na página, e se este faltar existe uma alternativa que lê os operadores de texto diretamente do ficheiro. O caso importante é aquele em que não funciona: um PDF digitalizado, que é uma imagem, não contém texto selecionável, e o sistema devolve uma mensagem explícita — carregue DOCX ou texto, ou passe o ficheiro por reconhecimento ótico antes. Não temos reconhecimento ótico implementado e não fingimos que temos.

Dividimos em fragmentos com sobreposição, cortando entre frases, não através delas

Os valores por omissão que usamos: 500 unidades lexicais com 50 de sobreposição para o pipeline de voz, 1.200 caracteres com 150 de sobreposição para o de texto; do lado do fornecedor de voz, 500 com 100. A sobreposição existe porque uma frase cortada a meio entre dois fragmentos torna-se inutilizável para ambos. O separador de frases foi escrito para as línguas com que trabalhamos: reconhece o fim de frase seguido de uma maiúscula latina **ou cirílica**, incluindo os diacríticos romenos. Um separador escrito para inglês corta mal em romeno e ainda pior em russo.

Indexamos com vetores onde o ativámos, e dizemos onde não

O modelo predefinido de representação é `text-embedding-3-small`, com 1.536 dimensões, pedido através da nossa gate de modelos, e não diretamente ao fornecedor — assim é possível mudar de fornecedor sem tocar no código da base de conhecimento. Na base, a coluna é do tipo vector com índice `ivfflat` em distância cosseno. Existe uma estratégia de reserva escrita na migração: se a extensão vetorial não estiver disponível, o esquema é criado de qualquer forma com índice trigram no texto, e a recuperação comuta para semelhança lexical. Não é hipotético — foi escrito precisamente porque os direitos sobre a extensão não são garantidos em qualquer instalação.

Recuperação híbrida, com os pesos visíveis

A pontuação final é 0,7 da similaridade vetorial mais 0,3 da correspondência de palavras, com a parte lexical normalizada. É pontuado um conjunto de candidatos e são retidos os primeiros fragmentos, e o contexto enviado ao modelo corta cada fragmento a um comprimento máximo, para que um único documento longo não ocupe todo o espaço. A configuração é por assistente, na base: recuperação ligada ou desligada, número máximo de fragmentos por pergunta (por defeito 5, limitado entre 1 e 20), limiar de similaridade, tipo de pesquisa — semântica, lexical ou híbrida — e peso da parte lexical.

Gate de aprovação: o conhecimento cresce a partir de lacunas, com um humano na cadeia

Quando o agente responde que não tem a informação, a pergunta é escrita numa tabela com o estado „em espera”, marcada com o canal de onde veio — voz ou widget. Um humano recebe a notificação e responde diretamente à mensagem; um webhook recolhe a resposta, passa a entrada ao estado „aprovado”, e daí entra no contexto do agente. Os estados possíveis são quatro: em espera, aprovado, ignorado, duplicado — e os duplicados são ligados à pergunta original por uma referência, para que não se acumulem dez formulações da mesma lacuna. A entrega ao agente é feita uma única vez por entrada aprovada.

Atualização: o que acontece quando a fonte muda

Na adição, calcula-se a impressão SHA-256 do conteúdo; se o texto for idêntico a algo já indexado, reutiliza-se e não se paga uma nova representação vetorial. Quando o documento se modifica, os seus fragmentos são apagados integralmente e reescritos — substituição completa por fonte, não atualização parcial, porque uma atualização parcial deixa para trás fragmentos órfãos da versão antiga, e são esses que produzem respostas contraditas pelo site. A fonte tem um ciclo de estados visível: em espera, em processamento, em reprocesamento, pronto ou falhado, e a reindexação pode ser pedida pontualmente, por fonte.

Não reindexamos o que não mudou

Numa rotina que corre a cada trinta minutos sobre uma lista que só cresce, o recálculo da representação para um elemento intocado é um pagamento recorrente por um vetor que não tinha como se mover. A nossa rotina compara o momento do último toque com o momento da última indexação e salta o que não foi modificado. As representações são pedidas em lotes, não uma a uma. São detalhes de custo, mas o custo mensal de uma base de conhecimento decide-se quase integralmente aqui.

Um catálogo ligado não é uma lista carregada

A diferença não é de formato, é de momento. Uma lista carregada é verdadeira no momento do carregamento: o preço, o stock e os produtos novos ficam congelados até alguém se lembrar de carregar de novo — e, regra geral, descobre-se que ninguém se lembra, quando um cliente pergunta por um preço antigo de três meses. Um catálogo ligado é lido no momento da pergunta, da fonte que mantém o registo. O que construímos nesta direção: um extrator que reconhece coleções de produtos num JSON ou CSV pelas chaves habituais e, quando a estrutura é invulgar, pede ajuda a um modelo com a obrigação de responder estritamente numa forma dada; e um percorredor que lê o preço da página viva da loja. A melhor rota continua a ser a interface estruturada própria da loja, onde exista — mas isso verifica-se por loja, não se pressupõe.

Percurso de um site: primeiro o mapa, depois a exploração

Procura-se primeiro o mapa do site, nas variantes habituais de nome, e desdobram-se os mapas aninhados em dois níveis. Só se não existir mapa é que se passa à exploração em largura, estritamente na mesma origem, com um limite de páginas configurável (por defeito 50, limitado a 200). A estratégia efetivamente usada é reportada de volta — mapa ou exploração — porque a partir dela se lê se um resultado fraco vem do site ou do método. Respeita-se o ficheiro de exclusão do site.

Qué aspecto tiene

El recorrido, paso a paso.

01

Estabelecemos o que o assistente pode saber, antes de qualquer importação

A lista das fontes admitidas e, igualmente importante, a lista das rejeitadas, com o motivo. Um ficheiro interno de preços de compra, um export de contactos, um arquivo de e-mails — são exatamente as coisas que entram no corpus „para ter contexto” e depois se tornam resposta para um cliente. Entregamos o inventário das fontes com decisão e motivo para cada uma, mais a regra sobre o que nunca pode entrar.

02

Importamos, dividimos e verificamos o que saiu, lendo efetivamente os fragmentos

Extração, divisão com sobreposição, indexação. A etapa de verificação não é saltada: lêem-se fragmentos ao acaso do corpus resultante. Aí aparecem as tabelas transformadas em cadeias de palavras coladas, os PDFs digitalizados que produziram zero texto e as páginas de menu de navegação entradas como documentos autónomos. Entregamos o corpus mais o relatório com o que não se conseguiu extrair e porquê.

03

Afinamos a recuperação com perguntas reais, não com perguntas confortáveis

Constrói-se um conjunto de perguntas a partir do que os clientes realmente perguntam, incluindo as formulações mal escritas e as em duas línguas misturadas, e ajusta-se o número de fragmentos, o limiar e o peso da parte lexical. O objetivo não é que cada pergunta receba uma resposta, mas que as perguntas sem cobertura no corpus recebam „não tenho esta informação”. Entregamos o conjunto de perguntas com os resultados e a configuração final.

04

Colocamos o gate de aprovação e a rotina de crescimento

Registo das perguntas sem resposta, notificação para a pessoa que aprova, caminho de resposta, passagem para o corpus e ligação dos duplicados. Entregamos o ciclo funcional e a instrução escrita para quem aprova — incluindo o que não se copia da pergunta do cliente.

05

Entregamos o procedimento de atualização, com o nome de quem o executa

Que fonte é atualizada, com que frequência, quem carrega e como se vê que aconteceu. Aqui dizemos abertamente o limite: nas plataformas que operamos, a reindexação é uma ação solicitada, não uma ação agendada automaticamente. Um procedimento sem um nome de pessoa ao seu lado é um procedimento que não é executado.

DocumenteConversațiiFontesSintezăAcțiuneEchipăContextualizaFontes autorizadas. Ações revistas.
O percurso de uma pergunta pelo sistema: as fontes aprovadas entram na extração e divisão, os fragmentos chegam ao índice; perante a pergunta, os candidatos são pontuados — vetorial e lexical — e apenas alguns fragmentos chegam ao contexto; e quando não se encontra nada, a pergunta sai pela ramificação de aprovação humana e regressa ao corpus.

Os dados

Qué tocamos, dónde están y cuánto se quedan

Las preguntas que hace cualquiera que tenga un delegado de protección de datos — hechas aquí antes de que las haga él.

O que entra no corpus
Apenas o que é explicitamente aprovado: documentos carregados, páginas escolhidas, pares pergunta-resposta, textos manuscritos. As fontes são tipificadas — texto, documento, site, pares pergunta-resposta — e têm um objetivo declarado: produtos, documentação, perguntas frequentes ou outros. Cada fonte tem o seu próprio comutador, e o comutador é verificado em cada consulta de recuperação, não apenas na interface — uma fonte desligada não pode chegar ao contexto nem por engano.
Onde ficam os fragmentos e as suas representações
Na base de dados da respetiva plataforma, junto com o resto dos dados do cliente, e não num serviço externo separado de pesquisa. Para as instalações em PostgreSQL, em tabelas próprias com a extensão vetorial ativada explicitamente; para a plataforma de assistentes de texto, em MySQL, com a representação numa coluna JSON e o cálculo da distância feito na aplicação. O segundo modelo funciona em pequena escala e não escala para centenas de milhares de fragmentos — é uma limitação de arquitetura, dita como tal.
Dados pessoais nas conversas
O corpus não deve conter dados pessoais, mas o ciclo de aprendizagem pode trazê-los, porque a pergunta de um cliente é texto escrito pelo cliente. Por isso, a entrada no corpus passa pela aprovação de uma pessoa: é aí que se filtra. A regra operacional que acompanha isto: quem aprova respostas recebe a instrução explícita para não copiar para o corpus os nomes, números de telefone ou detalhes da encomenda da pergunta original.
O que se guarda sobre as consultas
Existe uma tabela de registo das consultas de recuperação, com as pontuações obtidas, o tempo de recuperação e o número de fragmentos devolvidos. É o instrumento com que se responde a «por que disse isto» — sem ele, ajustar os limiares torna-se adivinhação. O registo é mantido enquanto for útil para afinação e entra nos prazos de retenção da plataforma, não num regime separado.
O que não existe e é declarado como tal
Não existe a aplicação de um prazo de expiração aos documentos: o campo é declarado no esquema, mas nada o lê, logo um documento não desaparece sozinho do corpus. Não existe reindexação automática agendada nas plataformas do cliente — a atualização é solicitada explicitamente, por fonte. Não existe reordenação com um modelo dedicado nem pontuação lexical do tipo BM25; o que chamamos «híbrido» é uma soma ponderada entre correspondência de palavras e distância vetorial, e essa formulação é a correta.

Un caso

Uma base de conhecimento que cresceu a partir das perguntas às quais o agente não sabia responder

A situação

Uma cadeia de restaurantes com entrega tinha um voice agent no telefone e um assistente na página web. O corpus inicial tinha sido carregado uma vez, no lançamento. O problema não era o agente errar, mas dizer muitas vezes «não tenho essa informação» — a perguntas perfeitamente razoáveis que ninguém tinha antecipado quando o corpus foi escrito. Cada uma dessas respostas era uma conversa perdida, e a lista delas não existia em lado nenhum.

Qué construimos

Transformámos a falta em entrada de sistema. Quando o agente responde que não tem a informação, a pergunta é escrita numa tabela com o estado «em espera» e com o canal de onde veio — voz ou widget. A pessoa responsável do lado do cliente recebe uma notificação no canal de mensagens que já usa, com a instrução de responder diretamente à mensagem. Um webhook recolhe a resposta, passa a entrada para o estado «aprovado» e, a partir daí, a resposta entra no contexto do agente. As formulações diferentes da mesma falta ligam-se à entrada original por uma referência, e a entrega ao agente é feita uma única vez por entrada, para que uma resposta aprovada não seja reinjetada a cada ciclo.

Qué salió

O corpus deixou de ser um ficheiro carregado no lançamento e passou a ser uma lista de faltas reais, ordenada pelos clientes, não pelas nossas suposições. Quem aprova vê exatamente o que foi perguntado e em que canal, por isso escreve a resposta na forma em que será usada. Os estados são quatro — em espera, aprovado, ignorado, duplicado — porque «ignorado» é um resultado legítimo: algumas perguntas nunca devem receber resposta automática.

Qué no dice el caso

O loop funciona exatamente tanto quanto funciona o humano que está nela. Se ninguém responde às notificações, as entradas “em espera” acumulam-se e o agente continua a não saber — o mecanismo torna a falta visível, não a resolve sozinho. A segunda limitação, igualmente importante: a implementação foi ligada para o fluxo deste cliente, com a tabela, a notificação e a injeção escritas para ele. Como função genérica, disponível a partir da interface para qualquer conta, ainda não existe.

Perguntas

Lo que nos pregunta la gente antes de llamar

Porque é que o assistente responde mal se a informação está nos documentos carregados?

Quase sempre porque o fragmento correto não foi recuperado, não porque o modelo seja “burro”. As causas frequentes, pela ordem em que as verificamos: fragmentos demasiado grandes, em que a informação útil se perde entre outras; corte a meio da frase, sem sobreposição; poucos fragmentos trazidos para contexto; ou um corpus em que a mesma informação aparece em três versões diferentes e a versão antiga pontua melhor. O registo de consultas, com as pontuações obtidas, mostra qual delas é o caso — sem ele, está-se a adivinhar.

Usam mesmo pesquisa semântica em todo o lado?

Não, e é mais honesto dizer exatamente onde. Com vetores próprios: a memória de um board interno usado diariamente, na coluna vetorial com índice por distância cosseno e limiar de semelhança 0,35. Com vetores no fornecedor: a base de conhecimento dos agentes de voz, sincronizada para o motor de recuperação do fornecedor. **Sem** vetores, hoje, em produção: a base de conhecimento da plataforma de assistentes de texto — o caminho de indexação escreve a coluna vetorial vazia, por isso a recuperação corre lexicalmente, e o código rotula o próprio módulo como `keyword-only`. Existe também um pipeline próprio totalmente escrito, com a migração marcada “a não publicar em produção” até se confirmarem os direitos na extensão vetorial. Esse é o mapa real.

O que acontece quando modificamos um documento já indexado?

Os fragmentos da fonte são apagados integralmente e reescritos a partir da nova versão. Escolhemos a substituição completa em vez da atualização parcial porque a parte difícil não é acrescentar, mas eliminar: os fragmentos restantes da versão antiga continuam a ser recuperados e produzem respostas que o cliente contradiz com a página da frente. Na adição existe também uma verificação de fingerprint do conteúdo — se o texto for idêntico, nada é recalculado e não se paga em vão.

Atualiza-se sozinho quando o site ou o catálogo mudam?

Não automaticamente, nas plataformas que operamos hoje. A reindexação é pedida explicitamente, por fonte, através de uma ação dedicada. Há uma única exceção, num sistema interno nosso, onde um loop corre de trinta em trinta minutos e reindexa apenas o que mudou. A programação automática pode ser construída e é uma etapa separada; o que não fazemos é deixar a impressão de que ela já existe. Existe também um campo de expiração declarado no esquema que ninguém lê — portanto, um documento não sai sozinho do corpus.

Qual é a diferença entre um catálogo ligado e uma lista de produtos carregada?

O momento da verdade. A lista carregada está correta no dia do carregamento; a partir do dia seguinte, o preço, o stock e os produtos novos divergem em silêncio, e a divergência é descoberta quando um cliente pede um preço que já não existe. O catálogo ligado é lido no momento da pergunta, do sistema que mantém o registo, por isso não pode ficar para trás. O custo é outro: surge uma dependência da disponibilidade desse sistema e tem de ser decidido o que o assistente diz quando a fonte não responde — sendo a resposta correta “não posso confirmar o preço agora”, e não o último valor conhecido.

Podem indexar PDFs digitalizados?

Não diretamente. Um PDF digitalizado é uma imagem dentro de uma embalagem PDF e não contém texto selecionável; o nosso extractor deteta isso e devolve uma mensagem explícita em vez de indexar um documento vazio — sendo o pior resultado possível uma fonte marcada como “pronta” que não contém nada. A solução é reconhecimento ótico antes da importação, ou fornecer o documento em DOCX ou texto. Reconhecimento ótico não o temos implementado na cadeia atual.

Funciona em romeno e russo?

Sim, e há um detalhe que conta mais do que parece. A divisão em fragmentos corta entre frases, e a nossa regra reconhece o fim de frase seguido de maiúscula latina **ou** cirílica, incluindo os diacríticos romenos. Um separador escrito para inglês não reconhece o início de uma frase em cirílico e produz fragmentos colados, com efeito direto na qualidade da recuperação. Para a vertente vocal, o modelo de representação usado no fornecedor é multilingue.

Quando NÃO vale a pena a pesquisa semântica?

Quando o corpus é pequeno, estável e escrito por vocês — por exemplo, o conteúdo do vosso próprio site. Numa plataforma pública própria escolhemos deliberadamente a recuperação por sobreposição de palavras sobre o próprio texto da plataforma, dividido em passagens curtas que transportam cada uma a página de origem. Sem vetores, sem depósito vetorial, sem um pedido adicional na rede e, essencialmente, sem qualquer fonte fora do depósito. O assistente pode apenas repetir o que o site já diz, e cada resposta pode ser verificada numa página escrita por um humano. Quando o objetivo principal é não inventar nada, isso supera a pesquisa semântica.

Estão a manter-nos presos na vossa plataforma?

As fontes são suas e permanecem na forma original; os fragmentos e as representações ficam na base de dados da sua instalação, não num serviço externo de pesquisa ao qual só nós temos acesso. O modelo de representação é pedido através de uma gate própria de modelos, portanto a mudança de fornecedor não toca no código da base de conhecimentos. As representações vetoriais estão, contudo, ligadas ao modelo que as produziu: mudar o modelo exige reindexar todo o corpus. É uma operação a calcular, não uma gratuita, e é melhor sabê-lo no início do que descobri-lo a meio.

Em que se baseiam as afirmações acima (29 fontes)

29 delas são código e ficheiros dos nossos repositórios. Não publicamos o nome nem a linha: juntos, numa única página, descreveriam com demasiada precisão como estão construídos sistemas que não são só nossos. Percorremo-los consigo, no repositório, a pedido — a verificação continua possível, só que se faz numa conversa.

O que gostaria que funcionasse melhor?

Conte-nos o seu processo. Em conjunto decidimos o que vale a pena construir, o que podemos ligar e como verificamos o resultado.

Vamos falar