Como Otimizar o Uso de Tokens com ChromaDB (Passo a Passo)
Se você não está prestando atenção ao uso de tokens em suas consultas de banco de dados vetoriais, está queimando créditos e desempenho mais rápido do que percebe—então aqui está como otimizar o uso de tokens no chromadb como se realmente quisesse economizar dinheiro e velocidade.
O Que Você Vai Construir e Por Que Isso É Importante
Estamos construindo um pipeline mínimo, mas eficaz, que transforma documentos bagunçados do mundo real, os armazena no ChromaDB e os consulta com embeddings da OpenAI, tudo enquanto corta o desperdício de tokens ao máximo.
Pré-requisitos
- Python 3.11+
- pip install chroma-core==0.10.0 (última versão a partir de março de 2026)
- Chave de API da OpenAI com acesso aos endpoints de embeddings
- Conhecimento básico sobre bancos de dados vetoriais e APIs de modelos de linguagem amplos
Passo 1: Configure Seu Ambiente ChromaDB
Primeiro e acima de tudo, você precisa ter o ChromaDB instalado e funcionando de uma maneira que funcione bem no seu ambiente. O chroma-core, o motor principal por trás do ChromaDB, atrai muita atenção no GitHub: mais de 26.759 estrelas e 2.140 forks, então está longe de ser apenas um experimento de nicho. Isso significa muita ajuda da comunidade, mas também alguns problemas abertos inevitáveis (e o ChromaDB tem 513 abertos até março de 2026).
A última versão, licenciada sob Apache-2.0, foi atualizada em 2026-03-21—portanto, está sendo mantida ativamente, e isso é bom porque você vai depender dela muito para gerenciar seus embeddings de forma inteligente.
# Instale o chroma-core se ainda não o fez
pip install chroma-core==0.10.0
Configurar um ChromaDB persistente geralmente significa apenas executar este trecho de código mínimo:
from chromadb import Client
client = Client() # Usa Configurações Padrão - SQLite + Disco Local
collection = client.get_or_create_collection(name="mydocs")
Por que fazer isso primeiro? Porque cada outro passo gira em torno do orçamento variável de tokens — e a gestão de dependência do ChromaDB vem primeiro.
Problemas comuns: Se você encontrar erros como ModuleNotFoundError, verifique suas versões. Atualizações do chroma-core frequentemente mudam as partes internas, então fixar uma versão específica evita quebras aleatórias.
Passo 2: Dividindo Conteúdo para Eficiência de Tokens
Seus documentos não são limpos e enfeitados—eles vêm com cabeçalhos, tabelas, notas de rodapé e partes indesejadas. Apenas enviar longas strings para APIs de embeddings é uma maneira infalível de se prejudicar no uso de tokens. Cada token conta.
Em vez disso, você divide seu conteúdo de forma esperta. Não muito grande, nem muito pequeno. Um tamanho de bloco que corresponda ou fique um pouco abaixo dos limites de tokens da sua API de embedding economiza muito processamento e custo desperdiçado.
import tiktoken # Para contagem de tokens, o próprio tokenizador da OpenAI
from langchain.text_splitter import RecursiveCharacterTextSplitter
# Inicializa um divisor para blocos de ~500 tokens (seguro para embeddings da OpenAI)
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
length_function=lambda text: len(tiktoken.encoding_for_model("text-embedding-3-small").encode(text))
)
def chunk_text(text):
chunks = splitter.split_text(text)
print(f"Dividido em {len(chunks)} blocos")
return chunks
# Exemplo de Uso
doc_text = open("messy_doc.txt").read()
chunks = chunk_text(doc_text)
Por que a divisão economiza tokens: Se você enviar documentos completos para a API de embedding, receberá grandes embeddings, mas principalmente ruído—além disso, você pode ultrapassar os limites de tokens ou acionar limites de taxa. Blocos pequenos, mas significativos, produzem embeddings focadas de forma confiável para uma busca vetorial precisa.
Erros a ficar atento: Blocos sobrepostos podem ocasionalmente parecer redundantes nos resultados da busca—ajuste chunk_overlap conforme necessário. E fique de olho na escolha do tokenizador. Se você desajustar os modelos de tokenizadores, suas contagens de tokens ficarão erradas em 20-30%, arruinando todo o seu plano de orçamento de tokens.
Passo 3: Embeddings Com ChromaDB – Otimize, Não Embed Tudo Sem Pensar
É aqui que muitos desenvolvedores estouram seus orçamentos de tokens. Alimentar cada bloco pelo endpoint de embedding da OpenAI compromete seu orçamento.
Em vez disso, você quer pré-filtrar os blocos antes de fazer o embedding. Use um filtro de similaridade de texto barato. Como uma rápida verificação TF-IDF, então apenas embede os melhores 30-40% dos blocos. Isso reduz armazenamento, tempo de consulta e custo de token.
from sklearn.feature_extraction.text import TfidfVectorizer
import numpy as np
# Uma aproximação barata para filtrar blocos significativos antes de embeddings caros
def filter_chunks(chunks, query, keep_ratio=0.4):
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(chunks + [query])
query_vec = X[-1]
chunk_vecs = X[:-1]
similarities = (chunk_vecs @ query_vec.T).toarray().flatten()
threshold = np.quantile(similarities, 1 - keep_ratio)
filtered = [chunk for chunk, sim in zip(chunks, similarities) if sim >= threshold]
print(f"Filtrado de {len(chunks)} para {len(filtered)} blocos antes do embedding")
return filtered
chunks_to_embed = filter_chunks(chunks, "O que é otimização de uso de tokens?")
Agora, embeda apenas chunks_to_embed no ChromaDB:
from openai import OpenAI
import chromadb
client = chromadb.Client()
collection = client.get_or_create_collection("mydocs")
embedding_model = OpenAI()
for chunk in chunks_to_embed:
embedding = embedding_model.embeddings.create(input=chunk).data[0].embedding
collection.add(
documents=[chunk],
embeddings=[embedding],
ids=[hash(chunk) % (10 ** 8)] # ID único, mas simples
)
Por que essa abordagem? Porque os embeddings são os grandes devoradores de tokens. Um único bloco de 500 tokens custa 500 tokens apenas para ser vetorizado. Você realmente quer embeder fragmentos inúteis? Não. Filtre de forma inteligente e economize tokens.
Erros que você pode encontrar: Limites de taxa da API. A inserção em lote do ChromaDB pode ajudar a mitigar chamadas de API—mais sobre isso abaixo.
Passo 4: Inserções em Lote para Reduzir Chamadas à API
Não se trata apenas de tokens—você enfrenta latência e acaba pagando mais por muitas chamadas pequenas. Inserções em lote eliminam dois problemas de uma vez: maior taxa de transferência e menos chamadas redundantes à API.
O método collection.add do ChromaDB suporta múltiplos documentos & embeddings ao mesmo tempo. Agrupe seus blocos filtrados em lotes de 50 ou 100 para economizar muito tempo.
BATCH_SIZE = 50
def batch(iterable, n=1):
l = len(iterable)
for ndx in range(0, l, n):
yield iterable[ndx:min(ndx + n, l)]
for chunk_batch in batch(chunks_to_embed, BATCH_SIZE):
embeddings = [embedding_model.embeddings.create(input=chunk).data[0].embedding for chunk in chunk_batch]
collection.add(
documents=chunk_batch,
embeddings=embeddings,
ids=[hash(chunk) % (10 ** 8) for chunk in chunk_batch]
)
print(f"Indexado {len(chunk_batch)} blocos em lote")
Por que usar lote? Uma solicitação = um evento de cobrança de token e uma viagem de rede. Não seja aquele desenvolvedor que espera por 100 chamadas individuais à API no relógio.
Cuidado com:
- Lotes grandes demais—mais de 100 por chamada pode causar lentidão.
- Falhas parciais—envolva suas chamadas em try/except para lidar com erros intermitentes como timeouts ou 429s.
Passo 5: Contagem Inteligente de Tokens Antes das Consultas — Não Adivinhe
Ao executar consultas, você precisa ter uma ideia de quantos tokens toda a pipeline consome. Isso é crítico, especialmente se suas consultas em si forem longas passagens ou uma combinação de entrada do usuário mais contexto.
Em vez de estimar a contagem de tokens, confie no tiktoken ou equivalente para contar tokens exatamente em cada etapa. Dessa forma, você pode truncar ou ajustar entradas em tempo real antes de enviar essa consulta.
def count_tokens(text, model_name="gpt-4o-mini"):
tokenizer = tiktoken.encoding_for_model(model_name)
tokens = tokenizer.encode(text)
return len(tokens)
query = "Explique a otimização de tokens no ChromaDB da forma mais simples possível."
tokens_used = count_tokens(query)
print(f"Contagem de tokens para a consulta: {tokens_used}")
Por que se importar? Porque os embeddings da OpenAI e as respostas de chat têm limites rigorosos de tokens. Ultrapassar 4.096 tokens geralmente aciona truncamento, erros ou cobranças extras que você preferiria evitar.
Erros que você verá: O temido OpenAIError: O comprimento máximo de contexto deste modelo é 4097 tokens. Lide com esses problemas contando tokens antes de enviar solicitações e ajustando a entrada do usuário ou contexto de acordo (detalhado na próxima seção).
Passo 6: Truncamento de Contexto e Cache de Embeddings
Quando você está alimentando documentos e histórico de chat no seu modelo de linguagem, o total de tokens usados importa imensamente. Um erro comum de iniciante é sempre enviar seu documento inteiro ou todo o histórico de chat indiscriminadamente. Você ultrapassa os limites de tokens em segundos.
A sua melhor opção: truncar e armazenar em cache.
- Armazene embeddings em cache para documentos estáticos (não re-embed o mesmo texto)
- Trunque o histórico de chat de forma inteligente, priorizando entradas importantes recentes
- Use uma janela de token rolante de ~3.000 tokens para contexto em chat/completions
Aqui está um trecho que ilustra a verificação de cache e truncamento:
embedding_cache = {}
def get_embedding(text):
if text in embedding_cache:
print("Cache hit for embedding")
return embedding_cache[text]
embedding = embedding_model.embeddings.create(input=text).data[0].embedding
embedding_cache[text] = embedding
return embedding
def trim_context(contexts, max_tokens=3000, model_name="gpt-4o-mini"):
trimmed = []
token_count = 0
for c in reversed(contexts): # start from latest
tcount = count_tokens(c, model_name)
if (token_count + tcount) <= max_tokens:
trimmed.insert(0, c)
token_count += tcount
else:
break
print(f"Trimmed context to {len(trimmed)} messages totaling {token_count} tokens")
return trimmed
Essa abordagem em cache economiza centenas, talvez milhares, de tokens por mês.
Os Perigos
Aqui é onde o código do tutorial idealizado falha na vida real:
| Perigo | O Que Acontece | Como Evitar |
|---|---|---|
| Desajuste nos limites de tokens | Seus contadores de tokens estão incorretos devido ao desajuste entre o tokenizador ou modelo; você pode ultrapassar ou ficar aquém do número de tokens | Sempre use tiktoken.encoding_for_model() que corresponda exatamente ao modelo de embedding ou de completions que você utiliza |
| Embeddings duplicados | Embeddar o mesmo trecho várias vezes desperdiça tokens e armazenamento | Implemente caching e hashing de checksum de documentos |
| Erros de sobreposição de trechos | Uma sobreposição muito alta cria vetores redundantes; uma sobreposição muito baixa perde contexto em documentos divididos | Experimente o parâmetro chunk_overlap, mantendo entre 10-15% do tamanho do trecho |
| Deadlocks de concorrência | Várias chamadas assíncronas para ChromaDB ou OpenAI podem causar condições de corrida ou inserções parciais | Use código síncrono ou enfileiramento assíncrono adequado; inserções em lote protegem você disso |
| Contagem de tokens não confiável em textos não ingleses | Tokenizers podem contar incorretamente devido a caracteres de múltiplos bytes ou scripts incomuns | Teste com idiomas representativos, ajuste os tamanhos dos trechos de acordo |
Exemplo Completo de Código: Juntando Tudo
Esse exemplo combina todos os passos anteriores em um pipeline funcional:
import tiktoken
from chromadb import Client
from openai import OpenAI
from langchain.text_splitter import RecursiveCharacterTextSplitter
from sklearn.feature_extraction.text import TfidfVectorizer
import numpy as np
# Constantes
MODEL_NAME = "text-embedding-3-small"
EMBEDDING_BATCH_SIZE = 50
CHUNK_SIZE = 500
CHUNK_OVERLAP = 50
# Configuração
client = Client()
collection = client.get_or_create_collection(name="mydocs")
openai_client = OpenAI()
tokenizer = tiktoken.encoding_for_model(MODEL_NAME)
def count_tokens(text):
return len(tokenizer.encode(text))
def chunk_text(text):
splitter = RecursiveCharacterTextSplitter(
chunk_size=CHUNK_SIZE,
chunk_overlap=CHUNK_OVERLAP,
length_function=count_tokens
)
return splitter.split_text(text)
def filter_chunks(chunks, query, keep_ratio=0.4):
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(chunks + [query])
query_vec = X[-1]
chunk_vecs = X[:-1]
sims = (chunk_vecs @ query_vec.T).toarray().flatten()
thresh = np.quantile(sims, 1 - keep_ratio)
return [chunk for chunk, sim in zip(chunks, sims) if sim >= thresh]
def batch(iterable, n=1):
l = len(iterable)
for ndx in range(0, l, n):
yield iterable[ndx:min(ndx + n, l)]
def embed_and_index(chunks):
for chunk_batch in batch(chunks, EMBEDDING_BATCH_SIZE):
embeddings = []
for chunk in chunk_batch:
response = openai_client.embeddings.create(input=chunk)
embeddings.append(response.data[0].embedding)
ids = [str(hash(chunk) % (10**8)) for chunk in chunk_batch]
collection.add(documents=chunk_batch, embeddings=embeddings, ids=ids)
print(f"Lote indexado com {len(chunk_batch)} trechos")
# Pipeline principal
doc_text = open("messy_doc.txt").read()
print("Dividindo documento...")
chunks = chunk_text(doc_text)
print(f"Gerados {len(chunks)} trechos")
# Simulando uma consulta de exemplo
query = "Como o uso de tokens é otimizado com ChromaDB?"
print("Filtrando trechos antes de embeddar...")
chunks_to_embed = filter_chunks(chunks, query)
print(f"Embeddando e indexando {len(chunks_to_embed)} trechos...")
embed_and_index(chunks_to_embed)
print("Pipeline completo.")
O Que Vem a Seguir
Se você conseguiu implementar isso com sucesso, seu próximo passo deve ser integrar a recuperação classificada com as completions do GPT da OpenAI para construir um sistema de geração aumentada de recuperação (RAG) que obtém respostas do seu banco de dados vetorial com contas de tokens minimizadas.
Especificamente, concentre-se em combinar suas consultas do ChromaDB com um gerador de respostas que trunque contextos de forma inteligente, aplicando idealmente reformulação de consultas para reduzir o uso de tokens na engenharia de prompts.
Perguntas Frequentes
P: Por que não enviar o documento inteiro para a API de embedding de uma vez?
A: Porque os limites de tokens da API de embedding geralmente são limitados a alguns milhares de tokens—muito pouco para documentos grandes—e você paga linearmente por token, então desperdiça dinheiro e obtém embeddings menos precisos que prejudicam a precisão da recuperação.
P: Como lido com documentos que mudam com frequência usando ChromaDB?
A: Você precisa implementar embeddings delta—apenas re-embeddar novos ou trechos alterados. Uma abordagem ingênua é hashear o texto de cada trecho e comparar com os IDs armazenados para saber o que atualizar.
P: Como posso depurar contagens de tokens incorretas no meu aplicativo?
A: Use a biblioteca oficial tiktoken da OpenAI e certifique-se de que o nome do modelo em encoding_for_model() corresponda exatamente ao que você envia para a API. Verifique sua contagem imprimindo arrays de tokens e verificando os tamanhos dos trechos.
Recomendações para Diferentes Perfis de Desenvolvedores
Desenvolvedores Juniores: Comece entendendo os limites de tokens e a fragmentação. Não tente otimizar tudo de uma vez. Seu foco deve estar em dividir documentos em trechos de ~500 tokens e garantir que suas chamadas de API funcionem sem erros.
Desenvolvedores em Nível Intermediário: Implemente filtragem e agrupamento em seguida. Use TF-IDF para pré-selecionar quais trechos embeddar. Adicione caching para que você não re-embedde duplicatas. Nesta fase, você economizará dinheiro e tempo reais.
Engenheiros Seniores: Automatize o monitoramento de tokens de ponta a ponta. Crie painéis que alertem quando o uso aumentar. Explore fragmentadores personalizados ajustados ao seu tipo de documento. Integre métricas de uso com painéis de custo e experimente a engenharia de prompts para truncamento de contexto.
Dados atualizados até 22 de março de 2026. Fontes: https://github.com/chroma-core/chroma, https://community.openai.com/t/issue-chromadb-document-and-token-openai-limitations/317378
Artigos Relacionados
- Recursos de segurança do kit de ferramentas de agentes de IA
- Opções de código aberto do kit de ferramentas de agentes de IA
- Guia do kit de ferramentas AutoGen
🕒 Published: