Guia Detalhado de Desenvolvimento do Chromadb Python

Instalação

pip install chromadb

Persistindo Dados no Chromadb

import chromadb

É possível especificar o caminho de armazenamento do arquivo do banco de dados do Chroma. Se os dados existirem, o arquivo do banco de dados será carregado automaticamente quando o programa iniciar.

cliente = chromadb.PersistentClient(path="/data/tizi365.db")

O parâmetro path é o caminho para o arquivo do banco de dados do Chroma.

Observação: Para um banco de dados do Chroma, criar um objeto cliente uma vez é suficiente. Carregar e salvar vários clientes no mesmo caminho pode levar a comportamentos inesperados, incluindo exclusão de dados. Geralmente, apenas um cliente do Chroma deve ser criado na aplicação.

Algumas funções comumente utilizadas do objeto cliente:

cliente.reset()  # Limpa e reinicia completamente o banco de dados

Operações de Coleção

O Chromadb utiliza a primitiva de collection para gerenciar coleções de dados vetoriais, que podem ser comparadas a tabelas no MYSQL.

Criar, Visualizar e Excluir Coleções

O Chroma utiliza o nome da coleção no URL, portanto, possui algumas restrições de nomenclatura:

  • O comprimento do nome deve estar entre 3 e 63 caracteres.
  • O nome deve começar e terminar com uma letra minúscula ou número, e pode conter pontos, hifens e sublinhados entre eles.
  • O nome não pode conter dois pontos consecutivos.
  • O nome não pode ser um endereço IP válido.

Para criar uma coleção, é necessário especificar o nome da coleção e uma função de cálculo de vetor opcional (também conhecida como função de incorporação). Se uma função de incorporação for fornecida, ela deve ser fornecida sempre que a coleção for acessada.

Observação: O objetivo da função de cálculo do vetor (função de incorporação) é computar o vetor de texto.

colecao = cliente.create_collection(nome="minha_colecao", embedding_function=emb_fn)
colecao = cliente.get_collection(nome="minha_colecao", embedding_function=emb_fn)

A função de incorporação recebe o texto como entrada e retorna um vetor calculado.

Observação: Iniciantes podem aprender sobre tutoriais de modelos de incorporação de texto.

É possível referenciar uma coleção existente com a função .get_collection e usar .delete_collection para excluir uma coleção. Também é possível usar .get_or_create_collection para referenciar uma coleção (se ela existir) ou criá-la se não existir.

colecao = cliente.get_collection(nome="tizi365")
colecao = cliente.get_or_create_collection(nome="tizi365")
cliente.delete_collection(nome="tizi365")

Outras operações comumente utilizadas em coleções:

colecao.peek() # Retorna uma lista dos primeiros 10 dados na coleção
colecao.count() # Retorna o número total de dados na coleção
colecao.modify(nome="novo_nome") # Renomeia a coleção

Especificar Método de Cálculo de Distância Vetorial

A função create_collection também inclui um parâmetro opcional de metadados. Ao definir o valor de hnsw:space é possível personalizar o método de cálculo de distância no espaço vetorial.

Observação: Os dados vetoriais representam a similaridade entre vetores através do cálculo da distância espacial entre eles. Quanto mais próxima a distância, maior a similaridade e vice-versa.

colecao = cliente.create_collection(
        nome="nome_da_colecao",
        metadados={"hnsw:space": "cosseno"} # l2 é o método de cálculo padrão
    )

As opções válidas para hnsw:space são "l2", "ip" ou "cosseno". O padrão é "l2".

Adicionar Dados a uma Coleção

Utilize o método .add para adicionar dados ao Chroma.

Adicione dados diretamente sem especificar vetores de documento:

collection.add(
    documents=["lorem ipsum...", "doc2", "doc3", ...],
    metadatas=[{"capítulo": "3", "versículo": "16"}, {"capítulo": "3", "versículo": "5"}, {"capítulo": "29", "versículo": "11"}, ...],
    ids=["id1", "id2", "id3", ...]
)

