Ollama-klienter: Jämförelse av SDK:er och exempel med Qwen3/GPT-OSS

Integrera Ollama med Go: SDK-guide, exempel och bästa praxis för produktion.

Sidinnehåll

Den här guiden ger en omfattande översikt över tillgängliga Go SDKs för Ollama och jämför deras funktioner.

Vi kommer att utforska praktiska Go-exempel för att anropa Qwen3 och GPT-OSS-modeller som värdas på Ollama—både via råa 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 ett litet, pragmatiskt HTTP-API (vanligtvis körande på http://localhost:11434) som är utformat för generate och chat-arbetsbelastningar, med inbyggt strömningsstöd och modellhanteringsfunktioner. Den officiella dokumentationen täcker fullständigt /api/generate och /api/chat-begärande/svarsstrukturer och strömningssemantik.

Go är ett utmärkt val för byggande av Ollama-klienter tack vare dess starka standardbiblioteksstöd för HTTP, utmärkt JSON-hantering, naturliga konkurrensprimitiver och statiskt typade gränssnitt som fångar fel vid kompileringsstadiet.

Per oktober 2025 finns här de Go SDK-alternativ du mest sannolikt kommer att överväga.


Go SDKs för Ollama — vad finns tillgängligt?

SDK / Paket Status & “ägare” Omfattning (Generate/Chat/Strömning) Modellhantering (pull/list/etc.) Extra / Anteckningar
github.com/ollama/ollama/api Officiellt paket inom Ollama-repo; används av ollama CLI själv Fullständig 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) Community-ramverk (LangChainGo) med Ollama LLM-modul Chat/Completion + strömning via ramverksabstraktioner Begränsad (modellhantering inte primärt mål) Bra om du vill ha kedjor, verktyg, vektorlagring i Go; mindre av en rå SDK.
github.com/swdunlop/ollama-client Community-klient Fokus på chat; bra verktygsanropsexperiment Delvis Byggd för experiment med verktygsanrop; inte en 1:1 full yta.
Andra community-SDKs (t.ex., ollamaclient, tredje parts “go-ollama-sdk”) Community Varierar Varierar Kvalitet och täckning varierar; utvärdera per repo.

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


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 slutlig utgång när det är aktiverat. Ollama dokumenterar ett förstaklassigt aktivera/inaktivera tänkande beteende över stödda modeller.
  • (https://www.glukhov.org/sv/post/2025/10/qwen3-30b-vs-gpt-oss-20b/ “Qwen3:30b vs GPT-OSS:20b: Tekniska detaljer, prestanda och hastighetsjämförelse”) stöder dynamisk växling: lägg till /think eller /no_think i system/användarmeddelanden för att byta läge tur för tur; den senaste instruktionen gäller.
  • GPT-OSS: användare rapporterar att inaktivering av tänkande (t.ex., /set nothink eller --think=false) kan vara opålitligt på gpt-oss:20b; planera för filtrering/döljande av allt resonemang som ditt gränssnitt inte bör visa.

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

Gemensamma typer

Först, låt oss definiera de gemensamma typerna och hjälpfunktioner vi kommer att använda i våra exempel:

package main

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

// ---- Chat 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änkandekontroll som en boolsk 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åslaget
	} `json:"message"`
	Done bool `json:"done"`
}

// ---- Generate 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ömning
	Thinking  string `json:"thinking,omitempty"` // närvarande när tänkande är påslaget
	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 pekar till en boolsk värde
func bptr(b bool) *bool { return &b }

