CZANIX
Tech Reference
Agora

IA & Agentes

O que funciona em produção vs o que é demo de conferência. RAG, custo, LGPD e como usar IA para entregar 10x sem virar refém da ferramenta.

"A IA amplifica quem pensa. Não substitui quem pensa."

— Cesar Zanis
01

A mudança de mentalidade (leia primeiro)

01

IA não substitui entendimento — amplifica

Código virou commoditie. Assim como ninguém mais se orgulha de saber compilar na mão, em 2 anos ninguém vai se orgulhar de escrever um CRUD do zero.

O profissional que sobrevive a 2030 entende o negócio, desenha a solução e orquestra máquinas para materializar. A IA amplifica quem pensa. Não substitui quem pensa.

A qualidade do que a IA devolve é diretamente proporcional à qualidade do que você entrega pra ela.

02

O problema primeiro, a ferramenta depois

Antes de abrir qualquer IA, mastigue o problema de negócio na sua cabeça. Quem é o usuário? Qual a dor real? O que acontece se essa feature não existir?

Se você jogar "faz um sistema de cadastro" no LLM, vai receber um sistema de cadastro genérico que não serve para ninguém. Se você jogar "o usuário é um inspetor de campo que preenche formulários em PDA Honeywell sem internet estável, precisa salvar localmente sem perder dados mesmo se a bateria morrer no meio" — a IA entrega ouro.

O contexto que você fornece é o diferencial. É sua experiência que faz o output ser útil.

03

IA para planejamento ≠ IA para implementação

Use modelos de raciocínio longo (Claude Opus, Gemini Pro) para arquitetura: explorar alternativas, analisar trade-offs, revisar ADRs, questionar decisões. São os melhores "arquitetos virtuais" — mas a decisão final é sempre sua.

Use agentes integrados ao editor (Gemini in IDE, Copilot) para implementação: boilerplate, CRUD, testes, refactoring. Eles operam dentro do projeto, leem arquivos reais, executam comandos e corrigem erros.

Nunca delegue a decisão arquitetural para a IA. Ela propõe. Você decide.

02

Padrões de produção

RAG — quando e como fazer certo

A diferença entre um chatbot inútil e um assistente real.

RAG (Retrieval-Augmented Generation) é buscar contexto relevante antes de gerar a resposta. Sem RAG, o LLM responde com conhecimento de treinamento genérico. Com RAG bem feito, responde com o seu contexto específico — documentação, base de conhecimento, histórico de decisões.

O erro mais comum: usar apenas busca semântica (vetores) ignorando busca léxica (BM25/FTS). Para a maioria dos casos de uso, uma busca híbrida (semântica + léxica) supera qualquer uma das duas isoladas.

Python
# Arquitetura RAG mínima funcional
# 1. Indexação (roda offline, quando o conteúdo muda)
def index_document(doc: str, doc_id: str):
    # Chunking — blocos com overlap para não perder contexto
    chunks = chunk_with_overlap(doc, size=800, overlap=100)
    
    for i, chunk in enumerate(chunks):
        embedding = embed(chunk)  # via API: text-embedding-004
        vector_store.upsert(
            id=f"{doc_id}_{i}",
            vector=embedding,
            metadata={"text": chunk, "doc_id": doc_id}
        )

# 2. Retrieval (em tempo real, por query do usuário)
def retrieve(query: str, top_k: int = 5) -> list[str]:
    query_embedding = embed(query)
    
    # Busca semântica (vetores)
    semantic_results = vector_store.query(query_embedding, top_k=top_k)
    
    # Busca léxica (palavras exatas) — não ignore isso
    lexical_results = full_text_search(query, top_k=top_k)
    
    # Rerank híbrido — RRF (Reciprocal Rank Fusion)
    return reciprocal_rank_fusion([semantic_results, lexical_results])

# 3. Generation — contexto injetado no prompt
def answer(query: str) -> str:
    context_chunks = retrieve(query)
    context = "\n\n".join(context_chunks)
    
    prompt = f"""Responda baseado APENAS no contexto abaixo.
Se a resposta não estiver no contexto, diga explicitamente.

CONTEXTO:
{context}

PERGUNTA: {query}"""
    
    return llm.generate(prompt)

Custo de LLM — o que não te contam

Token de entrada é mais barato que token de saída. Use isso.

O custo de LLM em produção não é o que parece no playground. Em produção, o custo vem de quatro frentes: tokens de sistema (fixos por request), histórico de conversa (cresce a cada turno), contexto RAG (por request) e output do modelo.

O que funciona na prática: comprimir o histórico ao invés de mandar 10 turnos completos, rotear por complexidade usando modelo pequeno para classificação e modelo grande só para resposta final, cache de resposta para queries idênticas ou similares (por embedding distance), e sempre definir maxOutputTokens explícito porque modelos são verbosos por natureza.

Python
# Controle de custo real em produção

# 1. Compressão de histórico — não mande tudo
def compress_history(history: list[dict], max_turns: int = 4) -> str:
    if len(history) <= max_turns:
        return format_history(history)
    
    # Resumo dos turnos antigos + últimas N mensagens completas
    old_turns = history[:-max_turns]
    recent_turns = history[-max_turns:]
    
    summary = llm.generate(
        f"Resuma em 2-3 frases os pontos principais desta conversa: {old_turns}",
        config={"maxOutputTokens": 150}  # resumo curto, modelo pequeno
    )
    
    return f"[Resumo anterior: {summary}]\n{format_history(recent_turns)}"

