Обновление интерфейса, совместимого с OpenAI: глубокая поддержка Claude

31 июл. 2026 г. · AIHubMix · 8 min read · Новости

Обновление интерфейса, совместимого с OpenAI: глубокая поддержка Claude

Мы обновили интерфейс, совместимый с OpenAI, с более глубокими оптимизациями, специально для моделей серии Claude. Теперь вы можете более точно и удобно управлять мышлением и кэшированием. Чередующееся мышление в многократных разговорах теперь более удобно для пользователей, позволяя бесшовную интеграцию без дополнительных параметров. Также поддерживается включение бета-функций, предлагаемых Anthropic.

1. Мышление модели (Расширенное мышление)

1.1 Преимущества чередующегося мышления

Когда чередующееся мышление не включено, модель выполняет мышление только один раз в начале поворота помощника; последующие ответы генерируются непосредственно после получения результатов инструмента, без создания новых блоков мышления:

User → [Thinking] → Tool Call → Tool Result → Response

Когда чередующееся мышление включено, модель вставляет новый блок мышления каждый раз, когда получает результат инструмента, формируя цепочку рассуждений:

User → [Thinking] → Tool Call → Tool Result → [Thinking] → Response
                                                ↑ Чередующееся мышление

Это позволяет модели:

  • Выполнять вторичное рассуждение на основе результатов инструмента, а не просто конкатенировать выводы.
  • Связывать рассуждения между несколькими вызовами инструментов, где каждое решение основано на анализе предыдущего шага.
Ссылка: Чередующееся мышление Anthropic

1.2 Включение мышления

Вы можете включить мышление четырьмя способами, выбрав любой из них:

Метод Пример Описание
reasoning_effort "reasoning_effort": "low" Стандартный параметр OpenAI, размещенный на верхнем уровне тела запроса
reasoning.effort "reasoning": {"effort": "low"} Эквивалент предыдущему методу, размещенный внутри объекта reasoning
reasoning.max_tokens "reasoning": {"max_tokens": 1024} Точно контролирует максимальное количество токенов для мышления
Имя модели с -think "model": "claude-sonnet-4-5-think" Самый простой способ, не требует дополнительных параметров
Приоритет (при использовании нескольких методов): reasoning_effort > reasoning.max_tokens > reasoning.effort > -think суффикс

Возможные значения для усилий: minimal / low / medium / high / xhigh

1.3 Возврат мышления

Сообщение ответа будет включать два новых поля:

  • reasoning_content: Содержимое мышления (строка), для удобного отображения.
  • reasoning_details: Полная структурированная информация о мышлении, которая должна быть возвращена как есть в многократных разговорах; внутренняя структура может отличаться между поставщиками.

Пример без потоковой передачи (опуская не относящиеся к делу поля):

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "Здравствуйте! Чем я могу вам помочь сегодня?",
      "reasoning_content": "Пользователь просто говорит привет...",
      "reasoning_details": {
        "type": "thinking",
        "thinking": "Пользователь просто говорит привет...",
        "signature": "Er8CCkYI..."
      }
    }
  }]
}

В потоковых ответах содержимое мышления будет отправлено частями через delta.reasoning_content и delta.reasoning_details. Для полной логики конкатенации потоковой передачи смотрите полный пример ниже.

1.4 Сохранение мышления в многократных разговорах (Чередующееся мышление встроено, дополнительные параметры не требуются)

Чтобы модель могла продолжать свои способности к рассуждению в многократных разговорах, просто поместите ранее возвращенные reasoning_details как есть в сообщение помощника следующего раунда:

messages = [
    {"role": "user", "content": "Какова погода в Бостоне?"},
    {
        "role": "assistant",
        "content": response.choices[0].message.content,
        "tool_calls": response.choices[0].message.tool_calls,
        "reasoning_details": response.choices[0].message.reasoning_details,
    },
    {
        "role": "tool",
        "tool_call_id": "toolu_xxx",
        "content": '{"temperature": 45, "condition": "дождливо"}',
    }
]

AIHubMix автоматически включит чередующееся мышление, когда обнаружит историческую информацию о мышлении в запросе, позволяя модели продолжать глубокое рассуждение после получения результатов вызова инструмента без необходимости в дополнительных параметрах.

1.5 Полный пример

Следующие два примера демонстрируют полный процесс многократного вызова инструмента + чередующегося мышления: запрос пользователя → модель думает и вызывает инструмент → внедрить результаты инструмента (сохраняя reasoning_details) → модель чередующееся мышление дает окончательный ответ.

Без потоковой передачи · Чередующееся мышление

import os
import json
from openai import OpenAI

client = OpenAI(
    base_url="https://aihubmix.com/v1",
    api_key=os.environ.get("AIHUBMIX_API_KEY", "sk-***"),
)

