Ollama Go-klienter: SDK-jämförelse och Qwen3/GPT-OSS-exempel

Integrera Ollama med Go: SDK-guide, exempel och produktionsrekommendationer.

Sidinnehåll

Den här guiden ger en omfattande översikt över tillgängliga Go SDK:er för Ollama och jämför deras funktionssätt.

Vi kommer utforska praktiska Go-exempel för att anropa Qwen3 och GPT-OSS-modeller som finns värd på Ollama—både via raw REST API-anrop och den officiella Go-klienten—inklusive detaljerad hantering av tänkande och icke-tänkande lägen i Qwen3.

go och ollama

Varför Ollama + Go?

Ollama exponerar en liten, pragmatisk HTTP-API (vanligtvis körande på http://localhost:11434) som är designad för generera och chatt-arbetsbelastningar, med inbyggd stöd för strömning och modellhantering. Den officiella dokumentationen täcker grundligt /api/generate och /api/chat-förfrågningar och svarsstrukturer samt strömningssemantik.

Go är ett utmärkt val för att bygga Ollama-klienter på grund av dess starka standardbibliotekssupport för HTTP, utmärkt JSON-hantering, inbyggda konkurrensprimitiver och statiskt typade gränssnitt som upptäcker fel vid kompileringsstadiet. För att se hur Ollama jämförs med vLLM, Docker Model Runner, LocalAI och molntillhandtagare — inklusive när du bör välja varje — se LLM Hosting: Lokal, självvärd och molninfrastruktur jämförd.

Upp till oktober 2025, här är de Go SDK-Alternativen du sannolikt kommer att överväga.


Go SDK:er för Ollama — vad finns tillgängligt?

SDK / Paket Status & “ägare” Omfång (Generera/Chatt/Strömning) Modellhantering (hämta/lista osv.) Extra / Anteckningar
github.com/ollama/ollama/api Officiell paket inom Ollama-repon; används av ollama CLI självt Full täckning mappad till REST; strömning stöds Ja Betraktas som den kanoniska Go-klienten; API speglar dokumentationen nära.
LangChainGo (github.com/tmc/langchaingo/llms/ollama) Communityramverk (LangChainGo) med Ollama LLM-modul Chatt/Slutförande + strömning via ramverksabstraktioner Begränsad (modellhantering inte primärt mål) Bra om du vill ha kedjor, verktyg, vektorlager i Go; mindre av en rå SDK.
github.com/swdunlop/ollama-client Communityklient Fokus på chatt; bra verktygsanrop experiment Delvis Byggd för att experimentera med verktygsanrop; inte en 1:1 full yta.
Andra community SDK:er (t.ex., ollamaclient, tredjeparts “go-ollama-sdk”) Community Varierar Varierar Kvalitet och täckning varierar; bedöm per repo.

Rekommendation: För produktion, föredra github.com/ollama/ollama/api—det underhålls med det centrala projektet och speglar REST API:et.


Qwen3 & GPT-OSS på Ollama: tänkande vs icke-tänkande (vad du bör veta)

  • Tänkande-läge i Ollama separerar modellens “resonemang” från slutresultatet när det är aktiverat. Ollama dokumenterar en första klass aktivera/avaktivera tänkande beteende över stödda modeller.
  • (https://www.glukhov.org/sv/llm-performance/benchmarks/qwen3-30b-vs-gpt-oss-20b/ “Qwen3:30b vs GPT-OSS:20b: Tekniska detaljer, prestanda och hastighetsjämförelse”) stöder dynamiskt växling: lägg till /think eller /no_think i systembrukaremeddelanden för att växla lägen turn-by-turn; den senaste instruktionen vinner.
  • GPT-OSS: användare rapporterar att avaktivera tänkande (t.ex., /set nothink eller --think=false) kan vara otrustvärt på gpt-oss:20b; planera att filtrera/dölja något resonemang din UI inte bör visa.

Del 1 — Anropa Ollama via rå REST (Go, net/http)

Delade typer

Först, definiera de gemensamma typerna och hjälpfunktionerna vi kommer använda i våra exempel:

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"
)

// ---- Chatt API Typer ----

type ChatMessage struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

type ChatRequest struct {
	Model    string        `json:"model"`
	Messages []ChatMessage `json:"messages"`
	// Vissa servrar exponerar tänkande kontroll som en boolesk flagga.
	// Även om den utelämnas, kan du fortfarande kontrollera Qwen3 via /think eller /no_think taggar.
	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"` // närvarande när tänkande är på
	} `json:"message"`
	Done bool `json:"done"`
}

