Klien Go untuk Ollama: Perbandingan SDK dan contoh Qwen3/GPT-OSS

Integrasi Ollama dengan Go: Panduan SDK, contoh, dan praktik terbaik untuk produksi.

Konten Halaman

Panduan ini memberikan gambaran menyeluruh tentang SDK Go untuk Ollama yang tersedia dan membandingkan set fitur mereka.

Kita akan menjelajahi contoh praktis Go untuk memanggil model Qwen3 dan GPT-OSS yang berjalan di Ollama—baik melalui raw REST API calls dan official Go client—termasuk penanganan rinci dari mode thinking dan non-thinking di Qwen3.

go dan ollama

Mengapa Ollama + Go?

Ollama mengekspos sebuah API HTTP kecil yang pragmatis (biasanya berjalan di http://localhost:11434) yang dirancang untuk beban kerja generate dan chat, dengan dukungan streaming bawaan dan kemampuan manajemen model. Dokumentasi resmi secara menyeluruh menuturkan struktur permintaan/ respons /api/generate dan /api/chat serta semantik streaming.

Go adalah pilihan yang sangat baik untuk membangun klien Ollama karena dukungan library standar yang kuat untuk HTTP, penanganan JSON yang sangat baik, primitif konkurensi bawaan, dan antarmuka bertipe statis yang menangkap kesalahan saat kompilasi.

Sejak Oktober 2025, berikut adalah pilihan SDK Go yang paling mungkin Anda pertimbangkan.


SDK Go untuk Ollama — apa yang tersedia?

SDK / Package Status & “owner” Lingkup (Generate/Chat/Streaming) Manajemen model (pull/list/etc.) Ekstra / Catatan
github.com/ollama/ollama/api Resmi package di dalam repo Ollama; digunakan oleh CLI ollama itu sendiri Lengkap penuturan yang dipetakan ke REST; streaming didukung Ya Dianggap sebagai kanonik klien Go; API meniru dokumentasi secara dekat.
LangChainGo (github.com/tmc/langchaingo/llms/ollama) Kerangka komunitas (LangChainGo) dengan modul LLM Ollama Chat/Completion + streaming melalui abstraksi kerangka Terbatas (manajemen model bukan tujuan utama) Bagus jika Anda ingin rantai, alat, penyimpanan vektor dalam Go; kurang dari SDK mentah.
github.com/swdunlop/ollama-client Klien komunitas Fokus pada chat; eksperimen tool-calling yang baik Parsial Dibangun untuk eksperimen dengan pemanggilan alat; bukan permukaan penuh 1:1.
SDK komunitas lainnya (misalnya, ollamaclient, SDK pihak ketiga “go-ollama-sdk”) Komunitas Beragam Beragam Kualitas dan cakupan bervariasi; evaluasi per repo.

Rekomendasi: Untuk produksi, pilih github.com/ollama/ollama/api—dikelola bersama proyek inti dan meniru API REST.


Qwen3 & GPT-OSS di Ollama: thinking vs non-thinking (apa yang perlu diketahui)

  • Mode thinking di Ollama memisahkan “penalaran” model dari output akhir ketika diaktifkan. Ollama mendokumentasikan perilaku aktif/non-aktif thinking sebagai fitur kelas satu di seluruh model yang didukung.
  • (https://www.glukhov.org/id/post/2025/10/qwen3-30b-vs-gpt-oss-20b/ “Qwen3:30b vs GPT-OSS:20b: Detail teknis, perbandingan kinerja dan kecepatan”) mendukung toggle dinamis: tambahkan /think atau /no_think dalam pesan sistem/pengguna untuk beralih mode secara berulang; instruksi terbaru menang.
  • GPT-OSS: pengguna melaporkan bahwa menonaktifkan thinking (misalnya, /set nothink atau --think=false) bisa tidak andal pada gpt-oss:20b; rencanakan untuk menyaring/menghilangkan setiap penalaran yang tidak boleh ditampilkan UI Anda.

Bagian 1 — Memanggil Ollama melalui raw REST (Go, net/http)

Tipe yang dibagi

Pertama, mari kita definisikan tipe umum dan fungsi bantu yang akan kita gunakan di seluruh contoh kita:

package main

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

// ---- Tipe API Chat ----

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

type ChatRequest struct {
	Model    string        `json:"model"`
	Messages []ChatMessage `json:"messages"`
	// Beberapa server mengekspos kontrol thinking sebagai bendera boolean.
	// Bahkan jika dihilangkan, Anda masih dapat mengontrol Qwen3 melalui /think atau /no_think tag.
	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"` // hadir ketika thinking diaktifkan
	} `json:"message"`
	Done bool `json:"done"`
}

// ---- Tipe 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"`           // teks akhir untuk non-stream
	Thinking  string `json:"thinking,omitempty"` // hadir ketika thinking diaktifkan
	Done      bool   `json:"done"`
}

