Клиенты 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
), предназначенный для нагрузок generate и chat, с встроенной поддержкой потоковой передачи и возможностями управления моделями. Официальная документация подробно описывает структуры запросов/ответов и семантику потоковой передачи для /api/generate
и /api/chat
.
Go — отличный выбор для создания клиентов Ollama благодаря мощной поддержке стандартной библиотеки для HTTP, отличной обработке JSON, нативным примитивам параллелизма и статически типизированным интерфейсам, которые обнаруживают ошибки на этапе компиляции.
По состоянию на октябрь 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/post/2025/10/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 Chat ----
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 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"` // окончательный текст для не-потокового
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 мышление vs не-мышление (практические рекомендации)
-
Два рычага:
- Булевый флаг
thinking
, поддерживаемый функцией мышления Ollama; и - Мягкие команды Qwen3
/think
и/no_think
в последнем системном/пользовательском сообщении. Самая последняя инструкция управляет следующими ходами.
- Булевый флаг
-
Режим по умолчанию: не-мышление для быстрых ответов; переключайтесь на мышление для задач, требующих пошагового рассуждения (математика, планирование, отладка, сложный анализ кода).
-
Потоковые интерфейсы: когда включено мышление, вы можете видеть перемешанные рассуждения/контент в потоковых кадрах — буферизуйте или отображайте их отдельно и предоставьте пользователям переключатель “показать рассуждения”. (См. документацию 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
- Официальный пакет Ollama для Go — Документация 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
- Шпаргалка по Ollama
- Шпаргалка по Go
- Как Ollama обрабатывает параллельные запросы
- Переранжирование текстовых документов с использованием Ollama и модели Qwen3 Embedding - на Go
- Сравнение ORM для PostgreSQL на Go: GORM vs Ent vs Bun vs sqlc
- Ограничение LLMs с помощью структурированного вывода: Ollama, Qwen3 & Python или Go
- Использование Ollama в коде на Python
- Сравнение LLMs: Qwen3:30b vs GPT-OSS:20b
- Проблемы с структурированным выводом Ollama GPT-OSS