// ---- Generera API Typer ----

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"`           // slutlig text för icke-ström
	Thinking  string `json:"thinking,omitempty"` // närvarande när tänkande är på
	Done      bool   `json:"done"`
}

// ---- Hjälpfunktioner ----

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 returnerar en pekare till en boolesk värde
func bptr(b bool) *bool { return &b }

Chatt — Qwen3 med tänkande PÅ (och hur du stänger av)

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

	req := ChatRequest{
		Model:   "qwen3:8b-thinking", // någon :*-thinking tagg du har hämtat
		Think:   bptr(true),
		Stream:  bptr(false),
		Messages: []ChatMessage{
			{Role: "system", Content: "Du är en exakt assistent."},
			{Role: "user",   Content: "Förklara rekursion med ett kort Go-exempel."},
		},
	}

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

// Stäng av tänkande för nästa omgång genom:
// (a) att ställa in Think=false, och/eller
// (b) lägga till "/no_think" i den senaste systembrukaremeddelandet (Qwen3 mjuk switch).
// Qwen3 följer den senaste /think eller /no_think instruktionen i flera omgångars chattar.
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: "Du är kort. /no_think"},
			{Role: "user",   Content: "Förklara rekursion i en mening."},
		},
	}

	raw, err := httpPostJSON(endpoint, req)
	if err != nil {
		return err
	}
	var out ChatResponse
	if err := json.Unmarshal(raw, &out); err != nil {
		return err
	}
	// Förvänta dig att tänkande är tomt; hantera försiktigt ändå.
	if out.Message.Thinking != "" {
		fmt.Println("🧠 tänkande (förväntat):\n", out.Message.Thinking)
	}
	fmt.Println("\n💬 svar:\n", out.Message.Content)
	return nil
}

(Qwen3s /think och /no_think mjuka switchar är dokumenterade av Qwen-teamet; den senaste instruktionen vinner i flera omgångars chattar.)

Chatt — GPT-OSS med tänkande (och en varning)

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

	req := ChatRequest{
		Model:  "gpt-oss:20b",
		Think:  bptr(true),   // begär separerat resonemang om det stöds
		Stream: bptr(false),
		Messages: []ChatMessage{
			{Role: "user", Content: "Vad är dynamisk programmering? Förklara det centrala idet."},
		},
	}

	raw, err := httpPostJSON(endpoint, req)
	if err != nil {
		return err
	}
	var out ChatResponse
	if err := json.Unmarshal(raw, &out); err != nil {
		return err
	}
	// Kända egenskaper: att avaktivera tänkande kan inte helt undertrycka resonemang på gpt-oss:20b.
	// Filtrera alltid tänkande i UI om du inte vill visa det.
	fmt.Println("🧠 tänkande:\n", out.Message.Thinking)
	fmt.Println("\n💬 svar:\n", out.Message.Content)
	return nil
}

Användare rapporterar att avaktivera tänkande på gpt-oss:20b (t.ex., /set nothink eller --think=false) kan ignoreras — planera för klientsidig filtrering om det behövs.

Generera — Qwen3 och GPT-OSS

func generateQwen3() error {
	endpoint := "http://localhost:11434/api/generate"
	req := GenerateRequest{
		Model:  "qwen3:4b-thinking",
		Prompt: "I 2–3 meningar, vad används B-träd för i databaser?",
		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("🧠 tänkande:\n", out.Thinking)
	}
	fmt.Println("\n💬 svar:\n", out.Response)
	return nil
}

func generateGptOss() error {
	endpoint := "http://localhost:11434/api/generate"
	req := GenerateRequest{
		Model:  "gpt-oss:20b",
		Prompt: "Förklara bakpropagering i neurala nätverk kort.",
		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("🧠 tänkande:\n", out.Thinking)
	}
	fmt.Println("\n💬 svar:\n", out.Response)
	return nil
}

REST-form och strömningsskede kommer direkt från Ollama API-referensen.


Del 2 — Anropa Ollama via den officiella Go SDK:n (github.com/ollama/ollama/api)

Den officiella paketet exponerar en Client med metoder som motsvarar REST API:et. Ollama CLI:et självt använder detta paket för att kommunicera med tjänsten, vilket gör det till den säkraste valen för kompatibilitet.

