Клиенты Go для Ollama: сравнение SDK и примеры Qwen3/GPT-OSS
Интеграция Ollama с Go: руководство по SDK, примеры и лучшие практики для продакшена.
Этот гайд предоставляет всесторонний обзор доступных Go SDK для Ollama и сравнивает их функциональные возможности.
Мы рассмотрим практические примеры на Go для вызова моделей Qwen3 и GPT-OSS, размещенных на Ollama — как через сырые REST-запросы, так и с использованием официального клиента Go, включая детальное управление режимами “мышления” и “без мышления” в Qwen3.

Почему Ollama + Go?
Ollama предоставляет небольшой, практичный HTTP API (обычно работающий на http://localhost:11434), предназначенный для нагрузок генерации и чата, с встроенной поддержкой потоковой передачи и возможностями управления моделями. Официальная документация подробно описывает структуры запросов/ответов и семантику потоковой передачи для /api/generate и /api/chat.
Go — отличный выбор для создания клиентов Ollama благодаря мощной поддержке стандартной библиотеки для HTTP, отличной обработке JSON, нативным примитивам параллелизма и статически типизированным интерфейсам, которые обнаруживают ошибки на этапе компиляции. Чтобы узнать, как Ollama сравнивается с vLLM, Docker Model Runner, LocalAI и облачными провайдерами — включая когда выбирать каждый — см. Хостинг LLM: локальный, самостоятельный и облачная инфраструктура сравниваются.
По состоянию на октябрь 2025 года вот варианты Go SDK, которые вы, скорее всего, будете рассматривать.
Go SDK для Ollama — что доступно?
| SDK / Пакет | Статус и “владелец” | Область (Генерация/Чат/Потоковая передача) | Управление моделями (pull/list и т.д.) | Дополнительно / Примечания |
|---|---|---|---|---|
github.com/ollama/ollama/api |
Официальный пакет внутри репозитория Ollama; используется самим CLI ollama |
Полное покрытие, отображаемое на REST; поддержка потоковой передачи | Да | Считается каноническим клиентом Go; API тесно отражает документацию. |
LangChainGo (github.com/tmc/langchaingo/llms/ollama) |
Сообщество (LangChainGo) с модулем LLM Ollama | Чат/Завершение + потоковая передача через абстракции фреймворка | Ограниченно (управление моделями не является основной целью) | Отлично, если вы хотите цепочки, инструменты, векторные хранилища на Go; менее полноценный SDK. |
github.com/swdunlop/ollama-client |
Клиент сообщества | Фокус на чате; хорошие эксперименты с вызовом инструментов | Частично | Создан для экспериментов с вызовом инструментов; не полное покрытие. |
Другие SDK сообщества (например, ollamaclient, сторонний “go-ollama-sdk”) |
Сообщество | Разное | Разное | Качество и покрытие различаются; оценивайте по репозиторию. |
Рекомендация: Для продакшена предпочтите github.com/ollama/ollama/api — он поддерживается вместе с основным проектом и отражает REST API.
Qwen3 & GPT-OSS на Ollama: мышление vs без мышления (что нужно знать)
- Режим мышления в Ollama разделяет “рассуждение” модели от окончательного вывода при включении. Ollama документирует поведение включения/отключения мышления для поддерживаемых моделей.
- (https://www.glukhov.org/ru/llm-performance/benchmarks/qwen3-30b-vs-gpt-oss-20b/ “Qwen3:30b vs GPT-OSS:20b: технические детали, сравнение производительности и скорости”) поддерживает динамическое переключение: добавьте
/thinkили/no_thinkв системные/пользовательские сообщения для переключения режимов по очереди; последняя инструкция побеждает. - GPT-OSS: пользователи сообщают, что отключение мышления (например,
/set nothinkили--think=false) может быть ненадежным дляgpt-oss:20b; планируйте фильтровать/скрывать любое рассуждение, которое ваш интерфейс не должен отображать.
Часть 1 — Вызов Ollama через сырой REST (Go, net/http)
Общие типы
Сначала давайте определим общие типы и вспомогательные функции, которые мы будем использовать в наших примерах:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
// ---- Типы API чата ----
type ChatMessage struct {
Role string `json:"role"`
Content string `json:"content"`
}
type ChatRequest struct {
Model string `json:"model"`
Messages []ChatMessage `json:"messages"`
// Некоторые серверы предоставляют управление мышлением как булевый флаг.
// Даже если он опущен, вы все равно можете управлять Qwen3 через теги /think или /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"` // присутствует, когда мышление включено
} `json:"message"`
Done bool `json:"done"`
}
// ---- Типы API генерации ----
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"` // окончательный текст для непоточного режима
Thinking string `json:"thinking,omitempty"` // присутствует, когда мышление включено
Done bool `json:"done"`
}
// ---- Вспомогательные функции ----
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 возвращает указатель на булево значение
func bptr(b bool) *bool { return &b }
Чат — Qwen3 с включенным мышлением (и как его отключить)
func chatQwen3Thinking() error {
endpoint := "http://localhost:11434/api/chat"
req := ChatRequest{
Model: "qwen3:8b-thinking", // любой тег :*-thinking, который вы загрузили
Think: bptr(true),
Stream: bptr(false),
Messages: []ChatMessage{
{Role: "system", Content: "Вы — точный помощник."},
{Role: "user", Content: "Объясните рекурсию с коротким примером на 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("🧠 мышление:\n", out.Message.Thinking)
fmt.Println("\n💬 ответ:\n", out.Message.Content)
return nil
}
// Отключите мышление для следующего хода, установив:
// (a) Think=false, и/или
// (b) добавьте "/no_think" в последнее сообщение системы/пользователя (мягкий переключатель Qwen3).
// Qwen3 учитывает последнюю инструкцию /think или /no_think в многоходовых чатах.
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: "Вы кратки. /no_think"},
{Role: "user", Content: "Объясните рекурсию в одном предложении."},
},
}
raw, err := httpPostJSON(endpoint, req)
if err != nil {
return err
}
var out ChatResponse
if err := json.Unmarshal(raw, &out); err != nil {
return err
}
// Ожидается, что мышление будет пустым; все же обрабатываем защитно.
if out.Message.Thinking != "" {
fmt.Println("🧠 мышление (неожиданное):\n", out.Message.Thinking)
}
fmt.Println("\n💬 ответ:\n", out.Message.Content)
return nil
}
(Мягкий переключатель /think и /no_think Qwen3 документирован командой Qwen; последняя инструкция побеждает в многоходовых чатах.)
Чат — GPT-OSS с мышлением (и оговорка)
func chatGptOss() error {
endpoint := "http://localhost:11434/api/chat"
req := ChatRequest{
Model: "gpt-oss:20b",
Think: bptr(true), // запрашивать раздельное обоснование, если поддерживается
Stream: bptr(false),
Messages: []ChatMessage{
{Role: "user", Content: "Что такое динамическое программирование? Объясните основную идею."},
},
}
raw, err := httpPostJSON(endpoint, req)
if err != nil {
return err
}
var out ChatResponse
if err := json.Unmarshal(raw, &out); err != nil {
return err
}
// Известная особенность: отключение мышления может не полностью подавлять обоснование на gpt-oss:20b.
// Всегда фильтруйте/скрывайте мышление в интерфейсе, если не хотите его отображать.
fmt.Println("🧠 мышление:\n", out.Message.Thinking)
fmt.Println("\n💬 ответ:\n", out.Message.Content)
return nil
}
Пользователи сообщают, что отключение мышления на gpt-oss:20b (например, /set nothink или --think=false) может игнорироваться — планируйте фильтрацию на стороне клиента при необходимости.
Генерация — Qwen3 и GPT-OSS
func generateQwen3() error {
endpoint := "http://localhost:11434/api/generate"
req := GenerateRequest{
Model: "qwen3:4b-thinking",
Prompt: "В 2–3 предложениях, для чего используются B-деревья в базах данных?",
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("🧠 мышление:\n", out.Thinking)
}
fmt.Println("\n💬 ответ:\n", out.Response)
return nil
}
func generateGptOss() error {
endpoint := "http://localhost:11434/api/generate"
req := GenerateRequest{
Model: "gpt-oss:20b",
Prompt: "Кратко объясните обратное распространение в нейронных сетях.",
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("🧠 мышление:\n", out.Thinking)
}
fmt.Println("\n💬 ответ:\n", out.Response)
return nil
}
Формы REST и поведение потоковой передачи взяты напрямую из справочника API Ollama.
Часть 2 — Вызов Ollama через официальный Go SDK (github.com/ollama/ollama/api)
Официальный пакет предоставляет клиент с методами, соответствующими REST API. Сам CLI Ollama использует этот пакет для связи с сервисом, что делает его наиболее надежным вариантом для совместимости.
Установка
go get github.com/ollama/ollama/api
Чат — Qwen3 (мышление ВКЛ/ВЫКЛ)
package main
import (
"context"
"fmt"
"log"
"github.com/ollama/ollama/api"
)
func chatWithQwen3Thinking(ctx context.Context, thinking bool) error {
client, err := api.ClientFromEnvironment() // учитывает OLLAMA_HOST, если установлен
if err != nil {
return err
}
req := &api.ChatRequest{
Model: "qwen3:8b-thinking",
// Многие сборки сервера предоставляют мышление как верхнеуровневый флаг;
// дополнительно, вы можете управлять Qwen3 через /think или /no_think в сообщениях.
Think: api.Ptr(thinking),
Messages: []api.Message{
{Role: "system", Content: "Вы — точный помощник."},
{Role: "user", Content: "Объясните сортировку слиянием с коротким фрагментом на Go."},
},
}
var resp api.ChatResponse
if err := client.Chat(ctx, req, &resp); err != nil {
return err
}
if resp.Message.Thinking != "" {
fmt.Println("🧠 мышление:\n", resp.Message.Thinking)
}
fmt.Println("\n💬 ответ:\n", resp.Message.Content)
return nil
}
func main() {
ctx := context.Background()
if err := chatWithQwen3Thinking(ctx, true); err != nil {
log.Fatal(err)
}
// Пример: без мышления
if err := chatWithQwen3Thinking(ctx, false); err != nil {
log.Fatal(err)
}
}
Чат — GPT-OSS (обработка обоснования защитно)
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: "Что такое мемоизация и когда она полезна?"},
},
}
var resp api.ChatResponse
if err := client.Chat(ctx, req, &resp); err != nil {
return err
}
// Если вы планируете скрывать обоснование, сделайте это здесь, независимо от флагов.
if resp.Message.Thinking != "" {
fmt.Println("🧠 мышление:\n", resp.Message.Thinking)
}
fmt.Println("\n💬 ответ:\n", resp.Message.Content)
return nil
}
Генерация — 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: "Суммируйте роль B-дерева в индексировании.",
Think: api.Ptr(true),
}
var resp api.GenerateResponse
if err := client.Generate(ctx, req, &resp); err != nil {
return err
}
if resp.Thinking != "" {
fmt.Println("🧠 мышление:\n", resp.Thinking)
}
fmt.Println("\n💬 ответ:\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: "Объясните градиентный спуск простыми словами.",
Think: api.Ptr(true),
}
var resp api.GenerateResponse
if err := client.Generate(ctx, req, &resp); err != nil {
return err
}
if resp.Thinking != "" {
fmt.Println("🧠 мышление:\n", resp.Thinking)
}
fmt.Println("\n💬 ответ:\n", resp.Response)
return nil
}
Поверхность официального пакета отражает документацию REST и обновляется вместе с основным проектом.
Потоковые ответы
Для потоковой передачи в реальном времени установите Stream: bptr(true) в вашем запросе. Ответ будет доставлен в виде JSON-фрагментов, разделенных новой строкой:
func streamChatExample() error {
endpoint := "http://localhost:11434/api/chat"
req := ChatRequest{
Model: "qwen3:8b-thinking",
Think: bptr(true),
Stream: bptr(true), // Включить потоковую передачу
Messages: []ChatMessage{
{Role: "user", Content: "Объясните алгоритм быстрой сортировки пошагово."},
},
}
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
}
// Обработка мыслей и контента по мере их поступления
if chunk.Message.Thinking != "" {
fmt.Print(chunk.Message.Thinking)
}
fmt.Print(chunk.Message.Content)
if chunk.Done {
break
}
}
return nil
}
С официальным SDK используйте функцию обратного вызова для обработки потоковых фрагментов:
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: "Объясните бинарные деревья поиска."},
},
}
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
}
Работа с Qwen3 thinking vs non-thinking (практические рекомендации)
-
Два рычага:
- Булевый флаг
thinking, поддерживаемый функцией мышления Ollama; и - Мягкие команды Qwen3
/thinkи/no_thinkв последнем системном/пользовательском сообщении. Самая последняя инструкция управляет следующими ходами.
- Булевый флаг
-
Режим по умолчанию: non-thinking для быстрых ответов; переключение на thinking для задач, требующих пошагового рассуждения (математика, планирование, отладка, сложный анализ кода).
-
Потоковые интерфейсы: при включенном мышлении вы можете видеть перемешанные рассуждения/контент в потоковых кадрах — буферизуйте или отображайте их отдельно и предоставьте пользователям переключатель “показать рассуждения”. (См. документацию API для формата потоковой передачи.)
-
Многоходовые диалоги: Qwen3 запоминает режим мышления из предыдущих ходов. Если вы хотите переключить его в середине диалога, используйте как флаг, так и команду мягкого переключения для надежности.
Примечания для GPT-OSS
- Рассматривайте рассуждения как присутствующие, даже если вы пытались их отключить; фильтруйте на стороне клиента, если ваш интерфейс не должен их отображать.
- Для производственных приложений, использующих GPT-OSS, реализуйте логику фильтрации на стороне клиента, которая может обнаруживать и удалять шаблоны рассуждений при необходимости.
- Тщательно протестируйте конкретную версию вашего варианта GPT-OSS, так как поведение может различаться между разными квантованиями и версиями.
Лучшие практики и советы для производства
Обработка ошибок и таймауты
Всегда реализуйте правильную обработку таймаутов и восстановление после ошибок:
func robustChatRequest(ctx context.Context, model string, messages []api.Message) (*api.ChatResponse, error) {
// Установите разумный таймаут
ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
defer cancel()
client, err := api.ClientFromEnvironment()
if err != nil {
return nil, fmt.Errorf("создание клиента: %w", err)
}
req := &api.ChatRequest{
Model: model,
Messages: messages,
Options: map[string]interface{}{
"temperature": 0.7,
"num_ctx": 4096, // размер окна контекста
},
}
var resp api.ChatResponse
if err := client.Chat(ctx, req, &resp); err != nil {
return nil, fmt.Errorf("запрос чата не удался: %w", err)
}
return &resp, nil
}
Пулинг и повторное использование соединений
Повторно используйте клиент Ollama для запросов вместо создания нового каждый раз:
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
}
Настройка окружения
Используйте переменные окружения для гибкого развертывания:
export OLLAMA_HOST=http://localhost:11434
export OLLAMA_NUM_PARALLEL=2
export OLLAMA_MAX_LOADED_MODELS=2
Официальный SDK автоматически учитывает OLLAMA_HOST через api.ClientFromEnvironment().
Мониторинг и ведение журнала
Реализуйте структурированное ведение журнала для производственных систем:
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
}
Заключение
-
Для проектов на Go
github.com/ollama/ollama/api— это самый полный, готовый к производству выбор. Он поддерживается вместе с основным проектом Ollama, используется официальным CLI и предоставляет всеобъемлющее покрытие API с гарантированной совместимостью. -
Для более высокоуровневых абстракций рассмотрите LangChainGo, когда вам нужны цепочки, инструменты, векторные хранилища и конвейеры RAG — хотя вы пожертвуете частью низкоуровневого контроля ради удобства.
-
Qwen3 дает вам чистый, надежный контроль над режимом мышления с помощью как флагов, так и переключателей на уровне сообщений (
/think,/no_think), что делает его идеальным для приложений, которым нужны как быстрые ответы, так и глубокое рассуждение. -
Для GPT-OSS всегда планируйте очистку вывода рассуждений на стороне клиента при необходимости, так как флаг отключения мышления может не соблюдаться последовательно.
-
В производстве реализуйте правильную обработку ошибок, пулинг соединений, таймауты и мониторинг для создания надежных приложений на базе Ollama.
Комбинация сильной типизации Go, отличной поддержки многопоточности и простого API Ollama делает эту стэков идеальной для создания приложений с искусственным интеллектом — от простых чат-ботов до сложных систем RAG.
Ключевые выводы
Вот краткое руководство для выбора подхода:
| Случай использования | Рекомендуемый подход | Почему |
|---|---|---|
| Производственное приложение | github.com/ollama/ollama/api |
Официальная поддержка, полное покрытие API, поддержка вместе с основным проектом |
| Конвейер RAG/цепочки/инструменты | LangChainGo | Высокоуровневые абстракции, интеграции с векторными хранилищами |
| Обучение/эксперименты | Сырая REST с net/http | Полная прозрачность, отсутствие зависимостей, образовательная ценность |
| Быстрое прототипирование | Официальный SDK | Баланс простоты и мощности |
| Потоковой чат-интерфейс | Официальный SDK с обратными вызовами | Встроенная поддержка потоковой передачи, чистый API |
Рекомендации по выбору модели:
- Qwen3: Лучший выбор для приложений, требующих управляемого режима мышления, надежных многоходовых диалогов
- GPT-OSS: Хорошая производительность, но требует защитной обработки вывода рассуждений
- Другие модели: Тщательно тестируйте; поведение мышления варьируется в зависимости от семейства моделей
Ссылки и дополнительные материалы
Официальная документация
- Справочник API Ollama — Полная документация REST API
- Официальный пакет Go Ollama — Документация SDK Go
- Репозиторий Ollama на GitHub — Исходный код и проблемы
Альтернативы SDK Go
- Интеграция LangChainGo Ollama — Для приложений на основе цепочек
- swdunlop/ollama-client — Клиент сообщества с вызовами инструментов
- xyproto/ollamaclient — Другая опция сообщества
Ресурсы, специфичные для моделей
- Документация Qwen — Официальная информация о модели Qwen
- Информация о GPT-OSS — Детали модели GPT-OSS
Связанные темы
- Создание приложений RAG с использованием Go — Примеры LangChainGo
- Пакет context Go — Существенный для таймаутов и отмены
- Лучшие практики HTTP-клиента Go — Документация стандартной библиотеки
Другие полезные ссылки
Для более широкого сравнения Ollama с другими локальными и облачными инфраструктурами LLM ознакомьтесь с нашим Хостинг LLM: локальный, самостоятельный и облачная инфраструктура сравниваются.
- Установка и настройка Ollama
- Шпаргалка по Ollama
- Шпаргалка по Go
- Переранжирование текстовых документов с использованием Ollama и модели встраивания Qwen3 - на Go
- Ограничение LLM с помощью структурированного вывода: Ollama, Qwen3 & Python или Go
- Сравнение LLM: Qwen3:30b vs GPT-OSS:20b
- Проблемы структурированного вывода Ollama GPT-OSS