Go-clients voor Ollama: SDK-vergelijking en voorbeelden met Qwen3/GPT-OSS

Integreer Ollama met Go: SDK-gids, voorbeelden en productiebest practices.

Inhoud

Deze gids biedt een uitgebreid overzicht van beschikbare Go SDKs voor Ollama en vergelijkt hun functionaliteiten.

We zullen praktische Go-voorbeelden bespreken voor het aanroepen van Qwen3 en GPT-OSS-modellen die worden gehost op Ollama—zowel via raw REST API-aanroepen als de officiële Go-client—waaronder gedetailleerde behandeling van denk- en niet-denkmodi in Qwen3.

go en ollama

Waarom Ollama + Go?

Ollama biedt een klein, pragmatisch HTTP-API (meestal actief op http://localhost:11434) die is ontworpen voor genereren en chatten-taken, met ingebouwde ondersteuning voor streaming en modelbeheer. De officiële documentatie bespreekt uitgebreid de /api/generate en /api/chat aanvraag/antwoordstructuur en streamingsemantiek.

Go is een uitstekende keuze voor het bouwen van Ollama-clients vanwege de sterke standaardbibliotheekondersteuning voor HTTP, uitstekende JSON-verwerking, native concurrentieprimitieven en statisch getypeerde interfaces die fouten tijdens het compileren opvangen. Kijk hoe Ollama vergelijkt met vLLM, Docker Model Runner, LocalAI en cloudproviders—waaronder wanneer je elk van hen zou moeten kiezen—bij LLM Hosting: Lokale, Self-Hosted & Cloud-infrastructuur vergeleken.

Tot oktober 2025 zijn hier de Go SDK-opties die je waarschijnlijk overweegt.


Go SDKs voor Ollama — wat is beschikbaar?

SDK / Pakket Status & “eigenaar” Scope (Genereren/Chat/Streaming) Modelbeheer (pull/list/etc.) Extra’s / Opmerkingen
github.com/ollama/ollama/api Officiële pakket binnen de Ollama-repo; gebruikt door de ollama CLI zelf Volledige dekking gemapt op REST; streaming ondersteund Ja Wordt beschouwd als de kanonieke Go-client; API spiegelt documentatie nauwkeurig.
LangChainGo (github.com/tmc/langchaingo/llms/ollama) Communityframework (LangChainGo) met Ollama LLM-module Chat/Compleet + streaming via framework-abstrakties Beperkt (modelbeheer is geen hoofddoel) Prima als je ketens, tools, vectoropslag in Go wilt; minder van een raw SDK.
github.com/swdunlop/ollama-client Communityclient Focus op chat; goede tool-aanroep experimenten Gedeeltelijk Gemaakt voor het experimenteren met tool-aanroepen; geen 1:1 volledige oppervlakte.
Andere community-SDKs (bijv. ollamaclient, derde partij “go-ollama-sdk”) Community Varieert Varieert Kwaliteit en dekking varieert; beoordeel per repo.

Aanbeveling: Voor productie, prefereer github.com/ollama/ollama/api—het wordt onderhouden met het kernproject en spiegelt de REST API nauwkeurig.


Qwen3 & GPT-OSS op Ollama: denken vs niet-denken (wat je moet weten)

  • Denkmodus in Ollama scheidt het model’s “redeneren” van het eindresultaat wanneer het is ingeschakeld. Ollama documenteert een eerste klasse aanzetten/uitzetten van denken gedrag over de ondersteunde modellen.
  • (https://www.glukhov.org/nl/llm-performance/benchmarks/qwen3-30b-vs-gpt-oss-20b/ “Qwen3:30b vs GPT-OSS:20b: Technische details, prestaties en snelheid vergelijking”) ondersteunt dynamisch schakelen: voeg /think of /no_think toe in systeem/gebruikersberichten om modi te schakelen per beurt; de nieuwste instructie wint.
  • GPT-OSS: gebruikers melden dat uitzetten van denken (bijv. /set nothink of --think=false) onbetrouwbaar kan zijn op gpt-oss:20b; plan om filteren/verbergen van elke redenering die je UI niet moet tonen.

Deel 1 — Ollama aanroepen via raw REST (Go, net/http)

Gedeelde types

Eerst definiëren we de algemene types en helperfuncties die we overal in onze voorbeelden zullen gebruiken:

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"`
	// Sommige servers tonen denkcontrole als een booleanvlag.
	// Zelfs als het wordt overgeslagen, kun je Qwen3 nog steeds beïnvloeden via /think of /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"` // aanwezig wanneer denken is ingeschakeld
	} `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"`           // eindtekst voor niet-stream
	Thinking  string `json:"thinking,omitempty"` // aanwezig wanneer denken is ingeschakeld
	Done      bool   `json:"done"`
}

