11 pontos por GN⁺ 2024-10-30 | 1 comentários | Compartilhar no WhatsApp
  • A mensagem que atormenta equipes de engenharia que querem construir aplicações de IA: "os embeddings não foram resincronizados"
  • Uma implementação simples de busca vetorial evolui para uma orquestra complexa de monitoramento, sincronização e resolução de problemas
  • Ao conversar com equipes de engenharia que constroem sistemas de IA com bancos de dados vetoriais, descobriu-se a abstração equivocada dos bancos de dados vetoriais e as falhas da forma como são usados hoje

"Caso comum de construção de um sistema RAG"

  • Usa-se o Pinecone como banco de dados vetorial para armazenar e buscar embeddings
  • Como os dados de texto não se encaixam bem nos metadados do Pinecone, usa-se o DynamoDB para blobs e dados da aplicação
  • Também foi necessário o OpenSearch para busca lexical
  • Agora, conectar e sincronizar 3 sistemas virou um pesadelo

Ao excluir um documento de origem, é preciso fazer o seguinte:

  1. Executar boto3 para remover o registro do DynamoDB
  2. Atualizar o Pinecone para garantir que o embedding foi excluído
  3. Enviar uma requisição POST para atualizar o índice de busca lexical
  • É preciso fazer isso para toda atualização, adição ou exclusão de documento de origem
  • O gerenciamento de configuração não é apenas bagunçado, mas também arriscado
  • Houve até equipe pagando US$ 2.000 por mês por um índice que deveria ter sido apagado 4 meses antes
  • Há risco de retornar aos usuários dados incorretos ou desatualizados

Bancos de dados vetoriais são construídos sobre uma abstração errada

  • Bancos de dados vetoriais tratam embeddings como dados independentes, e não como dados derivados
  • Ao tratar embeddings como dados independentes, criam complexidade desnecessária

Um caminho melhor: a abstração de "Vectorizer"

  • Propõe-se a abstração de "vectorizer", que trata embeddings como índices de banco de dados
  • Essa abordagem sincroniza automaticamente os embeddings com os dados de origem, eliminando o custo de manutenção
  • O equivalente de um comando CREATE INDEX para um vectorizer é o seguinte:
SELECT ai.create_vectorizer(
    'public.blogs'::regclass,
    embedding => ai.embedding_openai('text-embedding-3-small', 1536),
    chunking => ai.chunking_recursive_character_text_splitter('content')
);
  • Esse comando gera embeddings para todos os itens da tabela de blogs e continua atualizando os embeddings quando os dados da tabela mudam

Problemas dos bancos de dados vetoriais (e dos tipos de dados vetoriais)

  • Bancos de dados vetoriais foram desenvolvidos para lidar com grandes volumes de embeddings vetoriais para texto, imagem e dados multimodais
  • Bancos de dados de uso geral como PostgreSQL, MySQL, MongoDB e Oracle também adicionaram suporte a busca vetorial
  • No entanto, a abstração da busca vetorial, seja em sistemas independentes ou adicionada a bancos de dados existentes, tem uma falha fatal
  • Quando embeddings são inseridos no banco de dados, a ligação entre os dados não estruturados embeddados e o próprio embedding vetorial é rompida
  • Sem essa ligação, embeddings passam a ser tratados erroneamente como unidades de dados independentes que o desenvolvedor precisa gerenciar, e não como dados derivados
  • Ao reenquadrar embeddings como dados derivados, fica evidente o absurdo da abstração atual dos bancos de dados vetoriais, em que embeddings não estão ligados aos dados de origem

Equipes de desenvolvimento precisam gerenciar:

  • Pipelines ETL complexos (extração-carga-transformação)

  • Um banco de dados vetorial para embeddings, outro banco de dados para metadados e dados do app, e um índice de busca lexical

  • Serviços de sincronização de dados

  • Sistemas de filas para atualizações e sincronização

  • Ferramentas de monitoramento para detectar data drift e lidar com limites de taxa de serviços de embedding, entre outros

  • Sistemas de alerta quando a busca retorna resultados desatualizados

  • Verificações de validação para todos os sistemas

  • Para fazer upgrade para um novo modelo de embedding ou testar outro método de chunking, é preciso escrever código customizado e coordenar mudanças em vários serviços de dados e bancos de dados

  • Essas tarefas impõem às equipes a responsabilidade de garantir que embeddings sejam gerados no momento certo conforme os dados de origem mudam

  • Caso contrário, há grande risco de que embeddings fiquem desatualizados e entreguem uma experiência pior ao usuário

