Embeddings e Busca Semântica com Ollama, Qwen e RAG

10 min de leitura IA Generativa
Embeddings e Busca Semântica com Ollama, Qwen e RAG

O que são Embeddings e Por Que Eles Importam na Busca Semântica

Na era da Inteligência Artificial Generativa, a capacidade de fazer perguntas em linguagem natural e receber respostas precisas não depende apenas do tamanho do modelo de linguagem (LLM), mas fundamentalmente de como os dados são estruturados e recuperados. O conceito central que viabiliza essa revolução é o embedding.

Um embedding é uma representação vetorial de dados — texto, imagens ou áudio — convertida em uma lista de números (um array de floats) que captura o significado semântico do conteúdo. Em vez de buscar por palavras-chave exatas, como faz a busca tradicional baseada em índices inversos, a busca semântica compara a distância matemática entre esses vetores. Se dois textos têm significados semelhantes, mesmo sem compartilhar palavras, seus vetores estarão próximos no espaço multidimensional.

Para profissionais de TI que gerenciam infraestrutura local via VPS e utilizam ferramentas como Ollama, LM Studio ou Open WebUI, entender embeddings é crucial para implementar sistemas robustos de RAG (Retrieval-Augmented Generation). Este tutorial explica o funcionamento técnico dos embeddings, demonstra como gerar vetores usando modelos open-source como Qwen e Llama, e guia você na criação de um pipeline de busca semântica utilizando o banco de dados vetorial Qdrant.

A Matemática por Trás da Semântica

Para entender a implementação, é necessário compreender brevemente a mecânica. Um modelo de linguagem processa texto e extrai características latentes. Essas características são projetadas em um espaço vetorial de alta dimensão (por exemplo, 768, 1024 ou 3072 dimensões).

O algoritmo treina o modelo para garantir que:

  • Vetores de conceitos semelhantes tenham cosseno de similaridade alto.
  • Vetores de conceitos distintos estejam distantes uns dos outros.

Quando você realiza uma busca, o sistema transforma sua consulta em um vetor e encontra os vetores do banco de dados mais próximos geometricamente. Isso permite que a frase "Qual é a política de reembolso?" encontre documentos sobre "Como devolver meu dinheiro", mesmo que as palavras sejam completamente diferentes.

Passo 1: Preparando o Ambiente com Ollama

O Ollama é a ferramenta padrão da indústria para rodar LLMs localmente em Linux, macOS e Windows. Ele gerencia downloads de modelos, otimiza o uso de GPU/CPU e expõe uma API REST simples, perfeita para integração com aplicações Python ou scripts shell.

Certifique-se de que o Ollama está instalado e rodando em sua VPS. Se ainda não o fez, execute:

curl -fsSL https://ollama.com/install.sh | sh

Após a instalação, puxe um modelo eficiente para geração de embeddings. Embora modelos de linguagem como Llama 3 ou Qwen 2.5 sejam excelentes para chat, existem modelos específicos otimizados para embedding que oferecem melhor qualidade por token. No entanto, para este tutorial, utilizaremos a capacidade nativa de codificação de texto do Qwen 2.5 via Ollama, que é robusta e rápida.

ollama pull qwen2.5:7b

Passo 2: Gerando Embeddings com Python e a API do Ollama

Vamos criar um script Python simples para converter texto em vetores. Primeiro, instale as dependências necessárias:

pip install ollama numpy

Crie um arquivo chamado generate_embeddings.py e insira o seguinte código. Este script usa a API local do Ollama para gerar vetores de duas frases semanticamente relacionadas, mas lexicalmente distintas.

import ollama
import numpy as np

def get_embedding(text):
    """
    Gera um embedding usando o modelo Qwen via Ollama.
    Retorna uma lista de floats normalizada.
    """
    response = ollama.embeddings(
        model="qwen2.5:7b",
        prompt=text
    )
    return np.array(response['embedding'])

# Exemplo 1: Consulta do usuário
query = "Como posso cancelar minha assinatura mensal?"