// ---- Helperfuncties ----

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 retourneert een pointer naar een booleanwaarde
func bptr(b bool) *bool { return &b }

Chat — Qwen3 met denken AAN (en hoe je het UIT kan zetten)

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

	req := ChatRequest{
		Model:   "qwen3:8b-thinking", // elke :*-thinking tag die je hebt getrokken
		Think:   bptr(true),
		Stream:  bptr(false),
		Messages: []ChatMessage{
			{Role: "system", Content: "Je bent een nauwkeurige assistent."},
			{Role: "user",   Content: "Leg recursie uit met een korte Go-voorbeeld."},
		},
	}

	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("🧠 denken:\n", out.Message.Thinking)
	fmt.Println("\n💬 antwoord:\n", out.Message.Content)
	return nil
}

// Zet denken UIT voor de volgende beurt door:
// (a) Think=false instellen, en/of
// (b) "/no_think" toevoegen aan de meest recente systeem/gebruikersbericht (Qwen3 soft switch).
// Qwen3 respecteert de laatste /think of /no_think instructie in meervoudige 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: "Je bent kort. /no_think"},
			{Role: "user",   Content: "Leg recursie in één zin uit."},
		},
	}

	raw, err := httpPostJSON(endpoint, req)
	if err != nil {
		return err
	}
	var out ChatResponse
	if err := json.Unmarshal(raw, &out); err != nil {
		return err
	}
	// Verwacht dat denken leeg is; nog steeds verdedigend behandelen.
	if out.Message.Thinking != "" {
		fmt.Println("🧠 denken (onverwacht):\n", out.Message.Thinking)
	}
	fmt.Println("\n💬 antwoord:\n", out.Message.Content)
	return nil
}

(Qwen3’s /think en /no_think soft switch is gedocumenteerd door de Qwen-team; de laatste instructie wint in meervoudige chats.)

Chat — GPT-OSS met denken (en een waarschuwing)

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

	req := ChatRequest{
		Model:  "gpt-oss:20b",
		Think:  bptr(true),   // vraag gescheiden redenering indien ondersteund
		Stream: bptr(false),
		Messages: []ChatMessage{
			{Role: "user", Content: "Wat is dynamisch programmeren? Leg het kernidee uit."},
		},
	}

	raw, err := httpPostJSON(endpoint, req)
	if err != nil {
		return err
	}
	var out ChatResponse
	if err := json.Unmarshal(raw, &out); err != nil {
		return err
	}
	// Bekende kwal: uitschakelen van denken kan redenering niet volledig onderdrukken op gpt-oss:20b.
	// Filter altijd denken in UI als je het niet wilt tonen.
	fmt.Println("🧠 denken:\n", out.Message.Thinking)
	fmt.Println("\n💬 antwoord:\n", out.Message.Content)
	return nil
}

Gebruikers melden dat uitschakelen van denken op gpt-oss:20b (bijv. /set nothink of --think=false) genegeerd kan worden—plan voor clientzijde filtering indien nodig.

Genereren — Qwen3 en GPT-OSS

func generateQwen3() error {
	endpoint := "http://localhost:11434/api/generate"
	req := GenerateRequest{
		Model:  "qwen3:4b-thinking",
		Prompt: "In 2–3 zinnen, wat zijn B-Bomen gebruikt voor 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("🧠 denken:\n", out.Thinking)
	}
	fmt.Println("\n💬 antwoord:\n", out.Response)
	return nil
}

func generateGptOss() error {
	endpoint := "http://localhost:11434/api/generate"
	req := GenerateRequest{
		Model:  "gpt-oss:20b",
		Prompt: "Verkort uitleg van backpropagation in neurale netwerken.",
		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("🧠 denken:\n", out.Thinking)
	}
	fmt.Println("\n💬 antwoord:\n", out.Response)
	return nil
}

REST vormen en streaminggedrag komen rechtstreeks van de Ollama API-verwijzing.


Deel 2 — Ollama aanroepen via de officiële Go SDK (github.com/ollama/ollama/api)

Het officiële pakket biedt een Client met methoden die overeenkomen met de REST API. De Ollama CLI zelf gebruikt dit pakket om met de service te communiceren, wat het veiligste keuze is voor compatibiliteit.

Installatie

go get github.com/ollama/ollama/api

Chat — Qwen3 (denken AAN / UIT)

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ollama/ollama/api"
)

