Client Go per Ollama: confronto tra SDK e esempi con Qwen3/GPT-OSS
Integra Ollama con Go: guida all'SDK, esempi e best practice per la produzione.
Questo documento fornisce un’overview completa degli SDK Go per Ollama disponibili e confronta i loro set di funzionalità.
Esploreremo esempi pratici in Go per chiamare i modelli Qwen3 e GPT-OSS ospitati su Ollama, entrambi tramite chiamate REST API grezze e il client Go ufficiale, inclusi dettagliati trattamenti dei modi di pensiero e non-pensiero in Qwen3.

Perché Ollama + Go?
Ollama espone un piccolo, pragmatico HTTP API (tipicamente in esecuzione su http://localhost:11434) progettato per carichi di lavoro di generazione e chat, con supporto integrato per lo streaming e le capacità di gestione dei modelli. La documentazione ufficiale copre in modo completo /api/generate e /api/chat strutture richiesta/risposta e semantica dello streaming.
Go è un’ottima scelta per costruire client Ollama grazie al forte supporto della libreria standard per HTTP, eccellente gestione JSON, primitive di concorrenza native e interfacce staticamente tipizzate che catturano gli errori al momento della compilazione. Per vedere come Ollama si confronta con vLLM, Docker Model Runner, LocalAI e fornitori di cloud — incluso quando scegliere ciascuno — vedi LLM Hosting: Local, Self-Hosted & Cloud Infrastructure Compared.
Fino ad ottobre 2025, ecco le opzioni SDK Go che probabilmente considererai.
SDK Go per Ollama — cosa è disponibile?
| SDK / Package | Stato e “proprietario” | Ambito (Generazione/Chat/Streaming) | Gestione modelli (pull/list/etc.) | Extra / Note |
|---|---|---|---|---|
github.com/ollama/ollama/api |
Pacchetto ufficiale all’interno del repository Ollama; utilizzato dal CLI ollama stesso |
Copertura completa mappata al REST; supporto streaming | Sì | Considerato il client Go canonico; API specchio della documentazione. |
LangChainGo (github.com/tmc/langchaingo/llms/ollama) |
Framework della comunità (LangChainGo) con modulo LLM Ollama | Chat/Completion + streaming tramite astrazioni del framework | Limitato (gestione modelli non obiettivo principale) | Ottimo se desideri catene, strumenti, magazzini vettoriali in Go; meno di un SDK raw. |
github.com/swdunlop/ollama-client |
Client della comunità | Focalizzato su chat; buone esplorazioni di chiamata a strumenti | Parziale | Costruito per sperimentare con le chiamate a strumenti; non una superficie completa. |
Altri SDK della comunità (es. ollamaclient, terze parti “go-ollama-sdk”) |
Comunità | Varia | Varia | Qualità e copertura variano; valutare per repo. |
Consiglio: Per produzione, preferisci github.com/ollama/ollama/api—è mantenuto con il progetto principale e specchia l’API REST.
Qwen3 & GPT-OSS su Ollama: pensiero vs non-pensiero (cosa sapere)
- Modalità di pensiero in Ollama separa il “ragionamento” del modello dall’output finale quando abilitata. Ollama documenta un comportamento abilita/disabilita pensiero di prima classe su tutti 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, confronto di prestazioni e velocità”) supporta il commutatore dinamico: aggiungi
/thinko/no_thinknei messaggi di sistema/utente per commutare i modi turno per turno; l’ultima istruzione vince. - GPT-OSS: gli utenti segnalano che disabilitare il pensiero (es.
/set nothinko--think=false) può essere non affidabile sugpt-oss:20b; pianifica di filtrare/nascondere qualsiasi ragionamento che la tua interfaccia non dovrebbe mostrare.
Parte 1 — Chiamare Ollama tramite REST grezzo (Go, net/http)
Tipi condivisi
Per prima cosa, definiamo i tipi comuni e le funzioni helper che useremo nei nostri esempi:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
// ---- Tipi API Chat ----
type ChatMessage struct {
Role string `json:"role"`
Content string `json:"content"`
}
type ChatRequest struct {
Model string `json:"model"`
Messages []ChatMessage `json:"messages"`
// Alcuni server espongono il controllo del pensiero come flag booleano.
// Anche se omesso, puoi comunque controllare Qwen3 tramite /think o /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 il pensiero è attivo
} `json:"message"`
Done bool `json:"done"`
}
// ---- Tipi API Generate ----
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"` // testo finale per non-stream
Thinking string `json:"thinking,omitempty"` // presente quando il pensiero è attivo
Done bool `json:"done"`
}
// ---- Funzioni helper ----
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 restituisce un puntatore a un valore booleano
func bptr(b bool) *bool { return &b }
Chat — Qwen3 con pensiero ON (e come disattivarlo)
func chatQwen3Thinking() error {
endpoint := "http://localhost:11434/api/chat"
req := ChatRequest{
Model: "qwen3:8b-thinking", // qualsiasi tag :*-thinking che hai scaricato
Think: bptr(true),
Stream: bptr(false),
Messages: []ChatMessage{
{Role: "system", Content: "Sei un assistente preciso."},
{Role: "user", Content: "Spiega la ricorsione con un breve esempio in 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("🧠 pensiero:\n", out.Message.Thinking)
fmt.Println("\n💬 risposta:\n", out.Message.Content)
return nil
}
// Per disattivare il pensiero per il prossimo turno:
// (a) impostare Think=false, e/o
// (b) aggiungere "/no_think" al messaggio di sistema/utente più recente (Qwen3 soft switch).
// Qwen3 rispetta l'ultima istruzione /think o /no_think in chat multi-turn.
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: "Sei breve. /no_think"},
{Role: "user", Content: "Spiega la ricorsione in una frase."},
},
}
raw, err := httpPostJSON(endpoint, req)
if err != nil {
return err
}
var out ChatResponse
if err := json.Unmarshal(raw, &out); err != nil {
return err
}
// Aspettativa: pensiero vuoto; comunque gestire difensivamente.
if out.Message.Thinking != "" {
fmt.Println("🧠 pensiero (inaspettato):\n", out.Message.Thinking)
}
fmt.Println("\n💬 risposta:\n", out.Message.Content)
return nil
}
(Il soft switch di Qwen3 /think e /no_think è documentato dalla squadra Qwen; l’ultima istruzione vince in chat multi-turn.)
Chat — GPT-OSS con pensiero (e un avvertimento)
func chatGptOss() error {
endpoint := "http://localhost:11434/api/chat"
req := ChatRequest{
Model: "gpt-oss:20b",
Think: bptr(true), // richiede ragionamento separato se supportato
Stream: bptr(false),
Messages: []ChatMessage{
{Role: "user", Content: "Cos'è la programmazione dinamica? Spiega l'idea centrale."},
},
}
raw, err := httpPostJSON(endpoint, req)
if err != nil {
return err
}
var out ChatResponse
if err := json.Unmarshal(raw, &out); err != nil {
return err
}
// Nota conosciuta: disabilitare il pensiero potrebbe non sopprimere completamente il ragionamento su gpt-oss:20b.
// Filtra sempre il pensiero nell'interfaccia utente se non si desidera mostrarlo.
fmt.Println("🧠 pensiero:\n", out.Message.Thinking)
fmt.Println("\n💬 risposta:\n", out.Message.Content)
return nil
}
Gli utenti segnalano che disabilitare il pensiero su gpt-oss:20b (es. /set nothink o --think=false) potrebbe essere ignorato — pianifica il filtraggio client-side se necessario.
Genera — Qwen3 e GPT-OSS
func generateQwen3() error {
endpoint := "http://localhost:11434/api/generate"
req := GenerateRequest{
Model: "qwen3:4b-thinking",
Prompt: "In 2–3 frasi, a cosa servono gli alberi B nei database?",
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("🧠 pensiero:\n", out.Thinking)
}
fmt.Println("\n💬 risposta:\n", out.Response)
return nil
}
func generateGptOss() error {
endpoint := "http://localhost:11434/api/generate"
req := GenerateRequest{
Model: "gpt-oss:20b",
Prompt: "Spiega brevemente il backpropagation nei reti neurali.",
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("🧠 pensiero:\n", out.Thinking)
}
fmt.Println("\n💬 risposta:\n", out.Response)
return nil
}
Le forme REST e il comportamento dello streaming provengono direttamente dalla documentazione dell’API Ollama.
Parte 2 — Chiamare Ollama tramite l’SDK Go ufficiale (github.com/ollama/ollama/api)
Il pacchetto ufficiale espone un Client con metodi che corrispondono all’API REST. Il CLI Ollama stesso utilizza 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 (pensiero 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() // rispetta OLLAMA_HOST se impostato
if err != nil {
return err
}
req := &api.ChatRequest{
Model: "qwen3:8b-thinking",
// Molte build del server espongono il pensiero come flag a livello superiore;
// in aggiunta, puoi controllare Qwen3 tramite /think o /no_think nei messaggi.
Think: api.Ptr(thinking),
Messages: []api.Message{
{Role: "system", Content: "Sei un assistente preciso."},
{Role: "user", Content: "Spiega il merge sort con un breve frammento Go."},
},
}
var resp api.ChatResponse
if err := client.Chat(ctx, req, &resp); err != nil {
return err
}
if resp.Message.Thinking != "" {
fmt.Println("🧠 pensiero:\n", resp.Message.Thinking)
}
fmt.Println("\n💬 risposta:\n", resp.Message.Content)
return nil
}
func main() {
ctx := context.Background()
if err := chatWithQwen3Thinking(ctx, true); err != nil {
log.Fatal(err)
}
// Esempio: non-pensiero
if err := chatWithQwen3Thinking(ctx, false); err != nil {
log.Fatal(err)
}
}
Chat — GPT-OSS (gestire il ragionamento difensivamente)
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: "Cos'è la memoizzazione e quando è utile?"},
},
}
var resp api.ChatResponse
if err := client.Chat(ctx, req, &resp); err != nil {
return err
}
// Se intendi nascondere il ragionamento, fallo qui indipendentemente dai flag.
if resp.Message.Thinking != "" {
fmt.Println("🧠 pensiero:\n", resp.Message.Thinking)
}
fmt.Println("\n💬 risposta:\n", resp.Message.Content)
return nil
}
Genera — 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: "Riassumi il ruolo di un B-Tree nell'indicizzazione.",
Think: api.Ptr(true),
}
var resp api.GenerateResponse
if err := client.Generate(ctx, req, &resp); err != nil {
return err
}
if resp.Thinking != "" {
fmt.Println("🧠 pensiero:\n", resp.Thinking)
}
fmt.Println("\n💬 risposta:\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: "Spiega il gradiente discenso in termini semplici.",
Think: api.Ptr(true),
}
var resp api.GenerateResponse
if err := client.Generate(ctx, req, &resp); err != nil {
return err
}
if resp.Thinking != "" {
fmt.Println("🧠 pensiero:\n", resp.Thinking)
}
fmt.Println("\n💬 risposta:\n", resp.Response)
return nil
}
La superficie del pacchetto ufficiale specchia le documentazioni REST e viene aggiornata insieme al progetto principale.
Risposte in streaming
Per le risposte in tempo reale, imposta Stream: bptr(true) nella tua richiesta. La risposta verrà consegnata come frammenti JSON separati da newline:
func streamChatExample() error {
endpoint := "http://localhost:11434/api/chat"
req := ChatRequest{
Model: "qwen3:8b-thinking",
Think: bptr(true),
Stream: bptr(true), // Abilita lo streaming
Messages: []ChatMessage{
{Role: "user", Content: "Spiega l'algoritmo quicksort passo 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
}
// Processa pensiero e contenuto man mano che arrivano
if chunk.Message.Thinking != "" {
fmt.Print(chunk.Message.Thinking)
}
fmt.Print(chunk.Message.Content)
if chunk.Done {
break
}
}
return nil
}
Con l’SDK ufficiale, utilizza una funzione callback per gestire i frammenti dello 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: "Spiega gli alberi di ricerca binaria."},
},
}
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 Qwen3 pensiero vs non-pensiero (guida pratica)
-
Due leve:
- Un flag booleano
thinkingsupportato dalla funzionalità di pensiero di Ollama; e - Il soft switch di Qwen3
/thinke/no_thinknel messaggio più recente di sistema/utente. L’ultima istruzione governa il prossimo turno(i).
- Un flag booleano
-
Postura predefinita: non-pensiero per risposte rapide; passa a pensiero per compiti che richiedono ragionamento passo passo (matematica, pianificazione, debug, analisi complessa del codice).
-
Interfacce UI in streaming: quando il pensiero è abilitato, potresti vedere ragionamento e contenuto intercalati in frame in streaming — bufferizzali o renderizzali separatamente e fornisci agli utenti un “mostra ragionamento” toggle. (Vedi documentazione API per il formato dello streaming.)
-
Conversazioni a più turni: Qwen3 ricorda la modalità di pensiero dei turni precedenti. Se desideri commutare durante una conversazione, usa entrambi il flag e il comando soft-switch per affidabilità.
Note per GPT-OSS
- Tratta il ragionamento come presente anche se hai provato a disabilitarlo; filtra sul client se la tua interfaccia utente non dovrebbe mostrarlo.
- Per applicazioni di produzione che utilizzano GPT-OSS, implementa logica di filtraggio client-side che possa rilevare e rimuovere i pattern di ragionamento se necessario.
- Testa la tua variante specifica del modello GPT-OSS in modo approfondito, poiché il comportamento può variare tra diverse quantizzazioni e versioni.
Linee guida per le migliori pratiche e suggerimenti per la produzione
Gestione degli errori e dei timeout
Implementa sempre una gestione corretta dei timeout e del recupero degli errori:
func robustChatRequest(ctx context.Context, model string, messages []api.Message) (*api.ChatResponse, error) {
// Imposta un timeout ragionevole
ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
defer cancel()
client, err := api.ClientFromEnvironment()
if err != nil {
return nil, fmt.Errorf("creando client: %w", err)
}
req := &api.ChatRequest{
Model: model,
Messages: messages,
Options: map[string]interface{}{
"temperature": 0.7,
"num_ctx": 4096, // dimensione della finestra di contesto
},
}
var resp api.ChatResponse
if err := client.Chat(ctx, req, &resp); err != nil {
return nil, fmt.Errorf("richiesta chat fallita: %w", err)
}
return &resp, nil
}
Pools di connessioni e riutilizzo
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 dell’ambiente
Utilizza le variabili d’ambiente per una distribuzione flessibile:
export OLLAMA_HOST=http://localhost:11434
export OLLAMA_NUM_PARALLEL=2
export OLLAMA_MAX_LOADED_MODELS=2
L’SDK ufficiale rispetta automaticamente OLLAMA_HOST tramite api.ClientFromEnvironment().
Monitoraggio 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 principale Ollama, utilizzato dal CLI ufficiale e fornisce una copertura API completa con compatibilità garantita. -
Per astrazioni di livello superiore, considera LangChainGo quando hai bisogno di catene, strumenti, magazzini vettoriali e pipeline RAG — pur sacrificando alcuni controlli di basso livello per la comodità.
-
Qwen3 ti dà un controllo pulito e affidabile sul modalità di pensiero con entrambi i flag e i commutatori a livello di messaggio (
/think,/no_think), rendendolo ideale per applicazioni che necessitano sia di risposte rapide che di ragionamento approfondito. -
Per GPT-OSS, pianifica sempre di sanificare l’output del ragionamento sul client quando necessario, poiché il flag di disabilitazione del pensiero potrebbe non essere sempre rispettato.
-
In produzione, implementa una corretta gestione degli errori, pooling delle connessioni, timeout e monitoraggio per costruire applicazioni Ollama-powered robuste.
La combinazione di una forte tipizzazione Go, eccellente supporto alla concorrenza e dell’API semplice di Ollama lo rende un stack ideale per costruire applicazioni AI — dalle semplici chatbot a complessi sistemi RAG.
Punti chiave
Ecco un riferimento rapido per scegliere il tuo approccio:
| Caso d’uso | Approccio consigliato | Perché |
|---|---|---|
| Applicazione in produzione | github.com/ollama/ollama/api |
Supporto ufficiale, copertura API completa, mantenuto con il progetto principale |
| Pipeline RAG/catene/strumenti | LangChainGo | Astrazioni di alto livello, integrazioni con magazzini vettoriali |
| Apprendimento/sperimentazione | REST grezzo con net/http | Trasparenza completa, nessuna dipendenza, educativo |
| Prototipazione rapida | SDK ufficiale | Equilibrio tra semplicità e potenza |
| Interfaccia utente chat in streaming | SDK ufficiale con callback | Supporto integrato per lo streaming, API pulita |
Guida alla selezione dei modelli:
- Qwen3: Migliore per applicazioni che richiedono controllo della modalità di pensiero, conversazioni multi-turn affidabili
- GPT-OSS: Buone prestazioni ma richiede gestione difensiva dell’output del ragionamento
- Altri modelli: Testa a fondo; il comportamento del pensiero varia per famiglia di modelli
Riferimenti e ulteriore lettura
Documentazione ufficiale
- Riferimento 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 in LangChainGo — Per applicazioni basate su catene
- swdunlop/ollama-client — Client della comunità con chiamate a strumenti
- xyproto/ollamaclient — Altra opzione della comunità
Risorse specifiche per i modelli
- Documentazione Qwen — Informazioni ufficiali sui modelli Qwen
- Informazioni su GPT-OSS — Dettagli sui modelli GPT-OSS
Argomenti correlati
- Costruire applicazioni RAG con Go — Esempi LangChainGo
- Pacchetto context di Go — Essenziale per timeout e cancellazione
- Migliori pratiche per il client HTTP di Go — Documentazione della libreria standard
Altri link utili
Per un confronto più ampio tra Ollama e altre infrastrutture locali e cloud LLM, consulta la nostra LLM Hosting: Local, Self-Hosted & Cloud Infrastructure Compared.
- Installare e configurare Ollama
- Ollama cheatsheet
- Go Cheatsheet
- Riordinare documenti di testo con Ollama e modello Qwen3 Embedding - in Go
- Limitare gli LLM con Output Strutturato: Ollama, Qwen3 & Python o Go
- Confronto tra LLM: Qwen3:30b vs GPT-OSS:20b
- Problemi di Output Strutturato di Ollama GPT-OSS