Como criar um agente de IA com LangChain, Qdrant e Docker em VPS
-
Maicon Ramos
- agentes de IA, Docker, LangChain, Qdrant, RAG, VPS
- 13 minutos de leitura
Navegue por tópicos
Este tutorial cria um agente mínimo com RAG: ele indexa documentos, recupera trechos no Qdrant e usa uma ferramenta do LangChain para responder. Docker mantém aplicação e banco separados, enquanto o volume preserva dados. O exemplo não foi executado neste artigo: valide-o com seu provider, corpus e versão das dependências antes de usar em produção.
Criar um agente não é subir três containers e esperar autonomia. O primeiro resultado útil é menor: recuperar o documento certo, manter o índice após reiniciar e saber restaurá-lo.
Aqui você vai montar esse caminho com LangChain, Qdrant e Docker Compose em uma VPS. O exemplo usa um provider de embeddings e chat por API. Ele não promete precisão, custo mensal, capacidade ou segurança automática.
A ideia central é o Limiar do Segundo Serviço. Qdrant deixa de ser complexidade extra quando resolve recuperação persistente, filtros ou uma arquitetura necessária. Antes disso, pode ser mais um processo para atualizar, proteger e restaurar.
Quando esta stack faz sentido
💡 Vai rodar n8n numa VPS? A gente comparou o preço real em cada provedor — com renovação e requisitos — em VPS para n8n.
Use esta stack quando sua aplicação precisa consultar documentos próprios, manter um índice entre reinícios e separar a busca vetorial da aplicação. Ela também ajuda quando filtros e payloads fazem parte da recuperação.
LangChain não é o modelo. A documentação descreve create_agent como um harness configurável, composto por modelo, ferramentas, prompt e middleware. Para fluxos mais controláveis, o projeto aponta o LangGraph como framework separado. Consulte a documentação de agentes do LangChain antes de escolher a abstração.
RAG também não é sinônimo de agente. RAG recupera contexto antes da resposta. Um agente pode usar RAG como ferramenta, mas um chatbot que busca documentos não precisa de memória longa ou várias ferramentas para ser útil.
Não comece por Qdrant dedicado se você ainda valida uma ideia ou tem poucos documentos. O Qdrant oferece modo embutido para Python e Rust. O pgvector acrescenta busca vetorial a um PostgreSQL já existente. Essas opções reduzem a operação inicial.
| Camada | Faz | Não faz | O que proteger |
|---|---|---|---|
| LangChain | Compõe modelo, ferramentas, prompt e middleware. | Não é o modelo nem o banco vetorial. | Configuração da aplicação. |
| Provider de IA | Gera embeddings e respostas. | Não grava o índice automaticamente no Qdrant. | Chave de API. |
| Qdrant | Armazena vetores e payloads. | Não gera embeddings. | Volume, chaves, rede e TLS. |
| Docker Compose | Declara serviços, redes e volumes. | Não substitui backup ou observabilidade. | Arquivos e segredos. |
| Backup externo | Guarda cópia recuperável fora da VPS. | Não nasce de um volume. | Snapshot e processo de restore. |
O que você vai construir
O fluxo é: documentos locais viram embeddings; Qdrant guarda vetores e texto; uma ferramenta recupera os trechos; o agente LangChain chama essa ferramenta antes de responder. O modelo recebe apenas o contexto retornado pela busca.
A aplicação e o Qdrant conversam apenas na rede interna do Compose. O banco vetorial não precisa publicar uma porta na internet para atender a aplicação.
Pré-requisitos e limites do exemplo
Você precisa de uma VPS Linux com Docker Engine e o plugin Docker Compose instalados. Também precisa de uma chave de API de um provider compatível com os pacotes Python escolhidos e de documentos que você tenha permissão para indexar.
O exemplo abaixo usa OPENAI_API_KEY, langchain-openai e os modelos configurados no arquivo Python. Troque-os somente se também adaptar o código e testar a compatibilidade. A chave não vai para Git, imagem ou logs.
Não trate os comandos como resultado observado pelo Runzos. Eles formam um exemplo mínimo para você executar e validar. Dependências, provider, versão do Qdrant e comportamento do seu corpus podem exigir ajustes.
| Item | Por que existe | O que não conclui |
|---|---|---|
| Chave do provider | Permite criar embeddings e respostas. | Não garante qualidade da resposta. |
| Três documentos de teste | Permitem conferir recuperação conhecida. | Não representam um corpus real. |
| Volume Docker | Preserva o armazenamento ao recriar container. | Não substitui cópia externa. |
| Rede interna | Evita expor Qdrant por padrão. | Não substitui política de acesso. |
Crie os arquivos do projeto
Em uma pasta vazia da VPS, crie esta estrutura. O .env fica fora do Git.
rag-agent/
├── compose.yaml
├── Dockerfile
├── requirements.txt
├── app.py
├── .env
└── documents/
├── qdrant.txt
├── langchain.txt
└── operacao.txt
Crie requirements.txt com as bibliotecas usadas pelo exemplo:
langchain
langchain-openai
langchain-qdrant
qdrant-client
Crie .env e informe a chave apenas no seu ambiente:
OPENAI_API_KEY=cole_a_chave_apenas_no_seu_ambiente
O valor acima é um marcador de preenchimento, não uma configuração pronta. Não execute o projeto enquanto ele continuar literal. Remova .env do versionamento com este .gitignore:
.env
__pycache__/
Suba o Qdrant em rede interna
O Quickstart do Qdrant documenta o armazenamento em /qdrant/storage, REST na porta 6333 e gRPC na 6334. Ele também informa que a instância inicia sem criptografia ou autenticação por padrão. Leia o Quickstart do Qdrant junto da documentação de segurança do Qdrant.
Use uma tag fixa que você testou. No exemplo, QDRANT_IMAGE_TAG é uma variável obrigatória. Ela impede que uma tag fictícia seja apresentada como pronta para produção.
services:
qdrant:
image: qdrant/qdrant:${QDRANT_IMAGE_TAG:?defina_uma_tag_testada}
volumes:
- qdrant_storage:/qdrant/storage
networks: [internal]
agent:
build: .
env_file: .env
environment:
QDRANT_URL: http://qdrant:6333
depends_on:
- qdrant
networks: [internal]
profiles: [tools]
volumes:
qdrant_storage:
networks:
internal:
internal: true
Não há ports: nesse Compose. Isso é intencional. A aplicação acessa http://qdrant:6333 pela rede interna. Se você realmente precisar de acesso remoto, aplique controles de rede, API keys com escopo e TLS conforme a documentação de segurança. Não exponha 0.0.0.0:6333 por reflexo.
Crie o Dockerfile:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
COPY documents ./documents
CMD ["python", "app.py", "ask", "O que este agente recupera?"]
Antes de subir, escolha e exporte uma tag do Qdrant que tenha sido validada no seu ambiente. Não use latest como decisão de produção.
export QDRANT_IMAGE_TAG=TAG_QUE_VOCE_VALIDOU
mkdir -p documents
A string TAG_QUE_VOCE_VALIDOU também é um valor a substituir. Ela força uma decisão explícita de versão, em vez de disfarçar uma escolha não verificada.
Adicione documentos e o agente mínimo
Crie três arquivos simples. Eles tornam a validação observável, pois cada pergunta tem um trecho esperado.
# documents/qdrant.txt
Qdrant armazena vetores e payloads para recuperação por similaridade.
# documents/langchain.txt
LangChain compõe modelo, ferramentas, prompt e middleware em aplicações de IA.
# documents/operacao.txt
Um volume preserva dados do container, mas restore exige snapshot e cópia externa.
Agora crie app.py. O script cria ou reabre a coleção manual, indexa os arquivos e expõe a recuperação como ferramenta do agente. Ele é um exemplo de implementação; não é uma medição, benchmark ou prova de precisão.
import os
import sys
from pathlib import Path
from langchain.agents import create_agent
from langchain.tools import tool
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_qdrant import QdrantVectorStore
from qdrant_client import QdrantClient
COLLECTION = "manual"
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
client = QdrantClient(url=os.environ["QDRANT_URL"])
def vector_store():
return QdrantVectorStore(
client=client,
collection_name=COLLECTION,
embedding=embeddings,
)
def ingest():
texts = [path.read_text(encoding="utf-8") for path in Path("documents").glob("*.txt")]
if not texts:
raise RuntimeError("Adicione ao menos um arquivo .txt em documents/.")
QdrantVectorStore.from_texts(
texts=texts,
embedding=embeddings,
url=os.environ["QDRANT_URL"],
collection_name=COLLECTION,
force_recreate=True,
)
print(f"Indexação concluída: {len(texts)} documentos enviados para '{COLLECTION}'.")
@tool
def recuperar_documentos(pergunta: str) -> str:
"""Recupera trechos indexados no manual local para responder à pergunta."""
documentos = vector_store().similarity_search(pergunta, k=3)
if not documentos:
return "Nenhum trecho foi recuperado. Execute a indexação primeiro."
return "\n\n".join(documento.page_content for documento in documentos)
def ask(pergunta: str):
agente = create_agent(
model=ChatOpenAI(model="gpt-4.1-mini"),
tools=[recuperar_documentos],
system_prompt=(
"Use a ferramenta recuperar_documentos antes de responder. "
"Se o contexto não trouxer a resposta, diga que não encontrou no manual."
),
)
resultado = agente.invoke({"messages": [{"role": "user", "content": pergunta}]})
print(resultado["messages"][-1].content)
if __name__ == "__main__":
comando = sys.argv[1] if len(sys.argv) > 1 else ""
if comando == "ingest":
ingest()
elif comando == "ask" and len(sys.argv) > 2:
ask(" ".join(sys.argv[2:]))
else:
raise SystemExit("Uso: python app.py ingest | python app.py ask 'sua pergunta'")
Execute e confira se funcionou
Suba somente o Qdrant, depois execute a indexação pelo serviço agent.
docker compose up -d qdrant
docker compose --profile tools run --rm agent python app.py ingest
O critério observável da indexação é uma linha com a quantidade de arquivos enviados e o nome da coleção. Se o script encerra com erro de chave, URL ou dependência, não avance para a pergunta: corrija a causa exibida.
Faça uma pergunta cuja resposta esteja nos arquivos:
docker compose --profile tools run --rm agent python app.py ask "O que um volume Docker não substitui?"
A resposta deve se apoiar no trecho sobre snapshot e cópia externa. Não considere isso uma avaliação de qualidade do seu produto. É apenas um teste de que a cadeia documento, embedding, Qdrant, recuperação e modelo está conectada.
Reinicie o Qdrant e repita a pergunta:
docker compose restart qdrant
docker compose --profile tools run --rm agent python app.py ask "Qual é o papel do Qdrant?"
Se a recuperação continuar encontrando o documento após o reinício, o volume preservou a coleção nesse cenário. Ainda assim, volume não é backup.
| Teste | Como validar | Falha que evita |
|---|---|---|
| Indexação | O comando informa documentos enviados. | Agente sem corpus. |
| Recuperação conhecida | A resposta usa o tema do arquivo esperado. | Confundir resposta fluente com busca correta. |
| Reinício | A mesma consulta funciona após reiniciar Qdrant. | Índice mantido só no container. |
| Rede | Compose não publica 6333/6334. | Exposição desnecessária. |
| Snapshot | Snapshot é copiado para fora da VPS. | Tratar volume como backup. |
| Restore | Consultas conhecidas funcionam em instância descartável. | Backup sem prova de recuperação. |
Problemas comuns
O agente responde sem usar o documento esperado
Confira primeiro os trechos recuperados, não apenas a frase final. Revise documentos, recorte, modelo de embeddings, metadados e consulta. Trocar prompt não corrige necessariamente uma recuperação ruim.
O serviço não encontra o Qdrant
Confirme que qdrant está em execução e que QDRANT_URL aponta para http://qdrant:6333 dentro da rede Compose. O nome de serviço funciona entre containers; ele não é um endereço para usar fora dessa rede.
A chave aparece em Git ou log
Revogue a chave exposta e crie outra. Mantenha .env fora do repositório. O Docker documenta segredos concedidos explicitamente a serviços; quando sua aplicação aceita leitura por arquivo, consulte o guia de segredos do Docker Compose.
A coleção some depois de recriar containers
Verifique se o volume está montado em /qdrant/storage e se o comando não removeu volumes deliberadamente. Persistência local não prova recuperação externa: crie snapshot e teste restore.
Snapshot e restore
Quando você adiciona Qdrant, também assume a responsabilidade de recuperar seu estado. O produto permite snapshots de coleções e armazenamento. A cópia para fora da VPS e o teste de restauração são recomendações operacionais do Runzos; não ocorrem automaticamente porque existe um snapshot.
Monte uma rotina curta: crie um snapshot após indexação conhecida, copie-o para armazenamento externo sob seu controle, restaure em instância descartável e rode as mesmas consultas. Registre falhas antes de depender do processo.
Backup só é backup quando o restore funciona. Esse teste revela configurações não versionadas, chaves ausentes ou documentos que não deveriam ter entrado no índice.
Qdrant, pgvector, Supabase ou Cloud?
| Cenário | Opção inicial | Motivo | Operação assumida |
|---|---|---|---|
| Protótipo ou corpus pequeno | Qdrant embutido ou solução simples. | Reduz um serviço separado. | Validar recuperação e persistência local. |
| Aplicação com PostgreSQL | pgvector. | Adiciona similaridade ao banco existente. | Modelar consultas e acompanhar impacto. |
| Corpus persistente e filtros | Qdrant dedicado. | Separa a busca vetorial. | Volumes, chaves, snapshots e capacidade. |
| Menos operação própria | Qdrant Cloud ou Supabase Vector. | Usa serviço gerenciado. | Configuração, limites e portabilidade. |
Não há vencedor universal ou mínimo universal de RAM. Vetores, dimensão, payload, índices, réplicas, quantização, concorrência, modelo e outros processos alteram a necessidade. Faça um piloto com corpus realista antes de tratar uma configuração como produção.
Qual VPS usar
Para começar uma stack com Docker, aplicação e banco vetorial sob seu controle, veja a oferta de VPS com n8n da Hostinger. Confirme plano, região, recursos e condições no momento da contratação.
Você também pode comparar a VPS da Servla e a VPS NVMe da Turbo Cloud. GPU só entra na decisão se inferência local for requisito deliberado e dimensionado separadamente.
FAQ
Preciso de GPU?
Não necessariamente. Qdrant armazena e recupera vetores; LangChain orquestra componentes. GPU é uma decisão de inferência local para chat ou embeddings, não uma consequência automática de RAG.
Posso expor a porta 6333?
Não como padrão em VPS pública. O Quickstart informa que Qdrant inicia sem criptografia ou autenticação. Quando apenas a aplicação precisa acessar o banco, mantenha a rede interna.
Volume Docker já é backup?
Não. Ele preserva dados ao recriar container, mas não cria cópia externa nem prova restore. Use snapshot, cópia externa e restauração em ambiente descartável.
Quando pgvector é mais simples?
Quando a aplicação já opera PostgreSQL e a busca por similaridade pode ficar nesse banco. A decisão continua dependendo de filtros, corpus e carga.
Quanto de RAM a VPS precisa?
Não existe número universal. Faça um piloto, registre embedding, dimensão, documentos, volume e memória observada. Só então ajuste sua infraestrutura.
Comece pelo teste de recuperação
A parte útil de um agente não começa com uma lista de ferramentas. Começa quando a aplicação recupera o contexto esperado, preserva dados e sobrevive a um restore.
LangChain, Qdrant e Docker podem formar essa base. Comece pequeno e atravesse o Limiar do Segundo Serviço apenas quando a recuperação justificar a operação extra.














