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

Integreer Ollama met Go: SDK-handboek, 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 ruwe REST API-aanroepen als de officiële Go-client—waaronder gedetailleerde behandeling van denk- en niet-denkmodes in Qwen3.

go en ollama

Waarom Ollama + Go?

Ollama biedt een klein, pragmatisch HTTP API (meestal draaiend 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/antwoordstructuren en streamingsemantiek.

Go is een uitstekende keuze voor het bouwen van Ollama-clients vanwege de sterke ondersteuning van de standaardbibliotheek voor HTTP, uitstekende JSON-verwerking, native concurrentieprimitieven en statisch getypeerde interfaces die fouten op tijd van compilatie detecteren.

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


Go SDKs voor Ollama — wat is beschikbaar?

SDK / Package Status & “eigenaar” Scope (Generate/Chat/Streaming) Model mgmt (pull/list/etc.) Extras / Notes
github.com/ollama/ollama/api Officiële package binnen de Ollama-repo; gebruikt door de ollama CLI zelf Volledige coverage gemapt op REST; streaming ondersteund Ja Wordt beschouwd als de kanonieke Go-client; API spiegelt documentatie nauwkeurig.
LangChainGo (github.com/tmc/langchaingo/llms/ollama) Community framework (LangChainGo) met Ollama LLM-module Chat/Completion + streaming via frameworkabstrakties Beperkt (modelbeheer is geen primaire doelstelling) Prima als je ketens, tools, vectoropslag in Go wilt; minder van een ruwe SDK.
github.com/swdunlop/ollama-client Community client Focus op chat; goede tool-calling experimenten Deelgeldig Gemaakt voor het experimenteren met toolcalling; geen 1:1 volledige oppervlakte.
Andere community SDKs (bijv. ollamaclient, derde partij “go-ollama-sdk”) Community Varieert Varieert Kwaliteit en coverage varieert; beoordeel per repo.

Aanbeveling: Voor productie, voorkeur geven aan 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 schept een scheiding tussen de model’s “redenering” en het eindresultaat wanneer deze is ingeschakeld. Ollama documenteert een eerste klasse aanzetten/uitzetten van denken gedrag over de ondersteunde modellen.
  • (https://www.glukhov.org/nl/post/2025/10/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 ruwe 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 boolean vlag.
	// Zelfs als deze 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"`
}

// ---- 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 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 zachte schakelaar).
// 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; behandel toch voorzichtig.
	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 zachte schakelaar is gedocumenteerd door de Qwen-team; de laatste instructie wint in meervoudige chats.)

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

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

	req := ChatRequest{
		Model:  "gpt-oss:20b",
		Think:  bptr(true),   // verzoek gescheiden redenering als 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 kluif: uitschakelen van denken kan niet volledig onderdrukken redenering op gpt-oss:20b.
	// Filter altijd/verberg 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) kan worden genegeerd—plan voor clientzijde filtering als nodig is.

Generate — 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: "Leg backpropagation in neurale netwerken kort uit.",
		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 serverbouwen tonen denken als een topniveau vlag;
		// bovendien 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-snippet."},
		},
	}

	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-denken
	if err := chatWithQwen3Thinking(ctx, false); err != nil {
		log.Fatal(err)
	}
}

Chat — GPT-OSS (redenering defensief 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
}

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: "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 responses

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

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 de 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 streamingchunks 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 zachte schakelaar commando’s /think en /no_think in de nieuwste systeem/gebruikersbericht. De meest recente instructie beheerst de volgende beurt(en).
  • Standaard houding: niet-denken voor snelle antwoorden; verhoog naar denken voor taken die stap-voor-stap redenering vereisen (wiskunde, planning, debuggen, complexe codeanalyse).

  • Streaming UI’s: wanneer denken is ingeschakeld, zie je mogelijk door elkaar lopende redenering/inhoud in gestreamde frames—buffer of render ze apart en geef gebruikers een “toon redenering” schakelaar. (Zie API-documentatie voor streamingformaat.)

  • Meervoudige gesprekken: Qwen3 onthoudt de denkmodus van vorige beurten. Als je deze midden in een gesprek wilt schakelen, gebruik zowel de vlag als de zachte schakelaarcommando 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 productieapplicaties die GPT-OSS gebruiken, implementeer clientzijde filteringlogica die kan detecteren en verwijderen van redeneringspatronen als nodig is.
  • Test je specifieke GPT-OSS modelvariant grondig, omdat 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, // contextwindowgrootte
		},
	}
	
	var resp api.ChatResponse
	if err := client.Chat(ctx, req, &resp); err != nil {
		return nil, fmt.Errorf("chat aanvraag mislukt: %w", err)
	}
	
	return &resp, nil
}

Verbindingspoolen en hergebruik

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

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-coverage met gegarandeerde compatibiliteit.

  • Voor hogere niveau abstrakties, overweeg LangChainGo wanneer je ketens, tools, vectoropslag en RAG-pijplijnen nodig hebt—hoewel je dan enige lage niveau controle moet opofferen 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 verwerking van redenering clientzijde te doen wanneer nodig, aangezien de denkuitgeschakeld vlag mogelijk niet consistent wordt gevolgd.

  • In productie, implementeer correcte foutafhandeling, verbindingspoolen, 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:

Gebruikgeval Aanbevolen aanpak Waarom
Productietoepassing github.com/ollama/ollama/api Officiële ondersteuning, volledige API-coverage, onderhouden met kernproject
RAG/ketens/tools pijplijn LangChainGo Hogere niveau abstrakties, integraties met vectoropslag
Leren/experimenteren Ruwe 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 een controleerbare denkmodus vereisen, betrouwbare meervoudige gesprekken
  • GPT-OSS: Sterke prestaties maar vereist defensieve afhandeling van denk/redenering uitvoer
  • Andere modellen: Test grondig; denkgedrag varieert per modelfamilie

Referenties & verdere lezing

Officiële documentatie

Alternatieven voor de Go SDK

Modelspecifieke bronnen

Gerelateerde onderwerpen