Chat — Qwen3 med tänkande PÅ (och hur man stänger det AV)

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

	req := ChatRequest{
		Model:   "qwen3:8b-thinking", // någon :*-thinking-tagg du har laddat ner
		Think:   bptr(true),
		Stream:  bptr(false),
		Messages: []ChatMessage{
			{Role: "system", Content: "Du är en precis 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 tänkande AV för nästa tur genom:
// (a) att ställa in Think=false, och/eller
// (b) att lägga till "/no_think" i det senaste system/användarmeddelandet (Qwen3 mjuk switch).
// Qwen3 följer den senaste /think eller /no_think-instruktionen i flervägs-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 kortfattad. /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 ska vara tomt; hantera ändå försiktigt.
	if out.Message.Thinking != "" {
		fmt.Println("🧠 tänkande (oförväntat):\n", out.Message.Thinking)
	}
	fmt.Println("\n💬 svar:\n", out.Message.Content)
	return nil
}

(Qwen3:s /think och /no_think mjuk switch dokumenteras av Qwen-teamet; den senaste instruktionen gäller i flervägs-chattar.)

Chat — 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 kärnidén."},
		},
	}

	raw, err := httpPostJSON(endpoint, req)
	if err != nil {
		return err
	}
	var out ChatResponse
	if err := json.Unmarshal(raw, &out); err != nil {
		return err
	}
	// Känd egendomlighet: inaktivering av tänkande kan inte fullständigt undertrycka resonemang på gpt-oss:20b.
	// Filtrera/undanröj alltid tänkande i gränssnittet 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 inaktivering av tänkande på gpt-oss:20b (t.ex., /set nothink eller --think=false) kan ignoreras—planera för klientsidig filtrering om det behövs.

Generate — Qwen3 och GPT-OSS

func generateQwen3() error {
	endpoint := "http://localhost:11434/api/generate"
	req := GenerateRequest{
		Model:  "qwen3:4b-thinking",
		Prompt: "På 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 kort bakpropagering i neurala nätverk.",
		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-formatering och strömningsbeteende kommer direkt från Ollama API-referensen.


Del 2 — Anrop till Ollama via det officiella Go SDK (github.com/ollama/ollama/api)

Det officiella paketet exponerar en Client med metoder som motsvarar REST-API:t. Ollama CLI själv använder detta paket för att kommunicera med tjänsten, vilket gör det till det säkraste valet för kompatibilitet.

Installera

go get github.com/ollama/ollama/api

Chat — 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() // respektera OLLAMA_HOST om inställt
	if err != nil {
		return err
	}

	req := &api.ChatRequest{
		Model: "qwen3:8b-thinking",
		// Många serverbyggnader exponerar tänkande som en huvudflagga;
		// 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 noggrann assistent."},
			{Role: "user",   Content: "Förklara merge sort med ett kort Go-snippet."},
		},
	}

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

	if resp.Message.Thinking != "" {
		fmt.Println("🧠 tänker:\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)
	}
}

Chat — GPT-OSS (hantera resonemang defensivt)

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 det användbart?"},
		},
	}
	var resp api.ChatResponse
	if err := client.Chat(ctx, req, &resp); err != nil {
		return err
	}
	// Om du avser att dölja resonemang, gör det här oavsett flaggor.
	if resp.Message.Thinking != "" {
		fmt.Println("🧠 tänker:\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 för en B-Tree 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änker:\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änker:\n", resp.Thinking)
	}
	fmt.Println("\n💬 svar:\n", resp.Response)
	return nil
}

Det officiella paketets gränssnitt speglar REST-dokumentationen och uppdateras tillsammans med kärnprojektet.


Strömmande svar

För realtidströmning, ställ in Stream: bptr(true) i din begäran. Svaret kommer att levereras som newline-avgränsade JSON-kluster:

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 anländer
		if chunk.Message.Thinking != "" {
			fmt.Print(chunk.Message.Thinking)
		}
		fmt.Print(chunk.Message.Content)

		if chunk.Done {
			break
		}
	}
	return nil
}

