Teu progresso
0 / 83 módulos0%
Estágio 04 · 04-10
BloqueadoEm 2026, "AI feature" é cobrança em quase todo produto. A maioria das implementações é frágil: prompts hardcoded em strings, sem versioning; sem evals; latency 30s sem streaming; vazamento de PII pra provider; rate limit ignorado; cost imprevisível. Frente a frente com production reality, "ChatGPT mas pro nosso uso" frequentemente não passa do MVP.
Este módulo é AI/LLM como engenheiro de sistemas vê: APIs (Anthropic Claude, OpenAI, Gemini, open-source), token economy, streaming, structured outputs, function calling, RAG, embeddings, vector DBs, agents, evals, observability LLM, custo, segurança/PII. Não é curso de ML; é engenharia de software com modelos de linguagem como deps.
Trate como qualquer dep externa:
Aplicar resilience patterns (04-04): timeouts, retries em transient errors, circuit breaker, idempotency keys, fallback (modelo menor ou cached response).
Trade-off: API pago e top-tier vs self-hosted barato com mais ops.
Modelos cobram por token (input + output). Token ≈ 4 chars em inglês, menos em português.
Preços ordens de grandeza (2026, sujeito a):
Cost = sum(tokens) per request × volume. Em SaaS, isso vira parcela material da bill.
Anthropic e similares oferecem cache de prefix prompt: chamadas com mesmo início (system prompt, instructions longas, RAG context) reusam tokens cached, reduzindo cost 80-90% em hit.
Implementar: marca cache breakpoints na request. Em Logística com prompt sistema longo (regras do domínio, examples), cache vira no-brainer.
LLM produz tokens sequencialmente. UI deve streaming pra evitar 10s+ de "loading":
UX: user vê texto aparecer; perceived latency drasticamente menor.
Em vez de "chat" livre, force formato:
Function calling é o que permite agents robustos. Exemplo:
User: "Reagende minha entrega de amanhã pra quinta."
LLM (tool call): findOrders(customer="...", date="2026-04-29")
App executes; returns orders.
LLM (tool call): rescheduleOrder(orderId="...", newDate="2026-05-01")
App executes; returns confirmation.
LLM: "Reagendei pra quinta-feira, 1º de maio."
Exige tool definitions claras (name, description, params schema). E código robusto que executa as tools.
Modelos têm limit de tokens (8k, 32k, 200k, 1M). Conversas longas excedem. Estratégias:
Em apps de produção, gerenciar context é tarefa de engenharia.
LLM não sabe seu domínio. RAG alimenta LLM com contexto relevante:
Eficácia: RAG bem feito reduz hallucination, aumenta accuracy.
Embeddings:
Vector DBs em 2026 — matriz de decisão:
| DB | Modelo | Indexes | Hybrid search | Filter perf | Ops | Quando |
|---|---|---|---|---|---|---|
| pgvector | Postgres extension | IVFFlat, HNSW (0.5+) | Manual (query joins) | Excelente (Postgres planner) | Zero (já tem Postgres) | Default em 2026 até > 10M vectors ou QPS > 500 |
| Qdrant | Rust dedicated | HNSW, scalar quantization | Built-in (BM25 + vector) | Excelente (payload filtering) | Self-host fácil ou managed | Filter-heavy queries, multi-tenant via collections |
| Weaviate | Go dedicated | HNSW + variants | Built-in (BM25 + vector + reranker) | Bom | Self-host médio ou managed | Hybrid search nativo, GraphQL API |
| Milvus | Go + C++ dedicated | HNSW, IVF, DiskANN, GPU | Built-in (sparse + dense) | Bom em escala | Operação complexa (etcd + minio + 10+ services) | > 100M vectors, throughput extremo |
| Pinecone | Managed proprietário | Proprietary | Sparse-dense hybrid | Bom | Zero (managed) | Quer managed e tolera lock-in + custo |
| Chroma | Python dedicated | HNSW | Manual | Ok | Self-host simples | Prototyping, single-node, Python-first |
| LanceDB | Rust + Apache Arrow | IVF, HNSW | Sim | Excelente em columnar | Embedded ou serverless | Multimodal, dataset grande em S3 |
| Vespa | JVM dedicated | HNSW + tensor | Native (BM25 + vector + ML ranking) | Excelente | Operação complexa | Search com ML ranking nativo (Yahoo, Spotify) |
Heurística pragmática 2026:
WHERE): Qdrant ou pgvector com índices compostos.Anti-padrão: jogar tudo no Pinecone porque "managed é fácil". Custo escala não-linear; lock-in é real; Postgres pgvector cobre 80% dos casos com dor operacional zero.
Pattern: LLM decide ações em loop:
Frameworks:
Riscos: loops infinitos, hallucinated tools, costs explodem. Adicionar:
Sem eval, você não sabe se prompt mudou pra melhor ou pior.
Tools: Braintrust, LangSmith, PromptLayer, Phoenix, Inspect AI.
Without evals = fly blind.
LLM provider sees prompt content. Implicações:
Prompt injection: user input que sobrepõe instructions. Defense:
OWASP Top 10 LLM publicado.
OTel + tools (LangSmith, Helicone, Langfuse).
Vença custo + privacy. Espectro de maturidade:
Quantização é o multiplicador silencioso: GGUF Q4_K_M roda Llama 3.1 70B em uma única H100 80GB com perda de qualidade ~2-3% em benchmarks. Q8 ~zero perda, dobra de RAM.
Middle ground (managed inference de open weights):
Decisão pragmática:
Fine-tuning de modelo small (7B-14B) via LoRA ou QLoRA em 2026 custa ~$200-1000 em compute e cabe em 1 H100. Antes de Anthropic/OpenAI fine-tuning, considere se LoRA em Llama/Qwen + serving próprio resolve.
Default: prompt + RAG. Fine-tune só com dataset robusto (10k+) e razão clara.
App popular com LLM como core feature passa de $10k/mês trivialmente. Ordem de magnitude (preços públicos Anthropic em 2026, Sonnet 4.6 como referência):
Prompt caching (Anthropic ephemeral cache): cache hit cobra ~10% do preço de input. Em SaaS com system prompt de 5k tokens reutilizado em 100k requests/dia:
Padrões adicionais por ordem de impacto:
max_tokens apertado evita modelo viajar; cobre via stop_sequences.Observability cost trap: tracing toda chamada LLM com prompt + response inteiro multiplica span size por 10-100x vs span normal. Use head-based sampling (full conversation amostra) ou tail-based (samp anomalias: erro, latência > p99, custo > X). Senão observability cost ultrapassa LLM cost.
Números típicos 2026 (Anthropic Sonnet 4.6, 1k input tokens):
| Métrica | Sem otimização | Com otimização |
|---|---|---|
| Time to first token (TTFT) | 800-2000ms | 200-500ms (cache hit + region próxima) |
| Throughput per stream | 30-100 tok/s | mesmo (model-bound) |
| Prompt cache hit latency | — | ~10-30% redução em TTFT |
Streaming via SSE muda tudo na UX: TTFT é o que o usuário sente. Cold sync (block até resposta completa) com 5s output total = "lento". Stream mostrando primeiro token em 400ms = "responsivo", mesmo com mesma duração total. Stream sempre que UX permitir — só não-stream se workflow exige resposta atomic (ex: structured JSON pra DB write).
Em UX agent multi-step, usuário aguarda sequência de tool calls. Padrões:
Cruza com 02-14 (streaming SSE/WebSocket), 03-09 (perceived latency UX), 04-09 (LLM observability cost trap).
Default: LLM como augment, não core decision-maker, em domínios sensíveis.
§2.10 introduz agents como conceito. §2.20 entra em patterns operacionais que separam agent que sobrevive em produção de protótipo de demo. Três patterns dominam:
Cada um endereça modo de falha específico. Combinados, viram backbone de agent operacional.
Pattern 1: Planner-executor split. Sem split, LLM "raciocina e age" no mesmo step. Resultado: loop infinito de tool calls, custo explode em minutos, comportamento opaco pra debug. Com split, planner produz JSON/XML estruturado de steps (declarativo, validável); executor (código tradicional) valida schema + roda cada step com observability normal. Vantagens: replay determinístico do mesmo plano, auditoria step-a-step, abort early se step inválido, paralelização de steps independentes.
import { z } from 'zod';
const PlanStep = z.discriminatedUnion('action', [
z.object({
action: z.literal('lookup_courier'),
courier_id: z.string().uuid(),
}),
z.object({
action: z.literal('verify_documents'),
courier_id: z.string().uuid(),
doc_types: z.array(z.enum(['cnh', 'crlv', 'antecedentes'])),
}),
z.object({
action: z.literal('schedule_orientation'),
courier_id: z.string().uuid(),
slot_iso: z.string().datetime(),
}),
z.object({
action: z.literal('notify_courier'),
courier_id: z.string().uuid(),
channel: z.enum(['whatsapp', 'sms', 'email']),
template: z.string(),
}),
]);
const Plan = z.object({
reasoning: z.string().max(500),
steps: z.array(PlanStep).min(1).max(10),
});
Executor com budget cap + audit log:
async function executePlan(plan: Plan, ctx: Context) {
const audit: AuditEntry[] = [];
for (const step of plan.steps) {
const start = Date.now();
try {
const result = await executeStep(step, ctx);
audit.push({ step, status: 'ok', latency_ms: Date.now() - start, result });
if (audit.length > 20) throw new Error('Plan exceeded step budget');
} catch (err) {
audit.push({ step, status: 'error', latency_ms: Date.now() - start, error: String(err) });
if (step.action === 'verify_documents') throw err; // critical: abort
// soft errors: log + continue (notify failures are non-blocking)
}
}
await db.audit_log.insert({ trace_id: ctx.trace_id, plan, audit });
return audit;
}
Discriminated union no schema = exhaustive switch no executor (TypeScript compiler reclama de step não tratado). Audit log persiste plano + execução pra postmortem. Budget cap (max steps, max latency total, max tool calls) é mandatory; sem ele, agent runaway custa $1000+ em uma noite — caso real, não hipotético.
Pattern 2: Critic loop (LLM avalia LLM). Modo de falha: planner produz output sintaticamente válido (passa Zod) mas semanticamente errado — agendou orientation pra horário inexistente, trocou courier_id entre steps, escolheu canal whatsapp pra courier sem WhatsApp opt-in. Critic é segundo LLM (mesmo modelo ou diferente) que recebe input + output do planner + system prompt focado em "find errors". Output estruturado: { valid: bool, errors: [...] }. Loop: se critic invalida, retry planner com errors como context. Cap em 3 iterações pra evitar custo runaway.
import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic();
async function critiquePlan(input: string, plan: Plan): Promise<{ valid: boolean; errors: string[] }> {
const resp = await anthropic.messages.create({
model: 'claude-haiku-4-5-20251001', // critic é mais barato/rápido que planner
max_tokens: 500,
system: `Você é um critic agent. Recebe input e plano gerado por outro agent.
Sua tarefa: identificar erros lógicos, datas inválidas, IDs incoerentes, steps fora de ordem.
Retorne JSON: { "valid": bool, "errors": [string] }. Se valid, errors=[].`,
messages: [{
role: 'user',
content: `INPUT:\n${input}\n\nPLAN:\n${JSON.stringify(plan, null, 2)}`,
}],
});
const text = resp.content[0].type === 'text' ? resp.content[0].text : '';
return JSON.parse(text);
}
async function planWithCritic(input: string, ctx: Context): Promise<Plan> {
let lastErrors: string[] | undefined;
for (let attempt = 0; attempt < 3; attempt++) {
const plan = await generatePlan(input, ctx, lastErrors);
const review = await critiquePlan(input, plan);
if (review.valid) return plan;
lastErrors = review.errors;
}
throw new Error('Failed to produce valid plan after 3 attempts');
}
Critic usa modelo mais barato (Haiku 4.5) — heurística sólida: planner Sonnet/Opus, critic Haiku. Em produção: ~10-20% das primeiras tentativas falham critic; retry resolve 70-80% delas; resto vai pra fallback humano. Critic com mesmo modelo + mesmo prompt do planner é anti-pattern: vai concordar com mesmos erros (motivated reasoning). Use modelo diferente OU prompt deliberadamente adversarial.
Pattern 3: Tool selection com retrieval. Passar 50 tools no system prompt = context pollution + tool selection ruim. Anthropic e OpenAI documentam degradação acima de ~20 tools. Solução: catalog de tools indexado (vector DB ou keyword search). Pre-step roda LLM cheap pra escolher top-k tools relevantes ao query; main agent recebe só esses k.
import { embed } from '@/lib/embeddings';
type ToolDef = {
name: string;
description: string;
embedding?: number[];
input_schema: object;
};
const allTools: ToolDef[] = [
{ name: 'lookup_courier', description: 'Find courier by id or name', input_schema: {} },
{ name: 'schedule_pickup', description: 'Schedule pickup with available slot', input_schema: {} },
// ... 50+ tools
];
// Index step (cron / on tool register)
for (const tool of allTools) {
tool.embedding = await embed(`${tool.name}: ${tool.description}`);
}
async function selectTools(query: string, k = 5): Promise<ToolDef[]> {
const queryEmb = await embed(query);
return allTools
.map(t => ({ tool: t, score: cosineSimilarity(queryEmb, t.embedding!) }))
.sort((a, b) => b.score - a.score)
.slice(0, k)
.map(s => s.tool);
}
async function runAgent(query: string) {
const tools = await selectTools(query);
return anthropic.messages.create({
model: 'claude-sonnet-4-6',
tools: tools.map(({ embedding, ...rest }) => rest), // strip embedding
messages: [{ role: 'user', content: query }],
});
}
Embedding model leve (text-embedding-3-small ou cohere embed v3): ~$0.00002 por query, +20ms latency. Combine retrieval + always-include core tools (auth, error handling, escalate_to_human) — top-5 por relevance + 3 fixed. Sem fallback "always include", agent não sabe pedir help quando tools relevantes não foram retrieved.
Combinando patterns — Logística onboarding agent end-to-end.
User query → Tool retrieval (top-5 + 3 fixed) → Planner LLM (Sonnet 4.6)
→ Critic loop (Haiku 4.5, ≤3 iter) → Executor (audit + budget) → Result
Cada bloco é testável isoladamente: planner com golden inputs, critic com adversarial plans, executor com mock context. Custo típico Logística agent (Sonnet 4.6 planner + Haiku 4.5 critic + Sonnet 4.6 main): ~$0.04-0.08 por session, p99 latency 8-15s.
Anti-patterns observados.
reasoning string do plan: é o ÚNICO sinal de "por que" o plan foi escolhido. Audit sem ela é inútil.Quando NÃO usar agentic patterns.
Cruza com 04-10 §2.10 (agents fundamentos), 04-10 §2.11 (evals validam patterns end-to-end), 04-10 §2.13 (observability LLM mede agent quality), 03-07 §2.19 (AI ops trace agent steps), 03-08 §2.x (PII em audit log de agent precisa redaction).
§2.10 introduz RAG como pattern; §2.21 trata como sistema engenheirado com camadas mensuráveis. Naive RAG (embed query → top-K vector search → concat → LLM) resolve ~50% das queries em produção. Cada camada subsequente endereça um modo de falha específico, com cost/benefit decay agressivo no topo da stack.
Spectrum de arquitetura RAG (5 níveis).
Cost vs benefit decay: L0→L1 é o ganho gigante (+20pp por ~1.2x cost); L3→L4 é marginal (+5pp por ~5x cost). Default produção 2026: L2 (hybrid + rerank). Suba pra L3/L4 só com eval suite que prove ganho > 2pp em golden set.
Hybrid retrieval — Postgres pgvector 0.7+ + tsvector. Schema único, dois índices, RRF inline:
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE docs (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
content TEXT NOT NULL,
embedding vector(1536),
tsv tsvector GENERATED ALWAYS AS (to_tsvector('portuguese', content)) STORED
);
CREATE INDEX docs_embedding ON docs USING hnsw (embedding vector_cosine_ops);
CREATE INDEX docs_tsv ON docs USING gin (tsv);
CREATE INDEX docs_tenant ON docs (tenant_id);
-- $1 = query embedding, $2 = query text, $3 = tenant_id
WITH dense AS (
SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> $1) AS rank
FROM docs WHERE tenant_id = $3
ORDER BY embedding <=> $1 LIMIT 50
),
sparse AS (
SELECT id, ROW_NUMBER() OVER (ORDER BY ts_rank(tsv, plainto_tsquery('portuguese', $2)) DESC) AS rank
FROM docs WHERE tenant_id = $3 AND tsv @@ plainto_tsquery('portuguese', $2)
LIMIT 50
)
SELECT id, SUM(1.0 / (60 + rank)) AS rrf_score
FROM (SELECT * FROM dense UNION ALL SELECT * FROM sparse) u
GROUP BY id ORDER BY rrf_score DESC LIMIT 10;
60 é o k canônico de RRF (Cormack et al. 2009) — não toque sem eval. RRF é robusto a magnitudes incomparáveis entre dense/sparse scores; é por isso que vence weighted-sum em benchmarks.
Chunking strategy. Sweet spot 256-512 tokens, overlap 10-20%. Opções:
\n\n, headings). Respeita estrutura.["\n\n", "\n", ". ", " "]; cai pra próximo se chunk > limit.Chunk 4096 tokens é anti-pattern: retrieval precision colapsa, top-K vira 1 doc gigante irrelevante 80% do conteúdo. Chunk 64 tokens é o outro extremo: contexto insuficiente pro LLM responder.
Reranker (cross-encoder). Pattern: top-50 do hybrid → cross-encoder rerank → top-5-10 pro LLM. Cohere Rerank v3.5 (2025): $1/1k searches, latency ~200ms, multilingual incluindo PT-BR. Self-host alternativo: BGE-reranker via Text Embeddings Inference (TEI).
import { CohereClient } from 'cohere-ai';
const cohere = new CohereClient({ token: process.env.COHERE_API_KEY! });
async function retrieveWithRerank(query: string, tenantId: string) {
const candidates = await hybridSearch(query, tenantId, 50);
const ranked = await cohere.rerank({
model: 'rerank-v3.5',
query,
documents: candidates.map(c => c.content),
topN: 8,
});
return ranked.results.map(r => ({
...candidates[r.index],
rerank_score: r.relevanceScore,
}));
}
Latency budget: cap top-K candidates em 50 (não 200) — cross-encoder escala linear; 200 candidates = 800ms+ p99.
Query rewriting + HyDE. Multi-query: LLM gera 3-5 variações; retrieval em cada; dedup por doc id; merge. HyDE (Hypothetical Document Embeddings): LLM gera resposta sintética à query, embed da resposta, search por docs similares à resposta. Contraintuitivo, mas robusto pra queries curtas/ambíguas — embedding de doc-shaped texto casa melhor com docs reais que embedding de question-shaped texto.
Evaluation harness — disciplina de produção. Sem eval suite, mudança de prompt/retrieval é roleta. Componentes mandatory:
(query, expected_answer, source_doc_ids) curadas manualmente. Cobre happy path + edge cases + adversarial.Tools 2026: RAGAS 0.2+ (Python, faithfulness/answer-relevance/context-precision/context-recall via LLM-as-judge), Phoenix (Arize, LLM eval + RAG-specific debugging), DeepEval (Pytest-style assertions), Promptfoo (CLI eval framework).
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall
from datasets import Dataset
ds = Dataset.from_dict({
"question": [...],
"answer": [...], # output do RAG
"contexts": [...], # docs retrieved (lista de listas)
"ground_truth": [...], # expected answer do golden set
})
result = evaluate(ds, metrics=[faithfulness, answer_relevancy, context_precision, context_recall])
print(result)
CI pattern: cada PR que toca prompt/retrieval/chunking roda eval suite; regressão > 2pp em qualquer metric blocks merge. Online eval: 1-5% do tráfego sampleado, LLM-as-judge async grava score (cruza com 03-07 §2.19). Sempre human spot-check ~10% do output do judge — judge bias amplifica silencioso sem audit humano.
Cost optimization 2026.
Numbers Logística: 10k queries/dia × ($0.001 retrieval + $0.005 LLM) ≈ $1800/mês. Com prompt caching + tiered routing: ~$500/mês.
Production patterns Logística — KB docs + customer support.
Use case real: lojista pergunta "Como configurar webhook de status de entrega?". Pipeline:
NL query → embed (text-embedding-3-small)
→ hybrid search (pgvector + tsvector, RRF, top-50)
→ Cohere Rerank v3.5 (top-8)
→ LLM (Sonnet 4.6) com docs + citation enforcement
→ resposta com [doc_id] inline
Use case que NÃO é RAG: "Quantas entregas atrasadas no mês passado?" — structured data. Pipeline correto: query → query understanding LLM extrai filtros (tenant_id, date_range, status='delayed') → SQL aggregation → result. RAG sobre structured data é anti-pattern (LLM faz contagem errada lendo docs).
Citation enforcement + refusal. System prompt obriga LLM a citar [doc_id] por claim; UI verifica citations e renderiza link pro source. Sem context suficiente: "Não encontrei essa informação na documentação" em vez de hallucinate. Refusal pattern reduz hallucination ~80% e é trivial de adicionar.
Streaming + interactivity. LLM stream via SSE (cruza com 02-14 §2.10) pra perceived latency. UI render: retrieval (1-2s) → "Pesquisando docs..." → stream LLM response (chunks ao chegando). Tool calls inline: LLM streams partial response, hits tool boundary, resumes após tool call return.
Anti-patterns observados.
Cruza com: 02-15 (search engines, BM25 + relevance tuning), 02-09 (Postgres, pgvector + tsvector), 03-07 §2.19 (LLM observability + eval pipeline), 04-04 (resilience, fallback quando LLM down → degrade pra hybrid sem LLM), 04-09 (scaling, embedding pipeline + eval pipeline como batch jobs).
Eval e fine-tuning são as duas alavancas que separam LLM-toy de LLM-em-produção. Sem eval, não há critério para decidir prompt change, model swap, ou fine-tune ROI. Sem fine-tuning bem indicado, custos disparam ou latência mata UX. Decisão correta exige hierarquia clara.
Eval hierarchy (L0 → L5):
Eval frameworks 2026:
Promptfoo example — Logística intent classifier:
# promptfooconfig.yaml
prompts:
- "Classifique a intenção: {{query}}"
providers:
- openai:gpt-4o-mini
- anthropic:claude-haiku-4-5
tests:
- vars: { query: "Quero cancelar o pedido 123" }
assert:
- type: equals
value: "cancel_order"
- vars: { query: "Onde está minha entrega?" }
assert:
- type: equals
value: "track_order"
- vars: { query: "Como faço para criar conta?" }
assert:
- type: equals
value: "signup_help"
- type: latency
threshold: 1000 # ms
npx promptfoo eval
npx promptfoo view # web UI mostrando resultados, side-by-side por provider
Fine-tuning decision tree 2026:
NÃO fine-tune se:
Fine-tune SE:
Fine-tuning techniques:
OpenAI fine-tuning (gpt-4o-mini, Jul 2024+):
openai api fine_tuning.jobs.create \
-t file-abc123 \
-m "gpt-4o-mini-2024-07-18"
# training data — JSONL chat format
{"messages": [{"role": "system", "content": "Classifique intenção em português."}, {"role": "user", "content": "Quero cancelar"}, {"role": "assistant", "content": "cancel_order"}]}
{"messages": [{"role": "system", "content": "Classifique intenção em português."}, {"role": "user", "content": "Cadê meu pedido"}, {"role": "assistant", "content": "track_order"}]}
Self-hosted LoRA (Llama 3.3 70B + PEFT 0.12+):
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model, TaskType
from trl import SFTTrainer
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.3-70B-Instruct")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.3-70B-Instruct")
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16, # rank — controls capacity vs cost
lora_alpha=32,
lora_dropout=0.05,
target_modules=["q_proj", "v_proj"],
)
model = get_peft_model(model, lora_config)
trainer = SFTTrainer(
model=model,
train_dataset=train_dataset,
tokenizer=tokenizer,
dataset_text_field="text",
max_seq_length=2048,
)
trainer.train()
trainer.model.save_pretrained("./logistica-lora-v1")
Decisão Prompt eng vs RAG vs Fine-tuning:
Logística aplicado — eval program:
Anti-patterns (10 itens):
Cruza com: 04-10 §2.21 (RAG architectures + RAGAS eval), 04-10 §2.15 (fine-tuning vs prompt intro, decisão básica), 04-10 §2.20 (agentic patterns, eval de tool use), 03-07 §2.19 (LLM observability + tracing), 02-15 (search engines, eval framework shared com IR).
Model Context Protocol (MCP) é o USB-C para AI applications: spec aberta da Anthropic (rev 2025-06-18, stable) que padroniza como LLM hosts conectam a tools, resources e prompts externos. Antes do MCP, cada app (Claude Desktop, Cursor, Zed, Continue) reinventava sua camada de function calling — fragmentação total. Pós-MCP, escreve-se um servidor e qualquer host compatível consome. Em 2026, MCP é de facto o protocolo de interop entre LLM hosts e fontes de contexto/ferramentas; ignorar significa rebuildar wiring proprietário N vezes.
Architecture (host ↔ client ↔ server):
id, method, params, result/error).Claude Desktop (host)
├── client_1 ──stdio──> mcp-server-filesystem (Node subprocess)
├── client_2 ──stdio──> mcp-server-github (Node subprocess)
└── client_3 ──Streamable HTTP──> https://mcp.acme.com/orders (remote)
Transports (escolha = local vs remoto):
Capability negotiation (handshake obrigatório):
// client → server: initialize request
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
"protocolVersion":"2025-06-18",
"capabilities":{"roots":{"listChanged":true},"sampling":{}},
"clientInfo":{"name":"claude-desktop","version":"0.10.0"}
}}
// server → client: initialize result
{"jsonrpc":"2.0","id":1,"result":{
"protocolVersion":"2025-06-18",
"capabilities":{"tools":{"listChanged":true},"resources":{"subscribe":true},"prompts":{}},
"serverInfo":{"name":"orders-mcp","version":"1.2.0"}
}}
// client → server: notification post-handshake
{"jsonrpc":"2.0","method":"notifications/initialized"}
Capabilities declaram o que cada lado suporta: server expõe tools, resources, prompts, logging, completion; client expõe roots (filesystem boundaries), sampling (server pode pedir LLM completion via client — agentic).
Primitives (3 que importam):
tools/list retorna schemas, tools/call executa. Equivalente a function calling, mas portável. Cada tool = {name, description, inputSchema (JSON Schema)}.file://, postgres://, github://) que client pode resources/list, resources/read, resources/subscribe. Read-only por design.prompts/list retorna templates parametrizáveis. UI do host expõe como slash-commands ou botões (Claude Desktop: /prompt-name).Real MCP servers (2026 ecosystem):
modelcontextprotocol/servers): filesystem, github, gitlab, slack, postgres, sqlite, brave-search, puppeteer, fetch, memory, time, sequentialthinking, everything (test server).modelcontextprotocol.io/servers).~/Library/Application Support/Claude/claude_desktop_config.json):{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/nicolas/projects"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxx"}
},
"orders": {
"url": "https://mcp.acme.com/orders",
"headers": {"Authorization": "Bearer ${ORDERS_TOKEN}"}
}
}
}
Building um MCP server (TypeScript SDK @modelcontextprotocol/sdk v1.x):
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
const server = new Server(
{ name: "orders-mcp", version: "1.2.0" },
{ capabilities: { tools: {} } },
);
const GetOrderSchema = z.object({ orderId: z.string().regex(/^ord_[a-z0-9]{12}$/) });
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [{
name: "get_order",
description: "Fetch order by ID. Returns status, courier, ETA, items. Read-only.",
inputSchema: {
type: "object",
properties: { orderId: { type: "string", pattern: "^ord_[a-z0-9]{12}$" } },
required: ["orderId"],
},
}],
}));
server.setRequestHandler(CallToolRequestSchema, async (req) => {
if (req.params.name !== "get_order") throw new Error(`Unknown tool: ${req.params.name}`);
const { orderId } = GetOrderSchema.parse(req.params.arguments);
const order = await db.orders.findUnique({ where: { id: orderId } }); // read-only conn
if (!order) return { content: [{ type: "text", text: `Order ${orderId} not found` }], isError: true };
return { content: [{ type: "text", text: JSON.stringify(order, null, 2) }] };
});
const transport = new StdioServerTransport();
await server.connect(transport);
Para Streamable HTTP remoto: troque StdioServerTransport por StreamableHTTPServerTransport + Express handler em POST /mcp com session IDs em header Mcp-Session-Id.
Multi-agent patterns 2026 (MCP fornece tools, frameworks orquestram agentes):
max_iterations (15-25).transfer_to_<agent>. Sem hierarquia. Útil pra customer support routing (triage → billing → technical).// Supervisor pattern (LangGraph esqueleto)
const supervisor = new StateGraph(AgentState)
.addNode("supervisor", supervisorNode) // LLM decide next agent
.addNode("researcher", researcherNode) // usa MCP brave-search + fetch
.addNode("coder", coderNode) // usa MCP filesystem + github
.addEdge("researcher", "supervisor")
.addEdge("coder", "supervisor")
.addConditionalEdges("supervisor", routeNext, { researcher: "researcher", coder: "coder", END: END })
.compile({ checkpointer, interruptBefore: ["coder"] }); // human-in-loop antes de write
MCP wins quando: precisa expor mesma tool pra múltiplos hosts (Claude Desktop + Cursor + agente custom), quer ecosystem de servers prontos (GitHub, Slack, Postgres), quer separar concerns (server team owns tool, agent team owns orchestration). MCP overkill quando: app único usa LLM via API direto sem host externo — function calling nativo do provider é mais simples (sem subprocess/HTTP overhead).
Stack Logística aplicada: MCP server logistics-mcp expõe (a) tool get_order_status(orderId) read-only contra Postgres replica, (b) tool find_courier_eta(orderId) que chama API courier interna, (c) resource order://recent listando últimos 50 orders do tenant ativo, (d) prompt triage_complaint template pra agente de suporte. Claude Desktop dos atendentes consome via Streamable HTTP autenticado por mTLS + JWT por tenant. Agente de suporte roda em LangGraph (supervisor + worker refund_handler que requer human approval via interruptBefore).
10 anti-patterns:
resources/list retorna 10k items sem pagination/cursor — context window overflow + custo.max_iterations/recursion_limit — loop infinito, custo $$$, timeout do host.apiKey como param) — LLM loga, vaza em tracing/replay.inputSchema server-side com Zod/Pydantic — confia que LLM seguiu schema; ele não seguiu.Cruza com: 04-10 §2.6 (function calling foundation — MCP é a versão portável), 04-10 §2.10 (agents intro, ReAct), 04-10 §2.20 (agentic patterns + eval de tool use), 04-10 §2.13 (LLM observability — tracing de MCP calls), 03-08 (security — tool auth, sandbox, prompt injection defense), 04-05 (API design — JSON-RPC 2.0 vs REST), 02-16 §2.18 (GraphRAG patterns Microsoft Apr 2024 + Neo4j GenAI integrations), 05-01 §2.16 (toy GPT scratch — fundamentos LLM internals), 05-02 §2.11 (Capstones MCP agentic + on-device AI + tool-use orchestrator), 05-04 §2.15 (LLM papers 2021-2026 reading list), 05-09 §2.19 (scientific computing — overlap em ML inference patterns).
Você precisa, sem consultar:
Adicionar AI assistant ao Logística com production-quality.
@anthropic-ai/sdk).getOrders, getReport, rescheduleOrder, assignCourier, getCourierStatus.Threshold de Maestria
Acerte todas as 5 pra marcar o módulo como concluído. Sem pressa, sem timer. Tudo fica salvo no teu navegador.
Q1Por que prompt caching (Anthropic ephemeral cache) é uma das primeiras otimizações de custo a aplicar?
Q2Qual é o principal modo de falha do pattern critic loop quando se usa o mesmo modelo + mesmo prompt do planner?
Q3Quando vale escolher pgvector em vez de Pinecone/Qdrant para um projeto?
Q4Por que o pattern planner-executor split é superior a deixar o LLM 'raciocinar e agir' no mesmo step?
Q5Em qual situação NÃO usar LLM como decision-maker é a recomendação correta?
Destrava
04-10 é prereq dos seguintes módulos: