Ollama: API Compatível com OpenAI

A API compatível com OpenAI é a ponte para migração — mude duas linhas de código, passe de nuvem para local.

💡 Dica: A API compatível com OpenAI do Ollama torna a migração extremamente simples — basta mudar base_url para http://localhost:11434/v1, definir api_key para qualquer string não vazia (por exemplo, "ollama"), e mudar o nome do Modelo para um Modelo local (por exemplo, "qwen2.5"). Você pode então reutilizar todo o código existente da biblioteca openai. Isso significa que suas aplicações ChatGPT existentes, projetos LangChain e fluxos AutoGen podem mudar para local com zero alterações de código.

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

1. O Que Você Vai Aprender


2. Uma História Real de Uma Fundadora de SaaS

⚠️ Aviso: A API compatível com OpenAI do Ollama não é 100% completa — não suporta Function Calling (Tools), Streaming com tool_calls, Assistants API, etc. Antes de migrar, certifique-se de verificar se sua aplicação depende desses recursos; caso contrário, você precisará mudar para um Agente ReAct ou chamar o endpoint /api/chat diretamente como alternativa.

ℹ️ Info: api_key="ollama" é uma convenção placeholder para o endpoint compatível do Ollama — o Ollama não valida o conteúdo da API Key. Isso significa que api_key pode ser qualquer string (por exemplo, "sk-1234", "dummy"), com funcionalidade idêntica. Autenticação real deve ser implementada na camada de proxy reverso.

(1) O Problema: Custo Mensal do GPT-4 de 2.000 USD

O SupportBot da Alice usa a API do GPT-4, processando 5 milhões de tokens por mês com uma conta de 2.000 USD. A empresa exige redução de custos, mas a migração requer reescrever muito código — todas as chamadas estão vinculadas à biblioteca openai do Python.

(2) A Solução: Mude para Local com Duas Linhas de Código

A API compatível com OpenAI permite que Alice mude apenas base_url e api_key, completando a migração em 5 minutos:

PYTHON
from openai import OpenAI

# Before: OpenAI cloud
# client = OpenAI(api_key="sk-xxx")

# After: Ollama local (only 2 lines changed!)
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

3. Mapeamento de Endpoints Compatíveis

💡 Dica: api_key="ollama" é uma convenção placeholder para a API compatível do Ollama — o Ollama não valida a API Key. Isso significa que qualquer pessoa que saiba o endereço do seu Ollama pode chamá-lo. Em produção, sempre adicione autenticação real por Key via proxy reverso como Nginx.

(1) Referência de Endpoints da API

Endpoint OpenAI Endpoint Compatível do Ollama Status
/v1/chat/completions ✅ Totalmente compatível Uso principal
/v1/completions ✅ Compatível Completions legadas
/v1/embeddings ✅ Compatível Embedding Vetorial
/v1/models ✅ Compatível Listagem de Modelos
/v1/images/generations ❌ Não suportado Geração de imagens
/v1/audio/transcriptions ❌ Não suportado Transcrição de áudio
100%
flowchart LR
    A[OpenAI SDK] -->|troca base_url| B[Ollama /v1/...]
    B --> C[/v1/chat/completions]
    B --> D[/v1/completions]
    B --> E[/v1/embeddings]
    B --> F[/v1/models]
    B --> G[/v1/images ❌]

(2) Diferenças de Compatibilidade Detalhadas

Recurso OpenAI Ollama Compatível Diferença
Streaming Formato SSE ✅ Compatível SSE Consistente
Function Calling Suporte completo ⚠️ Suporte parcial Alguns Modelos suportam
JSON Mode response_format ✅ format=json Consistente
Embeddings text-embedding-3 ✅ Usa nomic-embed-text Modelo diferente
Vision gpt-4o vision ✅ Usa llava Modelo diferente
Fine-tuning Suportado Use Modelfile em vez
Rate Limiting RPM/TPM Requer implementação externa

▶ Exemplo 1: Troca Básica da Biblioteca openai

PYTHON
from openai import OpenAI

# Point to local Ollama server
client = OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama"  # Any non-empty string works
)

# Chat completion (identical to OpenAI API usage)
response = client.chat.completions.create(
    model="qwen2.5",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Explain RAG in 2 sentences"}
    ],
    temperature=0.3
)

print(response.choices[0].message.content)

Saída:

TEXT
# Execution successful

4. Migração de Recursos Centrais

(1) Referência de Migração de Recursos

Recurso Código OpenAI Mudança Ollama Esforço
Chat model="gpt-4" model="qwen2.5" Mudar nome do Modelo
Streaming stream=True Sem mudança necessária Zero mudanças
Embeddings model="text-embedding-3-small" model="nomic-embed-text" Mudar nome do Modelo
JSON Mode response_format={"type": "json_object"} Mesmo Sem mudança necessária
System Prompt messages=[{"role":"system"...}] Mesmo Zero mudanças
Function Calling tools=[...] ⚠️ Suporte parcial do Modelo Precisa de teste

▶ Exemplo 2: Migração de Saída Streaming

PYTHON
from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama"
)

# Streaming works identically
stream = client.chat.completions.create(
    model="qwen2.5",
    messages=[{"role": "user", "content": "Write a short poem about AI"}],
    stream=True,
    temperature=0.7
)

for chunk in stream:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
print()

Saída:

TEXT
# Execution successful

▶ Exemplo 3: Migração de Embeddings

PYTHON
from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama"
)

# Embeddings: just change the model name
response = client.embeddings.create(
    model="nomic-embed-text",  # Was: text-embedding-3-small
    input="What is the return policy for electronics?"
)

print(f"Embedding dimension: {len(response.data[0].embedding)}")
# 768 dimensions for nomic-embed-text

Saída:

TEXT
# Execution successful

▶ Exemplo 4: Migração do Modo JSON

PYTHON
from openai import OpenAI
import json

client = OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama"
)

# JSON mode: identical API
response = client.chat.completions.create(
    model="qwen2.5",
    messages=[
        {"role": "system", "content": "You are a product catalog API."},
        {"role": "user", "content": "List 3 laptops under $500"}
    ],
    response_format={"type": "json_object"},
    temperature=0.3
)

data = json.loads(response.choices[0].message.content)
print(json.dumps(data, indent=2))

Saída:

TEXT
# Execution successful

5. Migração na Prática e Limitações de Compatibilidade

⚠️ Nota: A API compatível com OpenAI do Ollama não é uma implementação 100% completa — Function Calling (Tools) é apenas parcialmente suportado por alguns Modelos e é menos estável que o GPT-4. Assistants API, Fine-tuning API e Batch API não são suportados. Antes de migrar, certifique-se de verificar se sua aplicação depende desses recursos; caso contrário, você precisará usar modo JSON + Prompt para simular Function Calling, ou chamar o endpoint /api/chat diretamente como alternativa.

(1) Checklist de Migração

Item de Verificação Descrição Risco
Nome do Modelo gpt-4 → qwen2.5/llama3.1 Precisa de avaliação de qualidade
Function Calling Testar se funciona corretamente Pode falhar parcialmente
Contexto máximo gpt-4: 128K → varia por Modelo local Notar num_ctx
Limite de tokens de saída Parâmetro max_tokens Precisa de teste
Rate limiting OpenAI RPM → local sem limite Deve construir você mesmo
Concorrência OpenAI alta concorrência → local limitado Precisa de escalabilidade

(2) Alternativas para Recursos Incompatíveis

Recurso OpenAI Alternativa Ollama Implementação
Fine-tuning Modelfile Detalhado na Aula 8
Function Calling Prompt + parsing JSON Implementação manual
Geração de Imagens llava (apenas compreensão) Geração não suportada
Batch API Scripts Shell/Python Auto-orquestração
Assistants API Agente LangChain Detalhado na Aula 13

▶ Exemplo 5: Alternativa ao Function Calling

PYTHON
from openai import OpenAI
import json

client = OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama"
)

# Instead of Function Calling, use JSON mode + prompt
tools = {
    "get_order_status": {"order_id": "string"},
    "process_refund": {"order_id": "string", "amount": "number"},
    "search_products": {"query": "string", "category": "string"}
}

response = client.chat.completions.create(
    model="qwen2.5",
    messages=[
        {"role": "system", "content": f"""You are a customer service bot.
Available tools: {json.dumps(tools)}
If you need to call a tool, respond with JSON: {{"tool": "name", "args": {{...}}}}
Otherwise, respond normally."""},
        {"role": "user", "content": "Where is my order #12345?"}
    ],
    response_format={"type": "json_object"},
    temperature=0.2
)

result = json.loads(response.choices[0].message.content)
if "tool" in result:
    print(f"Call tool: {result['tool']} with args: {result['args']}")
    # Call: get_order_status with {"order_id": "12345"}
else:
    print("Direct response:", result)

Saída:

TEXT
Direct response:

6. Exemplo Abrangente: Migração do SupportBot do GPT-4

PYTHON
# ============================================
# Comprehensive: SupportBot migration
# From GPT-4 to local Ollama with OpenAI SDK
# ============================================

from openai import OpenAI
import json
from dataclasses import dataclass
from typing import Optional

