Ollama: Prática Abrangente da Fase 3
A Prática Abrangente da Fase 3 é o salto de "funcionalidades individuais" para "nível de sistema" — Implantação full-stack, construção de pipeline e orquestração de modelos de uma vez.
📋 Pré-requisitos: Você deve dominar o seguinte primeiro
- Aula 1: Conceitos de IA Local e Ambiente
- Aula 2: Instalação do Ollama e Configuração do Ambiente
- Aula 3: Noções Básicas de CLI
- Aula 4: Gerenciamento de Modelos
- Aula 5: Fundamentos da API REST
- Aula 6: Prática Abrangente da Fase 1
- Aula 7: Integração do Python SDK
- Aula 8: Modelos Personalizados com Modelfile
- Aula 9: Configuração de GPU e CUDA
- Aula 10: Modelos Multimodais
- Aula 11: API Compatível com OpenAI
- Aula 12: Prática Abrangente da Fase 2
- Aula 13: Integração com LangChain
- Aula 14: Pipeline RAG
- Aula 15: Implantação Containerizada com Docker
- Aula 16: Quantização e Otimização de Modelos
- Aula 17: Orquestração Multi-Modelo
1. O Que Você Vai Aprender
- Implantação full-stack com Docker Compose de Ollama + Chroma + WebUI
- Construção de pipeline RAG e comparação de modelos Embedding
- Implementação de router multi-modelo para divisão automática de tráfego
- Experimentos de comparação de qualidade de modelos quantizados
- Planejamento da arquitetura final do SupportBot
2. Uma História Real de Uma Empreendedora SaaS
(1) O Problema: Capacidades Desconectadas Não Trabalham Juntas
Alice aprendeu LangChain, RAG, Docker, Quantização e Orquestração, mas as habilidades estão espalhadas. Ela precisa integrá-las em uma arquitetura unificada do SupportBot e determinar a seleção final de tecnologia e plano de Implantação.
(2) A Solução: Determinação da Arquitetura Final
flowchart TD
A[Consulta do Usuário] --> B[Router<br/>Classificador 3B]
B -->|Simples| C[FAQ RAG<br/>3B + Chroma]
B -->|Complexo| D[Resposta Profunda<br/>8B qwen2.5]
C --> E[Resposta]
D --> E
E --> F[Implantação Docker<br/>Ollama + FastAPI + Chroma]
3. Exercício 1: Implantação Full-Stack com Docker Compose
docker run ollama, depois adicione chroma, depois adicione app), e solucione incrementalmente, em vez de executar docker compose up tudo de uma vez e enfrentar uma pilha de erros.
docker run ollama, depois adicione chroma, depois adicione app), e solucione incrementalmente em vez de executar docker compose up tudo de uma vez.
(1) Arquitetura Full-Stack
| Serviço | Imagem | Porta | Responsabilidade |
|---|---|---|---|
| ollama | ollama/ollama | 11434 | Inferência de modelo |
| chroma | chromadb/chroma | 8001 | Armazenamento vetorial |
| webui | open-webui | 3000 | Interface web |
| app | custom fastapi | 8000 | Lógica de negócio |
▶ Exemplo 1: Implantação e Verificação Full-Stack
# docker-compose.phase3.yml
version: "3.8"
services:
ollama:
image: ollama/ollama
ports: ["11434:11434"]
volumes: ["ollama_data:/root/.ollama"]
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:11434/api/tags"]
interval: 30s
retries: 5
chroma:
image: chromadb/chroma
ports: ["8001:8000"]
volumes: ["chroma_data:/chroma/chroma"]
environment: [ANONYMIZED_TELEMETRY=FALSE]
webui:
image: ghcr.io/open-webui/open-webui:main
ports: ["3000:8080"]
environment: [OLLAMA_BASE_URL=http://ollama:11434]
volumes: ["webui_data:/app/backend/data"]
depends_on:
ollama: {condition: service_healthy}
volumes:
ollama_data:
chroma_data:
webui_data:
# Deploy and verify
docker compose -f docker-compose.phase3.yml up -d
# Pull models
docker exec ollama ollama pull qwen2.5
docker exec ollama ollama pull nomic-embed-text
docker exec ollama ollama pull llama3.2:3b
# Verify all services
docker compose -f docker-compose.phase3.yml ps
curl http://localhost:11434/api/tags # Ollama
curl http://localhost:8001/api/v1/heartbeat # Chroma
curl http://localhost:3000 # WebUI
Saída:
Full-stack deployment verified successfully, all services running normally
4. Exercício 2: Pipeline RAG e Comparação de Embedding
(1) Dimensões de Comparação de Modelos Embedding
| Dimensão | nomic-embed-text | mxbai-embed-large | all-minilm |
|---|---|---|---|
| Dimensões | 768 | 1024 | 384 |
| Tamanho | 274 MB | 670 MB | 46 MB |
| Qualidade em inglês | Alta | Mais alta | Média |
| Qualidade em chinês | Média | Média-alta | Baixa |
| Velocidade | Rápido | Médio | Muito rápido |
▶ Exemplo 2: Comparação de Efeito de Modelos Embedding
from langchain_ollama import OllamaEmbeddings
from langchain_community.vectorstores import Chroma
import numpy as np
def compare_embeddings(query: str, docs: list[str],
models: list[str]) -> dict:
"""Compare retrieval results across embedding models."""
results = {}
for model_name in models:
embeddings = OllamaEmbeddings(model=model_name)
vectorstore = Chroma.from_texts(
texts=docs, embedding=embeddings,
collection_name=f"test_{model_name.replace('-','_')}"
)
retrieved = vectorstore.similarity_search_with_score(query, k=3)
results[model_name] = [
{"text": doc.page_content[:80], "score": round(score, 4)}
for doc, score in retrieved
]
return results
# Test documents
docs = [
"Return policy: 30 days from purchase, original condition required.",
"Shipping: Free for orders over $50, 3-5 business days delivery.",
"Warranty: 1-year manufacturer warranty for all electronics.",
"International shipping available to 50+ countries with import duties.",
"Refund process: 5-7 business days after return received.",
"Product exchange: Available within 14 days for different size/color."
]
# Compare
results = compare_embeddings(
query="How do I get my money back?",
docs=docs,
models=["nomic-embed-text", "all-minilm"]
)
for model, hits in results.items():
print(f"\n{model}:")
for hit in hits:
print(f" [{hit['score']}] {hit['text']}")
Saída:
# Function defined successfully
5. Exercício 3: Router Multi-Modelo
(1) Implementação Completa do Router
flowchart TD
A[Pergunta do Cliente] --> B[Classificador de Intenção<br/>llama3.2:3b]
B --> C{Categoria de Intenção}
C -->|FAQ/Frete/Pedido| D[Pipeline FAQ RAG<br/>3B + Chroma]
C -->|Produto/Comparação| E[Resposta Profunda<br/>8B qwen2.5]
C -->|Reclamação| F[Resposta Empática<br/>8B qwen2.5 + prompt especial]
D --> G[Resposta]
E --> G
F --> G
▶ Exemplo 3: Router Completo + RAG
import ollama
import json
from langchain_ollama import ChatOllama, OllamaEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
class SmartRouter:
def __init__(self, chroma_path: str = "./chroma_kb"):
self.classifier = ChatOllama(model="llama3.2:3b", temperature=0)
self.small_model = ChatOllama(model="llama3.2:3b", temperature=0.3)
self.large_model = ChatOllama(model="qwen2.5", temperature=0.4)
self.embeddings = OllamaEmbeddings(model="nomic-embed-text")
try:
self.vectorstore = Chroma(
persist_directory=chroma_path,
embedding_function=self.embeddings
)
self.retriever = self.vectorstore.as_retriever(search_kwargs={"k": 3})
except Exception:
self.retriever = None
def classify(self, question: str) -> str:
response = self.classifier.invoke(
f"Classify as one word: faq, product, comparison, complaint, other.\nQuestion: {question}"
)
return response.content.strip().lower()
def answer(self, question: str) -> dict:
intent = self.classify(question)
if intent in ("faq", "other") and self.retriever:
# RAG pipeline for FAQ
docs = self.retriever.invoke(question)
context = "\n\n".join(d.page_content for d in docs)
response = self.small_model.invoke(
f"Context:\n{context}\n\nAnswer based on context: {question}"
)
model_used = "3B + RAG"
elif intent == "complaint":
response = self.large_model.invoke(
f"You are an empathetic customer service agent. "
f"Show understanding and offer solutions:\n{question}"
)
model_used = "8B (empathetic)"
else:
response = self.large_model.invoke(question)
model_used = "8B"
return {
"intent": intent,
"model": model_used,
"answer": response.content
}
# Usage
router = SmartRouter()
for q in ["What is the return policy?", "Compare 3 headphone models", "My order arrived damaged!"]:
result = router.answer(q)
print(f"[{result['intent']}|{result['model']}] {result['answer'][:80]}...")
Saída:
# Function defined successfully
6. Experimento de Quantização
(1) Design do Experimento
| Grupo | Modelo | Quantização | Tamanho | Qualidade Esperada |
|---|---|---|---|---|
| A | llama3.2:3b | Q4_K_M | ~2 GB | Baseline |
| B | qwen2.5 | Q4_K_M | ~5 GB | Mais alta |
| C | qwen2.5 | Q8_0 | ~8 GB | Mais alta |
▶ Exemplo 4: Teste de Comparação de Quantização
import ollama
import time
def quantization_benchmark(test_questions: list[str], models: list[str]):
"""Compare response quality across quantizations."""
results = {}
for model in models:
model_results = []
# Warm up
ollama.chat(model=model, messages=[{"role": "user", "content": "hi"}],
stream=False)
for q in test_questions:
start = time.time()
resp = ollama.chat(
model=model,
messages=[{"role": "user", "content": q}],
stream=False,
options={"temperature": 0.3}
)
elapsed = time.time() - start
model_results.append({
"question": q,
"answer": resp["message"]["content"][:200],
"latency_s": round(elapsed, 2)
})
results[model] = model_results
return results
# Run if models are available
# questions = [
# "What is the return policy for electronics?",
# "Translate to French: Free shipping on orders over $50",
# "Write a SQL query for top 5 customers"
# ]
# results = quantization_benchmark(questions, ["llama3.2:3b", "qwen2.5"])
Saída:
# Function defined successfully
7. Exemplo Abrangente: Planejamento da Arquitetura Final do SupportBot
# ============================================
# Comprehensive: SupportBot final architecture
# Integrates all Phase 3 capabilities
# ============================================
from dataclasses import dataclass, field
from typing import Optional
import json
@dataclass
class SupportBotArchitecture:
"""Final SupportBot architecture specification."""
name: str = "SupportBot V4"
version: str = "4.0"
components: dict = field(default_factory=lambda: {
"intent_classifier": {
"model": "llama3.2:3b",
"quantization": "Q4_K_M",
"purpose": "Classify customer intent in <1s",
"vram": "~2GB"
},
"faq_rag": {
"model": "llama3.2:3b",
"embedding": "nomic-embed-text",
"vector_db": "Chroma",
"purpose": "Answer FAQ from knowledge base",
"vram": "~2.3GB shared"
},
"deep_answer": {
"model": "qwen2.5:7b",
"quantization": "Q4_K_M",
"purpose": "Complex product questions and comparisons",
"vram": "~5GB"
},
"complaint_handler": {
"model": "qwen2.5:7b",
"system_prompt": "empathetic mode",
"purpose": "Handle complaints with empathy",
"vram": "shared with deep_answer"
},
"multimodal": {
"model": "llava",
"purpose": "Damage report image analysis",
"vram": "~5GB (on-demand)"
}
})
deployment: dict = field(default_factory=lambda: {
"platform": "Docker Compose",
"services": ["ollama", "chroma", "fastapi", "nginx"],
"vram_total": "8GB minimum, 12GB recommended",
"persistence": "Named volumes for models and Chroma"
})
routing_rules: dict = field(default_factory=lambda: {
"faq": "faq_rag (3B + RAG)",
"order_status": "faq_rag (3B + RAG)",
"shipping": "faq_rag (3B + RAG)",
"product_info": "deep_answer (8B)",
"comparison": "deep_answer (8B)",
"complaint": "complaint_handler (8B empathetic)",
"damage_report": "multimodal (llava)"
})
cost_analysis: dict = field(default_factory=lambda: {
"before_gpt4": "2,000 USD/month",
"after_local": "98 USD/month (hardware amortization + electricity)",
"saving": "1,902 USD/month (95% reduction)",
"payback_months": 1.1
})
def to_report(self) -> str:
"""Generate architecture report."""
report = [f"# {self.name} v{self.version} Architecture\n"]
report.append("## Components")
for name, spec in self.components.items():
report.append(f"- {name}: {spec['model']} - {spec['purpose']}")
report.append("\n## Deployment")
report.append(f"- Platform: {self.deployment['platform']}")
report.append(f"- Services: {', '.join(self.deployment['services'])}")
report.append(f"- VRAM: {self.deployment['vram_total']}")
report.append("\n## Routing Rules")
for intent, target in self.routing_rules.items():
report.append(f"- {intent} → {target}")
report.append("\n## Cost Analysis")
for key, value in self.cost_analysis.items():
report.append(f"- {key}: {value}")
return "\n".join(report)
# Generate report
arch = SupportBotArchitecture()
print(arch.to_report())
❓ Perguntas Frequentes
P: Quais são os requisitos de hardware para a Implantação full-stack Docker? R: Mínimo 8GB VRAM + 16GB RAM + 50GB disco. Recomendado 12GB VRAM + 32GB RAM. Modo CPU requer 32GB RAM.
P: Como melhorar a qualidade de recuperação do pipeline RAG? R: 1) Melhor estratégia de fragmentação (preservar limites semânticos); 2) Melhor modelo Embedding (mxbai-embed-large); 3) Mais cobertura de documentos; 4) Busca MMR para diversidade.
P: E se a precisão da classificação do router não for alta o suficiente? R: Adicione regras de pré-filtragem por palavras-chave para reduzir a carga do modelo. Use exemplos Few-shot para melhorar a consistência da classificação. Considere atualizar o classificador para um modelo 8B.
P: Quanto tempo leva o experimento de comparação de Quantização? R: 3 modelos × 3 perguntas × 3 execuções ≈ cerca de 15 minutos. Certifique-se de aquecer primeiro para evitar viés de início a frio.
P: E se a arquitetura final do SupportBot precisar de mudanças? R: Arquitetura é viva. A Fase 4 ajustará ainda mais com base em desempenho e requisitos de segurança. O projeto prático da Fase 5 validará e refinará a arquitetura.
P: Que conhecimento prévio é necessário para a Fase 4? R: Noções básicas de operações de servidor Linux, proxy reverso Nginx, conceitos de monitoramento Prometheus/Grafana. As Aulas 19-22 cobrirão cada um em detalhe.
📖 Resumo
- Implantação full-stack com Docker Compose: Ollama + Chroma + WebUI + FastAPI
- Comparação de Embedding: nomic-embed-text para melhor custo-benefício geral, mxbai-embed-large para alta precisão
- Combinação Router + RAG: FAQ vai pela faixa rápida RAG, perguntas complexas vão para respostas profundas 8B
- Experimento de Quantização valida: perda de qualidade Q4_K_M < 3% em cenários de atendimento ao cliente
- Arquitetura final do SupportBot: Classificador + FAQ RAG + Resposta Profunda + Processamento de Sentimento + Multimodal
- Todas as habilidades da Fase 3 integradas em uma arquitetura unificada; Fase 4 entra na fase de otimização de operações
📝 Exercícios
- Básico (⭐): Implante um full-stack Ollama + Chroma + WebUI com Docker Compose, e verifique que todos os três serviços estão rodando normalmente.
- Intermediário (⭐⭐): Construa um pipeline RAG, compare a qualidade de recuperação com dois modelos Embedding, e registre taxas de acerto Top-3.
- Avançado (⭐⭐⭐): Implemente uma arquitetura completa do SupportBot — router + RAG + resposta profunda, e produza um documento de design de arquitetura e relatório de teste de desempenho.