Clientes Go para Ollama: comparação de SDK e exemplos com Qwen3/GPT-OSS

Integre o Ollama com Go: guia do SDK, exemplos e melhores práticas para produção.

Conteúdo da página

Este guia fornece uma visão abrangente dos disponíveis SDKs Go para Ollama e compara seus conjuntos de funcionalidades.

Vamos explorar exemplos práticos em Go para chamar os modelos Qwen3 e GPT-OSS hospedados no Ollama—ambos via chamadas REST API brutas e o cliente oficial Go—incluindo o tratamento detalhado dos modos de pensamento e não-pensamento no Qwen3.

go e ollama

Por que Ollama + Go?

Ollama expõe uma pequena e pragmática API HTTP (normalmente rodando em http://localhost:11434) projetada para gerar e chat trabalhos, com suporte integrado a streaming e capacidades de gerenciamento de modelos. A documentação oficial aborda detalhadamente as estruturas de solicitação/resposta e semântica de streaming para /api/generate e /api/chat.

Go é uma excelente escolha para construir clientes Ollama devido ao seu forte suporte à biblioteca padrão HTTP, excelente manipulação de JSON, primitivos de concorrência nativos e interfaces estáticamente tipadas que capturam erros no momento da compilação. Para ver como o Ollama se compara com vLLM, Docker Model Runner, LocalAI e provedores de nuvem — incluindo quando escolher cada um — veja LLM Hosting: Local, Self-Hosted & Cloud Infrastructure Compared.

Até outubro de 2025, aqui estão as opções de SDK Go que você provavelmente considerará.


SDKs Go para Ollama — o que está disponível?

SDK / Pacote Status & “proprietário” Escopo (Gerar/Chat/Streaming) Gerenciamento de modelos (puxar/listar/etc.) Extras / Notas
github.com/ollama/ollama/api Pacote oficial dentro do repositório Ollama; usado pelo próprio CLI ollama Total cobertura mapeada para REST; streaming suportado Sim Considerado o canônico cliente Go; API espelha documentos de perto.
LangChainGo (github.com/tmc/langchaingo/llms/ollama) Framework comunitário (LangChainGo) com módulo Ollama LLM Chat/Completar + streaming via abstrações do framework Limitado (gerenciamento de modelos não é o objetivo principal) Excelente se quiser cadeias, ferramentas, armazenamento de vetores em Go; menos de um SDK bruto.
github.com/swdunlop/ollama-client Cliente comunitário Foco em chat; bons experimentos de chamada de ferramentas Parcial Criado para experimentar com chamadas 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 com o projeto principal e espelha a API REST.


Qwen3 & GPT-OSS no Ollama: modo de pensamento vs não-pensamento (o que saber)

  • Modo de pensamento no Ollama separa o “raciocínio” do modelo do resultado final quando ativado. Ollama documenta um comportamento de ativar/desativar o pensamento de primeira classe em todos os 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 /think ou /no_think nas mensagens do sistema/usuário para alternar modos passo a passo; a última instrução vence.
  • GPT-OSS: usuários relatam que desativar o pensamento (ex., /set nothink ou --think=false) pode ser imprevisível no gpt-oss:20b; planeje filtrar/ocultar qualquer raciocínio que sua interface não deva mostrar.

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 o controle de pensamento como um sinal booleano.
	// Mesmo se omitido, ainda assim você pode controlar o Qwen3 via /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 o pensamento 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-stream
	Thinking  string `json:"thinking,omitempty"` // presente quando o pensamento 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 pensamento 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 puxado
		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("🧠 pensamento:\n", out.Message.Thinking)
	fmt.Println("\n💬 resposta:\n", out.Message.Content)
	return nil
}

// Desative o pensamento para a próxima rodada ao:
// (a) definir Think=false, e/ou
// (b) adicionar "/no_think" à mensagem mais recente do sistema/usuário (chave de ativação suave do Qwen3).
// O Qwen3 honra a última instrução /think ou /no_think em chats multirrodadas.
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: "Você é 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 por pensamento estar vazio; ainda assim, trate defensivamente.
	if out.Message.Thinking != "" {
		fmt.Println("🧠 pensamento (inesperado):\n", out.Message.Thinking)
	}
	fmt.Println("\n💬 resposta:\n", out.Message.Content)
	return nil
}

(O soft switch de /think e /no_think do Qwen3 é documentado pela equipe Qwen; a última instrução vence em chats multirrodadas.)

Chat — GPT-OSS com pensamento (e uma observação)

func chatGptOss() error {
	endpoint := "http://localhost:11434/api/chat"

	req := ChatRequest{
		Model:  "gpt-oss:20b",
		Think:  bptr(true),   // solicita 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
	}
	// Conhecida peculiaridade: desativar o pensamento pode não suprimir completamente o raciocínio no gpt-oss:20b.
	// Sempre filtre/oculte o pensamento na interface se não quiser exibi-lo.
	fmt.Println("🧠 pensamento:\n", out.Message.Thinking)
	fmt.Println("\n💬 resposta:\n", out.Message.Content)
	return nil
}

Usuários relatam que desativar o pensamento no gpt-oss:20b (ex., /set nothink ou --think=false) pode ser ignorado—planeje filtragem no lado do cliente se necessário.

