ChromaDB + Autenticación y Colecciones

En este tutorial aprenderemos a trabajar con ChromaDB, una base de datos para manejar vectores de embeddings, con autenticación y la manipulación de colecciones.

iStock AI Generator

Configuración de un servidor ChromaDB

En este caso, usaremos Docker para implementar el servidor. Para poner en marcha un servidor de ChromaDB que cuente con sistemas de autenticación, resulta imprescindible definir correctamente las variables de autenticación en el archivo `docker-compose.yml`. Es fundamental no guardar el token o clave secreta en texto plano por motivos de seguridad; la presente demostración tiene únicamente fines ilustrativos sobre cómo proceder con la configuración.

version: "3.9"
services:
  chroma:
    image: ghcr.io/chroma-core/chroma:latest
    environment:
      CHROMA_SERVER_AUTHN_CREDENTIALS: "test-token"
      CHROMA_SERVER_AUTHN_PROVIDER: "chromadb.auth.token_authn.TokenAuthServerProvider"
    volumes:
      - index_data:/chroma/.chroma/index
    ports:
      - 8001:8000
    networks:
      - net
volumes:
  index_data:
    driver: local
  backups:
    driver: local

networks:
  net:
    driver: bridge

Para iniciar el servidor ChromaDB, ejecuta el siguiente comando:

docker-compose up

Parámetros para la colección y la consulta

Definimos algunos parámetros que usaremos para la colección y la consulta:

EMBEDDINGS_MAX_RESULTS = 2
CHROMA_SERVER_AUTHN_CREDENTIALS = os.getenv('CHROMA_SERVER_AUTHN_CREDENTIALS')
CHROMA_CLIENT_AUTHN_PROVIDER = 'chromadb.auth.token_authn.TokenAuthClientProvider'
VECTOR_EMBEDDING_HOST = 'localhost'
directory = 'Zero/'

Carga y división de documentos

Creamos una función para cargar documentos de un directorio y otra para dividirlos en fragmentos:

Función para cargar documentos desde un directorio

def load_docs(directory):
    loader = DirectoryLoader(directory, glob="**/*.txt", loader_cls=TextLoader)
    documents = loader.load()
    return documents

Función para dividir documentos en fragmentos

def split_docs(documents, chunk_size=1000, chunk_overlap=20):
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
    docs = text_splitter.split_documents(documents)
    return docs

Carga y división de los documentos

documents = load_docs(directory)
docs = split_docs(documents)

Generación de Embeddings

Generamos embeddings para los fragmentos de los documentos usando un modelo preentrenado:

embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")

Cliente de ChromaDB y operaciones

Para conectar con el servidor de ChromaDB y autenticarnos, primero debemos instanciar el cliente. En este ejemplo, configuramos el puerto y proveemos las credenciales de autenticación necesarias.

chroma_client = chromadb.HttpClient(
    port=8001,
    settings=Settings(
    chroma_client_auth_provider=CHROMA_CLIENT_AUTHN_PROVIDER,
    chroma_client_auth_credentials=CHROMA_SERVER_AUTHN_CREDENTIALS)
)

Creación o Recuperación de una Colección

Aquí veremos cómo verificar si una colección existe y, si no es así, cómo crearla.

collection = chroma_client.get_or_create_collection(name='myCollection')

Generación de Embeddings para los Documentos

Generamos los embeddings para los documentos, que nos permitirán luego hacer búsquedas dentro de la colección.

doc_embeddings = embeddings.embed_documents([doc.page_content for doc in docs])

Agregado de Documentos y sus Metadatos a la Colección

Una vez generados los embeddings, agregamos los documentos y sus metadatos correspondientes a la colección recién creada o recuperada.

collection.add(
    ids = [str(uuid.uuid4()) for _ in docs],
    embeddings = doc_embeddings,
    documents = [doc.page_content for doc in docs],
    metadatas = [{'timestamp': timestamp, 'chapter': 'A', 'region': 'AMER', 'book': 'REGISTRY'} for _ in docs]
)

Ejecución de una Consulta de Ejemplo

Realizamos una consulta de ejemplo donde primero creamos el embedding de la consulta y luego usamos este embedding para obtener resultados de la base de datos.

embed_query = embeddings.embed_documents('The search query')

results = collection.query(
    query_embeddings = embed_query,
    n_results= EMBEDDINGS_MAX_RESULTS,
    where= {'$and': [{'chapter': 'A'}, {'region': 'AMER'}]},
)

Finalmente, mostramos los resultados de la consulta.

print(f"Results: {results}")

Código Completo

A continuación, se presenta el código completo utilizado en este tutorial:

from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings import HuggingFaceEmbeddings
from dotenv import load_dotenv
import chromadb, uuid, os, datetime
from chromadb.config import Settings
load_dotenv()

EMBEDDINGS_MAX_RESULTS = 2
CHROMA_SERVER_AUTHN_CREDENTIALS = os.getenv('CHROMA_SERVER_AUTHN_CREDENTIALS')
CHROMA_CLIENT_AUTHN_PROVIDER = 'chromadb.auth.token_authn.TokenAuthClientProvider'
VECTOR_EMBEDDING_HOST = 'localhost'
directory = 'Zero/'

def load_docs(directory):
    loader = DirectoryLoader(directory, glob="**/*.txt", loader_cls=TextLoader)
    documents = loader.load()
    return documents

def split_docs(documents, chunk_size=1000, chunk_overlap=20):
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
    docs = text_splitter.split_documents(documents)
    return docs

documents = load_docs(directory)
docs = split_docs(documents)
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")

try:
    chroma_client = chromadb.HttpClient(
        port=8001,
        settings=Settings(
        chroma_client_auth_provider=CHROMA_CLIENT_AUTHN_PROVIDER,
        chroma_client_auth_credentials=CHROMA_SERVER_AUTHN_CREDENTIALS)
    )

    collection = chroma_client.get_or_create_collection(name='myCollection')

    timestamp = datetime.datetime.now().isoformat()
    doc_embeddings = embeddings.embed_documents([doc.page_content for doc in docs])

    collection.add(
        ids = [str(uuid.uuid4()) for _ in docs],
        embeddings = doc_embeddings,
        documents = [doc.page_content for doc in docs],
        metadatas = [{'timestamp': timestamp, 'chapter': 'A', 'region': 'AMER', 'book': 'REGISTRY'} for _ in docs]
    )

    embed_query = embeddings.embed_documents('The search query')
    results = collection.query(
        query_embeddings = embed_query,
        n_results= EMBEDDINGS_MAX_RESULTS,
        where= {'$and': [{'chapter': 'A'}, {'region': 'AMER'}]},
    )

    print(f"Results: {results}")
except Exception as error:
    print(f"Error: {error}")

Maximiliano Díaz Doglia

AI Platform Engineer & Full-Stack Developer
Building Enterprise Integrations & Automations

Publicado en: AI