// ---- Fungsi Bantu ----

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 mengembalikan pointer ke nilai boolean
func bptr(b bool) *bool { return &b }

Chat — Qwen3 dengan thinking ON (dan cara mematikannya)

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

	req := ChatRequest{
		Model:   "qwen3:8b-thinking", // tag :*-thinking apa pun yang telah Anda unduh
		Think:   bptr(true),
		Stream:  bptr(false),
		Messages: []ChatMessage{
			{Role: "system", Content: "Anda adalah asisten yang presisi."},
			{Role: "user",   Content: "Jelaskan rekursi dengan contoh pendek 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("🧠 thinking:\n", out.Message.Thinking)
	fmt.Println("\n💬 jawaban:\n", out.Message.Content)
	return nil
}

// Matikan thinking untuk putaran berikutnya dengan:
// (a) mengatur Think=false, dan/atau
// (b) menambahkan "/no_think" ke pesan sistem/pengguna terbaru (Qwen3 switch lembut).
// Qwen3 menghormati instruksi /think atau /no_think terbaru dalam percakapan multi-putaran.
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: "Anda ringkas. /no_think"},
			{Role: "user",   Content: "Jelaskan rekursi dalam satu kalimat."},
		},
	}

	raw, err := httpPostJSON(endpoint, req)
	if err != nil {
		return err
	}
	var out ChatResponse
	if err := json.Unmarshal(raw, &out); err != nil {
		return err
	}
	// Harapkan thinking untuk kosong; tetap tangani secara defensif.
	if out.Message.Thinking != "" {
		fmt.Println("🧠 thinking (tidak diharapkan):\n", out.Message.Thinking)
	}
	fmt.Println("\n💬 jawaban:\n", out.Message.Content)
	return nil
}

(Switch lembut Qwen3 /think dan /no_think didokumentasikan oleh tim Qwen; instruksi terakhir menang dalam percakapan multi-putaran.)

Chat — GPT-OSS dengan thinking (dan catatan)

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

	req := ChatRequest{
		Model:  "gpt-oss:20b",
		Think:  bptr(true),   // minta pemisahan penalaran jika didukung
		Stream: bptr(false),
		Messages: []ChatMessage{
			{Role: "user", Content: "Apa itu pemrograman dinamis? Jelaskan ide intinya."},
		},
	}

	raw, err := httpPostJSON(endpoint, req)
	if err != nil {
		return err
	}
	var out ChatResponse
	if err := json.Unmarshal(raw, &out); err != nil {
		return err
	}
	// Catatan yang diketahui: menonaktifkan thinking mungkin tidak sepenuhnya menghambat penalaran pada gpt-oss:20b.
	// Selalu saring/hilangkan thinking dalam UI jika Anda tidak ingin menampilkan hal itu.
	fmt.Println("🧠 thinking:\n", out.Message.Thinking)
	fmt.Println("\n💬 jawaban:\n", out.Message.Content)
	return nil
}

Pengguna melaporkan bahwa menonaktifkan thinking pada gpt-oss:20b (misalnya, /set nothink atau --think=false) dapat diabaikan—rencanakan untuk penyaringan sisi klien jika diperlukan.

Generate — Qwen3 dan GPT-OSS