Um caminho melhor: deixar o banco de dados lidar com a complexidade

  • Ao reenquadrar embeddings como dados derivados, pode-se deixar para o sistema de gerenciamento de banco de dados a responsabilidade de gerar e atualizar embeddings conforme os dados base mudam
  • Essa mudança livra os desenvolvedores do peso de manter manualmente os embeddings sincronizados com os dados de origem
  • Essa distinção pode não importar em aplicações simples que fazem uma importação única de dados para RAG
  • Mas, na maioria das aplicações reais, os dados mudam continuamente
  • Considere uma plataforma de e-commerce com busca semântica baseada em embeddings ou um app RAG de assistente de produto que precisa se manter atualizado com as informações mais recentes de produtos.
  • Acompanhar essas mudanças manualmente e regenerar embeddings não é apenas trabalhoso e sujeito a erros, como também desvia os desenvolvedores do foco em seus objetivos centrais de negócio
  • Por que desperdiçar tempo de desenvolvimento com isso, se o sistema de banco de dados pode cuidar automaticamente?

Vectorizer: embeddings vetoriais como índice

  • É uma abstração mais eficaz conceituar embeddings vetoriais como um índice especializado sobre os dados embeddados, e não como uma tabela ou tipo de dado independente
  • Embeddings vetoriais não são índices no sentido tradicional
  • Em vez disso, funcionam como um mecanismo de indexação que recupera as partes mais relevantes dos dados com base nos embeddings
  • Essa nova abstração semelhante a índice pode ser chamada de "vectorizer"

Principais benefícios da abstração de vectorizer:

Sincronização automática

  • Um dos principais benefícios da indexação em bancos de dados é manter automaticamente os índices sincronizados com os dados subjacentes
  • Quando os dados de uma coluna mudam, o índice é atualizado de acordo
  • Ao tratar embeddings vetoriais como uma forma de indexação, é possível aproveitar essa mesma sincronização automática
  • O sistema garante que embeddings vetoriais estejam sempre atualizados com os dados mais recentes, eliminando a necessidade de atualizações manuais e reduzindo o risco de erro

Relação dados-embedding fortalecida

  • Quando vetores são armazenados de forma independente, é fácil perder a relação com os dados originais
  • Esse vetor foi gerado a partir da versão mais recente dos dados? Ou é um vetor antigo de um modelo de embedding anterior?
  • Essas perguntas são importantes, e confusões nesse ponto podem causar erros graves
  • Ao conectar diretamente embeddings vetoriais aos dados como um índice, essa relação fica clara e é mantida automaticamente

Gestão de dados simplificada

  • Desenvolvedores frequentemente enfrentam dificuldades ao gerenciar sincronização de dados manualmente
  • Por exemplo, se esquecerem de apagar dados de um modelo de embedding anterior quando os dados base são excluídos, inconsistências podem surgir
  • A abstração de vectorizer torna o sistema responsável por gerenciar essas relações, reduzindo a carga cognitiva dos desenvolvedores e minimizando a chance de erro

Vectorizer é uma evolução natural da promessa central dos DBMS

  • O conceito de vectorizer é uma evolução natural das capacidades dos sistemas modernos de gerenciamento de banco de dados (DBMS)
  • Os DBMS atuais já são bons em gerenciar transformação e sincronização de dados por meio de estruturas declarativas como índices, triggers e materialized views
  • A abstração de vectorizer se encaixa bem nesse paradigma ao oferecer uma nova ferramenta para lidar com a tarefa cada vez mais importante de gerenciar embeddings vetoriais
  • Ao incluir essa capacidade diretamente no DBMS, chega-se mais perto de realizar a promessa definitiva dos sistemas de banco de dados
  • Gerenciar dados de forma que abstrai a complexidade, permitindo que os usuários foquem no que fazem de melhor: construir aplicações, analisar dados e impulsionar inovação

Implementação de vectorizer para PostgreSQL: pgai Vectorizer

  • Motivada pela promessa de aliviar a carga dos desenvolvedores, a equipe de engenharia de IA da Timescale implementou um vectorizer para PostgreSQL
  • Chama-se pgai Vectorizer e está atualmente em Early Access
  • Faz parte do projeto PGAI, voltado a tornar o PostgreSQL mais adequado para sistemas de IA e facilitar o desenvolvimento de IA para quem já conhece PostgreSQL
  • Para ver como o pgai Vectorizer gera e atualiza automaticamente embeddings para dados no PostgreSQL, confira o vídeo de demonstração

