Como criar RAG com Milvus, QwQ-32B e Ollama
Os modelos de IA estão evoluindo rapidamente, e o QwQ-32B da Alibaba vem fazendo uma entrada forte recentemente. Com apenas 32 bilhões de parâmetros, este modelo de raciocínio de porte médio oferece desempenho impressionante em raciocínio matemático, escrita criativa e geração de código — rivalizando com modelos muito maiores como o DeepSeek-R1. Sua eficiência e precisão em benchmarks o tornam uma opção atraente para uma ampla variedade de aplicações de IA.
11.jpeg
Figura 1: Desempenho do QwQ-32B em comparação com outros modelos líderes (Fonte)
Além de suas capacidades, o QwQ-32B se destaca por sua acessibilidade. Ao contrário de alguns modelos massivos que exigem hardware especializado, ele roda com eficiência em GPUs de nível consumidor, como a RTX 4090, tornando-se uma excelente escolha para desenvolvedores e pesquisadores que buscam IA de alta qualidade sem recursos em escala empresarial. No entanto, por ser um modelo denso, o QwQ-32B às vezes pode ter dificuldade com raciocínio complexo em textos longos e pode apresentar alucinações, particularmente ao lidar com janelas de contexto extensas.
Para mitigar esses desafios e aumentar sua confiabilidade, podemos integrar o QwQ-32B com Geração Aumentada por Recuperação (RAG). Neste tutorial, vamos percorrer como criar um sistema RAG usando QwQ-32B, Milvus (um banco de dados vetorial de alto desempenho) e Ollama. Ao final, você terá um pipeline de IA simplificado e poderoso que equilibra eficiência, precisão e escalabilidade.
Antes de entrarmos em detalhes sobre como criar uma aplicação RAG, vamos passar rapidamente por todas as tecnologias que usaremos neste tutorial.
QwQ-32B vs. DeepSeek-R1
Tanto o QwQ-32B quanto o DeepSeek-R1 são especializados em raciocínio, mas este último adota uma arquitetura Mixture-of-Experts (MoE), enquanto o QwQ-32B é um modelo denso clássico.
- Modelos MoE se destacam em cenários intensivos em conhecimento (por exemplo, sistemas de P&R, recuperação de informações) e processamento de dados em larga escala, onde diferentes especialistas lidam com subconjuntos distintos de dados para melhorar a eficiência. No entanto, sua enorme contagem de parâmetros exige recursos de nuvem ou servidores dedicados.
- Modelos densos, embora computacionalmente intensivos, são mais adequados para tarefas de raciocínio profundo e coerente (por exemplo, raciocínio lógico complexo, compreensão de leitura aprofundada) e design de algoritmos em que o desempenho em tempo real não é indispensável. Seu tamanho compacto permite a implantação local, mas às vezes pode produzir mensagens redundantes e desnecessárias.
| Modelo Denso (QwQ-32B) | Modelo MoE (DeepSeek-R1) | |
|---|---|---|
| Vantagens | Menor complexidade de treinamento; processo direto | Alta eficiência computacional (ativa especialistas parciais durante a inferência) |
| Vantagens | Raciocínio coerente; engajamento completo dos neurônios para compreensão contextual | Capacidade escalável do modelo via expansão de especialistas |
| Desvantagens | Altos custos computacionais para treinamento e inferência | Treinamento complexo (requer redes de roteamento e balanceamento de carga de especialistas) |
| Desvantagens | Escalabilidade limitada; propenso a overfitting; altos custos de armazenamento/implantação | Sobrecarga de roteamento (computação adicional para decisões de roteamento) |
Nenhuma das arquiteturas é perfeita. A escolha deve depender dos requisitos da tarefa, das características dos dados, dos recursos computacionais disponíveis e das restrições orçamentárias.
Acredito que veremos abordagens híbridas em um futuro próximo—usando MoE para recuperação inicial de conhecimento e processamento grosseiro, e depois modelos densos para raciocínio profundo e refinamento, a fim de alcançar melhores desempenhos.
Por que Milvus?
Milvus é um banco de dados vetorial de código aberto, alto desempenho e altamente escalável que pode armazenar, indexar e pesquisar dados não estruturados em escala de bilhões por meio de incorporações vetoriais de alta dimensão. Ele é perfeito para criar aplicações modernas de IA, como geração aumentada por recuperação (RAG), busca semântica, busca multimodal e sistemas de recomendação.
Para mitigar as possíveis alucinações do QwQ-32B (na verdade, as possíveis alucinações dos LLMs), o Milvus armazena conhecimento externo ou privado e fornece informações contextuais com o modelo QwQ-32B. Isso garante que o modelo QwQ-32B possa gerar resultados mais precisos.
Por que Ollama?
Ollama é uma plataforma de código aberto que simplifica a implantação e o gerenciamento locais de grandes modelos de linguagem (LLMs). Ela oferece uma experiência amigável e sem nuvem, permitindo downloads, instalação e interação com modelos sem esforço e sem exigir habilidades técnicas avançadas. Ela permite que os usuários implantem modelos rapidamente por meio de ferramentas simples de linha de comando e integração com Docker, e oferece suporte ao gerenciamento de Modelfile para simplificar o controle de versões e a reutilização de modelos.
Além disso, o Ollama oferece uma rica biblioteca de modelos—desde modelos de uso geral até específicos de domínio. Ele oferece compatibilidade entre plataformas e hardware—com suporte a macOS, Linux, Windows e implantações em contêineres Docker—com detecção automática de GPU e priorização de aceleração. Ele também fornece ferramentas amigáveis para desenvolvedores, como REST API e Python SDK, facilitando a integração de modelos em diversas aplicações.
E ele garante privacidade de dados e flexibilidade, capacitando os usuários a ajustar, otimizar e implantar soluções orientadas por IA inteiramente em suas máquinas.
Agora, vamos começar a construir um pipeline RAG simples com QwQ-32B como o modelo de linguagem, Milvus como o banco de dados vetorial e Ollama como o framework.
Preparação
Dependências e Ambiente
! pip install pymilvus ollama
Observação: Se você estiver usando o Google, para habilitar as dependências recém-instaladas, talvez seja necessário reiniciar o runtime (clique no menu "Runtime" na parte superior da tela e selecione "Restart session" no menu suspenso).
Preparar os dados
Usamos as páginas de FAQ da Documentação do Milvus 2.4.x como conhecimento privado em nosso RAG, que é uma boa fonte de dados para um pipeline RAG simples.
Baixe o arquivo zip e extraia os documentos para a pasta milvus_docs.
! wget https://github.com/milvus-io/milvus-docs/releases/download/v2.4.6-preview/milvus_docs_2.4.x_en.zip
! unzip -q milvus_docs_2.4.x_en.zip -d milvus_docs
Carregamos todos os arquivos markdown da pasta milvus_docs/en/faq. Para cada documento, simplesmente usamos "# " para separar o conteúdo no arquivo, o que pode separar aproximadamente o conteúdo de cada parte principal do arquivo markdown.
from glob import glob
text_lines = []
for file_path in glob("milvus_docs/en/faq/*.md", recursive=True):
with open(file_path, "r") as file:
file_text = file.read()
text_lines += file_text.split("# ")
Preparar o LLM e o Modelo de Embedding
Ollama oferece suporte a vários modelos tanto para tarefas baseadas em LLM quanto para geração de embeddings, facilitando o desenvolvimento de aplicações RAG. Para esta configuração:
- Usaremos QwQ (32B) como nosso LLM para tarefas de geração de texto.
- Para a geração de embeddings, usaremos mxbai-embed-large, um modelo de 334 milhões de parâmetros otimizado para similaridade semântica.
Antes de começar, certifique-se de que ambos os modelos foram baixados localmente:
! ollama pull mxbai-embed-large
! ollama pull qwq
Com esses modelos prontos, podemos prosseguir para implementar fluxos de trabalho de geração orientada por LLM e recuperação baseada em embeddings.
import ollama
from ollama import Client
ollama_client = Client(host="http://localhost:11434")
def emb_text(text):
response = ollama_client.embeddings(model="mxbai-embed-large", prompt=text)
return response["embedding"]
Gere um embedding de teste e imprima sua dimensão e os primeiros elementos.
test_embedding = emb_text("This is a test")
embedding_dim = len(test_embedding)
print(embedding_dim)
print(test_embedding[:10])
1024
[0.23217937350273132, 0.42540550231933594, 0.19742339849472046, 0.4618139863014221, -0.46017369627952576, -0.14087969064712524, -0.18214142322540283, -0.07724273949861526, 0.40015509724617004, 0.8331164121627808]
Carregar dados no Milvus
Criar a coleção
from pymilvus import MilvusClient
milvus_client = MilvusClient(uri="./milvus_demo.db")
collection_name = "my_rag_collection"
Quanto à configuração dos parâmetros do MilvusClient:
- Definir o
uricomo um arquivo local, por exemplo,./milvus.db, é o método mais conveniente, pois utiliza automaticamente o Milvus Lite para armazenar todos os dados nesse arquivo. - Se você tiver uma grande escala de dados, poderá configurar um servidor Milvus mais performático em docker ou kubernetes. Nesta configuração, use o
urido servidor, por exemplo,http://localhost:19530, como seuuri. - Se quiser usar o Zilliz Cloud, o serviço de nuvem totalmente gerenciado para Milvus, ajuste o
urie otoken, que correspondem ao Endpoint público e chave de API no Zilliz Cloud.
Verifique se a coleção já existe e remova-a se existir.
if milvus_client.has_collection(collection_name):
milvus_client.drop_collection(collection_name)
Crie uma nova coleção com parâmetros especificados.
Se não especificarmos nenhuma informação de campo, o Milvus criará automaticamente um campo id padrão para a chave primária e um campo vector para armazenar os dados vetoriais. Um campo JSON reservado é usado para armazenar campos não definidos no esquema e seus valores.
milvus_client.create_collection(
collection_name=collection_name,
dimension=embedding_dim,
metric_type="IP", # Inner product distance
consistency_level="Strong", # Strong consistency level
)
Inserir dados
Itere pelas linhas de texto, crie embeddings e então insira os dados no Milvus.
Aqui há um novo campo text, que é um campo não definido no esquema da coleção. Ele será adicionado automaticamente ao campo dinâmico JSON reservado, que pode ser tratado como um campo normal em alto nível.
from tqdm import tqdm
data = []
for i, line in enumerate(tqdm(text_lines, desc="Creating embeddings")):
data.append({"id": i, "vector": emb_text(line), "text": line})
milvus_client.insert(collection_name=collection_name, data=data)
Creating embeddings: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████| 72/72 [00:06<00:00, 11.86it/s]
{'insert_count': 72, 'ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71], 'cost': 0}
Construir um pipeline RAG
Recupere dados para uma consulta
Vamos especificar uma pergunta frequente sobre o Milvus.
question = "How is data stored in milvus?"
Pesquise a pergunta na coleção e recupere as 3 principais correspondências semânticas.
search_res = milvus_client.search(
collection_name=collection_name,
data=[
emb_text(question)
], # Use the `emb_text` function to convert the question to an embedding vector
limit=3, # Return top 3 results
search_params={"metric_type": "IP", "params": {}}, # Inner product distance
output_fields=["text"], # Return the text field
)
Vamos dar uma olhada nos resultados da pesquisa da consulta.
import json
retrieved_lines_with_distances = [
(res["entity"]["text"], res["distance"]) for res in search_res[0]
]
print(json.dumps(retrieved_lines_with_distances, indent=4))
[
[
" Where does Milvus store data?\n\nMilvus deals with two types of data, inserted data and metadata. \n\nInserted data, including vector data, scalar data, and collection-specific schema, are stored in persistent storage as incremental log. Milvus supports multiple object storage backends, including [MinIO](https://min.io/), [AWS S3](https://aws.amazon.com/s3/?nc1=h_ls), [Google Cloud Storage](https://cloud.google.com/storage?hl=en#object-storage-for-companies-of-all-sizes) (GCS), [Azure Blob Storage](https://azure.microsoft.com/en-us/products/storage/blobs), [Alibaba Cloud OSS](https://www.alibabacloud.com/product/object-storage-service), and [Tencent Cloud Object Storage](https://www.tencentcloud.com/products/cos) (COS).\n\nMetadata are generated within Milvus. Each Milvus module has its own metadata that are stored in etcd.\n\n###",
231.9922637939453
],
[
"How does Milvus flush data?\n\nMilvus returns success when inserted data are loaded to the message queue. However, the data are not yet flushed to the disk. Then Milvus' data node writes the data in the message queue to persistent storage as incremental logs. If `flush()` is called, the data node is forced to write all data in the message queue to persistent storage immediately.\n\n###",
226.54090881347656
],
[
"What is the maximum dataset size Milvus can handle?\n\n \nTheoretically, the maximum dataset size Milvus can handle is determined by the hardware it is run on, specifically system memory and storage:\n\n- Milvus loads all specified collections and partitions into memory before running queries. Therefore, memory size determines the maximum amount of data Milvus can query.\n- When new entities and and collection-related schema (currently only MinIO is supported for data persistence) are added to Milvus, system storage determines the maximum allowable size of inserted data.\n\n###",
210.63682556152344
]
]
Use LLM para obter uma resposta RAG
Converta os documentos recuperados em um formato de string.
context = "\n".join(
[line_with_distance[0] for line_with_distance in retrieved_lines_with_distances]
)
Defina prompts de sistema e de usuário para o LLM. Este prompt é montado com os documentos recuperados do Milvus.
SYSTEM_PROMPT = """
Human: You are an AI assistant. You are able to find answers to the questions from the contextual passage snippets provided.
"""
USER_PROMPT = f"""
Use the following pieces of information enclosed in <context> tags to provide an answer to the question enclosed in <question> tags.
<context>
{context}
</context>
<question>
{question}
</question>
"""
Use o modelo QwQ (32B) fornecido pelo Ollama para gerar uma resposta com base nos prompts.
from ollama import chat
from ollama import ChatResponse
response: ChatResponse = ollama_client.chat(
model="qwq",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": USER_PROMPT},
],
)
print(response["message"]["content"])
<think>
Certo, preciso responder à pergunta "Como os dados são armazenados no Milvus?" usando o contexto fornecido. Vou começar lendo o contexto com atenção.
Primeiro, olhando para a primeira seção do contexto intitulada "Onde o Milvus armazena dados?". Ela menciona que o Milvus lida com dois tipos de dados: dados inseridos e metadados.
Os dados inseridos incluem vetores, dados escalares e esquema da coleção, armazenados como logs incrementais em armazenamento persistente. Os backends compatíveis são listados, como MinIO, S3, GCS etc. Então devo observar que os dados inseridos vão para esses armazenamentos de objetos por meio de logs incrementais.
Depois, os metadados são armazenados no etcd para cada módulo do Milvus. Isso é direto — metadados no etcd.
O segundo contexto sobre a descarga de dados explica que os dados primeiro vão para a fila de mensagens e depois para o armazenamento persistente quando são descarregados. Mas isso talvez seja mais sobre o processo do que sobre onde eles são armazenados, então talvez mencionar que a inserção ocorre primeiro na fila de mensagens antes de serem gravados.
A terceira seção sobre o tamanho máximo do conjunto de dados menciona restrições de hardware: memória para dados consultáveis e armazenamento para persistência. Isso pode se relacionar a como os dados são gerenciados em termos de memória vs. disco. O usuário perguntou "como os dados são armazenados", o que pode incluir tanto onde (como armazenamentos de objetos, etcd) quanto o processo (fila de mensagens e depois logs persistentes).
Juntando tudo, a resposta deve mencionar:
- Dois tipos: dados inseridos e metadados.
Para dados inseridos:
- Armazenados como logs incrementais em armazenamento persistente usando backends como MinIO, S3 etc.
- Inicialmente carregados em filas de mensagens, depois gravados em disco. O flush() força a gravação imediata, se necessário.
Metadados:
- Armazenados no etcd para cada módulo.
Além disso, a parte de hardware diz que a memória limita o tamanho dos dados consultáveis e o armazenamento (como os armazenamentos de objetos) limita o tamanho total do conjunto de dados. Então talvez mencionar esses fatores como parte de como os dados são armazenados, considerando as restrições de capacidade.
Devo estruturar isso claramente, talvez com marcadores ou seções sobre dados inseridos vs. metadados, depois processos como descarga e, por fim, limitações baseadas em hardware.
</think>
Os dados no Milvus são armazenados de duas formas principais:
1. **Dados Inseridos**:
- Incluem dados vetoriais, dados escalares e esquema da coleção.
- Armazenados como **logs incrementais** em **armazenamento persistente**, com suporte a backends de armazenamento de objetos como MinIO, AWS S3, Google Cloud Storage (GCS), Azure Blob Storage, Alibaba Cloud OSS e Tencent Cloud Object Storage (COS).
- Inicialmente carregados em uma **fila de mensagens** quando inseridos. O Milvus retorna sucesso após a inserção na fila, mas não grava imediatamente no disco. Um processo em segundo plano posteriormente grava esses dados no armazenamento persistente. O método `flush()` pode forçar a gravação imediata de todos os dados enfileirados no armazenamento.
2. **Metadados**:
- Gerados internamente pelos módulos do Milvus (por exemplo, configurações de coleção, partições).
- Armazenados no **etcd**, um armazenamento distribuído de chave-valor.
**Considerações de Hardware**:
- **Memória**: A quantidade de dados que o Milvus pode consultar é limitada pela memória do sistema, pois ele carrega coleções/partições especificadas na memória para consultas.
- **Capacidade de Armazenamento**: O tamanho máximo do conjunto de dados é restringido pelo backend de armazenamento subjacente (por exemplo, armazenamento de objetos), que armazena todos os dados inseridos e o esquema de forma incremental.
Ótimo! Construímos com sucesso um pipeline RAG com Milvus, QWQ-32B e Ollama.
Conclusão
Ao integrar essas tecnologias, podemos construir um sistema RAG que aproveita o Milvus para armazenamento e recuperação eficientes de dados e as capacidades de raciocínio do QwQ-32B para gerar respostas precisas e contextualmente relevantes. O Ollama simplifica o processo de implantação, permitindo uma configuração fluida e eficiente. Essa combinação é particularmente benéfica para aplicações que exigem recuperação e geração de informações em tempo real, como tutoria assistida por IA, resolução de problemas baseada em lógica e muito mais.
Esperamos que, ao seguir este tutorial, você possa criar sistemas RAG adaptados às suas necessidades e se beneficiar verdadeiramente de suas próprias criações.
Continue lendo

Why Teams Are Migrating from Weaviate to Zilliz Cloud — and How to Do It Seamlessly
Explore how Milvus scales for large datasets and complex queries with advanced features, and discover how to migrate from Weaviate to Zilliz Cloud.

Announcing the General Availability of Zilliz Cloud BYOC on Google Cloud Platform
Zilliz Cloud BYOC on GCP offers enterprise vector search with full data sovereignty and seamless integration.

DeepSeek-VL2: Mixture-of-Experts Vision-Language Models for Advanced Multimodal Understanding
Explore DeepSeek-VL2, the open-source MoE vision-language model. Discover its architecture, efficient training pipeline, and top-tier performance.