func generateQwen3() error {
	endpoint := "http://localhost:11434/api/generate"
	req := GenerateRequest{
		Model:  "qwen3:4b-thinking",
		Prompt: "Dalam 2–3 kalimat, apa yang digunakan B-Trees dalam basis data?",
		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💬 jawaban:\n", out.Response)
	return nil
}

func generateGptOss() error {
	endpoint := "http://localhost:11434/api/generate"
	req := GenerateRequest{
		Model:  "gpt-oss:20b",
		Prompt: "Jelaskan secara singkat backpropagation dalam jaringan saraf.",
		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💬 jawaban:\n", out.Response)
	return nil
}

Bentuk REST dan perilaku streaming berasal langsung dari referensi API Ollama.


Bagian 2 — Memanggil Ollama melalui SDK Go resmi (github.com/ollama/ollama/api)

Paket resmi mengekspos sebuah Client dengan metode yang sesuai dengan API REST. CLI Ollama itu sendiri menggunakan paket ini untuk berbicara dengan layanan, yang membuatnya menjadi pilihan teraman untuk kompatibilitas.

Instalasi

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() // menghormati OLLAMA_HOST jika diatur
	if err != nil {
		return err
	}

	req := &api.ChatRequest{
		Model: "qwen3:8b-thinking",
		// Banyak konstruksi server mengekspos thinking sebagai bendera tingkat atas;
		// selain itu, Anda dapat mengontrol Qwen3 melalui /think atau /no_think dalam pesan.
		Think: api.Ptr(thinking),
		Messages: []api.Message{
			{Role: "system", Content: "Anda adalah asisten yang presisi."},
			{Role: "user",   Content: "Jelaskan merge sort dengan snippet Go pendek."},
		},
	}

	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💬 jawaban:\n", resp.Message.Content)
	return nil
}

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

Chat — GPT-OSS (handle reasoning secara defensif)

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: "Apa itu memoization dan kapan berguna?"},
		},
	}
	var resp api.ChatResponse
	if err := client.Chat(ctx, req, &resp); err != nil {
		return err
	}
	// Jika Anda bermaksud menyembunyikan penalaran, lakukan di sini terlepas dari bendera.
	if resp.Message.Thinking != "" {
		fmt.Println("🧠 thinking:\n", resp.Message.Thinking)
	}
	fmt.Println("\n💬 jawaban:\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: "Ringkas peran B-Tree dalam indeksing.",
		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💬 jawaban:\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: "Jelaskan descent gradien dalam istilah sederhana.",
		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💬 jawaban:\n", resp.Response)
	return nil
}

Permukaan paket resmi meniru dokumentasi REST dan diperbarui bersama proyek inti.


Respons streaming

Untuk respons streaming real-time, atur Stream: bptr(true) dalam permintaan Anda. Respons akan dikirim sebagai potongan JSON yang dipisahkan baris:

func streamChatExample() error {
	endpoint := "http://localhost:11434/api/chat"
	req := ChatRequest{
		Model:  "qwen3:8b-thinking",
		Think:  bptr(true),
		Stream: bptr(true), // Aktifkan streaming
		Messages: []ChatMessage{
			{Role: "user", Content: "Jelaskan algoritma quicksort langkah demi langkah."},
		},
	}

	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
		}
		
		// Proses thinking dan konten saat tiba
		if chunk.Message.Thinking != "" {
			fmt.Print(chunk.Message.Thinking)
		}
		fmt.Print(chunk.Message.Content)
		
		if chunk.Done {
			break
		}
	}
	return nil
}

Dengan SDK resmi, gunakan fungsi callback untuk menangani potongan 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: "Jelaskan pohon pencarian biner."},
		},
	}
	
	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
}

Bekerja dengan Qwen3 thinking vs non-thinking (panduan praktis)

  • Dua tombol:

    1. Bendera boolean thinking yang didukung oleh fitur thinking Ollama; dan
    2. switch lembut Qwen3 /think dan /no_think dalam pesan sistem/pengguna terbaru. Instruksi terbaru mengatur putaran berikutnya.
  • Postur default: non-thinking untuk jawaban cepat; tingkatkan ke thinking untuk tugas yang membutuhkan penalaran langkah demi langkah (matematika, perencanaan, debugging, analisis kode kompleks).

  • UI streaming: ketika thinking diaktifkan, Anda mungkin melihat penalaran/konten yang tercampur dalam frame streaming—buffer atau render secara terpisah dan berikan pengguna toggle “tampilkan penalaran”. (Lihat dokumentasi API untuk format streaming.)

  • Percakapan multi-putaran: Qwen3 mengingat mode thinking dari putaran sebelumnya. Jika Anda ingin beralih di tengah percakapan, gunakan bendera dan switch lembut untuk keandalan.

Catatan untuk GPT-OSS

  • Anggap penalaran sebagai hadir bahkan jika Anda mencoba menonaktifkannya; saring di sisi klien jika UX Anda tidak boleh menampilkannya.
  • Untuk aplikasi produksi yang menggunakan GPT-OSS, implementasikan logika penyaringan sisi klien yang dapat mendeteksi dan menghilangkan pola penalaran jika diperlukan.
  • Uji model GPT-OSS spesifik Anda secara menyeluruh, karena perilaku mungkin bervariasi antara kuantisasi dan versi berbeda.

