Clients Go pour Ollama : comparaison des SDK et exemples avec Qwen3/GPT-OSS

Intégrez Ollama avec Go : guide de l'API, exemples et bonnes pratiques en production.

Sommaire

Ce guide fournit un aperçu complet des SDK Go pour Ollama disponibles et compare leurs ensembles de fonctionnalités.

Nous explorerons des exemples pratiques en Go pour appeler les modèles Qwen3 et GPT-OSS hébergés sur Ollama, à la fois via des appels REST API bruts et le client Go officiel, y compris un traitement détaillé des modes de réflexion et non-réflexion dans Qwen3.

go et ollama

Pourquoi Ollama + Go ?

Ollama expose un petit API HTTP pragmatique (généralement en cours d’exécution à http://localhost:11434) conçu pour les charges de travail générer et chat, avec un support natif de streaming et des fonctionnalités de gestion des modèles. La documentation officielle couvre en détail les structures de requête/réponse /api/generate et /api/chat ainsi que les sémantiques de streaming.

Go est un excellent choix pour créer des clients Ollama en raison de son support fort de la bibliothèque standard pour HTTP, de son excellent traitement JSON, de ses primitives de concurrence natives et de ses interfaces statiquement typées qui captent les erreurs à la compilation.

À partir d’octobre 2025, voici les options de SDK Go que vous envisagerez probablement.


SDK Go pour Ollama — ce qui est disponible ?

SDK / Package Statut et « propriétaire » Portée (Générer/Chat/Streaming) Gestion des modèles (tirer/lister/etc.) Extras / Notes
github.com/ollama/ollama/api Officiel package à l’intérieur du repo Ollama ; utilisé par le CLI ollama lui-même Couverture complète mappée au REST ; streaming supporté Oui Considéré comme le client Go canonique ; l’API miroir les documents de manière proche.
LangChainGo (github.com/tmc/langchaingo/llms/ollama) Cadre communautaire (LangChainGo) avec module LLM Ollama Chat/Complétion + streaming via des abstractions de framework Limité (la gestion des modèles n’est pas l’objectif principal) Très bien si vous souhaitez des chaînes, des outils, des magasins de vecteurs en Go ; moins d’un SDK brut.
github.com/swdunlop/ollama-client Client communautaire Se concentre sur le chat ; bons expérimentations de appel d’outil Partiel Construit pour expérimenter avec l’appel d’outil ; pas une surface complète 1:1.
Autres SDK communautaires (ex. ollamaclient, « go-ollama-sdk » tiers) Communautaire Varie Varie La qualité et la couverture varient ; évaluez par repo.

Recommandation : Pour la production, préférez github.com/ollama/ollama/api — il est maintenu avec le projet principal et miroir l’API REST.


Qwen3 & GPT-OSS sur Ollama : mode de réflexion vs non-réflexion (ce que vous devez savoir)

  • Mode de réflexion sur Ollama sépare la « raison » du modèle du résultat final lorsqu’il est activé. Ollama documente un comportement activer/désactiver la réflexion de première classe à travers les modèles pris en charge.
  • (https://www.glukhov.org/fr/post/2025/10/qwen3-30b-vs-gpt-oss-20b/ “Qwen3:30b vs GPT-OSS:20b : détails techniques, comparaison des performances et vitesse”) prend en charge le basculement dynamique : ajoutez /think ou /no_think dans les messages système/utilisateur pour basculer les modes tour par tour ; l’instruction la plus récente gagne.
  • GPT-OSS : les utilisateurs rapportent que désactiver la réflexion (ex. /set nothink ou --think=false) peut être peu fiable sur gpt-oss:20b ; prévoyez de filtrer/cacher toute réflexion que votre interface utilisateur ne devrait pas afficher.

Partie 1 — Appel d’Ollama via REST brut (Go, net/http)

Types partagés

Tout d’abord, définissons les types communs et les fonctions d’aide que nous utiliserons dans nos exemples :

package main

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

// ---- Types API de chat ----

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

type ChatRequest struct {
	Model    string        `json:"model"`
	Messages []ChatMessage `json:"messages"`
	// Certains serveurs exposent le contrôle de la réflexion comme un drapeau booléen.
	// Même si omis, vous pouvez toujours contrôler Qwen3 via /think ou /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"` // présent lorsque la réflexion est activée
	} `json:"message"`
	Done bool `json:"done"`
}

// ---- Types API de génération ----

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"`           // texte final pour non-stream
	Thinking  string `json:"thinking,omitempty"` // présent lorsque la réflexion est activée
	Done      bool   `json:"done"`
}

