Pular para o conteúdo
MÓDULO 3.5

🔌 Servindo via API local compatível com OpenAI

Até agora você conversou com o modelo pelo terminal e por uma interface. Agora vem a peça que liga o modelo ao resto do mundo: uma API HTTP que fala o mesmo idioma da OpenAI. Aprenda a chamar seu modelo local de qualquer app, script ou agente — e, no fim, a expô-lo na sua tailnet com segurança.

6
Tópicos
~40
Minutos
Inter.
Nível
Prático
Tipo

Conteúdo detalhado

1

🔌 Por que uma API local

Uma interface de chat é ótima para você conversar. Mas um programa não conversa por uma tela — ele precisa de um endereço para mandar texto e receber resposta. Esse endereço é a API. O truque do Ollama é falar exatamente o mesmo "formato" que a OpenAI usa: assim, qualquer app, script ou agente que já sabe falar com o ChatGPT funciona com seu modelo local trocando uma única linha.

🆕 Novo aqui?

"API" (Interface de Programação de Aplicações) é uma porta de entrada por onde um programa pede algo a outro. "Endpoint" é o endereço exato dessa porta — uma URL como http://localhost:11434/v1/chat/completions. "OpenAI-compatível" quer dizer que o formato do pedido e da resposta é idêntico ao da API da OpenAI — mesmas chaves model, messages, choices — só que servido pela sua máquina.

🔑 A ideia central

A compatibilidade é a alavanca. Com ela você ganha três coisas de uma vez:

  • Reuso: bibliotecas, SDKs e ferramentas feitas para a OpenAI funcionam sem reescrever nada.
  • Portabilidade: troque entre nuvem e local mudando só a base_url — o código continua igual.
  • Automação: scripts e agentes (Trilha 4) passam a usar o modelo sem você abrir tela nenhuma.

✗ Sem API (só chat na tela)

  • Cada uso exige você digitando à mão
  • Nada de automação ou agendamento
  • Impossível plugar em outros programas
  • Sem agentes 24/7

✓ Com API local

  • Qualquer script chama o modelo via HTTP
  • Mesmo SDK da OpenAI, modelo na sua máquina
  • Base para automações, RAG e agentes
  • Tudo offline, sem pedágio por token
API
porta do modelo
Compatível
formato OpenAI
Troca
só a base_url
Destrava
scripts & agentes
2

🌐 O endpoint do Ollama

Quando você instalou o Ollama (Módulo 3.1), ele já subiu um servidor HTTP escutando em http://localhost:11434. localhost = a própria máquina; 11434 = a porta. Esse servidor oferece dois conjuntos de rotas: as nativas do Ollama e as OpenAI-compatíveis sob /v1. Para reaproveitar apps e SDKs existentes, use sempre as de /v1.

O caminho de uma requisição, do seu programa até o modelo e de volta:

1

Seu programa monta o pedido

Um JSON com o model e a lista de messages — exatamente o formato da OpenAI.

2

O servidor Ollama recebe na :11434

Ele carrega o modelo na memória (se ainda não estiver) e roda a inferência localmente.

3

A resposta volta em JSON

No mesmo formato da OpenAI: o texto vem em choices[0].message.content.

🗺️ As rotas que importam

Objetivo: conhecer os endereços. As de /v1 são as que você usa com SDKs prontos.

# Rotas nativas do Ollama
POST http://localhost:11434/api/chat
POST http://localhost:11434/api/generate
# Rotas OpenAI-compatíveis (use estas com SDKs/apps)
POST http://localhost:11434/v1/chat/completions
POST http://localhost:11434/v1/embeddings
GET  http://localhost:11434/v1/models

Como verificar: rode curl http://localhost:11434/v1/models — deve listar, em JSON, os modelos que você já baixou.

💡 Dica prática

Se a porta 11434 não responde, o servidor não está de pé. No Linux/macOS o app do Ollama já o mantém rodando; se precisar subir manualmente, use ollama serve. "Connection refused" quase sempre é isso: o serviço não está ativo ou está em outra máquina.

