DeepSeek API на Python
API DeepSeek полностью совместим с форматом OpenAI, поэтому на Python удобнее всего работать через официальный SDK openai. Ниже — рабочие примеры от простого запроса до вызова инструментов и расчёта стоимости.
Установка и ключ
pip install openai
export DEEPSEEK_API_KEY="ваш-ключ"
Ключ храните в переменной окружения: так он не попадёт в репозиторий и логи. Адрес API — https://api.deepseek.com, актуальные модели — deepseek-flash и deepseek-v4-pro.
Базовый запрос
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["DEEPSEEK_API_KEY"],
base_url="https://api.deepseek.com",
)
response = client.chat.completions.create(
model="deepseek-flash",
messages=[
{"role": "system", "content": "Отвечай по-русски, коротко и по делу."},
{"role": "user", "content": "Что такое контекстное кэширование в API?"},
],
)
print(response.choices[0].message.content)
Что приходит в ответе
| Поле | Что содержит |
|---|---|
choices[0].message.content | Текст ответа модели |
choices[0].message.reasoning_content | Цепочка рассуждений. Присутствует, когда режим рассуждений включён (по умолчанию он включён) |
choices[0].finish_reason | Почему генерация остановилась: stop, length, tool_calls, content_filter и другие |
usage.prompt_tokens | Все входные токены: сумма prompt_cache_hit_tokens и prompt_cache_miss_tokens |
usage.completion_tokens | Выходные токены, включая токены рассуждений |
SDK OpenAI не знает о нём, поэтому безопаснее читать через getattr(message, "reasoning_content", None), чем через точку. Если запрос использует tools, это поле обязательно возвращать назад в историю сообщений — см. раздел про инструменты.
Потоковая выдача
Стриминг нужен, когда ответ показывается пользователю сразу. Включите stream=True, а чтобы в конце получить расход токенов — stream_options.
stream = client.chat.completions.create(
model="deepseek-flash",
messages=[{"role": "user", "content": "Придумай три названия для кофейни."}],
stream=True,
stream_options={"include_usage": True},
)
usage = None
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
if chunk.usage: # приходит последним чанком
usage = chunk.usage
print()
if usage is None:
# Бывает, если поток оборвался и финальный чанк с usage не пришёл
print("usage не получен: проверьте stream_options.include_usage")
else:
print("Входных токенов:", usage.prompt_tokens)
Управление рассуждениями
Режим рассуждений включён по умолчанию и заметно увеличивает расход выходных токенов. Для простых задач (перевод, извлечение полей, классификация) его выгодно отключить.
# Отключаем рассуждения: быстрее и дешевле на простых задачах
fast = client.chat.completions.create(
model="deepseek-flash",
messages=[{"role": "user", "content": "Переведи на английский: «доброе утро»"}],
extra_body={"thinking": {"type": "disabled"}},
)
print(fast.choices[0].message.content)
# Управляем глубиной: none | low | high | max
deep = client.chat.completions.create(
model="deepseek-flash",
messages=[{"role": "user", "content": "Проверь расчёт: 17% от 4 890 ₽"}],
reasoning_effort="max",
)
print(getattr(deep.choices[0].message, "reasoning_content", None))
print(deep.choices[0].message.content)
Параметры temperature, presence_penalty и frequency_penalty в этом режиме не действуют — ошибки не будет, но и эффекта тоже. top_p, наоборот, работает только в режиме рассуждений и фактически ограничен диапазоном 0.95–1.0. Настройка tool_choice: "required" приводит к ошибке 400. Подробнее — в разделе «Режим рассуждений».
Многоходовой диалог
API не помнит историю: вы передаёте её сами. Официальный паттерн — добавлять в список сообщение целиком, а не только текст.
messages = [{"role": "system", "content": "Ты помогаешь подбирать ноутбуки."}]
while True:
question = input("Вы: ").strip()
if question in {"", "выход"}:
break
messages.append({"role": "user", "content": question})
response = client.chat.completions.create(
model="deepseek-flash",
messages=messages,
)
answer = response.choices[0].message
messages.append(answer) # объект целиком: сохраняются и content, и reasoning_content
print("Ассистент:", answer.content)
На длинных диалогах сервер автоматически кэширует повторяющийся префикс — это снижает цену входных токенов в десятки раз. Как это работает, описано на странице «Цены и расчёт».
Нужен API-ключ сегодня
Регистрация по email или Telegram занимает пару минут, оплата идёт в рублях, VPN не нужен. Один ключ работает и с DeepSeek, и с другими моделями.
Ответ строго в JSON
Пригодится, когда результат идёт не человеку, а в базу данных. В промпте должно быть слово «json» — это требование формата.
import json
response = client.chat.completions.create(
model="deepseek-flash",
messages=[
{"role": "system", "content": "Отвечай только валидным JSON, без пояснений."},
{"role": "user", "content": (
"Извлеки данные из текста и верни JSON с полями "
"invoice, amount, currency, due_date: "
"«Оплатите счёт № 4471 на 12 500 ₽ до 30 сентября 2026 года.»"
)},
],
response_format={"type": "json_object"},
)
data = json.loads(response.choices[0].message.content)
print(data["amount"])
Вызов инструментов
Tool calls позволяют модели запросить выполнение вашей функции: достать остаток по счёту, проверить заказ, посчитать что-то по вашим правилам. Цикл состоит из трёх шагов: модель просит вызвать функцию → вы вызываете её сами → результат возвращается модели.
import json
tools = [{
"type": "function",
"function": {
"name": "get_balance",
"description": "Возвращает текущий баланс клиента по его идентификатору",
"parameters": {
"type": "object",
"properties": {
"client_id": {"type": "string", "description": "Идентификатор клиента"}
},
"required": ["client_id"],
},
},
}]
def get_balance(client_id: str) -> dict:
# Здесь был бы запрос к вашей базе, CRM или биллингу
return {"client_id": client_id, "balance": 12500, "currency": "RUB"}
messages = [{"role": "user", "content": "Какой баланс у клиента C-1024?"}]
while True:
response = client.chat.completions.create(
model="deepseek-flash",
messages=messages,
tools=tools,
)
message = response.choices[0].message
# Добавляем объект целиком: при работе с tools поле reasoning_content
# обязательно должно вернуться в историю, иначе следующий запрос вернёт 400
messages.append(message)
if not message.tool_calls:
print(message.content)
break
for call in message.tool_calls:
args = json.loads(call.function.arguments)
result = get_balance(**args)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result, ensure_ascii=False),
})
Поддерживаются только функции (type: "function"). Имена уникальны и не длиннее 128 символов. В режиме рассуждений нельзя принудительно выбирать инструмент — работает только автоматический выбор. Строгие схемы (strict: true) доступны через базовый адрес https://api.deepseek.com/beta.
Считаем стоимость запроса
Расход виден в объекте usage, а цена зависит от модели, попадания входа в кэш и времени суток. Ниже — расчёт для deepseek-flash по ценам вне пика.
PRICES = { # USD за 1M токенов, deepseek-flash, вне пика
"cache_hit": 0.003,
"cache_miss": 0.15,
"output": 0.6,
}
u = response.usage
cost = (
u.prompt_cache_hit_tokens * PRICES["cache_hit"]
+ u.prompt_cache_miss_tokens * PRICES["cache_miss"]
+ u.completion_tokens * PRICES["output"]
) / 1_000_000
print(f"вход из кэша: {u.prompt_cache_hit_tokens} токенов")
print(f"вход без кэша: {u.prompt_cache_miss_tokens} токенов")
print(f"выход: {u.completion_tokens} токенов")
print(f"стоимость запроса: ${cost:.6f}")
Повторы при ошибках
Коды 429, 500 и 503 означают, что запрос имеет смысл повторить. Простая экспоненциальная задержка решает большинство случаев.
import time
import openai
def ask(messages, attempts: int = 5):
delay = 2.0
for attempt in range(1, attempts + 1):
try:
return client.chat.completions.create(
model="deepseek-flash",
messages=messages,
)
except (openai.RateLimitError, openai.InternalServerError) as exc:
if attempt == attempts:
raise
print(f"Попытка {attempt} не удалась ({exc}). Ждём {delay:.0f} с")
time.sleep(delay)
delay *= 2
Ошибки 401 и 402 повторять бессмысленно: первая означает неверный ключ, вторая — закончившийся баланс. Полная таблица кодов — на странице «Ошибки и лимиты».
Тот же код через агрегатор
Если нужен доступ с оплатой в рублях, тот же код работает через OpenAI-совместимый шлюз: меняется только адрес API и ключ. Идентификаторы моделей у агрегаторов могут отличаться — смотрите их каталог.
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["AITUNNEL_API_KEY"], # ключ вида sk-aitunnel-...
base_url="https://api.aitunnel.ru/v1/", # вместо https://api.deepseek.com
)
# Остальной код не меняется — кроме названия модели из каталога сервиса
response = client.chat.completions.create(
model="deepseek-v4.1-flash",
messages=[{"role": "user", "content": "Привет!"}],
)
print(response.choices[0].message.content)
Подробный разбор подключения через AITUNNEL →
Нужен API-ключ сегодня
Регистрация по email или Telegram занимает пару минут, оплата идёт в рублях, VPN не нужен. Один ключ работает и с DeepSeek, и с другими моделями.
Читайте также
- Тот же запрос через cURL Проверить ключ и увидеть «сырой» ответ API без библиотек — удобно для отладки.
- Примеры на PHP Для проектов на WordPress, Bitrix и самописных админках.
- Модели и параметры Полный список параметров Chat Completions с типами и значениями по умолчанию.
- Стриминг и токены Формат SSE, поля usage и типичные ошибки при потоковой выдаче.