// ---- Fonctions d'aide ----

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 retourne un pointeur vers une valeur booléenne
func bptr(b bool) *bool { return &b }

Chat — Qwen3 avec réflexion ACTIVÉE (et comment la désactiver)

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

	req := ChatRequest{
		Model:   "qwen3:8b-thinking", // tout tag :*-thinking que vous avez tiré
		Think:   bptr(true),
		Stream:  bptr(false),
		Messages: []ChatMessage{
			{Role: "system", Content: "Vous êtes un assistant précis."},
			{Role: "user",   Content: "Expliquez la récursion avec un exemple court en 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("🧠 réflexion:\n", out.Message.Thinking)
	fmt.Println("\n💬 réponse:\n", out.Message.Content)
	return nil
}

// Pour désactiver la réflexion pour le prochain tour, faites :
// (a) définissez Think=false, et/ou
// (b) ajoutez "/no_think" au message système/utilisateur le plus récent (commutateur doux de Qwen3).
// Qwen3 honore l'instruction /think ou /no_think la plus récente dans les conversations multi-tours.
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: "Vous êtes concis. /no_think"},
			{Role: "user",   Content: "Expliquez la récursion en une phrase."},
		},
	}

	raw, err := httpPostJSON(endpoint, req)
	if err != nil {
		return err
	}
	var out ChatResponse
	if err := json.Unmarshal(raw, &out); err != nil {
		return err
	}
	// S'attendre à ce que la réflexion soit vide ; toujours gérer défensivement.
	if out.Message.Thinking != "" {
		fmt.Println("🧠 réflexion (inattendue):\n", out.Message.Thinking)
	}
	fmt.Println("\n💬 réponse:\n", out.Message.Content)
	return nil
}

(Le commutateur doux de Qwen3 /think et /no_think est documenté par l’équipe Qwen ; l’instruction la plus récente gagne dans les conversations multi-tours.)

Chat — GPT-OSS avec réflexion (et une note)

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

	req := ChatRequest{
		Model:  "gpt-oss:20b",
		Think:  bptr(true),   // demande une réflexion séparée si prise en charge
		Stream: bptr(false),
		Messages: []ChatMessage{
			{Role: "user", Content: "Qu'est-ce que la programmation dynamique ? Expliquez l'idée centrale."},
		},
	}

	raw, err := httpPostJSON(endpoint, req)
	if err != nil {
		return err
	}
	var out ChatResponse
	if err := json.Unmarshal(raw, &out); err != nil {
		return err
	}
	// Quirks connus : désactiver la réflexion peut ne pas supprimer complètement la réflexion sur gpt-oss:20b.
	// Filtrez toujours la réflexion dans l'interface utilisateur si vous ne souhaitez pas l'afficher.
	fmt.Println("🧠 réflexion:\n", out.Message.Thinking)
	fmt.Println("\n💬 réponse:\n", out.Message.Content)
	return nil
}

Les utilisateurs rapportent que désactiver la réflexion sur gpt-oss:20b (ex. /set nothink ou --think=false) peut être ignoré — prévoyez un filtrage côté client si nécessaire.

Générer — Qwen3 et GPT-OSS

func generateQwen3() error {
	endpoint := "http://localhost:11434/api/generate"
	req := GenerateRequest{
		Model:  "qwen3:4b-thinking",
		Prompt: "En 2–3 phrases, à quoi servent les arbres-B dans les bases de données ?",
		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("🧠 réflexion:\n", out.Thinking)
	}
	fmt.Println("\n💬 réponse:\n", out.Response)
	return nil
}

func generateGptOss() error {
	endpoint := "http://localhost:11434/api/generate"
	req := GenerateRequest{
		Model:  "gpt-oss:20b",
		Prompt: "Expliquez brièvement la rétropropagation dans les réseaux de neurones.",
		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("🧠 réflexion:\n", out.Thinking)
	}
	fmt.Println("\n💬 réponse:\n", out.Response)
	return nil
}

Les formes REST et le comportement de streaming proviennent directement de la référence API Ollama.


Partie 2 — Appel d’Ollama via le SDK Go officiel (github.com/ollama/ollama/api)

Le package officiel expose un Client avec des méthodes correspondant à l’API REST. Le CLI Ollama lui-même utilise ce package pour communiquer avec le service, ce qui en fait le choix le plus sûr pour la compatibilité.

Installer

go get github.com/ollama/ollama/api

Chat — Qwen3 (réflexion ACTIVÉE / DÉSACTIVÉE)

package main