3

⌨️ Primeira chamada com curl

A forma mais rápida de provar que a API funciona é o curl — uma ferramenta de terminal que faz requisições HTTP. Nenhuma instalação de biblioteca, nenhum código: só o terminal e um JSON. Se isto responder, sua API local está no ar.

⌨️ Exemplo prático: chat via curl

Objetivo: mandar uma mensagem ao modelo e receber a resposta em JSON. Cole no terminal (com o Ollama rodando e o modelo qwen2.5:7b já baixado).

curl http://localhost:11434/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen2.5:7b",
    "messages": [
      {"role": "system", "content": "Responda em PT-BR, conciso."},
      {"role": "user", "content": "Explique o que é uma API em 1 frase."}
    ]
  }'

Como verificar: a saída é um JSON com um campo choices. O texto da resposta está em choices[0].message.content. Se aparecer, você acabou de servir seu modelo por API.

📊 Anatomia do JSON de resposta

  • model — qual modelo respondeu.
  • choices[0].message.content — o texto gerado (é o que você quer).
  • choices[0].finish_reason — por que parou (ex.: stop).
  • usage — contagem de tokens de entrada e saída.
curl
teste sem código
-d
o corpo JSON
messages
system + user
content
a resposta
4

🐍 Do código: SDK da OpenAI apontado pro local

Aqui a compatibilidade brilha. O SDK oficial da OpenAI (a biblioteca openai em Python) funciona contra o Ollama — basta mudar a base_url para o seu endpoint local e passar uma api_key qualquer (o Ollama não valida a chave; ela é exigida só porque o SDK pede uma).

🐍 Exemplo prático: chat em Python

Objetivo: chamar o modelo local pelo SDK da OpenAI. Instale com pip install openai e rode.

# pip install openai
from openai import OpenAI
client = OpenAI(
    base_url="http://localhost:11434/v1",  # aponta pro Ollama local
    api_key="ollama",               # qualquer string; o Ollama ignora
)
resp = client.chat.completions.create(
    model="qwen2.5:7b",
    messages=[{"role": "user", "content": "Diga oi em uma frase."}],
)
print(resp.choices[0].message.content)

Como verificar: o script imprime a frase gerada pelo modelo. Para voltar à nuvem, troque base_url e a chave — o resto do código fica idêntico. Esse é o ponto inteiro.

💡 Dica prática

Em JavaScript/TypeScript é a mesma história: o pacote openai aceita baseURL: "http://localhost:11434/v1" e apiKey: "ollama". Guarde a base_url numa variável de ambiente (OPENAI_BASE_URL) para alternar local/nuvem sem tocar no código.

✓ Faça

  • Use o nome de modelo que aparece em ollama list
  • Centralize base_url numa env var
  • Trate timeouts: o 1º request carrega o modelo (mais lento)

✗ Evite

  • Esquecer o /v1 no fim da base_url
  • Usar um nome de modelo que não foi baixado
  • Pôr uma chave real da OpenAI no código local
5

🌊 Streaming, embeddings e múltiplos modelos

A mesma API faz mais três coisas que você vai usar muito: streaming (a resposta chega palavra a palavra, como no ChatGPT), embeddings (transformar texto em vetores — base do RAG, no próximo módulo) e rotear por modelo (basta mudar o campo model).

🌊 Streaming: resposta token a token

Objetivo: mostrar o texto enquanto ele é gerado, sem esperar o fim. Basta stream=True.

stream = client.chat.completions.create(
    model="qwen2.5:7b",
    messages=[{"role": "user", "content": "Conte até cinco."}],
    stream=True,  # liga o modo streaming
)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="", flush=True)

Como verificar: o texto aparece aos poucos, não de uma vez. No curl, o equivalente é pôr "stream": true no corpo JSON.

🧮 Embeddings: texto vira vetor

Objetivo: gerar o vetor de significado de um texto — o tijolo do RAG (Módulo 3.6). Use um modelo de embedding como nomic-embed-text (baixe com ollama pull nomic-embed-text).