Installera

go get github.com/ollama/ollama/api

Chatt — Qwen3 (tänkande PÅ / AV)

package main

import (
	"context"
	"fmt"
	"log"

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

func chatWithQwen3Thinking(ctx context.Context, thinking bool) error {
	client, err := api.ClientFromEnvironment() // följer OLLAMA_HOST om inställt
	if err != nil {
		return err
	}

	req := &api.ChatRequest{
		Model: "qwen3:8b-thinking",
		// Många serverbyggare exponerar tänkande som en toppnivåflagga;
		// dessutom kan du kontrollera Qwen3 via /think eller /no_think i meddelanden.
		Think: api.Ptr(thinking),
		Messages: []api.Message{
			{Role: "system", Content: "Du är en exakt assistent."},
			{Role: "user",   Content: "Förklara mergesort med ett kort Go-snutt."},
		},
	}

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

	if resp.Message.Thinking != "" {
		fmt.Println("🧠 tänkande:\n", resp.Message.Thinking)
	}
	fmt.Println("\n💬 svar:\n", resp.Message.Content)
	return nil
}

func main() {
	ctx := context.Background()
	if err := chatWithQwen3Thinking(ctx, true); err != nil {
		log.Fatal(err)
	}
	// Exempel: icke-tänkande
	if err := chatWithQwen3Thinking(ctx, false); err != nil {
		log.Fatal(err)
	}
}

Chatt — GPT-OSS (hantera resonemang försiktigt)

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: "Vad är memoisering och när är den användbar?"},
		},
	}
	var resp api.ChatResponse
	if err := client.Chat(ctx, req, &resp); err != nil {
		return err
	}
	// Om du planerar att dölja resonemang, gör det här oavsett flaggor.
	if resp.Message.Thinking != "" {
		fmt.Println("🧠 tänkande:\n", resp.Message.Thinking)
	}
	fmt.Println("\n💬 svar:\n", resp.Message.Content)
	return nil
}

Generera — 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: "Sammanfatta rollen av ett B-träd i 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("🧠 tänkande:\n", resp.Thinking)
	}
	fmt.Println("\n💬 svar:\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: "Förklara gradient descent i enkla termer.",
		Think:  api.Ptr(true),
	}
	var resp api.GenerateResponse
	if err := client.Generate(ctx, req, &resp); err != nil {
		return err
	}
	if resp.Thinking != "" {
		fmt.Println("🧠 tänkande:\n", resp.Thinking)
	}
	fmt.Println("\n💬 svar:\n", resp.Response)
	return nil
}

Den officiella paketets yta speglar REST-dokumentationen och uppdateras tillsammans med det centrala projektet.


Strömmande svar

För realtidsströmning, ställ in Stream: bptr(true) i din begäran. Svar kommer levereras som 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), // Aktivera strömning
		Messages: []ChatMessage{
			{Role: "user", Content: "Förklara quicksort-algoritmen steg för steg."},
		},
	}

	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
		}
		
		// Bearbeta tänkande och innehåll när de kommer
		if chunk.Message.Thinking != "" {
			fmt.Print(chunk.Message.Thinking)
		}
		fmt.Print(chunk.Message.Content)
		
		if chunk.Done {
			break
		}
	}
	return nil
}

Med den officiella SDK:n, använd en callbackfunktion för att hantera strömmande chunks:

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: "Förklara binära sökträd."},
		},
	}
	
	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
}

Arbeta med Qwen3 tänkande vs icke-tänkande (praktisk vägledning)

  • Två hävarter:

    1. En boolesk tänkande flagga som stöds av Ollamas tänkande funktion; och
    2. Qwen3s mjuka switchkommandon /think och /no_think i den senaste systembrukaremeddelandet. Den senaste instruktionen styr nästa omgång(ar).
  • Standardposture: icke-tänkande för snabba svar; öka till tänkande för uppgifter som kräver steg-för-steg resonemang (matematik, planering, felsökning, komplex kodanalys).

  • Strömmande UI:er: när tänkande är aktiverat, kan du se sammanblandade resonemang/innehåll i strömmade ramar — buffra eller rendera dem separat och ge användarna en “visa resonemang” knapp. (Se API-dokumentation för strömmningsformat.)

  • Multi-turn konversationer: Qwen3 minns tänkandetillståndet från tidigare omgångar. Om du vill växla det mitt i en konversation, använd både flaggan och mjukswitchkommandot för pålitlighet.

