Один ключ к DeepSeek, GPT, Claude и Gemini — оплата в рублях, доступ без VPN.

Получить ключ

Структурированный JSON и вызов инструментов

Два способа заставить модель вернуть данные, а не текст: режим JSON-вывода, когда ответ целиком является JSON-объектом, и вызов инструментов (tool calls), когда модель сама просит выполнить вашу функцию и получает её результат обратно.

JSON-вывод: response_format

По умолчанию response_format равен text — модель отвечает свободным текстом. Значение json_object включает режим структурированного вывода: {"response_format": {"type": "json_object"}}.

Важно

В промпте должно присутствовать слово «json» — иначе запрос завершится ошибкой. Практический приём: описать схему ответа в системном сообщении словами, например «Отвечай json-объектом с полями number, date, total». Слово «json» при этом уже есть в тексте.

python
import json
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": (
                "Ты извлекаешь реквизиты из писем. "
                "Отвечай json-объектом с полями number, date, total."
            ),
        },
        {"role": "user", "content": "Счёт № 14 от 02.10.2026 на 18 400 руб."},
    ],
    response_format={"type": "json_object"},
    # Извлечение полей — задача без рассуждений: thinking можно выключить
    extra_body={"thinking": {"type": "disabled"}, "reasoning_effort": "none"},
)

data = json.loads(response.choices[0].message.content)
print(data["number"], data["date"], data["total"])

Ответ приходит строкой, поэтому его нужно разобрать через json.loads и проверить обязательные поля: режим гарантирует корректный JSON, но не гарантирует наличие конкретных ключей. Проверка схемы на стороне приложения остаётся обязательной.

Инструменты: как это работает

Инструменты описываются массивом tools. Поддерживается только тип function: у каждой функции есть имя (уникальное, не длиннее 128 символов), описание и JSON Schema параметров. Модель не вызывает функцию сама — она возвращает имя функции и аргументы, а выполняет их ваш код.

Признак того, что модель просит вызвать инструмент, — finish_reason: "tool_calls" и заполненное поле tool_calls в сообщении ассистента. Полный список значений finish_reason: stop, length, content_filter, tool_calls, insufficient_system_resource, aborted.

Важно

Если запрос содержит tools, поле reasoning_content обязательно возвращать назад во всех последующих запросах диалога — иначе HTTP 400. Это относится и к циклу вызова инструментов: каждый следующий запрос должен нести reasoning_content предыдущего ответа ассистента.

Важно

В thinking mode значения tool_choice: "required" и выбор конкретной функции возвращают HTTP 400. Рабочие варианты — auto или none. Если нужно обязательное обращение к инструменту, thinking придётся выключить.

Параметры JSON-вывода и вызова инструментов
Параметр Допустимые значения Комментарий
response_format text, json_object По умолчанию text. В промпте должно быть слово «json»
tools только type: "function" Имена уникальны, до 128 символов; strict по умолчанию false
tool_choice none, auto, required, конкретная функция required и конкретная функция недоступны в thinking mode — HTTP 400
finish_reason stop, length, content_filter, tool_calls, insufficient_system_resource, aborted Цикл инструментов продолжается, пока не вернётся значение, отличное от tool_calls

Нужен API-ключ сегодня

Регистрация по email или Telegram занимает пару минут, оплата идёт в рублях, VPN не нужен. Один ключ работает и с DeepSeek, и с другими моделями.

Получить ключ

Рабочий цикл вызова инструментов

Ниже — самодостаточный пример: инструмент один, но структура цикла не меняется при добавлении новых функций. Обратите внимание на три вещи: сообщение ассистента возвращается в историю вместе с reasoning_content и tool_calls, на каждый вызов отвечает сообщение с ролью tool и тем же tool_call_id, а цикл завершается по finish_reason, отличному от tool_calls.

python
import json
import os

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["DEEPSEEK_API_KEY"],
    base_url="https://api.deepseek.com",
)


def get_weather(city: str) -> dict:
    """Заглушка вместо реального вызова сервиса погоды."""
    return {"city": city, "temp_c": 7, "condition": "облачно"}