curl http://localhost:11434/v1/embeddings \
  -H "Content-Type: application/json" \
  -d '{
    "model": "nomic-embed-text",
    "input": "texto que vira um vetor de significado"
  }'

Como verificar: a resposta traz data[0].embedding — uma longa lista de números. É isso que um banco vetorial guarda para buscar por similaridade.

🔀 Vários modelos, um só endpoint

Você não sobe um servidor por modelo. O mesmo endpoint serve todos os que estão baixados — basta trocar o campo model: um modelo pequeno e rápido para classificação, um maior para raciocínio, um de embedding para RAG. O Ollama carrega/descarrega conforme a demanda.

stream
token a token
embeddings
texto → vetor
model
roteia o pedido
1 endpoint
N modelos
6

🔒 Expondo na tailnet com segurança

Por padrão, a API só responde em localhost — ninguém de fora alcança. Mas no Módulo 2.4 você montou uma tailnet (rede privada via Tailscale). Juntando as duas peças, você usa o modelo de casa pelo notebook do trabalho ou pelo celular — tudo dentro da sua rede privada, sem abrir nada para a internet.

App / Script SDK OpenAI · curl Outro device via Tailscale (100.x) API local Ollama · :11434 · /v1 Modelo qwen2.5 · gemma · …

Leia da esquerda para a direita: o app/script (roxo) chama a API local na porta 11434, que roda o modelo. O ramo em ciano mostra outro dispositivo alcançando a mesma API pela tailnet — sem expor nada à internet pública.

⌨️ Exemplo prático: abrir na tailnet

Objetivo: fazer o Ollama escutar em toda a interface (não só em localhost) para os outros devices da sua tailnet alcançarem.

# Na MÁQUINA que serve o modelo: escute em todas as interfaces
OLLAMA_HOST=0.0.0.0:11434 ollama serve
# Descubra o IP dela na tailnet (100.x.y.z)
tailscale ip -4
# De OUTRO device na tailnet, chame por esse IP
curl http://100.x.y.z:11434/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen2.5:7b","messages":[{"role":"user","content":"oi"}]}'

Como verificar: do segundo device, o curl com o IP 100.x deve responder em JSON. Se não responder, cheque se ambos estão na tailnet (tailscale status).

⚠️ Alerta: nunca exponha à internet pública

OLLAMA_HOST=0.0.0.0 abre a porta para qualquer rede a que a máquina esteja conectada. Dentro da tailnet isso é seguro: só seus devices autenticados enxergam o 100.x. Na internet aberta, não. A API do Ollama não tem login nem senha — qualquer um que alcance a porta usa (e abusa) do seu modelo.

  • NUNCA faça port-forward da 11434 no roteador.
  • NUNCA aponte a porta para um IP público.
  • Acesse remoto SÓ pela tailnet (IP 100.x) — é o caminho seguro.

✓ Tailnet (seguro)

  • Só devices seus e autenticados
  • Tráfego criptografado ponta a ponta
  • Nenhuma porta aberta no roteador

✗ Internet pública (perigoso)

  • Qualquer um na web alcança a porta
  • Sem login: API totalmente aberta
  • Modelo sequestrado e conta de luz sua

Auto-checagem (opcional): para usar o SDK da OpenAI com seu modelo local, o que você ajusta?

localhost
padrão fechado
0.0.0.0
abre na tailnet
100.x
IP do Tailscale
Pública
jamais

📌 Resumo do módulo

A API é a porta do modelo — sem ela, só dá pra conversar à mão; com ela, scripts e agentes usam o modelo.
Ollama serve em :11434 com rotas /v1 — formato idêntico ao da OpenAI; troca-se só a base_url.
curl prova, o SDK automatiza — mesma lib da OpenAI com base_url local e api_key qualquer.
Streaming, embeddings e N modelos num endpoint — e remoto só pela tailnet, nunca na internet pública.

Próximo módulo:

3.6 — Embeddings, memória & RAG