Clients Go pour Ollama : comparaison des SDK et exemples avec Qwen3/GPT-OSS
Intégrer Ollama avec Go : guide du SDK, exemples et bonnes pratiques en production.
Ce guide fournit un aperçu complet des SDK Go pour Ollama et compare leurs fonctionnalités.
Nous explorerons des exemples pratiques en Go pour appeler les modèles Qwen3 et GPT-OSS hébergés sur Ollama, que ce soit via des appels REST API bruts ou le client Go officiel, en incluant le traitement détaillé des modes de réflexion (thinking) et non-réfléchis (non-thinking) dans Qwen3.

Pourquoi Ollama + Go ?
Ollama expose une petite API HTTP pragmatique (fonctionnant généralement sur http://localhost:11434) conçue pour les charges de travail de génération et de discussion (chat), avec un support intégré du streaming et des fonctionnalités de gestion des modèles. La documentation officielle couvre en détail les structures de requête/réponse et la sémantique de streaming pour /api/generate et /api/chat.
Go est un excellent choix pour construire des clients Ollama grâce à son solide support de la bibliothèque standard pour HTTP, son excellent traitement du JSON, ses primitives de concurrence natives et ses interfaces typées statiquement qui détectent les erreurs à la compilation. Pour voir comment Ollama se compare à vLLM, Docker Model Runner, LocalAI et aux fournisseurs cloud, y compris quand choisir chaque solution, consultez Hébergement de LLM : Infrastructure locale, auto-hébergée et cloud comparée.
À octobre 2025, voici les options de SDK Go que vous êtes le plus susceptible de considérer.
SDK Go pour Ollama — que trouve-t-on ?
| SDK / Package | Statut et « propriétaire » | Périmètre (Génération/Chat/Streaming) | Gestion des modèles (pull/list/etc.) | Extras / Notes |
|---|---|---|---|---|
github.com/ollama/ollama/api |
Package officiel dans le dépôt Ollama ; utilisé par le CLI ollama lui-même |
Couverture complète mappée sur le REST ; streaming pris en charge | Oui | Considéré comme le client Go officiel ; l’API reflète étroitement la documentation. |
LangChainGo (github.com/tmc/langchaingo/llms/ollama) |
Framework communautaire (LangChainGo) avec module LLM Ollama | Chat/Completion + streaming via les abstractions du framework | Limité (gestion des modèles non prioritaire) | Idéal si vous voulez des chaînes, des outils et des stockages vectoriels en Go ; moins un SDK brut. |
github.com/swdunlop/ollama-client |
Client communautaire | Axé sur le chat ; bonnes expériences de tool-calling | Partiel | Conçu pour expérimenter le tool calling ; ne couvre pas 1:1 toute la surface d’API. |
Autres SDK communautaires (ex. : ollamaclient, « go-ollama-sdk » tiers) |
Communauté | Variable | Variable | La qualité et la couverture varient ; évaluez selon le dépôt. |
Recommandation : Pour la production, privilégiez github.com/ollama/ollama/api — il est maintenu avec le projet principal et reflète l’API REST.
Qwen3 & GPT-OSS sur Ollama : thinking vs non-thinking (points clés)
- Le mode thinking dans Ollama sépare le « raisonnement » du modèle de la sortie finale lorsqu’il est activé. Notre aide-mémoire Ollama documente les drapeaux d’activation/désactivation de la réflexion (
--think,--hidethinking) pour les modèles pris en charge. - (https://www.glukhov.org/fr/llm-performance/benchmarks/qwen3-30b-vs-gpt-oss-20b/ “Qwen3:30b vs GPT-OSS:20b : Détails techniques, performance et comparaison de vitesse”) supporte le basculement dynamique : ajoutez
/thinkou/no_thinkdans les messages système/utilisateur pour changer de mode à chaque tour ; la dernière instruction l’emporte. - GPT-OSS : les utilisateurs rapportent que la désactivation de la réflexion (ex. :
/set nothinkou--think=false) peut être peu fiable surgpt-oss:20b; prévoyez de filtrer/masquer tout raisonnement que votre interface ne devrait pas afficher.
Partie 1 — Appeler Ollama via REST brut (Go, net/http)
Types partagés
D’abord, définissons les types et fonctions utilitaires communs que nous utiliserons à travers nos exemples :
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
// ---- Types de l'API 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 s'il est omis, vous pouvez toujours contrôler Qwen3 via les tags /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 de l'API Generate ----
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 le non-streaming
Thinking string `json:"thinking,omitempty"` // présent lorsque la réflexion est activée
Done bool `json:"done"`
}
// ---- Fonctions utilitaires ----
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 thinking ACTIVÉ (et comment le désactiver)
func chatQwen3Thinking() error {
endpoint := "http://localhost:11434/api/chat"
req := ChatRequest{
Model: "qwen3:8b-thinking", // n'importe quel tag :*-thinking que vous avez téléchargé
Think: bptr(true),
Stream: bptr(false),
Messages: []ChatMessage{
{Role: "system", Content: "You are a precise assistant."},
{Role: "user", Content: "Explain recursion with a short Go example."},
},
}
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("🧠 thinking:\n", out.Message.Thinking)
fmt.Println("\n💬 answer:\n", out.Message.Content)
return nil
}
// Désactiver la réflexion pour le tour suivant en :
// (a) définissant Think=false, et/ou
// (b) ajoutant "/no_think" au message système/utilisateur le plus récent (bascule souple Qwen3).
// Qwen3 respecte la dernière instruction /think ou /no_think dans les discussions 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: "You are brief. /no_think"},
{Role: "user", Content: "Explain recursion in one sentence."},
},
}
raw, err := httpPostJSON(endpoint, req)
if err != nil {
return err
}
var out ChatResponse
if err := json.Unmarshal(raw, &out); err != nil {
return err
}
// La réflexion devrait être vide ; gérez cela défensivement.
if out.Message.Thinking != "" {
fmt.Println("🧠 thinking (inattendu):\n", out.Message.Thinking)
}
fmt.Println("\n💬 answer:\n", out.Message.Content)
return nil
}
(Le basculement souple /think et /no_think de Qwen3 est documenté par l’équipe Qwen ; la dernière instruction l’emporte dans les discussions multi-tours.)
Chat — GPT-OSS avec thinking (et une mise en garde)
func chatGptOss() error {
endpoint := "http://localhost:11434/api/chat"
req := ChatRequest{
Model: "gpt-oss:20b",
Think: bptr(true), // demander un raisonnement séparé si pris en charge
Stream: bptr(false),
Messages: []ChatMessage{
{Role: "user", Content: "What is dynamic programming? Explain the core idea."},
},
}
raw, err := httpPostJSON(endpoint, req)
if err != nil {
return err
}
var out ChatResponse
if err := json.Unmarshal(raw, &out); err != nil {
return err
}
// Particularité connue : désactiver la réflexion peut ne pas supprimer complètement le raisonnement sur gpt-oss:20b.
// Filtrez/toujours masquez la réflexion dans l'UI si vous ne souhaitez pas l'afficher.
fmt.Println("🧠 thinking:\n", out.Message.Thinking)
fmt.Println("\n💬 answer:\n", out.Message.Content)
return nil
}
Les utilisateurs rapportent que la désactivation de la réflexion sur gpt-oss:20b (ex. : /set nothink ou --think=false) peut être ignorée — prévoyez un filtrage côté client si nécessaire.
Generate — Qwen3 et GPT-OSS
func generateQwen3() error {
endpoint := "http://localhost:11434/api/generate"
req := GenerateRequest{
Model: "qwen3:4b-thinking",
Prompt: "In 2–3 sentences, what are B-Trees used for 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("🧠 thinking:\n", out.Thinking)
}
fmt.Println("\n💬 answer:\n", out.Response)
return nil
}
func generateGptOss() error {
endpoint := "http://localhost:11434/api/generate"
req := GenerateRequest{
Model: "gpt-oss:20b",
Prompt: "Briefly explain backpropagation in neural networks.",
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("🧠 thinking:\n", out.Thinking)
}
fmt.Println("\n💬 answer:\n", out.Response)
return nil
}
Les structures REST et le comportement du streaming proviennent directement de la référence API Ollama.
Partie 2 — Appeler 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 en termes de compatibilité.
Installation
go get github.com/ollama/ollama/api
Chat — Qwen3 (thinking ON / OFF)
package main
import (
"context"
"fmt"
"log"
"github.com/ollama/ollama/api"
)
func chatWithQwen3Thinking(ctx context.Context, thinking bool) error {
client, err := api.ClientFromEnvironment() // respecte OLLAMA_HOST s'il est défini
if err != nil {
return err
}
req := &api.ChatRequest{
Model: "qwen3:8b-thinking",
// De nombreuses builds serveur exposent la réflexion comme un drapeau de haut niveau ;
// de plus, vous pouvez contrôler Qwen3 via /think ou /no_think dans les messages.
Think: api.Ptr(thinking),
Messages: []api.Message{
{Role: "system", Content: "You are a precise assistant."},
{Role: "user", Content: "Explain merge sort with a short Go snippet."},
},
}
var resp api.ChatResponse
if err := client.Chat(ctx, req, &resp); err != nil {
return err
}
if resp.Message.Thinking != "" {
fmt.Println("🧠 thinking:\n", resp.Message.Thinking)
}
fmt.Println("\n💬 answer:\n", resp.Message.Content)
return nil
}
func main() {
ctx := context.Background()
if err := chatWithQwen3Thinking(ctx, true); err != nil {
log.Fatal(err)
}
// Exemple : sans réflexion
if err := chatWithQwen3Thinking(ctx, false); err != nil {
log.Fatal(err)
}
}
Chat — GPT-OSS (traiter le raisonnement défensivement)
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: "What is memoization and when is it useful?"},
},
}
var resp api.ChatResponse
if err := client.Chat(ctx, req, &resp); err != nil {
return err
}
// Si vous prévoyez de masquer le raisonnement, faites-le ici quels que soient les drapeaux.
if resp.Message.Thinking != "" {
fmt.Println("🧠 thinking:\n", resp.Message.Thinking)
}
fmt.Println("\n💬 answer:\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: "Summarize the role of a B-Tree in indexing.",
Think: api.Ptr(true),
}
var resp api.GenerateResponse
if err := client.Generate(ctx, req, &resp); err != nil {
return err
}
if resp.Thinking != "" {
fmt.Println("🧠 thinking:\n", resp.Thinking)
}
fmt.Println("\n💬 answer:\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: "Explain gradient descent in simple terms.",
Think: api.Ptr(true),
}
var resp api.GenerateResponse
if err := client.Generate(ctx, req, &resp); err != nil {
return err
}
if resp.Thinking != "" {
fmt.Println("🧠 thinking:\n", resp.Thinking)
}
fmt.Println("\n💬 answer:\n", resp.Response)
return nil
}
La surface du package officiel reflète la documentation REST et est mise à jour avec le projet principal.
Réponses en streaming
Pour le streaming en temps réel, définissez Stream: bptr(true) dans votre requête. La réponse sera livrée sous forme de chunks JSON délimités par des sauts de ligne :
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: "Explain quicksort algorithm step by step."},
},
}
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 callback pour gérer les chunks 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: "Explain binary search trees."},
},
}
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
}
Travailler avec Qwen3 thinking vs non-thinking (orientations pratiques)
-
Deux leviers :
- Un drapeau booléen
thinkingpris en charge par la fonctionnalité de réflexion d’Ollama ; et - Les commandes de bascule souple de Qwen3
/thinket/no_thinkdans le dernier message système/utilisateur. La plus récente instruction gouverne les tours suivants.
- Un drapeau booléen
-
Posture par défaut : non-thinking pour les réponses rapides ; passez à thinking pour les tâches nécessitant un raisonnement étape par étape (mathématiques, planification, débogage, analyse de code complexe).
-
Interfaces en streaming : lorsque la réflexion est activée, vous pouvez voir un entrelacement de raisonnement/contenu dans les trames streamées — tamponnez ou affichez-les séparément et offrez aux utilisateurs un interrupteur « afficher le raisonnement ». (Consultez la documentation API pour le format du streaming.)
-
Conversations multi-tours : Qwen3 se souvient du mode de réflexion des tours précédents. Si vous souhaitez le basculer en cours de conversation, utilisez à la fois le drapeau ET la commande de bascule souple pour fiabilité.
Notes pour GPT-OSS
- Traitez le raisonnement comme présent même si vous avez tenté de le désactiver ; filtrez côté client si votre UX ne doit pas l’afficher.
- Pour les applications de production utilisant GPT-OSS, implémentez une logique de filtrage côté client capable de détecter et de supprimer les motifs de raisonnement si nécessaire.
- Testez soigneusement votre variante spécifique de modèle GPT-OSS, car le comportement peut varier selon les différentes quantisations et versions.
Bonnes pratiques et conseils pour la production
Gestion des erreurs et timeouts
Implémentez toujours un traitement approprié des timeouts et une récupération d’erreurs :
func robustChatRequest(ctx context.Context, model string, messages []api.Message) (*api.ChatResponse, error) {
// Définir un timeout 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("la requête chat a échoué: %w", err)
}
return &resp, nil
}
Pool de connexions et réutilisation
Réutilisez le client Ollama à travers les requêtes au lieu d’en créer un nouveau à 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 un 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().
Supervision 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("modèle=%s durée=%v erreur=%v tokens=%d",
req.Model, duration, err, len(resp.Message.Content))
return err
}
Conclusion
-
Pour les projets Go,
github.com/ollama/ollama/apiest le choix le plus complet et prêt pour la production. Il est maintenu avec le projet principal Ollama, utilisé par le CLI officiel et offre une couverture API complète avec une compatibilité garantie. -
Pour des abstractions de plus haut niveau, envisagez LangChainGo lorsque vous avez besoin de chaînes, d’outils, de stockages vectoriels et de pipelines RAG, bien que vous troquerez un peu de contrôle de bas niveau contre de la commodité.
-
Qwen3 vous offre un contrôle propre et fiable sur le mode thinking à la fois avec des drapeaux et des bascules au niveau des messages (
/think,/no_think), ce qui en fait un choix idéal pour les applications nécessitant à la fois des réponses rapides et un raisonnement approfondi. -
Pour GPT-OSS, prévoyez toujours de sanitiser la sortie du raisonnement côté client si nécessaire, car le drapeau de désactivation de la réflexion peut ne pas être constamment respecté.
-
En production, implémentez une bonne gestion des erreurs, un pool de connexions, des timeouts et une supervision pour construire des applications robustes propulsées par Ollama.
La combinaison de la forte typage de Go, de son excellent support de la concurrence et de l’API simple d’Ollama en fait une pile idéale pour construire des applications alimentées par l’IA, des simples chatbots aux systèmes RAG complexes.
Points clés
Voici une référence rapide pour choisir votre approche :
| Cas d’usage | 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/chaînes/outils | LangChainGo | Abstractions de haut niveau, intégrations avec les stockages vectoriels |
| Apprentissage/expérimentation | REST brut avec net/http | Transparence totale, aucune dépendance, éducatif |
| Prototypage rapide | SDK officiel | Équilibre entre simplicité et puissance |
| Interface de chat en streaming | SDK officiel avec callbacks | Support de streaming intégré, API propre |
Orientation pour le choix du modèle :
- Qwen3 : Idéal pour les applications nécessitant un mode thinking contrôlable et des conversations multi-tours fiables
- GPT-OSS : Bonnes performances 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 lectures complémentaires
Documentation officielle
- Référence API Ollama — Documentation complète de l’API REST
- Package Go officiel Ollama — Documentation du SDK Go
- Dépôt GitHub Ollama — Code source et problèmes
Alternatives de SDK Go
- Intégration Ollama LangChainGo — Pour les applications basées sur des chaînes
- swdunlop/ollama-client — Client communautaire avec tool calling
- xyproto/ollamaclient — Autre option communautaire
Ressources spécifiques aux modèles
- Documentation Qwen — Informations officielles sur les modèles Qwen
- Informations sur GPT-OSS — Détails du modèle GPT-OSS
Sujets connexes
- Construire des applications RAG avec Go — Exemples LangChainGo
- Package context Go — Essentiel pour les timeouts et l’annulation
- Bonnes pratiques client HTTP Go — Documentation de la bibliothèque standard
Autres liens utiles
Pour une comparaison plus large d’Ollama avec d’autres infrastructures LLM locales et cloud, consultez notre Hébergement de LLM : Infrastructure locale, auto-hébergée et cloud comparée.
- Installer et configurer Ollama
- Aide-mémoire Ollama
- Aide-mémoire Go
- Architecture et implémentation des systèmes RAG
- Réordonnancement de documents texte avec Ollama et le modèle d’embedding Qwen3 - en Go
- Contraintes sur les LLM avec Sortie Structurée : Ollama, Qwen3 & Python ou Go
- Comparaison de LLMs : Qwen3:30b vs GPT-OSS:20b
- Problèmes de sortie structurée d’Ollama GPT-OSS