# ── Определение инструмента ───────────────────────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Получить текущую погоду для местоположения",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "Название города"}},
            "required": ["location"]
        }
    }
}]

# ── Моковая реализация инструмента ─────────────────────────────────────
WEATHER_DB = {
    "boston": {"temperature": "45°F (7°C)", "condition": "дождливо", "humidity": "85%", "wind": "15 mph NE"},
    "tokyo":  {"temperature": "72°F (22°C)", "condition": "солнечно", "humidity": "45%", "wind": "5 mph S"},
}

def execute_tool(name: str, args: dict) -> str:
    if name == "get_weather":
        key = next((k for k in WEATHER_DB if k in args.get("location", "").lower()), None)
        return json.dumps(WEATHER_DB.get(key, {"temperature": "65°F", "condition": "ясно"}))
    return "{}"

# ── Цикл многократного разговора ─────────────────────────────
messages = [
    {"role": "user", "content": "Какова погода в Бостоне? Затем порекомендуйте, что надеть."}
]

turn = 0
while True:
    turn += 1
    print(f"\n── Поворот {turn} ──")

    response = client.chat.completions.create(
        model="claude-sonnet-4-5",
        messages=messages,
        tools=tools,
        extra_body={"reasoning": {"max_tokens": 2000}},
    )
    msg = response.choices[0].message

    # Печать процесса мышления
    if msg.reasoning_content:
        label = "Чередующееся мышление" if turn > 1 else "Мышление"
        print(f"[{label}] {msg.reasoning_content}")

    # Печать содержимого ответа
    if msg.content:
        print(f"[Ответ] {msg.content}")

    # Печать вызовов инструментов
    if msg.tool_calls:
        for tc in msg.tool_calls:
            print(f"[Вызов инструмента: {tc.function.name}] {tc.function.arguments}")

    # Создание сообщения помощника, сохранить reasoning_details (критично!)
    assistant_msg = {"role": "assistant", "content": msg.content}
    if msg.tool_calls:
        assistant_msg["tool_calls"] = msg.tool_calls
    if msg.reasoning_details:
        assistant_msg["reasoning_details"] = msg.reasoning_details  # передать обратно без изменений
    messages.append(assistant_msg)

    # Отсутствие tool_calls означает, что разговор завершен
    if not msg.tool_calls:
        break

    # Выполнение инструментов и добавление результатов в сообщения
    for tc in msg.tool_calls:
        args = json.loads(tc.function.arguments)
        result = execute_tool(tc.function.name, args)
        print(f"[Результат инструмента: {tc.function.name}] {result}")
        messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})

Потоковая передача · Чередующееся мышление

import os
import sys
import json
from openai import OpenAI

client = OpenAI(
    base_url="https://aihubmix.com/v1",
    api_key=os.environ.get("AIHUBMIX_API_KEY", "sk-***"),
)

# ── Определение инструмента и моковая реализация ─────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Получить текущую погоду для местоположения",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "Название города"}},
            "required": ["location"]
        }
    }
}]

WEATHER_DB = {
    "boston": {"temperature": "45°F (7°C)", "condition": "дождливо", "humidity": "85%", "wind": "15 mph NE"},
    "tokyo":  {"temperature": "72°F (22°C)", "condition": "солнечно", "humidity": "45%", "wind": "5 mph S"},
}

def execute_tool(name: str, args: dict) -> str:
    if name == "get_weather":
        key = next((k for k in WEATHER_DB if k in args.get("location", "").lower()), None)
        return json.dumps(WEATHER_DB.get(key, {"temperature": "65°F", "condition": "ясно"}))
    return "{}"