import (
	"context"
	"fmt"
	"log"

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

func chatWithQwen3Thinking(ctx context.Context, thinking bool) error {
	client, err := api.ClientFromEnvironment() // honore OLLAMA_HOST si défini
	if err != nil {
		return err
	}

	req := &api.ChatRequest{
		Model: "qwen3:8b-thinking",
		// Beaucoup de builds de serveur exposent la réflexion comme un drapeau au niveau supérieur ;
		// en outre, vous pouvez contrôler Qwen3 via /think ou /no_think dans les messages.
		Think: api.Ptr(thinking),
		Messages: []api.Message{
			{Role: "system", Content: "Vous êtes un assistant précis."},
			{Role: "user",   Content: "Expliquez le tri fusion avec un court extrait de code Go."},
		},
	}

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

	if resp.Message.Thinking != "" {
		fmt.Println("🧠 réflexion:\n", resp.Message.Thinking)
	}
	fmt.Println("\n💬 réponse:\n", resp.Message.Content)
	return nil
}

func main() {
	ctx := context.Background()
	if err := chatWithQwen3Thinking(ctx, true); err != nil {
		log.Fatal(err)
	}
	// Exemple : non-réflexion
	if err := chatWithQwen3Thinking(ctx, false); err != nil {
		log.Fatal(err)
	}
}

Chat — GPT-OSS (gérer la réflexion de manière défensive)

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: "Qu'est-ce que la mémorisation et quand est-elle utile ?"},
		},
	}
	var resp api.ChatResponse
	if err := client.Chat(ctx, req, &resp); err != nil {
		return err
	}
	// Si vous souhaitez cacher la réflexion, faites-le ici indépendamment des drapeaux.
	if resp.Message.Thinking != "" {
		fmt.Println("🧠 réflexion:\n", resp.Message.Thinking)
	}
	fmt.Println("\n💬 réponse:\n", resp.Message.Content)
	return nil
}

Générer — 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: "Résumez le rôle d'un arbre-B dans l'indexation.",
		Think:  api.Ptr(true),
	}
	var resp api.GenerateResponse
	if err := client.Generate(ctx, req, &resp); err != nil {
		return err
	}
	if resp.Thinking != "" {
		fmt.Println("🧠 réflexion:\n", resp.Thinking)
	}
	fmt.Println("\n💬 réponse:\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: "Expliquez la descente de gradient en termes simples.",
		Think:  api.Ptr(true),
	}
	var resp api.GenerateResponse
	if err := client.Generate(ctx, req, &resp); err != nil {
		return err
	}
	if resp.Thinking != "" {
		fmt.Println("🧠 réflexion:\n", resp.Thinking)
	}
	fmt.Println("\n💬 réponse:\n", resp.Response)
	return nil
}

La surface du package officiel miroire les documents REST et est mise à jour en parallèle avec le projet principal.


Réponses en flux

Pour les réponses en temps réel, définissez Stream: bptr(true) dans votre requête. La réponse sera livrée comme des morceaux JSON délimités par des nouvelles lignes :

func streamChatExample() error {
	endpoint := "http://localhost:11434/api/chat"
	req := ChatRequest{
		Model:  "qwen3:8b-thinking",
		Think:  bptr(true),
		Stream: bptr(true), // Activer le streaming
		Messages: []ChatMessage{
			{Role: "user", Content: "Expliquez l'algorithme de tri rapide étape par étape."},
		},
	}

	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
		}
		
		// Traiter la réflexion et le contenu à mesure qu'ils arrivent
		if chunk.Message.Thinking != "" {
			fmt.Print(chunk.Message.Thinking)
		}
		fmt.Print(chunk.Message.Content)
		
		if chunk.Done {
			break
		}
	}
	return nil
}

Avec le SDK officiel, utilisez une fonction de rappel pour gérer les morceaux de 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: "Expliquez les arbres de recherche binaire."},
		},
	}
	
	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
}

Travail avec Qwen3 réflexion vs non-réflexion (guidance pratique)

  • Deux leviers :

    1. Un drapeau booléen thinking pris en charge par la fonctionnalité de réflexion d’Ollama ; et
    2. Le commutateur doux de Qwen3 /think et /no_think dans le message système/utilisateur le plus récent. L’instruction la plus récente gouverne le prochain tour(s).
  • Posture par défaut : non-réflexion pour les réponses rapides ; passer à réflexion pour les tâches nécessitant une raison étape par étape (math, planification, débogage, analyse complexe de code).

  • Interfaces utilisateur en flux : lorsque la réflexion est activée, vous pouvez voir des raisonnements/contenu entrelacés dans les cadres en flux — tamponnez ou affichez-les séparément et donnez aux utilisateurs un commutateur « afficher le raisonnement ». (Voir les documents API pour le format de flux.)

  • Conversations multi-tours : Qwen3 se souvient du mode de réflexion des tours précédents. Si vous souhaitez basculer au milieu d’une conversation, utilisez à la fois le drapeau ET le commutateur doux pour la fiabilité.

