OpenAI API в Ollama: один код для локальной и облачной модели
Ollama отвечает на запросы в формате OpenAI. Значит, один и тот же код можно направить на локальную модель или на шлюз, сменив base_url и имя модели.
5 мин · · разработка · API-ключи
Ollama умеет отвечать на запросы в формате OpenAI, и это очень удобно: приложение, написанное под openai SDK, можно направить на модель, запущенную у вас на машине, а потом на облачный шлюз, не переписывая логику. Ниже разберём, какой адрес и порт использует Ollama, что из OpenAI API поддерживается, а что нет, и как организовать код так, чтобы локальная и облачная модель переключались настройкой.
Ollama OpenAI API: адрес, порт и ключ
По документации Ollama, сервер по умолчанию слушает 127.0.0.1 на порту 11434, а OpenAI-совместимые маршруты лежат под префиксом /v1. Поэтому base_url для клиента OpenAI такой:
http://localhost:11434/v1/
Ключ клиентская библиотека требует, но Ollama его игнорирует; в документации в качестве значения используют ollama. Если нужно открыть сервер в локальной сети, меняют адрес привязки переменной окружения OLLAMA_HOST (например, 0.0.0.0:11434). Это потребует дополнительной защиты: Ollama сам авторизацию в локальном режиме не проверяет.
Простейший запрос через curl:
curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "qwen3:8b", "messages": [{"role": "user", "content": "Скажи привет"}]}'Модель должна быть скачана заранее (ollama pull qwen3:8b или ollama run). Имена вида qwen3:8b и gpt-oss:20b взяты из примеров документации; доступные модели ищите в каталоге Ollama.
Что поддерживается
Документация перечисляет эндпоинты /v1/chat/completions, /v1/responses, /v1/completions и /v1/models. Для чат-запросов отмечены:
- стриминг, JSON-режим, воспроизводимые результаты (
seed); - зрение: изображения передаются в base64, ссылки на изображения (image URL) не поддерживаются;
- инструменты (
tools); - управление рассуждением через
reasoning_effortдля моделей с такой возможностью; - параметры
temperature,top_p,max_tokens,stop,frequency_penalty,presence_penalty,response_format.
Не поддерживаются, среди прочего: tool_choice, logprobs, logit_bias, n и user. Responses API Ollama поддерживает только без хранения состояния: previous_response_id и conversation не работают. Это важное место для переносимого кода: всё, что опирается на tool_choice или n, придётся обходить.
Ещё один нюанс: окно контекста. В документации Ollama значение по умолчанию зависит от видеопамяти: до 24 GiB это 4k токенов, от 24 до 48 GiB это 32k, от 48 GiB это 256k, а для задач с агентами и инструментами рекомендуют не меньше 64 000 токенов. Облачные модели используют максимальную длину по умолчанию. Если локальная модель «забывает» начало диалога, причина часто в этом. Менять длину можно переменной OLLAMA_CONTEXT_LENGTH или в приложении Ollama.
Общий клиент для двух режимов
Идея простая: адрес, ключ и модель берутся из настроек, остальной код не знает, куда ходит. В качестве облачного варианта возьмём шлюз Zukko: OpenAI-совместимый адрес https://api.zukko.pro/v1, ключ вида fetch_....
import os
from openai import OpenAI
PROFILES = {
"local": {
"base_url": "http://localhost:11434/v1/",
"api_key": "ollama",
"model": os.environ.get("LOCAL_MODEL", "qwen3:8b"),
},
"cloud": {
"base_url": "https://api.zukko.pro/v1",
"api_key": os.environ.get("ZUKKO_API_KEY", ""),
"model": os.environ.get("CLOUD_MODEL", "deepseek-v4.1-flash"),
},Запуск на локальной модели: LLM_PROFILE=local python app.py, на облаке: LLM_PROFILE=cloud python app.py. Больше ничего менять не нужно. Модели в облаке меняются той же переменной CLOUD_MODEL: например, glm-5.3-flash, gpt-6.1-sol или claude-sonnet-5-5. Актуальный список даёт GET https://api.zukko.pro/v1/models.
Автоматический запасной вариант
Часто нужно «пробовать локально, а если Ollama недоступна, идти в облако». Для этого достаточно перехватить ошибку соединения:
import openai
def ask_with_fallback(prompt: str) -> str:
try:
return ask(prompt, "local")
except openai.APIConnectionError:
return ask(prompt, "cloud")Такая схема пригодится в разработке на ноутбуке и на сервере, где локальной модели нет. Но помните, что ответы разных моделей отличаются, а ваши тесты на формат ответа стоит запускать на обоих режимах.
Стриминг и особенности
Потоковый вывод в обоих режимах идёт одним кодом. В облачном шлюзе в конце потока приходит чанк с usage и пустым списком choices, поэтому перед chunk.choices[0] всегда проверяйте, что массив не пуст:
client, model = make_client("cloud")
stream = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": "Расскажи про TCP за три предложения"}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)Эта проверка защитит и от других серверов, у которых служебные чанки тоже бывают пустыми. Подробнее про потоки: потоковая передача stream=true.
Два практических правила переносимости:
- Картинки передавайте в base64: ссылки не работают локально.
- Не полагайтесь на
tool_choiceиn: локальная Ollama их не поддерживает.
Что с расходом
Локальный режим не тарифицируется токенами: вы платите железом и электричеством. В облачном режиме Zukko считает токены по цене из каталога. Допустим, 50 000 запросов в месяц, в каждом 800 токенов входа и 200 выхода: 40M входа и 10M выхода. Цены на 18 октября 2026, $ за 1M токенов:
deepseek-v4.1-flash(0.013 / 0.038): 40 × 0.013 + 10 × 0.038 = $0.52 + $0.38 = $0.90;gpt-6.1-sol(0.27 / 1.32): 40 × 0.27 + 10 × 1.32 = $10.8 + $13.2 = $24.
Практичный порядок: разрабатывайте и тестируйте локально, затем прогоняйте боевой трафик через шлюз с дневным лимитом на ключ. Как решать, что держать локально, разобрано в заметке локальная нейросеть или API, а общие принципы формата описаны в статье про OpenAI-совместимые API и в справочнике Chat Completions.
Частые вопросы
Какой URL у Ollama API?
Для OpenAI-клиентов http://localhost:11434/v1/, для собственного API Ollama http://localhost:11434/api/.... Порт 11434, и он меняется через OLLAMA_HOST.
Нужен ли ключ для локальной Ollama?
Клиентской библиотеке нужно любое непустое значение, например ollama, но сервер его не проверяет.
Почему в облаке base_url с /v1, а у Claude Code без?
OpenAI SDK и совместимые клиенты не добавляют /v1 сами, поэтому он входит в адрес. Claude Code и официальный Anthropic SDK добавляют его сами, и для них адрес https://api.zukko.pro без /v1.
Работает ли это из России?
Оплата из России — СБП, MasterCard или криптовалюта. Локальная Ollama от оплаты не зависит.
Создайте ключ в кабинете, задайте ZUKKO_API_KEY и переключайте LLM_PROFILE между local и cloud.
zukko
Платите в 25×
меньше
за Claude Code & ChatGPT
Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.
Похожие статьи
- Свой прокси к нейросети на Node.js: не показывать API-ключ во фронтендеНебольшой сервер на Express между вашим фронтендом и API нейросети: ключ остаётся на сервере, а модели, длина запроса и частота ограничены.
- DeepSeek API на Python: запросы, стриминг, JSON и обработка ошибокГотовый код для DeepSeek V4.1 Flash и V4 Pro на OpenAI SDK: запрос, стриминг, разбор JSON и аккуратная обработка ошибок.
- Gemini API на Python: запросы через OpenAI SDK и OpenAI-форматРабочий пример Gemini API на Python без отдельной библиотеки: меняем base_url в OpenAI SDK, добавляем стриминг и JSON и сравниваем с google-genai.
- Anthropic SDK для Python: base_url, ключ и первый запрос через шлюзОфициальный пакет anthropic для Python нужно только направить на другой адрес. Разбираем base_url, ключ, стриминг, подсчёт токенов, кэш и ошибки.