# 2. Roteamento por complexidade — modelo certo para cada tarefa
def route_to_model(query: str) -> str:
    # Classificação rápida e barata
    is_complex = classifier.predict(query)  # modelo local ou flash
    
    return "gemini-2.5-pro" if is_complex else "gemini-2.5-flash"

# 3. Limite explícito de output
generation_config = {
    "maxOutputTokens": 600,   # suficiente para resposta útil
    "temperature": 0.3,       # mais determinístico = menos tokens desperdiçados
}

LGPD + IA — o que é obrigatório

Dados pessoais não saem da sua infraestrutura sem consentimento explícito.

Toda vez que você manda um dado para uma API de LLM externa (OpenAI, Anthropic, Google), esse dado potencialmente alimenta treinamento futuro — a menos que você tenha acordos específicos (API tier, contratos enterprise).

Para dados pessoais (CPF, email, nome, localização), você tem três opções: modelo local (sem dados saindo), anonymization antes de mandar, ou contrato explícito com a plataforma garantindo que os dados não são usados para treinamento.

O artigo 6º da LGPD exige finalidade, adequação e necessidade. Se você está mandando um registro completo de cliente para o LLM mas só precisa de parte, já viola a necessidade.

Python
# Anonymization básica antes de enviar para LLM externo
import re

def anonymize_for_llm(text: str) -> tuple[str, dict]:
    """Remove PII antes de enviar. Retorna texto limpo + mapa para substituição."""
    replacements = {}
    counter = {"cpf": 0, "email": 0, "phone": 0}
    
    # CPF
    def replace_cpf(m):
        key = f"[CPF_{counter['cpf']}]"
        replacements[key] = m.group(0)
        counter["cpf"] += 1
        return key
    
    # Email  
    def replace_email(m):
        key = f"[EMAIL_{counter['email']}]"
        replacements[key] = m.group(0)
        counter["email"] += 1
        return key
    
    text = re.sub(r'\d{3}\.\d{3}\.\d{3}-\d{2}', replace_cpf, text)
    text = re.sub(r'[\w.-]+@[\w.-]+\.\w+', replace_email, text)
    
    return text, replacements

# Uso
clean_text, pii_map = anonymize_for_llm(user_message)
llm_response = llm.generate(clean_text)

# Nunca logue pii_map — ele contém os dados originais
# Use apenas se precisar reconstituir a resposta com dados reais

IA Local vs IA Cloud

Não é "ou". É saber onde cada uma faz sentido.

IA local (Ollama, vLLM, llama.cpp) roda no seu hardware. Zero dados saindo. Latência previsível. Custo fixo. Funciona para classificação, extração de entidades, moderação de conteúdo e qualquer caso onde privacidade é inegociável.

IA cloud (Vertex AI, AWS Bedrock, Azure OpenAI) entrega modelos maiores e mais capazes sem o custo de manter GPU. Funciona para geração de texto complexo, RAG com modelos de 70B+, e qualquer cenário onde a qualidade do output compensa o custo por token.

A decisão correta quase sempre é usar os dois. Modelo local pequeno para roteamento e classificação, modelo cloud grande para resposta final. O roteamento local custa centavos de eletricidade. O modelo cloud só é chamado quando o resultado precisa ser excepcional.

Para quem opera em múltiplas clouds (Azure, GCP, AWS), o padrão é abstrair o provider por interface. Troca de Vertex para Bedrock sem mexer na lógica. O mesmo princípio de Dependency Inversion que funciona em banco de dados funciona aqui.

Python
# Abstração de provider: troca de cloud sem mexer na lógica
from abc import ABC, abstractmethod

class LLMProvider(ABC):
    @abstractmethod
    def generate(self, prompt: str, config: dict) -> str: ...

# Google Cloud
class VertexProvider(LLMProvider):
    def generate(self, prompt: str, config: dict) -> str:
        from vertexai.generative_models import GenerativeModel
        model = GenerativeModel(config.get("model", "gemini-2.5-flash"))
        response = model.generate_content(prompt)
        return response.text

# AWS
class BedrockProvider(LLMProvider):
    def generate(self, prompt: str, config: dict) -> str:
        import boto3
        client = boto3.client("bedrock-runtime")
        # Claude via Bedrock
        response = client.invoke_model(
            modelId=config.get("model", "anthropic.claude-3-haiku"),
            body={"prompt": prompt, "max_tokens": config.get("max_tokens", 600)}
        )
        return response["body"].read().decode()

# Local (Ollama)
class OllamaProvider(LLMProvider):
    def generate(self, prompt: str, config: dict) -> str:
        import requests
        r = requests.post("http://localhost:11434/api/generate", json={
            "model": config.get("model", "llama3.2"),
            "prompt": prompt, "stream": False
        })
        return r.json()["response"]

# Roteamento inteligente
def get_provider(task_type: str) -> LLMProvider:
    if task_type in ("classify", "extract", "moderate"):
        return OllamaProvider()   # local, rápido, privado
    if task_type == "generate":
        return VertexProvider()   # cloud, modelo grande
    return BedrockProvider()      # fallback multi-cloud

O critério que não falha: se você não consegue explicar o que o agente faz em uma frase, você não entendeu o suficiente para colocá-lo em produção. Simplicidade aqui não é limitação — é sinal de domínio.

Precisa de ajuda para estruturar uma solução de IA para o seu negócio?

Fale com o Nix →