# Exemplo 2: Documento relevante (política de reembolso)
document_1 = "Política de Devolução: Você pode solicitar o estorno do valor pago em até 30 dias."

# Exemplo 3: Documento irrelevante
document_2 = "Nosso escritório está localizado na Rua das Flores, número 123."

# Gerando vetores
vec_query = get_embedding(query)
vec_doc_1 = get_embedding(document_1)
vec_doc_2 = get_embedding(document_2)

print(f"Dimensão do vetor: {len(vec_query)}")

Ao executar este script, você verá que o modelo converteu strings complexas em arrays numéricos fixos. A chave aqui é a consistência: o mesmo texto sempre gerará o mesmo vetor, e textos semelhantes terão vetores semelhantes.

Passo 3: Calculando Similaridade

Agora que temos os vetores, precisamos quantificar a similaridade. A métrica mais comum para buscas semânticas é a similaridade do cosseno. Ela mede o ângulo entre dois vetores, ignorando sua magnitude, focando apenas na direção (o significado).

Adicione esta função ao seu script Python:

def cosine_similarity(vec1, vec2):
    """Calcula a similaridade do cosseno entre dois vetores."""
    dot_product = np.dot(vec1, vec2)
    norm1 = np.linalg.norm(vec1)
    norm2 = np.linalg.norm(vec2)
    return dot_product / (norm1 * norm2)

# Comparando a consulta com os documentos
similarity_1 = cosine_similarity(vec_query, vec_doc_1)
similarity_2 = cosine_similarity(vec_query, vec_doc_2)

print(f"Similaridade com Política de Devolução: {similarity_1:.4f}")
print(f"Similaridade com Endereço do Escritório: {similarity_2:.4f}")

O resultado mostrará um valor próximo a 0.8 ou superior para o documento relevante e um valor muito baixo (próximo de 0) para o irrelevante. Isso prova que o modelo entendeu a intenção semântica, mesmo sem palavras-chave em comum.

Passo 4: Persistindo Dados com Qdrant

Em um cenário real, você não calculará embeddings sob demanda para milhões de documentos. Você os pré-calcula e armazena em um banco de dados vetorial. O Qdrant é uma solução open-source, escrita em Rust, altamente performática e ideal para rodar em VPS.

Inicie o Qdrant usando Docker Compose para facilitar a gestão. Crie um arquivo docker-compose.yml:

version: '3.8'
services:
  qdrant:
    image: qdrant/qdrant:latest
    ports:
      - "6333:6333" # Porta HTTP API
      - "6334:6334" # Porta gRPC
    volumes:
      - ./qdrant_storage:/qdrant/storage
    restart: unless-stopped

Inicie o serviço:

docker compose up -d

Agora, vamos integrar o Qdrant ao nosso pipeline. Instale a biblioteca oficial do Qdrant para Python:

pip install qdrant-client

Passo 5: Implementando o Pipeline Completo de RAG

Crie um novo script, rag_pipeline.py, que conecta o Ollama (geração) ao Qdrant (armazenamento e busca).

import ollama
import numpy as np
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct

# 1. Conexão com o Qdrant local
client = QdrantClient(url="http://localhost:6333")

# Nome da coleção (equivalente a um banco de dados ou tabela)
COLLECTION_NAME = "meus_documentos"

# 2. Criar a coleção e definir a dimensão do vetor
# A dimensão deve corresponder à saída do modelo Qwen
EMBEDDING_DIM = 3072 

client.create_collection(
    collection_name=COLLECTION_NAME,
    vectors_config=VectorParams(size=EMBEDDING_DIM, distance=Distance.COSINE)
)

def get_embedding(text):
    response = ollama.embeddings(
        model="qwen2.5:7b",
        prompt=text
    )
    return np.array(response['embedding'])

# 3. Inserir documentos na coleção
documents = [
    {"id": 1, "text": "Como posso cancelar minha assinatura mensal?"},
    {"id": 2, "text": "Política de Devolução: Estornos em até 30 dias."},
    {"id": 3, "text": "Nosso escritório fica na Rua das Flores, 123."}
]