AVAILABLE = {"get_weather": get_weather}

TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Текущая погода в указанном городе",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "Название города, например Казань",
                    }
                },
                "required": ["city"],
            },
        },
    }
]

messages = [{"role": "user", "content": "Какая сейчас погода в Казани?"}]

while True:
    response = client.chat.completions.create(
        model="deepseek-flash",
        messages=messages,
        tools=TOOLS,
        tool_choice="auto",
        # thinking включён по умолчанию; мысли reasoning_content
        extra_body={"thinking": {"type": "enabled"}, "reasoning_effort": "high"},
    )
    choice = response.choices[0]
    message = choice.message

    # Собираем сообщение ассистента вручную, чтобы гарантированно
    # вернуть reasoning_content назад: с tools это обязательно.
    assistant_message = {
        "role": "assistant",
        "content": message.content or "",
        "reasoning_content": message.reasoning_content or "",
    }
    if message.tool_calls:
        assistant_message["tool_calls"] = [
            {
                "id": call.id,
                "type": "function",
                "function": {
                    "name": call.function.name,
                    "arguments": call.function.arguments,
                },
            }
            for call in message.tool_calls
        ]
    messages.append(assistant_message)

    if choice.finish_reason != "tool_calls":
        print(message.content)
        break

    for call in message.tool_calls:
        arguments = json.loads(call.function.arguments)
        result = AVAILABLE[call.function.name](**arguments)
        messages.append(
            {
                "role": "tool",
                "tool_call_id": call.id,
                "content": json.dumps(result, ensure_ascii=False),
            }
        )

Аргументы приходят строкой JSON в tool_calls[].function.arguments — их нужно разобрать самостоятельно. Проверяйте имя функции по белому списку и валидируйте аргументы: содержимое формирует модель, а не пользователь, но доверять ему без проверки не стоит.

Strict-схемы в beta

Строгое соответствие аргументов схеме доступно в бета-режиме. Для этого нужно обращаться к другому base URL — https://api.deepseek.com/beta — и явно включать strict в описании функции. Остальные бета-возможности того же адреса: FIM и Chat Prefix Completion (FIM доступен только в non-thinking у обеих моделей).

python
import os

from openai import OpenAI

# strict-инструменты — бета-функция, поэтому другой base_url
client = OpenAI(
    api_key=os.environ["DEEPSEEK_API_KEY"],
    base_url="https://api.deepseek.com/beta",
)

response = client.chat.completions.create(
    model="deepseek-flash",
    messages=[{"role": "user", "content": "Добавь задачу: позвонить в банк завтра."}],
    tools=[
        {
            "type": "function",
            "function": {
                "name": "add_task",
                "description": "Создаёт задачу в списке дел",
                "strict": True,
                "parameters": {
                    "type": "object",
                    "properties": {
                        "title": {"type": "string"},
                        "due_date": {"type": "string"},
                    },
                    "required": ["title", "due_date"],
                    "additionalProperties": False,
                },
            },
        }
    ],
    tool_choice="auto",
    extra_body={"thinking": {"type": "disabled"}, "reasoning_effort": "none"},
)

for call in response.choices[0].message.tool_calls or []:
    print(call.function.name, call.function.arguments)
Совместимость с OpenAI

Формат tools, tool_choice и сообщений с ролью tool совпадает с OpenAI, поэтому код цикла переносится на DeepSeek заменой base_url и имени модели. Единственное системное отличие — обязательный возврат reasoning_content, когда в запросе есть tools.

Нужен API-ключ сегодня

Регистрация по email или Telegram занимает пару минут, оплата идёт в рублях, VPN не нужен. Один ключ работает и с DeepSeek, и с другими моделями.

Получить ключ

Читайте также

Один ключ вместо пяти аккаунтов

DeepSeek, GPT, Claude и Gemini через один OpenAI-совместимый адрес: api.aitunnel.ru/v1. Регистрация занимает пару минут, оплата в рублях, доступ без VPN.

  • Один ключ на все модели
  • Оплата в рублях и счёт для юрлиц
  • Совместимо с OpenAI SDK