func chatWithQwen3Thinking(ctx context.Context, thinking bool) error {
	client, err := api.ClientFromEnvironment() // respecteert OLLAMA_HOST als ingesteld
	if err != nil {
		return err
	}

	req := &api.ChatRequest{
		Model: "qwen3:8b-thinking",
		// Veel serverbuilds tonen denken als een top-level vlag;
		// daarnaast kun je Qwen3 beïnvloeden via /think of /no_think in berichten.
		Think: api.Ptr(thinking),
		Messages: []api.Message{
			{Role: "system", Content: "Je bent een nauwkeurige assistent."},
			{Role: "user",   Content: "Leg mergesort uit met een korte Go-voorbeeld."},
		},
	}

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

	if resp.Message.Thinking != "" {
		fmt.Println("🧠 denken:\n", resp.Message.Thinking)
	}
	fmt.Println("\n💬 antwoord:\n", resp.Message.Content)
	return nil
}

func main() {
	ctx := context.Background()
	if err := chatWithQwen3Thinking(ctx, true); err != nil {
		log.Fatal(err)
	}
	// Voorbeeld: niet-denkend
	if err := chatWithQwen3Thinking(ctx, false); err != nil {
		log.Fatal(err)
	}
}

Chat — GPT-OSS (redenering verdedigend behandelen)

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: "Wat is memoisatie en wanneer is het nuttig?"},
		},
	}
	var resp api.ChatResponse
	if err := client.Chat(ctx, req, &resp); err != nil {
		return err
	}
	// Als je redenering wilt verbergen, doe het hier ongeacht vlaggen.
	if resp.Message.Thinking != "" {
		fmt.Println("🧠 denken:\n", resp.Message.Thinking)
	}
	fmt.Println("\n💬 antwoord:\n", resp.Message.Content)
	return nil
}

Genereren — 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: "Samenvat de rol van een B-Boom in indexering.",
		Think:  api.Ptr(true),
	}
	var resp api.GenerateResponse
	if err := client.Generate(ctx, req, &resp); err != nil {
		return err
	}
	if resp.Thinking != "" {
		fmt.Println("🧠 denken:\n", resp.Thinking)
	}
	fmt.Println("\n💬 antwoord:\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: "Leg gradient descent in eenvoudige termen uit.",
		Think:  api.Ptr(true),
	}
	var resp api.GenerateResponse
	if err := client.Generate(ctx, req, &resp); err != nil {
		return err
	}
	if resp.Thinking != "" {
		fmt.Println("🧠 denken:\n", resp.Thinking)
	}
	fmt.Println("\n💬 antwoord:\n", resp.Response)
	return nil
}

Het oppervlak van het officiële pakket spiegelt de REST-documentatie en wordt bijgewerkt met het kernproject.


Streaming-antwoorden

Voor real-time streaming, stel Stream: bptr(true) in je aanvraag. Het antwoord wordt geleverd als newline-delimited JSON-blokken:

func streamChatExample() error {
	endpoint := "http://localhost:11434/api/chat"
	req := ChatRequest{
		Model:  "qwen3:8b-thinking",
		Think:  bptr(true),
		Stream: bptr(true), // Streaming inschakelen
		Messages: []ChatMessage{
			{Role: "user", Content: "Leg quicksort algoritme stap voor stap uit."},
		},
	}

	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
		}
		
		// Verwerk denken en inhoud terwijl ze aankomen
		if chunk.Message.Thinking != "" {
			fmt.Print(chunk.Message.Thinking)
		}
		fmt.Print(chunk.Message.Content)
		
		if chunk.Done {
			break
		}
	}
	return nil
}

Met de officiële SDK, gebruik een callbackfunctie om streamingblokken te verwerken:

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: "Leg binaire zoekbomen uit."},
		},
	}
	
	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
}

Werken met Qwen3 denken vs niet-denken (praktische richtlijnen)

  • Twee koppels:

    1. Een boolean denken vlag ondersteund door Ollama’s denkfunctie; en
    2. Qwen3’s soft switch commando’s /think en /no_think in de meest recente systeem/gebruikersbericht. De meest recente instructie beheerst de volgende beurt(s).
  • Standaard houding: niet-denkend voor snelle antwoorden; verhoog naar denken voor taken die stap-voor-stap redenering vereisen (wiskunde, planning, debuggen, complexe codeanalyse).

  • Streaming UIs: wanneer denken is ingeschakeld, zie je mogelijk doorlopende redenering/inhoud in gestreamde frames—buffer of render ze apart en geef gebruikers een “toon redenering” schakelknop. (Zie API-documentatie voor streamingformaat.)

  • Meervoudige conversaties: Qwen3 herinnert zich de denkmodus van vorige beurten. Als je deze midden in een conversatie wilt schakelen, gebruik zowel de vlag als het soft-switchcommando voor betrouwbaarheid.

