1. Chaves e variáveis de ambiente
Nunca hardcode a API key. Use variáveis de ambiente e, em produção, um secret manager (GitHub Actions secrets, Doppler, AWS Secrets Manager, etc.).
# .env (nunca commitado)
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_API_KEY=AIza...
No Node: dotenv ou variáveis nativas do runtime. No Python: python-dotenv ou o loader do framework.
2. OpenAI
Node.js
import OpenAI from "openai";
const client = new OpenAI(); // lê OPENAI_API_KEY
const completion = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [
{ role: "system", content: "Você é um assistente de engenharia de software." },
{ role: "user", content: "Explique este erro de TypeScript..." }
],
temperature: 0.2,
});
console.log(completion.choices[0].message.content);
Python
from openai import OpenAI
client = OpenAI() # OPENAI_API_KEY
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Você é um assistente de engenharia de software."},
{"role": "user", "content": "Explique este erro de TypeScript..."}
],
temperature=0.2,
)
print(resp.choices[0].message.content)
3. Anthropic Claude
Node.js
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic(); // ANTHROPIC_API_KEY
const msg = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
messages: [{ role: "user", content: "Revise este diff de segurança..." }],
});
console.log(msg.content[0].text);
Python
import anthropic
client = anthropic.Anthropic()
msg = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": "Revise este diff de segurança..."}],
)
print(msg.content[0].text)
Claude usa messages (não há role system separado no mesmo formato; use o parâmetro system no create quando precisar).
4. Google Gemini
Python (SDK oficial)
import google.generativeai as genai
import os
genai.configure(api_key=os.environ["GOOGLE_API_KEY"])
model = genai.GenerativeModel("gemini-2.0-flash")
resp = model.generate_content("Gere um schema JSON para um pedido de e-commerce")
print(resp.text)
No Node, use @google/generative-ai com o mesmo fluxo: configurar a key e chamar generateContent.
5. Streaming no front-end
Para UX de “digitando…”, use streaming. Exemplo genérico com Server-Sent Events (backend Node):
// backend (express)
app.post("/api/chat", async (req, res) => {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.flushHeaders();
const stream = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: req.body.messages,
stream: true,
});
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content || "";
if (delta) res.write(`data: ${JSON.stringify({ delta })}\n\n`);
}
res.write("data: [DONE]\n\n");
res.end();
});
No front, consuma com EventSource ou fetch + ReadableStream e vá concatenando o texto na UI.
6. Checklist de produção
- Timeouts e retries com backoff (erros 429 e 5xx)
- Limite de tokens de entrada e saída
- Logging de custo (tokens in/out) por usuário ou feature
- Sanitização de prompt: não deixe o usuário injetar instruções que quebrem o system prompt
- Fallback: se um provedor cair, ter um segundo modelo configurado
- Observabilidade: latência p50/p95, taxa de erro, tokens médios
Com esses três clientes você cobre a maior parte dos casos. Escolha o modelo conforme a tarefa (código, raciocínio longo, custo) e mantenha a camada de aplicação desacoplada do provedor — facilita trocar depois.
7. Os parâmetros explicados
| Provedor | Pacote | Variável de ambiente | Chamada principal | Onde vem o texto |
|---|---|---|---|---|
| OpenAI | openai | OPENAI_API_KEY | chat.completions.create | choices[0].message.content |
| Anthropic | anthropic ou @anthropic-ai/sdk | ANTHROPIC_API_KEY | messages.create | content[0].text |
| Google Gemini | google-generativeai ou @google/generative-ai | GOOGLE_API_KEY | generate_content | resp.text |
| Parâmetro | O que faz |
|---|---|
model | Escolhe o modelo. Os nomes mudam e alguns são descontinuados, então confira a lista atual do provedor. |
messages | A conversa: uma lista de mensagens, cada uma com role (system, user ou assistant) e content. |
temperature | Controla a variação. Valores baixos, como 0,2, deixam as respostas mais estáveis. |
max_tokens | Limite de tamanho da resposta. Na API da Anthropic ele é obrigatório. |
system (Anthropic) | Instruções de sistema, passadas em um parâmetro separado das mensagens. |
stream: true | Devolve a resposta em pedaços, à medida que são gerados. |
Streaming com Server-Sent Events
| Trecho | O que faz |
|---|---|
Content-Type: text/event-stream | Avisa ao navegador que a resposta é um fluxo de eventos. |
Cache-Control: no-cache | Impede que a resposta seja guardada em cache. |
res.flushHeaders() | Envia os cabeçalhos na hora, antes do primeiro pedaço de texto. |
for await (const chunk of stream) | Percorre os pedaços conforme chegam do provedor. |
res.write("data: ...\n\n") | Formato de evento SSE: a linha começa com data: e termina com uma linha em branco. |
data: [DONE] | Marcador combinado para o front saber que terminou. |
8. Erros comuns e como resolver
| Sintoma | Causa provável | Solução |
|---|---|---|
| Erro 401 ou "invalid API key" | A chave está errada ou o .env não foi carregado. | Confirme se a variável existe no processo e se o dotenv roda antes do cliente. |
| Erro 429 (limite de requisições) | Muitas requisições ou cota esgotada. | Faça nova tentativa com espera crescente e defina limites por usuário. |
| Erro 404 ou "model not found" | O nome do modelo mudou ou foi descontinuado. | Atualize para um modelo atual da lista do provedor. |
| O streaming chega tudo de uma vez | Um proxy, como o Nginx, está guardando a resposta em buffer. | Desative o buffering no proxy, com proxy_buffering off;. |
| Uma chave foi parar no repositório | O .env foi commitado. | Revogue a chave imediatamente, gere outra e inclua o .env no .gitignore. |