Gerar — 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 servem 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("🧠 pensamento:\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 a retropropagação em redes neurais de forma breve.",
		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("🧠 pensamento:\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 Ollama.


Parte 2 — Chamando Ollama via o SDK oficial Go (github.com/ollama/ollama/api)

O pacote oficial expõe um Client com métodos que correspondem à API REST. O próprio CLI Ollama usa este pacote para se comunicar com o serviço, o que o torna a melhor escolha para compatibilidade.

Instalar

go get github.com/ollama/ollama/api

Chat — Qwen3 (pensamento 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() // honra OLLAMA_HOST se definido
	if err != nil {
		return err
	}

	req := &api.ChatRequest{
		Model: "qwen3:8b-thinking",
		// Muitas construções de servidor expõem o pensamento como um sinal 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 o merge sort com um snippet curto em Go."},
		},
	}

	var resp api.ChatResponse
	if err := client.Chat(ctx, req, &resp); err != nil {
		return err
	}

	if resp.Message.Thinking != "" {
		fmt.Println("🧠 pensamento:\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: não-pensando
	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ê planeja ocultar o raciocínio, faça-o aqui independentemente dos sinalizadores.
	if resp.Message.Thinking != "" {
		fmt.Println("🧠 pensamento:\n", resp.Message.Thinking)
	}
	fmt.Println("\n💬 resposta:\n", resp.Message.Content)
	return nil
}

Gerar — 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 em 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("🧠 pensamento:\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 o descenso de 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("🧠 pensamento:\n", resp.Thinking)
	}
	fmt.Println("\n💬 resposta:\n", resp.Response)
	return nil
}

A superfície do pacote oficial espelha os documentos REST e é atualizada junto com o projeto principal.


Respostas de streaming

Para streaming em tempo real, defina Stream: bptr(true) em sua solicitação. A resposta será entregue como fragmentos JSON delimitados por nova linha:

func streamChatExample() error {
	endpoint := "http://localhost:11434/api/chat"
	req := ChatRequest{
		Model:  "qwen3:8b-thinking",
		Think:  bptr(true),
		Stream: bptr(true), // Ative o streaming
		Messages: []ChatMessage{
			{Role: "user", Content: "Explique o algoritmo de 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
		}
		
		// Processar pensamento 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 fragmentos 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 pensamento vs não-pensamento no Qwen3 (orientação prática)

  • Dois levers:

    1. Um sinal booleano thinking suportado pela funcionalidade de pensamento do Ollama; e
    2. O comando de ativação suave do Qwen3 /think e /no_think na mensagem do sistema/usuário mais recente. A instrução mais recente governa a próxima rodada(s).
  • Postura padrão: não-pensando para respostas rápidas; aumente para pensando para tarefas que precisam de raciocínio passo a passo (matemática, planejamento, depuração, análise complexa de código).

  • UIs de streaming: quando o pensamento está ativado, você pode ver raciocínio/contúdo intercalados em frames de streaming—bufferize ou renderize-os separadamente e dê aos usuários um botão “mostrar raciocínio”. (Veja os documentos da API para o formato de streaming.)

  • Conversas multirrodadas: o Qwen3 lembra o modo de pensamento das rodadas anteriores. Se quiser alternar durante a conversa, use tanto o sinal quanto o comando de ativação suave para confiabilidade.

Observações para GPT-OSS

  • Trate o raciocínio como presente mesmo se tentou desativá-lo; filtre no lado do cliente se sua UX não deve mostrá-lo.
  • Para aplicações 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 seu variante específica do modelo GPT-OSS rigorosamente, pois o comportamento pode variar entre diferentes quantizações e versões.

Boas práticas e dicas para produção

Tratamento de erros e timeouts

Sempre implemente tratamento adequado de timeout e recuperação de erros:

func robustChatRequest(ctx context.Context, model string, messages []api.Message) (*api.ChatResponse, error) {
	// Defina um timeout 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
}

Piscina 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 logging

Implemente logging 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. É mantida junto com o projeto principal do Ollama, usada pelo próprio CLI e fornece cobertura abrangente da API com compatibilidade garantida.

  • Para abstrações de nível mais alto, considere LangChainGo quando precisar de cadeias, ferramentas, armazenamento de vetores e pipelines RAG—embora você troque algum controle de nível baixo pela conveniência.

  • Qwen3 lhe dá controle limpo e confiável sobre o modo de pensamento com ambos os sinalizadores e comutadores de nível de mensagem (/think, /no_think), tornando-o ideal para aplicações que precisam de respostas rápidas e raciocínio profundo.

  • Para GPT-OSS, sempre planeje sanitizar a saída de raciocínio do lado do cliente quando necessário, pois o sinalizador de desativação de pensamento pode não ser respeitado consistentemente.

  • Em produção, implemente tratamento adequado de erros, pooling de conexões, timeouts e monitoramento para construir aplicações robustas com Ollama.

A combinação do forte tipagem do Go, excelente suporte à concorrência e API direta do Ollama faz dele um stack ideal para construir aplicações com IA — desde chatbots simples até sistemas complexos RAG.

Principais conclusões

Aqui está uma referência rápida para escolher sua abordagem:

Caso de uso Abordagem recomendada Por quê
Aplicação de produção github.com/ollama/ollama/api Suporte oficial, cobertura completa da API, mantido com o projeto principal
Pipeline de RAG/cadeias/ferramentas LangChainGo Abstrações de nível superior, integrações com armazenamento de vetores
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 de streaming SDK oficial com callbacks Suporte integrado a streaming, API limpa

Orientação de seleção de modelos:

  • Qwen3: Melhor para aplicações que exigem controle sobre o modo de pensamento, conversas multirrodadas confiáveis
  • GPT-OSS: Bom desempenho, mas requer tratamento defensivo da saída de raciocínio
  • Outros modelos: Teste rigorosamente; o comportamento de pensamento varia por família de modelo

Referências e leitura adicional

Documentação oficial

Alternativas ao SDK Go

Recursos específicos de modelo

Tópicos relacionados

Para uma comparação mais ampla do Ollama com outras infraestruturas locais e de nuvem LLM, consulte nossa LLM Hosting: Local, Self-Hosted & Cloud Infrastructure Compared.