# ── Коллектор потоковых ответов ────────────────────────────────
def stream_and_collect(turn: int, **kwargs):
    """Потоковая передача ответа, печать мышления/содержимого в реальном времени, накопление reasoning_details/tool_calls."""
    rd = {}            # накопленные reasoning_details
    content = ""       # накопленный текст ответа
    tc_map = {}        # накопленные tool_calls (по индексу)
    cur = "none"       # текущий раздел вывода: none / thinking / content

    stream = client.chat.completions.create(stream=True, **kwargs)
    for chunk in stream:
        if not chunk.choices:
            continue
        delta = chunk.choices[0].delta

        # ── Обработка мышления ──
        rd_delta = getattr(delta, "reasoning_details", None)
        if rd_delta and isinstance(rd_delta, dict):
            for k, v in rd_delta.items():
                if k == "type":
                    rd[k] = v
                elif isinstance(v, str):
                    rd[k] = rd.get(k, "") + v
                elif v is not None:
                    rd[k] = v
            # Печать частей мышления в реальном времени
            thinking_chunk = rd_delta.get("thinking", "")
            if thinking_chunk:
                if cur != "thinking":
                    cur = "thinking"
                    label = "Чередующееся мышление" if turn > 1 else "Мышление"
                    sys.stdout.write(f"\n[{label}] ")
                sys.stdout.write(thinking_chunk)
                sys.stdout.flush()

        # ── Обработка содержимого ──
        if delta.content:
            if cur != "content":
                if cur == "thinking":
                    sys.stdout.write("\n")
                cur = "content"
                sys.stdout.write("\n[Ответ] ")
            sys.stdout.write(delta.content)
            sys.stdout.flush()
            content += delta.content

        # ── Обработка tool_calls ──
        for tc in delta.tool_calls or []:
            i = tc.index
            if i not in tc_map:
                tc_map[i] = {"id": "", "type": "function",
                             "function": {"name": "", "arguments": ""}}
            if tc.id:
                tc_map[i]["id"] = tc.id
            if tc.function:
                tc_map[i]["function"]["name"] += tc.function.name or ""
                tc_map[i]["function"]["arguments"] += tc.function.arguments or ""

    # Завершение текущего раздела вывода
    if cur in ("thinking", "content"):
        sys.stdout.write("\n")

    tool_calls = [tc_map[i] for i in sorted(tc_map)] if tc_map else None
    return {
        "content": content or None,
        "reasoning_details": rd or None,
        "tool_calls": tool_calls,
    }

# ── Цикл многократного разговора ─────────────────────────────
messages = [
    {"role": "user", "content": "Какова погода в Бостоне? Затем порекомендуйте, что надеть."}
]

turn = 0
while True:
    turn += 1
    print(f"\n── Поворот {turn} ──")

    result = stream_and_collect(
        turn,
        model="claude-sonnet-4-5",
        messages=messages,
        tools=tools,
        extra_body={"reasoning": {"max_tokens": 2000}},
    )

    # Печать вызовов инструментов
    if result["tool_calls"]:
        for tc in result["tool_calls"]:
            print(f"[Вызов инструмента: {tc['function']['name']}] {tc['function']['arguments']}")

    # Создание сообщения помощника, сохранить reasoning_details (критично!)
    assistant_msg = {"role": "assistant", "content": result["content"]}
    if result["tool_calls"]:
        assistant_msg["tool_calls"] = result["tool_calls"]
    if result["reasoning_details"]:
        assistant_msg["reasoning_details"] = result["reasoning_details"]  # передать обратно без изменений
    messages.append(assistant_msg)

    # Отсутствие tool_calls означает, что разговор завершен
    if not result["tool_calls"]:
        break

    # Выполнение инструментов и добавление результатов в сообщения
    for tc in result["tool_calls"]:
        args = json.loads(tc["function"]["arguments"])
        tool_result = execute_tool(tc["function"]["name"], args)
        print(f"[Результат инструмента: {tc['function']['name']}] {tool_result}")
        messages.append({"role": "tool", "tool_call_id": tc["id"], "content": tool_result})

1.6 Правила сопоставления интенсивности мышления

Режим усилий:

  • Opus 4.6 / Sonnet 4.6 и выше: сопоставляется с родным уровнем усилий Адаптивного мышления Anthropic.
  • Другие модели: рассчитываются по формуле для budget_tokens:
budget_tokens = max(min(max_tokens × effort_ratio, 128000), 1024)
усилие усилие_ratio
xhigh 0.95
high 0.80
medium 0.50
low 0.20
minimal 0.10

Сопоставление усилий адаптивного мышления:

Входящее усилие Opus 4.6 Sonnet 4.6
xhigh max high
high high high
medium medium medium
low low low
minimal low low

Режим max_tokens: Непосредственно назначается как budget_tokens Anthropic.

-think суффикс: Opus/Sonnet 4.6+ использует адаптивное мышление (усилие=среднее); другие модели устанавливают budget_tokens = min(10240, max_tokens - 1), с умолчательным max_tokens равным 4096.


2. Кэширование подсказок

Вы можете использовать кэширование подсказок при отправке запросов к модели Claude через интерфейс чата. Установив контрольные точки cache_control в сообщениях, большие блоки текста (такие как карточки ролей, данные RAG, главы книг и т. д.) могут быть закэшированы для повторного использования, позволяя последующим запросам напрямую обращаться к кэшу и значительно снижать затраты.

Официальная документация Claude: Кэширование подсказок

2.1 Затраты на кэширование

Операция Множитель цены (относительно оригинальной цены ввода)
Запись в кэш (TTL 5 минут) 1.25x
Запись в кэш (TTL 1 час) 2x
Чтение из кэша 0.1x

2.2 Поддерживаемые модели и минимальная длина кэша

