Client Go per Ollama: confronto SDK ed esempi con Qwen3/GPT-OSS
Integrazione di Ollama con Go: guida SDK, esempi e migliori pratiche per la produzione.
Questa guida fornisce una panoramica completa delle Go SDK per Ollama disponibili e ne confronta le funzionalità.
Esamineremo esempi pratici in Go per richiamare i modelli Qwen3 e GPT-OSS hostati su Ollama—sia tramite chiamate REST API dirette che il client Go ufficiale—includendo la gestione dettagliata delle modalità con e senza ragionamento (thinking e non-thinking) in Qwen3.

Perché Ollama + Go?
Ollama espone una piccola API HTTP pragmatica (tipicamente eseguita a http://localhost:11434) progettata per carichi di lavoro di generate e chat, con supporto integrato per lo streaming e capacità di gestione dei modelli. La documentazione ufficiale copre a fondo le strutture di richiesta/risposta e la semantica dello streaming per /api/generate e /api/chat.
Go è un’ottima scelta per costruire client Ollama grazie al suo forte supporto per HTTP nella libreria standard, un’eccellente gestione del JSON, primitiva di concorrenza native e interfacce con tipi statici che catturano gli errori in fase di compilazione. Per vedere come Ollama si confronta con vLLM, Docker Model Runner, LocalAI e i provider cloud—including quando scegliere ciascuno—consulta LLM Hosting: Local, Self-Hosted & Cloud Infrastructure Compared.
A partire da ottobre 2025, ecco le opzioni Go SDK che prenderai più probabilmente in considerazione.
Go SDK per Ollama — cosa c’è a disposizione?
| SDK / Pacchetto | Stato e “proprietario” | Ambito (Generate/Chat/Streaming) | Gestione modelli (pull/list/etc.) | Extra / Note |
|---|---|---|---|---|
github.com/ollama/ollama/api |
Pacchetto Ufficiale nel repository Ollama; usato dallo stesso CLI ollama |
Copertura completa mappata su REST; streaming supportato | Sì | Considerato il client Go canonico; l’API rispecchia da vicino la documentazione. |
LangChainGo (github.com/tmc/langchaingo/llms/ollama) |
Framework community (LangChainGo) con modulo LLM Ollama | Chat/Completion + streaming tramite astrazioni del framework | Limitata (la gestione modelli non è l’obiettivo principale) | Ottimo se si desiderano catene, tool, vettori in Go; meno un SDK raw. |
github.com/swdunlop/ollama-client |
Client community | Focus sulla chat; buone sperimentazioni con tool-calling | Parziale | Progettato per sperimentare il tool calling; non è una superficie completa 1:1. |
Altri SDK community (es. ollamaclient, “go-ollama-sdk” di terze parti) |
Community | Variabile | Variabile | Qualità e copertura variano; valutare per ogni repository. |
Raccomandazione: Per la produzione, preferisci github.com/ollama/ollama/api—viene mantenuto insieme al progetto core e rispecchia l’API REST.
Qwen3 & GPT-OSS su Ollama: thinking vs non-thinking (cosa sapere)
- Modalità thinking in Ollama separa il “ragionamento” del modello dall’output finale quando attivata. La nostra scheda rapida Ollama documenta i flag attiva/disattiva thinking (
--think,--hidethinking) per i modelli supportati. - (https://www.glukhov.org/it/llm-performance/benchmarks/qwen3-30b-vs-gpt-oss-20b/ “Qwen3:30b vs GPT-OSS:20b: Dettagli tecnici, prestazioni e confronto di velocità”) supporta il commutamento dinamico: aggiungi
/thinko/no_thinknei messaggi di sistema/utente per cambiare modalità turno per turno; l’istruzione più recente prevale. - GPT-OSS: gli utenti riportano che disabilitare il thinking (es.
/set nothinko--think=false) può essere inaffidabile sugpt-oss:20b; pianifica di filtrare/nascondere qualsiasi ragionamento che la tua interfaccia non dovrebbe mostrare.
Parte 1 — Chiamare Ollama tramite REST diretto (Go, net/http)
Tipi condivisi
Innanzitutto, definiamo i tipi comuni e le funzioni helper che useremo in tutti i nostri esempi:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
// ---- Chat API Types ----
type ChatMessage struct {
Role string `json:"role"`
Content string `json:"content"`
}
type ChatRequest struct {
Model string `json:"model"`
Messages []ChatMessage `json:"messages"`
// Some servers expose thinking control as a boolean flag.
// Even if omitted, you can still control Qwen3 via /think or /no_think tags.
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"` // present when thinking is on
} `json:"message"`
Done bool `json:"done"`
}
// ---- Generate API Types ----
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"` // final text for non-stream
Thinking string `json:"thinking,omitempty"` // present when thinking is on
Done bool `json:"done"`
}
// ---- Helper Functions ----
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 returns a pointer to a boolean value
func bptr(b bool) *bool { return &b }
Chat — Qwen3 con thinking ATTIVO (e come disattivarlo)
func chatQwen3Thinking() error {
endpoint := "http://localhost:11434/api/chat"
req := ChatRequest{
Model: "qwen3:8b-thinking", // any :*-thinking tag you have pulled
Think: bptr(true),
Stream: bptr(false),
Messages: []ChatMessage{
{Role: "system", Content: "You are a precise assistant."},
{Role: "user", Content: "Explain recursion with a short Go example."},
},
}
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("🧠 thinking:\n", out.Message.Thinking)
fmt.Println("\n💬 answer:\n", out.Message.Content)
return nil
}
// Turn thinking OFF for the next turn by:
// (a) setting Think=false, and/or
// (b) adding "/no_think" to the most-recent system/user message (Qwen3 soft switch).
// Qwen3 honors the latest /think or /no_think instruction in multi-turn chats.
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: "You are brief. /no_think"},
{Role: "user", Content: "Explain recursion in one sentence."},
},
}
raw, err := httpPostJSON(endpoint, req)
if err != nil {
return err
}
var out ChatResponse
if err := json.Unmarshal(raw, &out); err != nil {
return err
}
// Expect thinking to be empty; still handle defensively.
if out.Message.Thinking != "" {
fmt.Println("🧠 thinking (unexpected):\n", out.Message.Thinking)
}
fmt.Println("\n💬 answer:\n", out.Message.Content)
return nil
}
(Il soft switch /think e /no_think di Qwen3 è documentato dal team Qwen; l’istruzione ultima prevale nelle chat multi-turno.)
Chat — GPT-OSS con thinking (e un’avvertenza)
func chatGptOss() error {
endpoint := "http://localhost:11434/api/chat"
req := ChatRequest{
Model: "gpt-oss:20b",
Think: bptr(true), // request separated reasoning if supported
Stream: bptr(false),
Messages: []ChatMessage{
{Role: "user", Content: "What is dynamic programming? Explain the core idea."},
},
}
raw, err := httpPostJSON(endpoint, req)
if err != nil {
return err
}
var out ChatResponse
if err := json.Unmarshal(raw, &out); err != nil {
return err
}
// Known quirk: disabling thinking may not fully suppress reasoning on gpt-oss:20b.
// Always filter/hide thinking in UI if you don't want to surface it.
fmt.Println("🧠 thinking:\n", out.Message.Thinking)
fmt.Println("\n💬 answer:\n", out.Message.Content)
return nil
}
Gli utenti riportano che disabilitare il thinking su gpt-oss:20b (es. /set nothink o --think=false) può essere ignorato—pianifica un filtraggio lato client se necessario.
Generate — Qwen3 e GPT-OSS
func generateQwen3() error {
endpoint := "http://localhost:11434/api/generate"
req := GenerateRequest{
Model: "qwen3:4b-thinking",
Prompt: "In 2–3 sentences, what are B-Trees used for in databases?",
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("🧠 thinking:\n", out.Thinking)
}
fmt.Println("\n💬 answer:\n", out.Response)
return nil
}
func generateGptOss() error {
endpoint := "http://localhost:11434/api/generate"
req := GenerateRequest{
Model: "gpt-oss:20b",
Prompt: "Briefly explain backpropagation in neural networks.",
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("🧠 thinking:\n", out.Thinking)
}
fmt.Println("\n💬 answer:\n", out.Response)
return nil
}
Le forme REST e il comportamento dello streaming provengono direttamente dalla referenza API di Ollama.
Parte 2 — Chiamare Ollama tramite il Go SDK ufficiale (github.com/ollama/ollama/api)
Il pacchetto ufficiale espone un Client con metodi che corrispondono all’API REST. Lo stesso CLI di Ollama usa questo pacchetto per comunicare con il servizio, il che lo rende la scelta più sicura per la compatibilità.
Installazione
go get github.com/ollama/ollama/api
Chat — Qwen3 (thinking ON / OFF)
package main
import (
"context"
"fmt"
"log"
"github.com/ollama/ollama/api"
)
func chatWithQwen3Thinking(ctx context.Context, thinking bool) error {
client, err := api.ClientFromEnvironment() // honors OLLAMA_HOST if set
if err != nil {
return err
}
req := &api.ChatRequest{
Model: "qwen3:8b-thinking",
// Many server builds expose thinking as a top-level flag;
// additionally, you can control Qwen3 via /think or /no_think in messages.
Think: api.Ptr(thinking),
Messages: []api.Message{
{Role: "system", Content: "You are a precise assistant."},
{Role: "user", Content: "Explain merge sort with a short Go snippet."},
},
}
var resp api.ChatResponse
if err := client.Chat(ctx, req, &resp); err != nil {
return err
}
if resp.Message.Thinking != "" {
fmt.Println("🧠 thinking:\n", resp.Message.Thinking)
}
fmt.Println("\n💬 answer:\n", resp.Message.Content)
return nil
}
func main() {
ctx := context.Background()
if err := chatWithQwen3Thinking(ctx, true); err != nil {
log.Fatal(err)
}
// Example: non-thinking
if err := chatWithQwen3Thinking(ctx, false); err != nil {
log.Fatal(err)
}
}
Chat — GPT-OSS (gestire il ragionamento in modo difensivo)
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: "What is memoization and when is it useful?"},
},
}
var resp api.ChatResponse
if err := client.Chat(ctx, req, &resp); err != nil {
return err
}
// If you intend to hide reasoning, do it here regardless of flags.
if resp.Message.Thinking != "" {
fmt.Println("🧠 thinking:\n", resp.Message.Thinking)
}
fmt.Println("\n💬 answer:\n", resp.Message.Content)
return nil
}
Generate — 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: "Summarize the role of a B-Tree in indexing.",
Think: api.Ptr(true),
}
var resp api.GenerateResponse
if err := client.Generate(ctx, req, &resp); err != nil {
return err
}
if resp.Thinking != "" {
fmt.Println("🧠 thinking:\n", resp.Thinking)
}
fmt.Println("\n💬 answer:\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: "Explain gradient descent in simple terms.",
Think: api.Ptr(true),
}
var resp api.GenerateResponse
if err := client.Generate(ctx, req, &resp); err != nil {
return err
}
if resp.Thinking != "" {
fmt.Println("🧠 thinking:\n", resp.Thinking)
}
fmt.Println("\n💬 answer:\n", resp.Response)
return nil
}
La superficie del pacchetto ufficiale rispecchia la documentazione REST e viene aggiornata insieme al progetto core.
Risposte con streaming
Per lo streaming in tempo reale, imposta Stream: bptr(true) nella tua richiesta. La risposta verrà consegnata come chunk JSON delimitati da newline:
func streamChatExample() error {
endpoint := "http://localhost:11434/api/chat"
req := ChatRequest{
Model: "qwen3:8b-thinking",
Think: bptr(true),
Stream: bptr(true), // Enable streaming
Messages: []ChatMessage{
{Role: "user", Content: "Explain quicksort algorithm step by step."},
},
}
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
}
// Process thinking and content as they arrive
if chunk.Message.Thinking != "" {
fmt.Print(chunk.Message.Thinking)
}
fmt.Print(chunk.Message.Content)
if chunk.Done {
break
}
}
return nil
}
Con il SDK ufficiale, usa una funzione callback per gestire i chunk di 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: "Explain binary search trees."},
},
}
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
}
Lavorare con thinking vs non-thinking in Qwen3 (guida pratica)
-
Due leve:
- Un flag booleano
thinkingsupportato dalla funzione thinking di Ollama; e - I comandi soft switch di Qwen3
/thinke/no_thinknell’ultimo messaggio di sistema/utente. L’istruzione più recente governa i prossimi turni.
- Un flag booleano
-
Postura predefinita: non-thinking per risposte rapide; passare al thinking per compiti che richiedono ragionamento passo-passo (matematica, pianificazione, debugging, analisi di codice complesso).
-
Interfacce con streaming: quando il thinking è attivato, potresti vedere ragionamento/contenuto intercalati nei frame di streaming—bufferizzali o renderizzali separatamente e fornisci agli utenti un’interruttore “mostra ragionamento”. (Vedi la documentazione API per il formato di streaming.)
-
Conversazioni multi-turno: Qwen3 ricorda la modalità thinking dai turni precedenti. Se vuoi commutarla a metà conversazione, usa sia il flag che il comando soft-switch per affidabilità.
Note per GPT-OSS
- Tratta il ragionamento come presente anche se hai provato a disattivarlo; filtra lato client se la tua UX non dovrebbe mostrarlo.
- Per le applicazioni di produzione che utilizzano GPT-OSS, implementa una logica di filtraggio lato client che possa rilevare e rimuovere i pattern di ragionamento se necessario.
- Testa accuratamente la tua specifica variante di modello GPT-OSS, poiché il comportamento può variare tra diverse quantizzazioni e versioni.
Migliori pratiche e suggerimenti per la produzione
Gestione degli errori e timeout
Implementa sempre una corretta gestione dei timeout e del recupero degli errori:
func robustChatRequest(ctx context.Context, model string, messages []api.Message) (*api.ChatResponse, error) {
// Set a reasonable timeout
ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
defer cancel()
client, err := api.ClientFromEnvironment()
if err != nil {
return nil, fmt.Errorf("creating client: %w", err)
}
req := &api.ChatRequest{
Model: model,
Messages: messages,
Options: map[string]interface{}{
"temperature": 0.7,
"num_ctx": 4096, // context window size
},
}
var resp api.ChatResponse
if err := client.Chat(ctx, req, &resp); err != nil {
return nil, fmt.Errorf("chat request failed: %w", err)
}
return &resp, nil
}
Pooling e riutilizzo delle connessioni
Riutilizza il client Ollama tra le richieste invece di crearne uno nuovo ogni volta:
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
}
Configurazione ambientale
Usa variabili d’ambiente per una deployment flessibile:
export OLLAMA_HOST=http://localhost:11434
export OLLAMA_NUM_PARALLEL=2
export OLLAMA_MAX_LOADED_MODELS=2
Il SDK ufficiale rispetta automaticamente OLLAMA_HOST tramite api.ClientFromEnvironment().
Monitoring e logging
Implementa il logging strutturato per i sistemi di produzione:
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
}
Conclusione
-
Per i progetti Go,
github.com/ollama/ollama/apiè la scelta più completa e pronta per la produzione. È mantenuto insieme al progetto core Ollama, usato dal CLI ufficiale e fornisce una copertura completa dell’API con compatibilità garantita. -
Per astrazioni di livello superiore, considera LangChainGo quando hai bisogno di catene, tool, vettori e pipeline RAG anche se sacrificherai un po’ di controllo di basso livello in cambio di comodità.
-
Qwen3 ti dà un controllo pulito e affidabile sulla modalità thinking sia con i flag che con i commutatori a livello di messaggio (
/think,/no_think), rendendolo ideale per applicazioni che hanno bisogno di risposte rapide e ragionamento profondo. -
Per GPT-OSS, pianifica sempre di sanificare l’output del ragionamento lato client quando necessario, poiché il flag di disattivazione del thinking potrebbe non essere onorato in modo coerente.
-
In produzione, implementa una corretta gestione degli errori, pooling delle connessioni, timeout e monitoring per costruire applicazioni robuste basate su Ollama.
La combinazione del forte tipizzazione di Go, del eccellente supporto per la concorrenza e dell’API intuitiva di Ollama lo rende uno stack ideale per costruire applicazioni basate su AI, dai semplici chatbot ai sistemi RAG complessi.
Punti chiave
Ecco un riferimento rapido per scegliere il tuo approccio:
| Caso d’uso | Approccio Raccomandato | Perché |
|---|---|---|
| Applicazione di produzione | github.com/ollama/ollama/api |
Supporto ufficiale, copertura completa dell’API, mantenuto con il progetto core |
| Pipeline RAG/catene/tool | LangChainGo | Astrazioni di alto livello, integrazioni con vettori |
| Apprendimento/sperimentazione | REST diretto con net/http | Trasparenza completa, nessuna dipendenza, educativo |
| Prototipazione rapida | SDK ufficiale | Equilibrio tra semplicità e potenza |
| Interfaccia di chat con streaming | SDK ufficiale con callback | Supporto streaming integrato, API pulita |
Guida alla selezione del modello:
- Qwen3: Il migliore per applicazioni che richiedono una modalità thinking controllabile, conversazioni multi-turno affidabili
- GPT-OSS: Prestazioni elevate ma richiede una gestione difensiva dell’output di thinking/ragionamento
- Altri modelli: Testa accuratamente; il comportamento del thinking varia per famiglia di modelli
Riferimenti e letture ulteriori
Documentazione ufficiale
- Referenza API Ollama — Documentazione completa dell’API REST
- Pacchetto Go ufficiale Ollama — Documentazione SDK Go
- Repository GitHub Ollama — Codice sorgente e issue
Alternative SDK Go
- Integrazione Ollama LangChainGo — Per applicazioni basate su catene
- swdunlop/ollama-client — Client community con tool calling
- xyproto/ollamaclient — Un’altra opzione community
Risorse specifiche per i modelli
- Documentazione Qwen — Informazioni ufficiali sul modello Qwen
- Informazioni su GPT-OSS — Dettagli sul modello GPT-OSS
Argomenti correlati
- Costruire applicazioni RAG con Go — Esempi LangChainGo
- Pacchetto context di Go — Essenziale per timeout e annullamento
- Migliori pratiche per il client HTTP di Go — Documentazione della libreria standard
Altri link utili
Per un confronto più ampio di Ollama con altre infrastrutture LLM locali e cloud, consulta il nostro LLM Hosting: Local, Self-Hosted & Cloud Infrastructure Compared.
- Installare e configurare Ollama
- Scheda rapida Ollama
- Scheda rapida Go
- Architettura e implementazione dei sistemi RAG
- Reranking di documenti di testo con Ollama e modello Qwen3 Embedding - in Go
- Vincolare gli LLM con Output Strutturato: Ollama, Qwen3 & Python o Go
- Confronto LLM: Qwen3:30b vs GPT-OSS:20b
- Problemi di Output Strutturato di GPT-OSS su Ollama