@dataclass
class SupportBotMigrator:
    """SupportBot with easy OpenAI/Ollama switching."""

    # Change these 2 lines to switch between cloud and local
    base_url: str = "http://localhost:11434/v1"
    api_key: str = "ollama"
    model: str = "qwen2.5"
    temperature: float = 0.4

    def __post_init__(self):
        self.client = OpenAI(
            base_url=self.base_url,
            api_key=self.api_key
        )
        self.system_prompt = (
            "You are SupportBot, an e-commerce customer service agent. "
            "Be polite, concise, and helpful. "
            "For order queries, ask for order number. "
            "If unsure, say 'Let me connect you with a human agent.'"
        )
        self.messages = [
            {"role": "system", "content": self.system_prompt}
        ]

    def chat(self, user_input: str) -> str:
        self.messages.append({"role": "user", "content": user_input})
        try:
            response = self.client.chat.completions.create(
                model=self.model,
                messages=self.messages[-10:],  # sliding window
                temperature=self.temperature
            )
            reply = response.choices[0].message.content
            self.messages.append({"role": "assistant", "content": reply})
            return reply
        except Exception as e:
            self.messages.pop()
            return f"Error: {e}"

    def classify_intent(self, user_input: str) -> dict:
        response = self.client.chat.completions.create(
            model=self.model,
            messages=[
                {"role": "system", "content": """Classify the customer intent.
Return JSON: {"intent": "order_status|refund|product_query|shipping|other", "confidence": 0.0-1.0}"""},
                {"role": "user", "content": user_input}
            ],
            response_format={"type": "json_object"},
            temperature=0.1
        )
        return json.loads(response.choices[0].message.content)

    def generate_embedding(self, text: str) -> list[float]:
        response = self.client.embeddings.create(
            model="nomic-embed-text",
            input=text
        )
        return response.data[0].embedding

# Usage - compare cloud vs local
if __name__ == "__main__":
    # Local Ollama (current config)
    bot = SupportBotMigrator()

    # Switch to OpenAI cloud (uncomment to use)
    # bot = SupportBotMigrator(
    #     base_url="https://api.openai.com/v1",
    #     api_key="sk-your-key",
    #     model="gpt-4"
    # )

    queries = [
        "Where is my order #88765?",
        "I want a refund for damaged goods",
        "Does this laptop have HDMI port?"
    ]

    for q in queries:
        intent = bot.classify_intent(q)
        reply = bot.chat(q)
        print(f"Q: {q}")
        print(f"Intent: {intent}")
        print(f"A: {reply[:100]}...")
        print()

❓ Perguntas Frequentes

P: O que devo preencher em api_key? R: O Ollama não valida a API Key — qualquer string não vazia funciona (por exemplo, "ollama"). Mas você deve fornecer uma, caso contrário a biblioteca openai lançará um erro.

P: Function Calling pode ser usado? R: Parcialmente suportado por alguns Modelos (por exemplo, qwen2.5, llama3.1), mas menos estável que o GPT-4. Recomendamos usar modo JSON + prompt para simular Function Calling para mais controle.

P: E se a qualidade degradar após a migração? R: Use uma estratégia híbrida — perguntas simples usam o Modelo 8B local, perguntas complexas recorrem ao GPT-4. Um Modelo 8B cobrindo 80% dos cenários já pode reduzir significativamente os custos.

P: Dimensões diferentes de Embedding afetam o RAG? R: Sim. nomic-embed-text produz 768 dimensões; OpenAI text-embedding-3-small produz 1536 dimensões. Migrar um sistema RAG requer reconstruir o índice Vetorial.

P: Como uso OpenAI e Ollama simultaneamente? R: Instancie dois clientes — um apontando para OpenAI, outro para Ollama. Roteeie requisições para diferentes clientes com base em complexidade ou orçamento.

P: Há diferença de desempenho entre os endpoints /v1 e /api do Ollama? R: Não. O endpoint /v1 é uma camada de compatibilidade sobre o endpoint /api, chamando o mesmo motor de Inferência subjacente. O desempenho é idêntico.


📖 Resumo


📝 Exercícios

  1. Básico (Dificuldade ⭐): Conecte ao Ollama usando a biblioteca openai do Python, complete uma chamada chat.completions e verifique a compatibilidade.
  2. Intermediário (Dificuldade ⭐⭐): Migre um script OpenAI existente (com saída streaming e modo JSON) para Ollama, documentando as mudanças necessárias.
  3. Avançado (Dificuldade ⭐⭐⭐): Implemente um roteador de duplo backend que automaticamente roteia para Ollama local ou GPT-4 em nuvem com base na complexidade da pergunta, e rastreie economia de custos.
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%