Se o Chroma receber uma lista de documentos, automaticamente utilizará a função de incorporação da coleção para calcular os vetores dos documentos (se uma função de incorporação não foi fornecida ao criar a coleção, o valor padrão será utilizado). O Chroma também armazenará os próprios documentos. Se um documento for muito grande para calcular usando a função de incorporação selecionada, ocorrerá uma exceção.

Cada documento deve ter um ID único (ids). Adicionar o mesmo ID duas vezes resultará no armazenamento apenas do valor inicial. Opcionalmente, você pode fornecer uma lista de dicionários de metadados (metadatas) para cada documento, para armazenar informações adicionais que podem ser usadas para filtrar dados durante consultas.

Alternativamente, você pode fornecer diretamente uma lista dos dados de vetor relacionados ao documento, e o Chroma usará os dados de vetor fornecidos sem calcular automaticamente os vetores.

collection.add(
    documents=["doc1", "doc2", "doc3", ...],
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadatas=[{"capítulo": "3", "versículo": "16"}, {"capítulo": "3", "versículo": "5"}, {"capítulo": "29", "versículo": "11"}, ...],
    ids=["id1", "id2", "id3", ...]
)

Se as dimensões (comprimento) dos dados de vetor fornecidos não corresponderem às dimensões da coleção, ocorrerá uma exceção.

Você também pode armazenar os documentos em outro lugar e fornecer ao Chroma os dados de vetor e a lista de metadados. Você pode usar IDs para associar os vetores aos documentos armazenados em outro lugar.

collection.add(
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadatas=[{"capítulo": "3", "versículo": "16"}, {"capítulo": "3", "versículo": "5"}, {"capítulo": "29", "versículo": "11"}, ...],
    ids=["id1", "id2", "id3", ...]
)

Nota: A função principal do banco de dados de vetores é a busca de similaridade semântica com base em dados de vetor. Para reduzir o tamanho do banco de dados de vetores e melhorar a eficiência, podemos optar por armazenar os dados de vetor e alguns atributos de filtragem necessários no banco de dados de vetores. Outros dados, como conteúdo do artigo, podem ser armazenados em bancos de dados como MYSQL, contanto que sejam associados por IDs.

Consulta de Dados da Coleção

O método .query pode ser utilizado para consultar conjuntos de dados do Chroma de várias maneiras.

Você pode consultar usando um conjunto de query_embeddings (dados vetoriais).

Dica: Em cenários de desenvolvimento reais, query_embeddings geralmente são obtidos primeiro calculando o vetor da consulta do usuário através de um modelo de incorporação de texto e, em seguida, usando este vetor para consultar conteúdo semelhante.

collection.query(
    query_embeddings=[[11.1, 12.1, 13.1],[1.1, 2.3, 3.2], ...],
    n_results=10,
    where={"metadata_field": "é_igual_a_isso"},
    where_document={"$contains": "string_de_pesquisa"}
)

A consulta retornará os n_results resultados que melhor correspondem a cada vetor de consulta (query_embedding) em sequência. Um dicionário de filtro opcional where pode ser fornecido para filtrar os resultados com base nos metadados associados a cada documento. Além disso, um dicionário de filtro where_document opcional pode ser fornecido para filtrar os resultados com base no conteúdo do documento.

Se os query_embeddings fornecidos não forem consistentes com as dimensões da coleção, ocorrerá uma exceção. Para garantir dimensões de vetor consistentes, use o mesmo modelo de incorporação de texto para calcular vetores.

Você também pode consultar usando um conjunto de textos de consulta. O Chroma calculará primeiro o vetor para cada texto de consulta usando a função de incorporação da coleção e, em seguida, executará a consulta usando os vetores de texto gerados.

collection.query(
    query_texts=["doc10", "assim falou zaratustra", ...],
    n_results=10,
    where={"metadata_field": "é_igual_a_isso"},
    where_document={"$contains": "string_de_pesquisa"}
)

Também é possível usar .get para consultar dados da coleção por id.

collection.get(
    ids=["id1", "id2", "id3", ...],
    where={"estilo": "estilo1"}
)

.get também suporta filtros where e where_document. Se nenhum id for fornecido, ele retornará todos os itens na coleção que correspondam aos filtros where e where_document.

Especificando Campos de Retorno

