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.

💡 Dica: A prática avançada integra habilidades de todas as 17 aulas anteriores — Implantação Docker (A15) + Pipeline RAG (A14) + Roteamento multi-modelo (A17) + Seleção de Quantização (A16) + Orquestração LangChain (A13). Siga a sequência "implantar primeiro → construir pipeline → adicionar roteamento" para integração incremental, em vez de tentar construir tudo de uma vez.

📋 Pré-requisitos: Você deve dominar o seguinte primeiro

1. O Que Você Vai Aprender


2. Uma História Real de Uma Empreendedora SaaS

💡 Dica: Após finalizar a arquitetura, use uma "tabela de matriz de capacidades" (modelo × cenário × métricas) para documentar a justificativa de seleção e baseline de desempenho de cada componente. Isso fornece rastreabilidade para otimizações futuras ou mudanças de modelo, e ajuda a explicar decisões técnicas à equipe.

ℹ️ Info: A Prática Abrangente da Fase 3 é um pré-requisito direto para a Fase 4 (segurança/monitoramento/Implantação em produção). Após completar esta prática, você terá um SupportBot V4 rodando (Implantação Docker + RAG + roteamento multi-modelo). A Fase 4 vai endurecer e otimizar ainda mais.

(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

100%
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

⚠️ Nota: A Implantação full-stack com Docker Compose envolve 4+ serviços e passthrough de GPU, o que pode causar vários problemas na primeira inicialização. Teste cada serviço individualmente primeiro (comece com 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.

⚠️ Aviso: A Implantação full-stack com Docker Compose envolve 4+ serviços e passthrough de GPU. A primeira inicialização pode encontrar vários problemas. Teste cada serviço individualmente primeiro (comece com 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

YAML
# 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:
BASH
# 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:

TEXT
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

PYTHON
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:

TEXT
# Function defined successfully

5. Exercício 3: Router Multi-Modelo

(1) Implementação Completa do Router

100%
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

PYTHON
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:

TEXT
# 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

PYTHON
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:

TEXT
# Function defined successfully

7. Exemplo Abrangente: Planejamento da Arquitetura Final do SupportBot

PYTHON
# ============================================
# 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


📝 Exercícios

  1. 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.
  2. Intermediário (⭐⭐): Construa um pipeline RAG, compare a qualidade de recuperação com dois modelos Embedding, e registre taxas de acerto Top-3.
  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.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%