Med det officiella SDK använder du en callback-funktion för att hantera strömmande kluster:

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
}

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

  • Två reglage:

    1. En boolean thinking-flagga som stöds av Ollamas tänkandefunktion; och
    2. Qwen3:s mjuk switch-kommandon /think och /no_think i det senaste system/användarmeddelandet. Den senaste instruktionen styr nästa tur(er).
  • Standardinställning: icke-tänkande för snabba svar; eskalera till tänkande för uppgifter som kräver steg-för-steg-resonemang (matematik, planering, felsökning, komplex kodanalys).

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

  • Multi-turn-samtal: Qwen3 kommer ihåg tänkande-läget från tidigare turer. Om du vill byta det under samtalet, använd både flaggan OCH mjuk-switch-kommandot för pålitlighet.

Anteckningar för GPT-OSS

  • Behandla resonemang som närvarande även om du försökt att stänga av det; filtrera på klientsidan om ditt UX inte ska visa det.
  • För produktionsapplikationer som använder GPT-OSS, implementera klientsidans filtreringslogik som kan upptäcka och ta bort resonemangsmönster om det behövs.
  • Testa din specifika GPT-OSS-modellvariant noggrant, eftersom beteendet kan variera mellan olika kvantiseringar och versioner.

Bästa praxis och produktionsråd

Felhantering och tidsgränser

Implementera alltid korrekt hantering av tidsgränser och felåterhämtning:

func robustChatRequest(ctx context.Context, model string, messages []api.Message) (*api.ChatResponse, error) {
	// Ställ in en rimlig tidsgräns
	ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
	defer cancel()

	client, err := api.ClientFromEnvironment()
	if err != nil {
		return nil, fmt.Errorf("skapande av 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("chat-begäran misslyckades: %w", err)
	}

	return &resp, nil
}

Anslutningspooling och återanvändning

Återanvänd Ollama-klienten över 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ökontroll

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

Det officiella SDK respekterar automatiskt OLLAMA_HOST via api.ClientFromEnvironment().

Övervakning och loggning

Implementera strukturerad loggning för produktionssystem:

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 är github.com/ollama/ollama/api det mest kompletta, produktionsklara valet. Det underhålls tillsammans med Ollama-kärnprojektet, används av den officiella CLI:n och erbjuder omfattande API-täckning med garanterad kompatibilitet.

  • För högre nivåer av abstraktion, överväg LangChainGo när du behöver kedjor, verktyg, vektorlagring och RAG-pipelines—fast du kommer att byta ut viss låg nivå kontroll för bekvämlighet.

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

  • För GPT-OSS, planera alltid för att sanitera resonemang på klientsidan när det behövs, eftersom flaggan för att stänga av tänkande kanske inte alltid följs.

  • I produktion, implementera korrekt felhantering, anslutningspooling, tidsgränser och övervakning för att bygga robusta Ollama-drivna applikationer.

Kombinationen av Gos starka typning, utmärkt stöd för parallellitet och Ollamas enkla API gör det till en idealisk stack för att bygga AI-drivna applikationer - från enkla chattbotar till komplexa RAG-system.

Nyckelpunkter

Här är en snabb referens för att välja din tillvägagångssätt:

Användningsområde Rekommenderat tillvägagångssätt Varför
Produktionsapplikation github.com/ollama/ollama/api Officiellt stöd, fullständig API-täckning, underhålls med kärnprojektet
RAG/kedjor/verktygspipeline LangChainGo Högre nivåer av abstraktion, integrationer med vektorlagring
Inlärning/experimentering Raw REST med net/http Full transparens, inga beroenden, pedagogisk
Snabb prototypering Officiellt SDK Balans mellan enkelhet och kraft
Strömmande chat-UI Officiellt SDK med callbacks Inbyggt stöd för strömning, ren API

Modellval vägledning:

  • Qwen3: Bäst för applikationer som kräver kontrollerbart tänkande-läge, pålitliga multi-turn-samtal
  • GPT-OSS: Stark prestanda men kräver defensiv hantering av tänkande/resonemang
  • Andra modeller: Testa noggrant; tänkande-beteende varierar mellan modellfamiljer

Referenser och vidare läsning

Officiell dokumentation

Go SDK-alternativ

Modellspecifika resurser

Relaterade ämnen

Andra användbara länkar