Praktik terbaik dan tips produksi

Penanganan kesalahan dan timeout

Selalu implementasikan penanganan timeout dan pemulihan kesalahan yang tepat:

func robustChatRequest(ctx context.Context, model string, messages []api.Message) (*api.ChatResponse, error) {
	// Tetapkan timeout yang wajar
	ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
	defer cancel()
	
	client, err := api.ClientFromEnvironment()
	if err != nil {
		return nil, fmt.Errorf("membuat klien: %w", err)
	}
	
	req := &api.ChatRequest{
		Model:    model,
		Messages: messages,
		Options: map[string]interface{}{
			"temperature": 0.7,
			"num_ctx":     4096, // ukuran jendela konteks
		},
	}
	
	var resp api.ChatResponse
	if err := client.Chat(ctx, req, &resp); err != nil {
		return nil, fmt.Errorf("permintaan chat gagal: %w", err)
	}
	
	return &resp, nil
}

Pemrosesan koneksi dan penggunaan ulang

Gunakan ulang klien Ollama di antara permintaan alih-alih membuat satu baru setiap kali:

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
}

Konfigurasi lingkungan

Gunakan variabel lingkungan untuk pengembangan fleksibel:

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

SDK resmi secara otomatis menghormati OLLAMA_HOST melalui api.ClientFromEnvironment().

Pemantauan dan logging

Implementasikan logging terstruktur untuk sistem produksi:

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
}

Kesimpulan

  • Untuk proyek Go, github.com/ollama/ollama/api adalah pilihan terlengkap dan siap produksi. Dikelola bersama proyek inti Ollama, digunakan oleh CLI resmi, dan menyediakan cakupan API menyeluruh dengan kompatibilitas yang dijamin.

  • Untuk abstraksi tingkat tinggi, pertimbangkan LangChainGo ketika Anda membutuhkan rantai, alat, penyimpanan vektor, dan pipeline RAG—meskipun Anda akan menukar sedikit kontrol tingkat rendah untuk kenyamanan.

  • Qwen3 memberi Anda kontrol bersih dan andal terhadap mode thinking dengan bendera dan toggle pesan tingkat pesan (/think, /no_think), membuatnya ideal untuk aplikasi yang membutuhkan jawaban cepat dan penalaran mendalam.

  • Untuk GPT-OSS, selalu rencanakan untuk membersihkan output penalaran di sisi klien ketika diperlukan, karena bendera disable thinking mungkin tidak selalu dihormati.

  • Di produksi, implementasikan penanganan kesalahan yang tepat, pemrosesan koneksi, timeout, dan pemantauan untuk membangun aplikasi berbasis Ollama yang andal.

Kombinasi tipe kuat Go, dukungan konkurensi yang sangat baik, dan API Ollama yang sederhana membuatnya menjadi stack ideal untuk membangun aplikasi berbasis AI—dari chatbot sederhana hingga sistem RAG kompleks.

Poin penting

Berikut adalah referensi cepat untuk memilih pendekatan Anda:

Kasus Penggunaan Pendekatan yang Direkomendasikan Mengapa
Aplikasi produksi github.com/ollama/ollama/api Dukungan resmi, cakupan API lengkap, dikelola bersama proyek inti
RAG/rantai/alat pipeline LangChainGo Abstraksi tingkat tinggi, integrasi dengan penyimpanan vektor
Pembelajaran/eksperimen Raw REST dengan net/http Transparansi penuh, tanpa ketergantungan, pendidikan
Prototipe cepat SDK resmi Keseimbangan antara kesederhanaan dan kekuatan
UI chat streaming SDK resmi dengan callback Dukungan streaming bawaan, API bersih

Panduan pemilihan model:

  • Qwen3: Terbaik untuk aplikasi yang memerlukan kontrol mode thinking yang dapat diatur, percakapan multi-putaran yang andal
  • GPT-OSS: Kinerja yang kuat tetapi memerlukan penanganan defensif terhadap output penalaran
  • Model lainnya: Uji secara menyeluruh; perilaku thinking bervariasi berdasarkan keluarga model

Referensi & bacaan lanjutan

Dokumentasi resmi

Alternatif SDK Go

Sumber daya spesifik model

Topik terkait

Tautan yang Berguna Lainnya