Модель Минимальное количество токенов в кэше
Claude Opus 4.8 1024
Claude Opus 4.7 2048
Claude Opus 4.6 / Opus 4.5 4096
Claude Sonnet 4.6 / Sonnet 4.5 / Opus 4.1 / Opus 4 / Sonnet 4 / Sonnet 3.7 (устаревшая) 1024
Claude Haiku 4.5 4096
Claude Haiku 3.5 (устаревшая) / Haiku 3 2048
Ограничение на количество контрольных точек: Максимум 4 контрольные точки cache_control на запрос.

2.3 TTL кэша

TTL Синтаксис Применимые сценарии
5 минут (по умолчанию) "cache_control": {"type": "ephemeral"} Короткие сессии, рутинные запросы
1 час "cache_control": {"type": "ephemeral", "ttl": "1h"} Долгие сессии, чтобы избежать повторных записей в кэш

Затраты на запись для TTL 1 час выше, но они могут сэкономить общие расходы, снижая повторные записи в длительных сессиях. Все модели с Claude 4.5 и выше от всех поставщиков (включая Anthropic, Amazon Bedrock, Google Vertex AI) поддерживают TTL 1 час.

2.4 Использование

Вы можете установить контрольные точки кэша, используя поле cache_control в system, user (включая изображения) и tools. Следующие примеры показывают только ключевую структуру, опуская большие блоки текста.

Кэширование системного сообщения (по умолчанию TTL 5 минут):

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [
        {"type": "text", "text": "Вы - AI помощник"},
        {
          "type": "text",
          "text": "(долгий контекст)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {
      "role": "user",
      "content": [{"type": "text", "text": "Здравствуйте"}]
    }
  ]
}

Кэширование пользовательского сообщения (TTL 1 час):

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [{"type": "text", "text": "Вы - AI помощник"}]
    },
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "(долгий контекст)",
          "cache_control": {"type": "ephemeral", "ttl": "1h"}
        },
        {"type": "text", "text": "Здравствуйте"}
      ]
    }
  ]
}

Кэширование сообщения с изображением:

{
  "role": "user",
  "content": [
    {
      "type": "image_url",
      "image_url": {"detail": "auto", "url": "data:image/jpeg;base64,/9j/4AAQ..."},
      "cache_control": {"type": "ephemeral"}
    },
    {"type": "text", "text": "Что это?"}
  ]
}

Кэширование определения инструмента:

cache_control размещается на верхнем уровне объекта инструмента (наряду с type и function):

{
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Получить текущую погоду для местоположения",
      "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    },
    "cache_control": {"type": "ephemeral", "ttl": "1h"}
  }]
}

2.5 Просмотр статуса кэша

Поле usage ответа вернет claude_cache_tokens_details, записывая подробную информацию о кэше:

Первый запрос (Создание кэша):

{
  "usage": {
    "prompt_tokens": 22,
    "completion_tokens": 890,
    "total_tokens": 912,
    "claude_cache_tokens_details": {
      "cache_creation_input_tokens": 6266,
      "cache_read_input_tokens": 0,
      "cache_write_5_minutes_input_tokens": 6266,
      "cache_write_1_hour_input_tokens": 0
    }
  }
}

Последующие запросы (Кэш попал):

{
  "usage": {
    "prompt_tokens": 22,
    "completion_tokens": 810,
    "total_tokens": 832,
    "prompt_tokens_details": {
      "cached_tokens": 6266
    },
    "claude_cache_tokens_details": {
      "cache_creation_input_tokens": 0,
      "cache_read_input_tokens": 6266,
      "cache_write_5_minutes_input_tokens": 0,
      "cache_write_1_hour_input_tokens": 0
    }
  }
}
Поле Значение
cache_creation_input_tokens Количество токенов, записанных в кэш в этом запросе
cache_read_input_tokens Количество токенов, прочитанных из кэша в этом запросе
cache_write_5_minutes_input_tokens Количество токенов, записанных в кэш с TTL 5 минут
cache_write_1_hour_input_tokens Количество токенов, записанных в кэш с TTL 1 час
prompt_tokens_details.cached_tokens Количество закэшированных токенов при попадании в кэш, совместимо с форматом OpenAI

3. Заголовок запроса для anthropic-beta

Вы можете включить бета-функции модели Claude через HTTP-заголовок anthropic-beta, который AIHubMix передаст в API Anthropic.

Использование

Добавьте anthropic-beta в заголовок запроса, значение которого будет соответствующим идентификатором бета-функции:

curl "https://aihubmix.com/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
  -H "anthropic-beta: context-1m-2025-08-07" \
  -d '{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [
        {"type": "text", "text": "Вы - AI помощник"},
        {
          "type": "text",
          "text": "(долгий контекст)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {"role": "user", "content": [{"type": "text", "text": "привет"}]}
  ]
}'
Для конкретных доступных идентификаторов бета-версий, пожалуйста, обратитесь к Документации API Anthropic.

Последнее обновление: 2026-06-01

More from the blog