Opmerkingen voor GPT-OSS

  • Behandel redenering als aanwezig zelfs als je het probeert uit te schakelen; filter op de client als je UX het niet moet tonen.
  • Voor productietoepassingen met GPT-OSS, implementeer clientzijde filterlogica die kan detecteren en verwijderen van redeneringspatronen indien nodig.
  • Test je specifieke GPT-OSS-modellvariant grondig, aangezien gedrag kan variëren tussen verschillende kwantificaties en versies.

Best practices en productietips

Foutafhandeling en time-outs

Implementeer altijd correcte time-outafhandeling en foutherstel:

func robustChatRequest(ctx context.Context, model string, messages []api.Message) (*api.ChatResponse, error) {
	// Stel een redelijke time-out in
	ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
	defer cancel()
	
	client, err := api.ClientFromEnvironment()
	if err != nil {
		return nil, fmt.Errorf("client aanmaken: %w", err)
	}
	
	req := &api.ChatRequest{
		Model:    model,
		Messages: messages,
		Options: map[string]interface{}{
			"temperature": 0.7,
			"num_ctx":     4096, // contextvensteromvang
		},
	}
	
	var resp api.ChatResponse
	if err := client.Chat(ctx, req, &resp); err != nil {
		return nil, fmt.Errorf("chat-aanvraag is mislukt: %w", err)
	}
	
	return &resp, nil
}

Verbindingspooling en hergebruik

Hergebruik de Ollama-client over meerdere aanvragen in plaats van een nieuwe te maken bij elke aanvraag:

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
}

Omgevingsconfiguratie

Gebruik omgevingsvariabelen voor flexibele implementatie:

export OLLAMA_HOST=http://localhost:11434
export OLLAMA_NUM_PARALLEL=2
export OLLAMA_MAX_LOADED_MODELS=2

De officiële SDK respecteert automatisch OLLAMA_HOST via api.ClientFromEnvironment().

Monitoring en logboekregistratie

Implementeer gestructureerde logboekregistratie voor productiesystemen:

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
}

Conclusie

  • Voor Go-projecten, github.com/ollama/ollama/api is de meest volledige, productie-geoorloofde keuze. Het wordt onderhouden met het Ollama-kernproject, gebruikt door de officiële CLI en biedt uitgebreide API-dekking met gegarandeerde compatibiliteit.

  • Voor hogere abstracties, overweeg LangChainGo wanneer je ketens, tools, vectoropslag en RAG-pijplijnen nodig hebt—hoewel je enige lage-niveaucontrole moet opgeven voor handigheid.

  • Qwen3 geeft je schone, betrouwbare controle over denkmodus met zowel vlaggen als berichtniveau schakelaars (/think, /no_think), waardoor het ideaal is voor toepassingen die zowel snelle antwoorden als diepe redenering nodig hebben.

  • Voor GPT-OSS, plan altijd om redenering uit te schakelen op clientzijde wanneer nodig, aangezien de denk-uitgeschakelde vlag mogelijk niet consistent wordt geëerbiedigd.

  • In productie, implementeer correcte foutafhandeling, verbindingspooling, time-outs en monitoring om robuuste Ollama-aangedreven toepassingen te bouwen.

De combinatie van Go’s sterke typen, uitstekende ondersteuning voor concurrentie en Ollama’s eenvoudige API maakt het een ideaal stack voor het bouwen van AI-aangedreven toepassingen—van eenvoudige chatbots tot complexe RAG-systemen.

Belangrijkste conclusies

Hier is een snelle verwijzing voor het kiezen van je aanpak:

Gebruiksscenario Aanbevolen aanpak Waarom
Productietoepassing github.com/ollama/ollama/api Officiële ondersteuning, volledige API-dekking, onderhouden met kernproject
RAG/kettingen/tools-pijplijn LangChainGo Hogere abstracties, integraties met vectoropslag
Leren/experimenteren Raw REST met net/http Volledige transparantie, geen afhankelijkheden, educatief
Snel prototyperen Officiële SDK Evenwicht tussen eenvoud en kracht
Streaming chat UI Officiële SDK met callbacks Ingebouwde streamingondersteuning, nette API

Modelkeuze-richtlijnen:

  • Qwen3: Beste voor toepassingen die controleerbaar denkenmodus nodig hebben, betrouwbare meervoudige conversaties
  • GPT-OSS: Sterke prestaties maar vereist verdedigende behandeling van denken/redenering-uitvoer
  • Andere modellen: Test grondig; denkgedrag varieert per modelfamilie

Referenties & verdere lezing

Officiële documentatie

Go-SDK-alternatieven

Modelspecifieke bronnen

Gerelateerde onderwerpen

Voor een breder vergelijken van Ollama met andere lokale en cloud LLM-infrastructuur, zie onze LLM Hosting: Lokale, Self-Hosted & Cloud-infrastructuur vergeleken.