Clientes Go para o Ollama: comparação de SDKs e exemplos com Qwen3/GPT-OSS
Integre o Ollama com Go: guia do SDK, exemplos e melhores práticas para produção.
Este guia oferece uma visão abrangente dos SDKs Go para Ollama e compara seus conjuntos de funcionalidades.
Exploraremos exemplos práticos em Go para chamar os modelos Qwen3 e GPT-OSS hospedados no Ollama, tanto via chamadas de API REST brutas quanto pelo cliente oficial Go, incluindo o tratamento detalhado dos modos de raciocínio (thinking) e não raciocínio no Qwen3.

Por que Ollama + Go?
O Ollama expõe uma API HTTP pequena e pragmática (geralmente executando em http://localhost:11434) projetada para cargas de trabalho de geração e chat, com suporte nativo a streaming e capacidades de gerenciamento de modelos. A documentação oficial cobre exaustivamente as estruturas de solicitação/resposta de /api/generate e /api/chat e a semântica de streaming.
Go é uma excelente escolha para criar clientes Ollama devido ao seu forte suporte de biblioteca padrão para HTTP, excelente manipulação de JSON, primitivas de concorrência nativas e interfaces com tipos estáticos que capturam erros em tempo de compilação. Para ver como o Ollama se compara ao vLLM, Docker Model Runner, LocalAI e provedores de nuvem — incluindo quando escolher cada um — veja Hospedagem de LLM: Infraestrutura Local, Autohospedada e em Nuvem Comparadas.
Em outubro de 2025, estas são as opções de SDK Go que você provavelmente considerará.
SDKs Go para Ollama — o que está disponível?
| SDK / Pacote | Status e “dono” | Escopo (Geração/Chat/Streaming) | Gerenciamento de modelo (pull/list/etc.) | Extras / Observações |
|---|---|---|---|---|
github.com/ollama/ollama/api |
Pacote Oficial dentro do repositório Ollama; usado pelo próprio CLI ollama |
Cobertura Completa mapeada ao REST; streaming suportado | Sim | Considerado o cliente Go canônico; a API espelha a documentação de perto. |
LangChainGo (github.com/tmc/langchaingo/llms/ollama) |
Framework comunitário (LangChainGo) com módulo LLM Ollama | Chat/Completion + streaming via abstrações do framework | Limitado (gerenciamento de modelo não é o objetivo principal) | Ótimo se você quiser cadeias, ferramentas e vetores em Go; menos focado em SDK bruto. |
github.com/swdunlop/ollama-client |
Cliente comunitário | Foco em chat; bons experimentos de chamada de ferramentas | Parcial | Construído para experimentos com chamada de ferramentas; não é uma superfície 1:1 completa. |
Outros SDKs comunitários (ex., ollamaclient, “go-ollama-sdk” de terceiros) |
Comunidade | Varia | Varia | Qualidade e cobertura variam; avalie por repositório. |
Recomendação: Para produção, prefira github.com/ollama/ollama/api — é mantido junto com o projeto principal e espelha a API REST.
Qwen3 & GPT-OSS no Ollama: raciocínio (thinking) vs sem raciocínio (o que você precisa saber)
- Modo de raciocínio no Ollama separa o “raciocínio” do modelo da saída final quando ativado. A documentação do Ollama trata o comportamento de ativar/desativar raciocínio como uma funcionalidade de primeira classe em modelos suportados.
- (https://www.glukhov.org/pt/llm-performance/benchmarks/qwen3-30b-vs-gpt-oss-20b/ “Qwen3:30b vs GPT-OSS:20b: Detalhes técnicos, comparação de desempenho e velocidade”) suporta alternância dinâmica: adicione
/thinkou/no_thinknas mensagens de sistema/usuário para alternar modos turno a turno; a instrução mais recente prevalece. - GPT-OSS: usuários relatam que desativar o raciocínio (ex.,
/set nothinkou--think=false) pode ser pouco confiável emgpt-oss:20b; planeje-se para filtrar/ocultar qualquer raciocínio que sua interface não deve exibir.
Parte 1 — Chamando Ollama via REST bruto (Go, net/http)
Tipos compartilhados
Primeiro, vamos definir os tipos comuns e funções auxiliares que usaremos em nossos exemplos:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
// ---- Tipos da API de Chat ----
type ChatMessage struct {
Role string `json:"role"`
Content string `json:"content"`
}
type ChatRequest struct {
Model string `json:"model"`
Messages []ChatMessage `json:"messages"`
// Alguns servidores expõem controle de raciocínio como uma flag booleana.
// Mesmo que omitido, você ainda pode controlar o Qwen3 via tags /think ou /no_think.
Think *bool `json:"think,omitempty"`
Stream *bool `json:"stream,omitempty"`
Options map[string]any `json:"options,omitempty"`
}
type ChatResponse struct {
Model string `json:"model"`
CreatedAt string `json:"created_at"`
Message struct {
Role string `json:"role"`
Content string `json:"content"`
Thinking string `json:"thinking,omitempty"` // presente quando raciocínio está ativado
} `json:"message"`
Done bool `json:"done"`
}
// ---- Tipos da API de Geração ----
type GenerateRequest struct {
Model string `json:"model"`
Prompt string `json:"prompt"`
Think *bool `json:"think,omitempty"`
Stream *bool `json:"stream,omitempty"`
Options map[string]any `json:"options,omitempty"`
}
type GenerateResponse struct {
Model string `json:"model"`
CreatedAt string `json:"created_at"`
Response string `json:"response"` // texto final para não-streaming
Thinking string `json:"thinking,omitempty"` // presente quando raciocínio está ativado
Done bool `json:"done"`
}
// ---- Funções Auxiliares ----
func httpPostJSON(url string, payload any) ([]byte, error) {
body, err := json.Marshal(payload)
if err != nil {
return nil, err
}
c := &http.Client{Timeout: 60 * time.Second}
resp, err := c.Post(url, "application/json", bytes.NewReader(body))
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
// bptr retorna um ponteiro para um valor booleano
func bptr(b bool) *bool { return &b }
Chat — Qwen3 com raciocínio ATIVADO (e como desativá-lo)
func chatQwen3Thinking() error {
endpoint := "http://localhost:11434/api/chat"
req := ChatRequest{
Model: "qwen3:8b-thinking", // qualquer tag :*-thinking que você tenha baixado
Think: bptr(true),
Stream: bptr(false),
Messages: []ChatMessage{
{Role: "system", Content: "Você é um assistente preciso."},
{Role: "user", Content: "Explique recursão com um exemplo curto em Go."},
},
}
raw, err := httpPostJSON(endpoint, req)
if err != nil {
return err
}
var out ChatResponse
if err := json.Unmarshal(raw, &out); err != nil {
return err
}
fmt.Println("🧠 raciocínio:\n", out.Message.Thinking)
fmt.Println("\n💬 resposta:\n", out.Message.Content)
return nil
}
// Desative o raciocínio para o próximo turno:
// (a) definindo Think=false, e/ou
// (b) adicionando "/no_think" à mensagem de sistema/usuário mais recente (alternador suave do Qwen3).
// O Qwen3 respeita a última instrução /think ou /no_think em chats multi-turno.
func chatQwen3NoThinking() error {
endpoint := "http://localhost:11434/api/chat"
req := ChatRequest{
Model: "qwen3:8b-thinking",
Think: bptr(false),
Stream: bptr(false),
Messages: []ChatMessage{
{Role: "system", Content: "Seja breve. /no_think"},
{Role: "user", Content: "Explique recursão em uma frase."},
},
}
raw, err := httpPostJSON(endpoint, req)
if err != nil {
return err
}
var out ChatResponse
if err := json.Unmarshal(raw, &out); err != nil {
return err
}
// Espere que o raciocínio esteja vazio; ainda assim, trate defensivamente.
if out.Message.Thinking != "" {
fmt.Println("🧠 raciocínio (inesperado):\n", out.Message.Thinking)
}
fmt.Println("\n💬 resposta:\n", out.Message.Content)
return nil
}
(O alternador suave /think e /no_think do Qwen3 é documentado pela equipe do Qwen; a última instrução prevalece em chats multi-turno.)
Chat — GPT-OSS com raciocínio (e uma ressalva)
func chatGptOss() error {
endpoint := "http://localhost:11434/api/chat"
req := ChatRequest{
Model: "gpt-oss:20b",
Think: bptr(true), // solicite raciocínio separado se suportado
Stream: bptr(false),
Messages: []ChatMessage{
{Role: "user", Content: "O que é programação dinâmica? Explique a ideia central."},
},
}
raw, err := httpPostJSON(endpoint, req)
if err != nil {
return err
}
var out ChatResponse
if err := json.Unmarshal(raw, &out); err != nil {
return err
}
// Comportamento conhecido: desativar o raciocínio pode não suprimir totalmente o raciocínio no gpt-oss:20b.
// Sempre filtre/oculte o raciocínio na UI se você não quiser exibi-lo.
fmt.Println("🧠 raciocínio:\n", out.Message.Thinking)
fmt.Println("\n💬 resposta:\n", out.Message.Content)
return nil
}
Usuários relatam que desativar o raciocínio em gpt-oss:20b (ex., /set nothink ou --think=false) pode ser ignorado — planeje-se para filtragem no lado do cliente se necessário.
Geração — Qwen3 e GPT-OSS
func generateQwen3() error {
endpoint := "http://localhost:11434/api/generate"
req := GenerateRequest{
Model: "qwen3:4b-thinking",
Prompt: "Em 2–3 frases, para que são usadas as Árvores B em bancos de dados?",
Think: bptr(true),
}
raw, err := httpPostJSON(endpoint, req)
if err != nil {
return err
}
var out GenerateResponse
if err := json.Unmarshal(raw, &out); err != nil {
return err
}
if out.Thinking != "" {
fmt.Println("🧠 raciocínio:\n", out.Thinking)
}
fmt.Println("\n💬 resposta:\n", out.Response)
return nil
}
func generateGptOss() error {
endpoint := "http://localhost:11434/api/generate"
req := GenerateRequest{
Model: "gpt-oss:20b",
Prompt: "Explique brevemente a retropropagação em redes neurais.",
Think: bptr(true),
}
raw, err := httpPostJSON(endpoint, req)
if err != nil {
return err
}
var out GenerateResponse
if err := json.Unmarshal(raw, &out); err != nil {
return err
}
if out.Thinking != "" {
fmt.Println("🧠 raciocínio:\n", out.Thinking)
}
fmt.Println("\n💬 resposta:\n", out.Response)
return nil
}
As formas REST e o comportamento de streaming vêm diretamente da referência da API do Ollama.
Parte 2 — Chamando Ollama via o SDK Go oficial (github.com/ollama/ollama/api)
O pacote oficial expõe um Client com métodos que correspondem à API REST. O próprio CLI do Ollama usa este pacote para se comunicar com o serviço, o que o torna a aposta mais segura para compatibilidade.
Instalação
go get github.com/ollama/ollama/api
Chat — Qwen3 (raciocínio ATIVADO / DESATIVADO)
package main
import (
"context"
"fmt"
"log"
"github.com/ollama/ollama/api"
)
func chatWithQwen3Thinking(ctx context.Context, thinking bool) error {
client, err := api.ClientFromEnvironment() // respeita OLLAMA_HOST se definido
if err != nil {
return err
}
req := &api.ChatRequest{
Model: "qwen3:8b-thinking",
// Muitas versões do servidor expõem o raciocínio como uma flag de nível superior;
// além disso, você pode controlar o Qwen3 via /think ou /no_think nas mensagens.
Think: api.Ptr(thinking),
Messages: []api.Message{
{Role: "system", Content: "Você é um assistente preciso."},
{Role: "user", Content: "Explique merge sort com um trecho curto de Go."},
},
}
var resp api.ChatResponse
if err := client.Chat(ctx, req, &resp); err != nil {
return err
}
if resp.Message.Thinking != "" {
fmt.Println("🧠 raciocínio:\n", resp.Message.Thinking)
}
fmt.Println("\n💬 resposta:\n", resp.Message.Content)
return nil
}
func main() {
ctx := context.Background()
if err := chatWithQwen3Thinking(ctx, true); err != nil {
log.Fatal(err)
}
// Exemplo: sem raciocínio
if err := chatWithQwen3Thinking(ctx, false); err != nil {
log.Fatal(err)
}
}
Chat — GPT-OSS (trate o raciocínio defensivamente)
func chatWithGptOss(ctx context.Context) error {
client, err := api.ClientFromEnvironment()
if err != nil {
return err
}
req := &api.ChatRequest{
Model: "gpt-oss:20b",
Think: api.Ptr(true),
Messages: []api.Message{
{Role: "user", Content: "O que é memoização e quando é útil?"},
},
}
var resp api.ChatResponse
if err := client.Chat(ctx, req, &resp); err != nil {
return err
}
// Se você pretende ocultar o raciocínio, faça isso aqui independentemente das flags.
if resp.Message.Thinking != "" {
fmt.Println("🧠 raciocínio:\n", resp.Message.Thinking)
}
fmt.Println("\n💬 resposta:\n", resp.Message.Content)
return nil
}
Geração — Qwen3 & GPT-OSS
func generateWithQwen3(ctx context.Context) error {
client, err := api.ClientFromEnvironment()
if err != nil {
return err
}
req := &api.GenerateRequest{
Model: "qwen3:4b-thinking",
Prompt: "Resuma o papel de uma Árvore B na indexação.",
Think: api.Ptr(true),
}
var resp api.GenerateResponse
if err := client.Generate(ctx, req, &resp); err != nil {
return err
}
if resp.Thinking != "" {
fmt.Println("🧠 raciocínio:\n", resp.Thinking)
}
fmt.Println("\n💬 resposta:\n", resp.Response)
return nil
}
func generateWithGptOss(ctx context.Context) error {
client, err := api.ClientFromEnvironment()
if err != nil {
return err
}
req := &api.GenerateRequest{
Model: "gpt-oss:20b",
Prompt: "Explique a descida do gradiente em termos simples.",
Think: api.Ptr(true),
}
var resp api.GenerateResponse
if err := client.Generate(ctx, req, &resp); err != nil {
return err
}
if resp.Thinking != "" {
fmt.Println("🧠 raciocínio:\n", resp.Thinking)
}
fmt.Println("\n💬 resposta:\n", resp.Response)
return nil
}
A superfície do pacote oficial espelha a documentação REST e é atualizada junto com o projeto principal.
Respostas em Streaming
Para streaming em tempo real, defina Stream: bptr(true) na sua solicitação. A resposta será entregue como blocos JSON separados por nova linha:
func streamChatExample() error {
endpoint := "http://localhost:11434/api/chat"
req := ChatRequest{
Model: "qwen3:8b-thinking",
Think: bptr(true),
Stream: bptr(true), // Ativar streaming
Messages: []ChatMessage{
{Role: "user", Content: "Explique o algoritmo quicksort passo a passo."},
},
}
body, _ := json.Marshal(req)
resp, err := http.Post(endpoint, "application/json", bytes.NewReader(body))
if err != nil {
return err
}
defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
for {
var chunk ChatResponse
if err := decoder.Decode(&chunk); err == io.EOF {
break
} else if err != nil {
return err
}
// Processe raciocínio e conteúdo conforme chegam
if chunk.Message.Thinking != "" {
fmt.Print(chunk.Message.Thinking)
}
fmt.Print(chunk.Message.Content)
if chunk.Done {
break
}
}
return nil
}
Com o SDK oficial, use uma função de callback para lidar com os blocos de streaming:
func streamWithOfficialSDK(ctx context.Context) error {
client, _ := api.ClientFromEnvironment()
req := &api.ChatRequest{
Model: "qwen3:8b-thinking",
Think: api.Ptr(true),
Messages: []api.Message{
{Role: "user", Content: "Explique árvores de busca binária."},
},
}
err := client.Chat(ctx, req, func(resp api.ChatResponse) error {
if resp.Message.Thinking != "" {
fmt.Print(resp.Message.Thinking)
}
fmt.Print(resp.Message.Content)
return nil
})
return err
}
Trabalhando com raciocínio vs sem raciocínio do Qwen3 (orientação prática)
-
Dois alavancas:
- Uma flag booleana
thinkingsuportada pelo recurso de raciocínio do Ollama; e - Comandos de alternador suave do Qwen3
/thinke/no_thinkna mensagem de sistema/usuário mais recente. A instrução mais recente governa o próximo turno(s).
- Uma flag booleana
-
Postura padrão: sem raciocínio para respostas rápidas; escalar para raciocínio para tarefas que precisam de raciocínio passo a passo (matemática, planejamento, depuração, análise de código complexa).
-
UIs em Streaming: quando o raciocínio está ativado, você pode ver raciocínio/conteúdo entrelaçados nos quadros streamados — faça buffer ou renderize-os separadamente e forneça aos usuários um alternador “mostrar raciocínio”. (Veja a documentação da API para o formato de streaming.)
-
Conversas multi-turno: O Qwen3 lembra o modo de raciocínio de turnos anteriores. Se você quiser alterná-lo no meio da conversa, use tanto a flag quanto o comando de alternador suave para confiabilidade.
Notas para GPT-OSS
- Trate o raciocínio como presente mesmo que você tenha tentado desativá-lo; filtre no cliente se sua UX não deve exibi-lo.
- Para aplicativos de produção usando GPT-OSS, implemente lógica de filtragem no lado do cliente que possa detectar e remover padrões de raciocínio, se necessário.
- Teste exaustivamente sua variante específica do modelo GPT-OSS, pois o comportamento pode variar entre diferentes quantizações e versões.
Melhores práticas e dicas de produção
Tratamento de erros e tempos limite (timeouts)
Sempre implemente tratamento de tempo limite e recuperação de erros apropriados:
func robustChatRequest(ctx context.Context, model string, messages []api.Message) (*api.ChatResponse, error) {
// Defina um tempo limite razoável
ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
defer cancel()
client, err := api.ClientFromEnvironment()
if err != nil {
return nil, fmt.Errorf("criando cliente: %w", err)
}
req := &api.ChatRequest{
Model: model,
Messages: messages,
Options: map[string]interface{}{
"temperature": 0.7,
"num_ctx": 4096, // tamanho da janela de contexto
},
}
var resp api.ChatResponse
if err := client.Chat(ctx, req, &resp); err != nil {
return nil, fmt.Errorf("solicitação de chat falhou: %w", err)
}
return &resp, nil
}
Pool de conexões e reutilização
Reutilize o cliente Ollama entre solicitações em vez de criar um novo a cada vez:
type OllamaService struct {
client *api.Client
}
func NewOllamaService() (*OllamaService, error) {
client, err := api.ClientFromEnvironment()
if err != nil {
return nil, err
}
return &OllamaService{client: client}, nil
}
func (s *OllamaService) Chat(ctx context.Context, req *api.ChatRequest) (*api.ChatResponse, error) {
var resp api.ChatResponse
if err := s.client.Chat(ctx, req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
Configuração de ambiente
Use variáveis de ambiente para implantação flexível:
export OLLAMA_HOST=http://localhost:11434
export OLLAMA_NUM_PARALLEL=2
export OLLAMA_MAX_LOADED_MODELS=2
O SDK oficial respeita automaticamente OLLAMA_HOST via api.ClientFromEnvironment().
Monitoramento e logs
Implemente log estruturado para sistemas de produção:
func loggedChat(ctx context.Context, logger *log.Logger, req *api.ChatRequest) error {
start := time.Now()
client, _ := api.ClientFromEnvironment()
var resp api.ChatResponse
err := client.Chat(ctx, req, &resp)
duration := time.Since(start)
logger.Printf("model=%s duration=%v error=%v tokens=%d",
req.Model, duration, err, len(resp.Message.Content))
return err
}
Conclusão
-
Para projetos Go,
github.com/ollama/ollama/apié a escolha mais completa e pronta para produção. É mantido junto com o projeto principal do Ollama, usado pelo CLI oficial e oferece cobertura completa da API com compatibilidade garantida. -
Para abstrações de nível superior, considere LangChainGo quando você precisar de cadeias, ferramentas, armazenamentos vetoriais e pipelines RAG, embora você troque algum controle de baixo nível por conveniência.
-
Qwen3 oferece controle limpo e confiável sobre o modo de raciocínio com flags e alternadores de nível de mensagem (
/think,/no_think), tornando-o ideal para aplicativos que precisam tanto de respostas rápidas quanto de raciocínio profundo. -
Para GPT-OSS, sempre planeje-se para sanitizar a saída de raciocínio no lado do cliente quando necessário, pois a flag de desativação de raciocínio pode não ser consistentemente respeitada.
-
Na produção, implemente tratamento de erros adequado, pool de conexões, tempos limite e monitoramento para construir aplicativos robustos alimentados pelo Ollama.
A combinação da tipagem forte do Go, excelente suporte a concorrência e API direta do Ollama torna esta uma pilha ideal para construir aplicativos alimentados por IA — de chatbots simples a sistemas RAG complexos.
Principais conclusões
Aqui está uma referência rápida para escolher sua abordagem:
| Caso de Uso | Abordagem Recomendada | Por quê |
|---|---|---|
| Aplicativo de produção | github.com/ollama/ollama/api |
Suporte oficial, cobertura completa da API, mantido com o projeto principal |
| Pipeline RAG/cadeias/ferramentas | LangChainGo | Abstrações de alto nível, integrações com armazenamentos vetoriais |
| Aprendizado/experimentação | REST bruto com net/http | Transparência total, sem dependências, educativo |
| Prototipagem rápida | SDK Oficial | Equilíbrio entre simplicidade e poder |
| UI de chat em streaming | SDK Oficial com callbacks | Suporte de streaming nativo, API limpa |
Orientação de seleção de modelo:
- Qwen3: Melhor para aplicativos que exigem modo de raciocínio controlável e conversas multi-turno confiáveis
- GPT-OSS: Desempenho forte, mas exige tratamento defensivo da saída de raciocínio
- Outros modelos: Teste exaustivamente; o comportamento de raciocínio varia por família de modelos
Referências e leitura adicional
Documentação oficial
- Referência da API Ollama — Documentação completa da API REST
- Pacote Go oficial Ollama — Documentação do SDK Go
- Repositório do GitHub do Ollama — Código-fonte e problemas
Alternativas de SDK Go
- Integração Ollama LangChainGo — Para aplicativos baseados em cadeias
- swdunlop/ollama-client — Cliente comunitário com chamada de ferramentas
- xyproto/ollamaclient — Outra opção comunitária
Recursos específicos de modelo
- Documentação Qwen — Informações oficiais do modelo Qwen
- Informações GPT-OSS — Detalhes do modelo GPT-OSS
Tópicos relacionados
- Construindo aplicativos RAG com Go — Exemplos LangChainGo
- Pacote Go context — Essencial para tempos limite e cancelamento
- Melhores práticas do cliente HTTP Go — Documentação da biblioteca padrão
Outros Links Úteis
Para uma comparação mais ampla do Ollama com outras infraestruturas LLM locais e em nuvem, consulte nosso Hospedagem de LLM: Infraestrutura Local, Autohospedada e em Nuvem Comparadas.
- Instalar e configurar Ollama
- Cheat sheet do Ollama
- Cheat sheet do Go
- Arquitetura e Implementação de Sistemas RAG
- Reranking de documentos de texto com Ollama e modelo de Embedding Qwen3 - em Go
- Restringindo LLMs com Saída Estruturada: Ollama, Qwen3 & Python ou Go
- Comparação de LLMs: Qwen3:30b vs GPT-OSS:20b
- Problemas de Saída Estruturada do Ollama GPT-OSS