Ao usar get ou query, você pode usar o parâmetro include para especificar os dados a serem retornados -- embeddings, documents ou metadatas, e para consultas, os dados de distância precisam ser retornados. Por padrão, o Chroma retorna documentos e metadados e retorna dados de distância para consultas, enquanto "ids" sempre é retornado. Você pode especificar os campos a serem retornados passando um array de nomes de campos para o parâmetro includes do método query ou get.

collection.get(
    include=["documents"]
)

collection.query(
    query_embeddings=[[11.1, 12.1, 13.1],[1.1, 2.3, 3.2], ...],
    include=["documents"]
)

Usando Filtros Where

O Chroma suporta a filtragem de consultas com base em metadados e conteúdo do documento. O filtro where é usado para filtrar metadados, e o filtro where_document é usado para filtrar o conteúdo do documento, e a seguir explica como escrever expressões de condição de filtro.

Filtrando por Metadados

Para filtrar metadados, você deve fornecer um dicionário de filtro where para a consulta. O dicionário deve ter a seguinte estrutura:

{
    "campo_metadado": {
        <Operador>: <Valor>
    }
}

A filtragem de metadados suporta os seguintes operadores:

  • $eq - igual a (string, inteiro, float)
  • $ne - diferente de (string, inteiro, float)
  • $gt - maior que (int, float)
  • $gte - maior que ou igual a (int, float)
  • $lt - menor que (inteiro, float)
  • $lte - menor que ou igual a (int, float)

Usar o operador $eq é equivalente a usar o filtro where.

{
    "campo_metadado": "string_de_pesquisa"
}

{
    "campo_metadado": {
        "$eq": "string_de_pesquisa"
    }
}

Filtrando o Conteúdo do Documento

Para filtrar o conteúdo do documento, você deve fornecer um dicionário de filtro where_document para a consulta. O dicionário deve ter a seguinte estrutura:

{
    "$contains": "string_de_pesquisa"
}

Utilizando Operadores Lógicos

Também é possível utilizar os operadores lógicos $and e $or para combinar múltiplos filtros.

O operador $and retornará resultados que correspondam a todos os filtros na lista.

{
    "$and": [
        {
            "campo_metadados": {
                <Operador>: <Valor>
            }
        },
        {
            "campo_metadados": {
                <Operador>: <Valor>
            }
        }
    ]
}

O operador $or retornará resultados que correspondam a qualquer uma das condições de filtro na lista.

{
    "$or": [
        {
            "campo_metadados": {
                <Operador>: <Valor>
            }
        },
        {
            "campo_metadados": {
                <Operador>: <Valor>
            }
        }
    ]
}

Atualizando Dados em uma Coleção

Utilizando .update, é possível atualizar quaisquer propriedades dos dados em uma coleção.

colecao.update(
    ids=["id1", "id2", "id3", ...],
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadados=[{"capitulo": "3", "versiculo": "16"}, {"capitulo": "3", "versiculo": "5"}, {"capitulo": "29", "versiculo": "11"}, ...],
    documentos=["doc1", "doc2", "doc3", ...],
)

Caso um id não seja encontrado na coleção, um erro será registrado e a atualização será ignorada. Se o documento fornecido não tiver um vetor correspondente, a função de incorporação da coleção será usada para calcular o vetor.

Caso os dados de vetor fornecidos tenham uma dimensão diferente da coleção, ocorrerá uma exceção.

Chroma também suporta a operação de upsert, que pode atualizar dados existentes e inserir novos dados caso não existam.

colecao.upsert(
    ids=["id1", "id2", "id3", ...],
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadados=[{"capitulo": "3", "versiculo": "16"}, {"capitulo": "3", "versiculo": "5"}, {"capitulo": "29", "versiculo": "11"}, ...],
    documentos=["doc1", "doc2", "doc3", ...],
)

Excluindo Dados da Coleção

Chroma suporta o uso de .delete para remover dados de uma coleção pelo id. Os vetores, documentos e metadados associados a cada dado também serão excluídos.

colecao.delete(
    ids=["id1", "id2", "id3",...],
    where={"capitulo": "20"}
)

.delete também suporta um filtro where. Caso nenhum id seja fornecido, ele excluirá todos os itens na coleção que correspondam ao filtro where.