Anteckningar för GPT-OSS

  • Behandla resonemang som närvarande även om du försökte inaktivera det; filtrera på klienten om din UX inte ska visa det.
  • För produktionsapplikationer som använder GPT-OSS, implementera klientsidig filtreringslogik som kan upptäcka och ta bort resonemangsmönster om det behövs.
  • Testa din specifika GPT-OSS-modellvariant grundligt, eftersom beteendet kan variera mellan olika kvantiseringar och versioner.

Bästa praxis och produktionsförslag

Felhantering och timeout

Implementera alltid korrekt timeouthantering och felåterställning:

func robustChatRequest(ctx context.Context, model string, messages []api.Message) (*api.ChatResponse, error) {
	// Sätt en rimlig timeout
	ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
	defer cancel()
	
	client, err := api.ClientFromEnvironment()
	if err != nil {
		return nil, fmt.Errorf("skapa klient: %w", err)
	}
	
	req := &api.ChatRequest{
		Model:    model,
		Messages: messages,
		Options: map[string]interface{}{
			"temperature": 0.7,
			"num_ctx":     4096, // kontextfönsterstorlek
		},
	}
	
	var resp api.ChatResponse
	if err := client.Chat(ctx, req, &resp); err != nil {
		return nil, fmt.Errorf("chatbegäran misslyckades: %w", err)
	}
	
	return &resp, nil
}

Anslutningspool och återanvändning

Återanvänd Ollama-klienten över flera begäranden istället för att skapa en ny varje gång:

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
}

Miljökonfiguration

Använd miljövariabler för flexibel distribution:

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

Den officiella SDK:n respekterar automatiskt OLLAMA_HOST via api.ClientFromEnvironment().

Övervakning och loggning

Implementera strukturerad loggning för produktionsystem:

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
}

Slutsats

  • För Go-projekt, github.com/ollama/ollama/api är den mest kompletta, produktionsklara valen. Den underhålls tillsammans med Ollama-core-projektet, används av den officiella CLI:n, och ger omfattande API-coverage med garanterad kompatibilitet.

  • För högre abstraktioner, överväg LangChainGo när du behöver kedjor, verktyg, vektorlager och RAG-pipelines — även om du kommer att byta något lågnivåkontroll mot bekvämlighet.

  • Qwen3 ger dig ren, pålitlig kontroll över tänkande-läge med både flaggor och meddelandesnitt (/think, /no_think), vilket gör det idealiskt för applikationer som behöver både snabba svar och djup resonemang.

  • För GPT-OSS, planera alltid att rensning av resonemang utdata på klienten när det behövs, eftersom tänkande inaktiveringsflaggan kan inte vara konsekvent beaktad.

  • I produktion, implementera korrekt felhantering, anslutningspool, timeout och övervakning för att bygga robusta Ollama-drivna applikationer.

Kombinationen av Gös starka typning, utmärkt stöd för konkurrens och Ollamas enkel API gör det till ett idealiskt stack för att bygga AI-drivna applikationer — från enkla chattbotar till komplexa RAG-system.

Viktiga punkter

Här är en snabb referens för att välja din metod:

Användningsscenarie Rekommenderad metod Varför
Produktionapplikation github.com/ollama/ollama/api Officiell stöd, komplett API-coverage, underhålls tillsammans med core-projektet
RAG/kedjor/verktygspipeline LangChainGo Högre abstraktioner, integreringar med vektorlager
Lärande/experimentering Rå REST med net/http Full transparens, inga beroenden, pedagogisk
Snabb prototyp Officiell SDK Balans mellan enkelhet och kraft
Strömmande chatt UI Officiell SDK med callbacks Inbyggd strömningssupport, ren API

Modellvalsinstruktioner:

  • Qwen3: Bästa för applikationer som kräver kontrollerat tänkande-läge, pålitliga flera omgångars konversationer
  • GPT-OSS: Stark prestanda men kräver försiktigt hantering av tänkande/resonemang utdata
  • Andra modeller: Testa grundligt; tänkande beteende varierar per modellfamilj

Referenser & vidare läsning

Officiell dokumentation

Go SDK-alternativ

Modellspecifika resurser

Relaterade ämnen

Andra användbara länkar

För en bredare jämförelse av Ollama med andra lokala och molna LLM-infrastrukturer, kontrollera vår LLM Hosting: Lokal, självvärd och molninfrastruktur jämförd.