points = []
for doc in documents:
    vec = get_embedding(doc["text"])
    points.append(PointStruct(id=doc["id"], vector=vec.tolist(), payload={"text": doc["text"]}))

client.upsert(collection_name=COLLECTION_NAME, points=points)

# 4. Realizar a busca semântica
query_text = "Quero devolver o dinheiro pago"
query_vector = get_embedding(query_text).tolist()

results = client.search(
    collection_name=COLLECTION_NAME,
    query_vector=query_vector,
    limit=2 # Retorna os 2 resultados mais similares
)

print("Resultados da Busca Semântica:")
for r in results:
    print(f"Score: {r.score:.4f} | Texto: {r.payload['text']}")

Ao executar este script, o Qdrant retornará os dois primeiros documentos como os mais relevantes para a consulta "Quero devolver o dinheiro pago", ignorando o endereço do escritório. O campo score indica a confiança da similaridade.

Otimizações Avançadas: Fine-Tuning e Modelos Especializados

Embora modelos gerais como Llama 3 e Qwen funcionem bem, cenários corporativos específicos podem exigir maior precisão. Existem duas abordagens principais para melhorar a qualidade dos embeddings:

  1. Fine-tuning de Embeddings: Diferente do fine-tuning de chat (que ajusta os pesos do modelo para seguir instruções), o fine-tuning de embedding ajusta o modelo para que vetores de pares de dados relacionados sejam ainda mais próximos. Isso requer um conjunto de dados de treinamento com pares positivos e negativos.
  2. Uso de Modelos Especializados: Para tarefas críticas, considere modelos como nomic-embed-text ou BGE-M3, que são treinados especificamente para recuperação de informação e frequentemente superam LLMs gerais em métricas de precisão semântica.

Se você estiver usando Ollama, pode trocar o modelo facilmente alterando a linha model="qwen2.5:7b" para model="nomic-embed-text:v1.5", desde que o modelo esteja puxado.

Integração com Open WebUI e Aplicações Reais

O poder real desse pipeline se revela quando integrado a interfaces de usuário. Ferramentas como Open WebUI permitem conectar fontes de dados externas (PDFs, bancos de texto) ao seu LLM local.

Para configurar isso em uma infraestrutura self-hosted:

  1. Processamento de Dados: Use scripts Python para ler seus documentos, quebrá-los em chunks (fragmentos) de tamanho adequado (ex: 500 tokens) e gerar embeddings usando o Ollama.
  2. Armazenamento: Envie esses vetores para o Qdrant ou Pinecone (se preferir managed).
  3. Consulta: Configure o Open WebUI ou sua aplicação customizada para consultar o Qdrant antes de enviar a pergunta ao LLM. O contexto recuperado é injetado no prompt do LLM.

Essa arquitetura garante que seu LLM responda com base em dados atualizados e específicos da sua empresa, reduzindo alucinações e mantendo a privacidade dos dados dentro da sua VPS.

Conclusão

A busca semântica baseada em embeddings transforma dados brutos em conhecimento acessível por linguagem natural. Ao combinar a facilidade do Ollama, a robustez de modelos como Qwen e Llama, e a performance do Qdrant, você constrói uma infraestrutura de IA privada, escalável e altamente precisa.

Este tutorial forneceu os blocos de construção fundamentais: geração de vetores, cálculo de similaridade e persistência em banco de dados vetorial. O próximo passo é integrar isso ao seu stack atual, seja para criar um chatbot interno, um motor de recomendação ou um sistema de suporte técnico inteligente.

Lembre-se: a qualidade dos embeddings depende da qualidade do modelo e da relevância dos dados inseridos. Monitore os scores de similaridade e ajuste seus chunks de texto para obter os melhores resultados em seu ambiente self-hosted.

Compartilhar: Link copiado!
Esse tutorial foi útil?

Comentários (0)

Seja o primeiro a comentar.

Deixe seu comentário

Seu comentário será analisado antes de ser publicado.

0/2000