Como o pgai Vectorizer funciona

  • Define-se e cria-se o vectorizer em SQL
  • A consulta a seguir cria um vectorizer e especifica a tabela sobre a qual ele atua, a coluna a ser vetorizada, o modelo de embedding a usar e formatações adicionais sobre outras informações a incluir nos dados de origem a serem embeddados
-- Cria um vectorizer que embeda automaticamente os dados da tabela blogs
SELECT ai.create_vectorizer(
   'public.blogs'::regclass
   -- Usa o modelo OpenAI text-embedding-3-small
 , embedding=>ai.embedding_openai('text-embedding-3-small', 1536, api_key_name=>'OPENAI_API_KEY')
   -- Cria automaticamente um índice StreamingDiskANN quando a tabela tiver 100k linhas
 , indexing => ai.indexing_diskann(min_rows => 100000, storage_layout => 'memory_optimized'),
   -- Aplica chunking recursivo na coluna content
 , chunking=>ai.chunking_recursive_character_text_splitter('content')
   -- Adiciona metadados de outras colunas ao embedding para melhorar a busca
 , formatting=>ai.formatting_python_template('Blog title: $title url: $url blog chunk: $chunk')
);
-- O vectorizer atualiza os embeddings quando a tabela de origem muda
-- Nenhuma outra ação do usuário é necessária
  • Também define uma função padrão de chunking, já que textos longos precisam ser divididos em vários pedaços menores que se encaixem no limite de tokens do modelo de embedding

Rastreamento de mudanças nos dados de origem

  • Internamente, o pgai Vectorizer verifica modificações na tabela de origem (inserções, atualizações e exclusões) e gera e atualiza embeddings vetoriais de forma assíncrona
  • O pgai Vectorizer foi construído para dois tipos de implantação: self-hosted e totalmente gerenciado no Timescale Cloud
  • Na implementação hospedada em nuvem, o pgai Vectorizer usa recursos de cloud da plataforma Timescale Cloud para gerar embeddings
  • Na versão open source do pgai Vectorizer, embeddings são gerados executando workers externos
  • O pgai Vectorizer armazena configuração e informações de catálogo dentro do banco de dados, junto com dados internos essenciais de bookkeeping

Onde os embeddings são realmente gerados?

  • O processo real de embedding acontece em um processo externo, fora do banco de dados
  • Isso reduz a carga sobre o servidor de banco de dados e significa que o vectorizer não afeta a capacidade do banco de processar consultas da aplicação
  • Também permite escalar facilmente o trabalho de embedding de forma independente das demais tarefas do banco de dados
  • O processo primeiro lê o banco de dados para verificar se há trabalho a ser feito
  • Se houver, lê os dados do banco, faz o chunking e a formatação, chama um provedor de modelo de embedding como a OpenAI para gerar os embeddings e grava os resultados de volta no banco

Personalização do processo

  • O pgai Vectorizer é flexível: é possível especificar as regras de chunking e formatação usadas para criar embeddings
  • Em especial, é possível configurar as colunas da tabela de origem a serem vetorizadas e as regras de chunking e formatação para garantir que os dados caibam no limite de tokens do embedding e que informações relevantes sejam incluídas em cada embedding
  • Na versão Early Access do pgai Vectorizer, é possível customizar a escolha do modelo de embedding da OpenAI, estratégias de chunking para dividir texto em pedaços menores, opções de formatação para injetar contexto extra em cada chunk, criação automática de índices e configurações personalizadas de indexação para ajuste de desempenho
  • Em breve, planeja-se torná-lo ainda mais flexível, permitindo que usuários enviem seu próprio código Python para personalizar totalmente chunking, embedding e formatação

Por exemplo, o seguinte é um vectorizer configurado para dividir recursivamente arquivos-fonte HTML e criar embeddings OpenAI a partir dos dados de origem. É possível configurar chunking e formatação de acordo com os dados da aplicação, como código, documentação, Markdown e outros

-- Configuração avançada de vectorizer
SELECT ai.create_vectorizer(
   'public.blogs'::regclass,
   destination => 'blogs_embedding_recursive',
   embedding => ai.embedding_openai('text-embedding-3-small', 1536),
   -- Aplica chunking recursivo com configurações específicas para conteúdo HTML
   chunking => ai.chunking_recursive_character_text_splitter(
       'content',
       chunk_size => 800,
       chunk_overlap => 400,
       -- Separadores com reconhecimento de HTML, ordenados da maior para a menor prioridade
       separator => array[
           E'

', -- divide nas seções principais do documento
           E'

',    -- divide nos limites de div
           E'

',
           E'

',      -- divide nos parágrafos
           E'
',      -- divide nas quebras de linha
           E'
',     -- divide nos itens de lista
           E'. ',        -- alternativa para limite de sentença
           ' '          -- último recurso: divide em espaços
       ]
   ),
   formatting => ai.formatting_python_template('title: $title url: $url $chunk')
);

Opinião do GN⁺

  • O pgai Vectorizer parece ser uma ferramenta poderosa e inovadora que pode simplificar bastante o gerenciamento de embeddings. A abstração de vectorizer reduz a carga de os desenvolvedores terem de gerenciar embeddings manualmente e garante que eles permaneçam sincronizados com os dados de origem.
  • Isso parece especialmente útil ao aplicar mudanças como upgrade do modelo de embedding ou alteração da estratégia de chunking. Em bancos de dados vetoriais tradicionais, essas mudanças podem levar a um processo complexo de escrever código customizado e coordenar alterações em vários sistemas, mas com o pgai Vectorizer basta atualizar a configuração do vectorizer.
  • Além disso, gerenciar embeddings em um banco de dados de uso geral como o PostgreSQL pode evitar o problema de ter de orquestrar vários sistemas especializados. Isso pode simplificar bastante o desenvolvimento de aplicações.
  • Um ponto a considerar é que os embeddings reais são gerados em um processo externo em Python. Essa é uma boa escolha de design para não impactar o desempenho do banco, mas também significa que o processo de geração de embeddings precisa ser monitorado e gerenciado separadamente.
  • Em última análise, o pgai Vectorizer representa um avanço significativo na forma de gerenciar embeddings para aplicações de IA. À medida que mais equipes o adotarem e fornecerem feedback, espera-se que essa ferramenta poderosa evolua ainda mais. Integrar o gerenciamento de embeddings em ferramentas familiares como o Postgres deve permitir que mais desenvolvedores aproveitem recursos avançados de IA.

1 comentários

 
GN⁺ 2024-10-30
Opiniões no Hacker News
  • Estão superestimando o overhead da sincronização de dados, e a maioria dos fluxos de trabalho baseados em embeddings não tem muitas atualizações ou exclusões. Mesmo em conjuntos de dados pequenos, é difícil perceber problemas de consistência. Ainda assim, é ótimo não precisar se preocupar com sincronização de dados

    • A maior desvantagem de armazenar embeddings em um banco de dados Postgres é que a carga de trabalho é muito diferente. Índices HNSW consomem muitos recursos e podem causar problemas de competição por recursos. Se mover o banco de dados, o problema de consistência reaparece
    • Há uma pergunta sobre a interação com filtragem. Fica a dúvida se é possível usar índices parciais e se as limitações da implementação HNSW do pgvector ainda existem
  • Como funcionário da Elastic, menciona que o Elasticsearch adicionou recentemente um tipo de dado chamado semantic_text. Ele divide automaticamente o texto em chunks e calcula/armazena os embeddings. As consultas também ficam mais simples, reduzindo I/O e simplificando o código do cliente

  • Apresenta uma ferramenta para PostgreSQL que reconceitua embeddings vetoriais como índices de banco de dados. No momento, só oferece suporte à OpenAI, mas há planos de adicionar suporte a modelos locais e OSS em breve. Espera feedback e reações

  • Levanta dúvidas sobre usar o FAISS como banco de dados único. Seria como um sqlite para embeddings vetoriais, permitindo armazenar metadados e vetores juntos para manter os relacionamentos

  • É positivo sobre usar vetores no Postgres e levanta uma dúvida sobre a ordem da filtragem ao incluir busca vetorial e lógica em consultas SQL. Gosta da DX do pg_vector, mas filtrar depois da busca vetorial pode reduzir a velocidade

  • Menciona que armazenar embeddings brutos em um banco vetorial é como armazenar n-gramas brutos de texto em um banco de dados. Armazenar os documentos faz mais sentido

  • Diz que usa sqlite-vec e FTS5 no SQLite e que isso é muito útil

  • Criou um ORM para PostgreSQL em Node.js que permite escrever código com campos vetoriais. Isso possibilita consultar dados ou conteúdo de embeddings e definir como campos do modelo podem ser armazenados como embeddings

  • Diz que Materialized Views são boas

  • Menciona que apps de IA que usam chunks baseados em caracteres não passaram da fase de PoC