Notes pour GPT-OSS

  • Traitez la réflexion comme présente même si vous avez tenté de la désactiver ; filtrez côté client si votre interface utilisateur ne doit pas l’afficher.
  • Pour les applications de production utilisant GPT-OSS, implémentez une logique de filtrage côté client qui peut détecter et supprimer les motifs de réflexion si nécessaire.
  • Testez votre variante spécifique de modèle GPT-OSS en détail, car le comportement peut varier entre différentes quantifications et versions.

Bonnes pratiques et conseils pour la production

Gestion des erreurs et des délais

Implémentez toujours une gestion correcte des délais et de la récupération d’erreurs :

func robustChatRequest(ctx context.Context, model string, messages []api.Message) (*api.ChatResponse, error) {
	// Définissez un délai raisonnable
	ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
	defer cancel()
	
	client, err := api.ClientFromEnvironment()
	if err != nil {
		return nil, fmt.Errorf("création du client : %w", err)
	}
	
	req := &api.ChatRequest{
		Model:    model,
		Messages: messages,
		Options: map[string]interface{}{
			"temperature": 0.7,
			"num_ctx":     4096, // taille de la fenêtre de contexte
		},
	}
	
	var resp api.ChatResponse
	if err := client.Chat(ctx, req, &resp); err != nil {
		return nil, fmt.Errorf("requête de chat échouée : %w", err)
	}
	
	return &resp, nil
}

Gestion des connexions et réutilisation

Réutilisez le client Ollama entre les requêtes au lieu de créer un nouveau client à chaque fois :

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
}

Configuration de l’environnement

Utilisez des variables d’environnement pour une déploiement flexible :

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

Le SDK officiel respecte automatiquement OLLAMA_HOST via api.ClientFromEnvironment().

Surveillance et journalisation

Implémentez une journalisation structurée pour les systèmes de production :

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
}

Conclusion

  • Pour les projets Go, github.com/ollama/ollama/api est le choix le plus complet et prêt pour la production. Il est maintenu en parallèle avec le projet principal Ollama, utilisé par le CLI officiel, et fournit une couverture API complète avec une compatibilité garantie.

  • Pour des abstractions de niveau supérieur, envisagez LangChainGo lorsque vous avez besoin de chaînes, d’outils, de magasins de vecteurs et de pipelines RAG — bien que vous deviez sacrifier un peu de contrôle bas niveau pour la commodité.

  • Qwen3 vous donne un contrôle propre et fiable sur le mode de réflexion avec à la fois des drapeaux et des commutateurs au niveau du message (/think, /no_think), ce qui le rend idéal pour les applications nécessitant à la fois des réponses rapides et une réflexion approfondie.

  • Pour GPT-OSS, planifiez toujours de nettoyer la sortie de réflexion côté client lorsqu’il est nécessaire, car le drapeau de désactivation de la réflexion peut ne pas être respecté de manière cohérente.

  • En production, implémentez une gestion correcte des erreurs, une réutilisation des connexions, des délais et de la surveillance pour construire des applications Ollama-powered robustes.

La combinaison du typage fort de Go, du support de concurrence excellent et de l’API simple d’Ollama en fait un stack idéal pour construire des applications alimentées par l’IA — des simples chatbots à des systèmes RAG complexes.

Points clés

Voici un résumé rapide pour choisir votre approche :

Cas d’utilisation Approche recommandée Pourquoi
Application de production github.com/ollama/ollama/api Support officiel, couverture API complète, maintenu avec le projet principal
Pipeline RAG/chaine/outils LangChainGo Abstractions de haut niveau, intégrations avec les magasins de vecteurs
Apprendre/expérimenter REST brut avec net/http Transparence totale, pas de dépendances, éducatif
Prototypage rapide SDK officiel Équilibre entre simplicité et puissance
Interface utilisateur de chat en flux SDK officiel avec rappels Support de streaming intégré, API propre

Guidance pour la sélection du modèle :

  • Qwen3 : Meilleur pour les applications nécessitant un mode de réflexion contrôlable, des conversations multi-tours fiables
  • GPT-OSS : Bonne performance mais nécessite un traitement défensif de la sortie de réflexion/raisonnement
  • Autres modèles : Testez soigneusement ; le comportement de réflexion varie selon la famille de modèles

Références et lecture supplémentaire

Documentation officielle

Alternatives SDK Go

Ressources spécifiques aux modèles

Sujets connexes

Autres liens utiles