Atualização da Interface Compatível com OpenAI: Suporte Aprofundado ao Claude

A AIHubMix atualizou sua API compatível com OpenAI para o Claude: pensamento intercalado sem parâmetros extras, cache de prompts e suporte aos recursos beta da Anthropic.

AIHubMix8 min de leitura
Atualização da Interface Compatível com OpenAI: Suporte Aprofundado ao Claude

Atualizamos a interface compatível com OpenAI com otimizações mais profundas, especificamente para os modelos da série Claude. Agora você pode controlar o pensamento e o cache de forma mais precisa e conveniente. O pensamento intercalado em conversas com múltiplos turnos ficou mais fácil de usar, permitindo uma integração perfeita sem parâmetros adicionais. Também é possível ativar os recursos beta oferecidos pela Anthropic.

1. Pensamento do Modelo (Extended Thinking)

1.1 Vantagens do Pensamento Intercalado

Quando o pensamento intercalado não está ativado, o modelo realiza o pensamento apenas uma vez, no início de um turno do assistente; as respostas seguintes são geradas diretamente após o recebimento dos resultados das ferramentas, sem produzir novos blocos de pensamento:

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

Quando o pensamento intercalado está ativado, o modelo insere um novo bloco de pensamento a cada vez que recebe um resultado de ferramenta, formando uma cadeia de raciocínio:

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

Isso permite que o modelo:

  • Realize raciocínio secundário com base nos resultados das ferramentas, em vez de simplesmente concatenar as saídas.
  • Encadeie o raciocínio entre múltiplas chamadas de ferramentas, em que cada decisão se baseia na análise do passo anterior.
Referência: Anthropic Interleaved Thinking

1.2 Ativando o Pensamento

Você pode ativar o pensamento de quatro maneiras, escolhendo qualquer uma delas:

Método Exemplo Descrição
reasoning_effort "reasoning_effort": "low" Parâmetro padrão do OpenAI, colocado no nível superior do corpo da requisição
reasoning.effort "reasoning": {"effort": "low"} Equivalente ao método anterior, colocado dentro do objeto reasoning
reasoning.max_tokens "reasoning": {"max_tokens": 1024} Controla com precisão o número máximo de tokens para o pensamento
Nome do modelo com -think "model": "claude-sonnet-4-5-think" A forma mais simples, não exige parâmetros adicionais
Prioridade (quando vários métodos são usados): reasoning_effort > reasoning.max_tokens > reasoning.effort > sufixo -think

Valores possíveis para effort: minimal / low / medium / high / xhigh

1.3 Retorno do Pensamento

A mensagem de resposta incluirá dois novos campos:

  • reasoning_content: Conteúdo do pensamento (string), para exibição fácil.
  • reasoning_details: Informações estruturadas completas sobre o pensamento, que precisam ser devolvidas exatamente como estão em conversas com múltiplos turnos; a estrutura interna pode variar entre provedores.

Exemplo de resposta sem streaming (omitindo campos não relacionados):

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "Hello! How can I help you today?",
      "reasoning_content": "The user is just saying hello...",
      "reasoning_details": {
        "type": "thinking",
        "thinking": "The user is just saying hello...",
        "signature": "Er8CCkYI..."
      }
    }
  }]
}

Em respostas em streaming, o conteúdo do pensamento será enviado em partes por meio de delta.reasoning_content e delta.reasoning_details. Para a lógica completa de concatenação em streaming, consulte o exemplo completo abaixo.

1.4 Mantendo o Pensamento em Conversas com Múltiplos Turnos (O pensamento intercalado já está integrado, sem parâmetros adicionais)

Para que o modelo continue usando suas capacidades de raciocínio em conversas com múltiplos turnos, basta incluir o reasoning_details retornado anteriormente exatamente como está na mensagem do assistente do turno seguinte:

messages = [
    {"role": "user", "content": "What's the weather like in Boston?"},
    {
        "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": "rainy"}',
    }
]

A AIHubMix ativa automaticamente o pensamento intercalado quando detecta informações de pensamento anteriores na requisição, permitindo que o modelo continue o raciocínio profundo após receber resultados de chamadas de ferramentas, sem exigir parâmetros adicionais.

1.5 Exemplo Completo

Os dois exemplos a seguir demonstram o processo completo de chamada de ferramenta em múltiplos turnos com pensamento intercalado: consulta do usuário → o modelo pensa e chama uma ferramenta → inserção dos resultados da ferramenta (preservando reasoning_details) → o modelo pensa de forma intercalada e dá a resposta final.

Sem streaming · Pensamento Intercalado

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-***"),
)

# ── Tool definition ───────────────────────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get current weather for a location",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "City name"}},
            "required": ["location"]
        }
    }
}]

# ── Mock tool execution ───────────────────────────────────────
WEATHER_DB = {
    "boston": {"temperature": "45°F (7°C)", "condition": "rainy", "humidity": "85%", "wind": "15 mph NE"},
    "tokyo":  {"temperature": "72°F (22°C)", "condition": "sunny", "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": "clear"}))
    return "{}"

# ── Multi-turn conversation loop ─────────────────────────────
messages = [
    {"role": "user", "content": "What's the weather like in Boston? Then recommend what to wear."}
]

turn = 0
while True:
    turn += 1
    print(f"\n── Turn {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

    # Print thinking process
    if msg.reasoning_content:
        label = "Interleaved Thinking" if turn > 1 else "Thinking"
        print(f"[{label}] {msg.reasoning_content}")

    # Print response content
    if msg.content:
        print(f"[Response] {msg.content}")

    # Print tool calls
    if msg.tool_calls:
        for tc in msg.tool_calls:
            print(f"[Tool Call: {tc.function.name}] {tc.function.arguments}")

    # Build assistant message, preserve reasoning_details (critical!)
    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  # pass back unmodified
    messages.append(assistant_msg)

    # No tool_calls means conversation is done
    if not msg.tool_calls:
        break

    # Execute tools and append results to messages
    for tc in msg.tool_calls:
        args = json.loads(tc.function.arguments)
        result = execute_tool(tc.function.name, args)
        print(f"[Tool Result: {tc.function.name}] {result}")
        messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})

Com streaming · Pensamento Intercalado

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-***"),
)

# ── Tool definition & mock execution ─────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get current weather for a location",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "City name"}},
            "required": ["location"]
        }
    }
}]

WEATHER_DB = {
    "boston": {"temperature": "45°F (7°C)", "condition": "rainy", "humidity": "85%", "wind": "15 mph NE"},
    "tokyo":  {"temperature": "72°F (22°C)", "condition": "sunny", "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": "clear"}))
    return "{}"

# ── Stream response collector ────────────────────────────────
def stream_and_collect(turn: int, **kwargs):
    """Stream response, print thinking/content in real-time, accumulate reasoning_details/tool_calls."""
    rd = {}            # accumulated reasoning_details
    content = ""       # accumulated response text
    tc_map = {}        # accumulated tool_calls (by index)
    cur = "none"       # current output section: 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

        # ── Handle thinking ──
        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
            # Print thinking chunks in real-time
            thinking_chunk = rd_delta.get("thinking", "")
            if thinking_chunk:
                if cur != "thinking":
                    cur = "thinking"
                    label = "Interleaved Thinking" if turn > 1 else "Thinking"
                    sys.stdout.write(f"\n[{label}] ")
                sys.stdout.write(thinking_chunk)
                sys.stdout.flush()

        # ── Handle content ──
        if delta.content:
            if cur != "content":
                if cur == "thinking":
                    sys.stdout.write("\n")
                cur = "content"
                sys.stdout.write("\n[Response] ")
            sys.stdout.write(delta.content)
            sys.stdout.flush()
            content += delta.content

        # ── Handle 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 ""

    # End current output section
    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,
    }

# ── Multi-turn conversation loop ─────────────────────────────
messages = [
    {"role": "user", "content": "What's the weather like in Boston? Then recommend what to wear."}
]

turn = 0
while True:
    turn += 1
    print(f"\n── Turn {turn} ──")

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

    # Print tool calls
    if result["tool_calls"]:
        for tc in result["tool_calls"]:
            print(f"[Tool Call: {tc['function']['name']}] {tc['function']['arguments']}")

    # Build assistant message, preserve reasoning_details (critical!)
    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"]  # pass back unmodified
    messages.append(assistant_msg)

    # No tool_calls means conversation is done
    if not result["tool_calls"]:
        break

    # Execute tools and append results to messages
    for tc in result["tool_calls"]:
        args = json.loads(tc["function"]["arguments"])
        tool_result = execute_tool(tc["function"]["name"], args)
        print(f"[Tool Result: {tc['function']['name']}] {tool_result}")
        messages.append({"role": "tool", "tool_call_id": tc["id"], "content": tool_result})

1.6 Regras de Mapeamento da Intensidade do Pensamento

Modo Effort:

  • Opus 4.6 / Sonnet 4.6 e superiores: mapeia para o nível de effort nativo do Adaptive Thinking da Anthropic.
  • Outros modelos: calculado pela fórmula de budget_tokens:
budget_tokens = max(min(max_tokens × effort_ratio, 128000), 1024)
effort effort_ratio
xhigh 0.95
high 0.80
medium 0.50
low 0.20
minimal 0.10

Mapeamento de Effort do Adaptive Thinking:

Effort recebido Opus 4.6 Sonnet 4.6
xhigh max high
high high high
medium medium medium
low low low
minimal low low

Modo max_tokens: atribuído diretamente como budget_tokens da Anthropic.

Sufixo -think Opus/Sonnet 4.6+ usa adaptive thinking (effort=medium); os demais modelos definem budget_tokens = min(10240, max_tokens - 1), com max_tokens padrão de 4096.


2. Cache de Prompts

Você pode usar o Prompt Caching ao fazer requisições ao modelo Claude pela interface de Chat. Ao definir pontos de interrupção cache_control nas mensagens, blocos grandes de texto (como cartões de personagem, dados de RAG, capítulos de livros etc.) podem ser armazenados em cache para reutilização, permitindo que as requisições seguintes acessem o cache diretamente e reduzam significativamente os custos.

Documentação Oficial do Claude: Prompt Caching

2.1 Custos de Cache

Operação Multiplicador de preço (em relação ao preço original de entrada)
Escrita em cache (TTL de 5 minutos) 1.25x
Escrita em cache (TTL de 1 hora) 2x
Leitura do cache 0.1x

2.2 Modelos Suportados e Tamanho Mínimo do Cache

Modelo Quantidade mínima de tokens em cache
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 (descontinuado) 1024
Claude Haiku 4.5 4096
Claude Haiku 3.5 (descontinuado) / Haiku 3 2048
Limite de quantidade de breakpoints: no máximo 4 breakpoints cache_control por requisição.

2.3 TTL do Cache

TTL Sintaxe Cenários aplicáveis
5 minutos (padrão) "cache_control": {"type": "ephemeral"} Sessões curtas e requisições rotineiras
1 hora "cache_control": {"type": "ephemeral", "ttl": "1h"} Sessões longas, para evitar escritas repetidas no cache

Os custos de escrita para o TTL de 1 hora são mais altos, mas podem reduzir o gasto total ao diminuir as escritas repetidas em sessões longas. Todos os modelos do Claude 4.5 em diante, de todos os provedores (incluindo Anthropic, Amazon Bedrock e Google Vertex AI), suportam TTL de 1 hora.

2.4 Uso

Você pode definir breakpoints de cache usando o campo cache_control em system, user (incluindo imagens) e tools. Os exemplos a seguir mostram apenas a estrutura principal, omitindo blocos grandes de texto.

Cache de mensagem de sistema (TTL padrão de 5 minutos):

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [
        {"type": "text", "text": "You are an AI assistant"},
        {
          "type": "text",
          "text": "(long context)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {
      "role": "user",
      "content": [{"type": "text", "text": "Hello"}]
    }
  ]
}

Cache de mensagem do usuário (TTL de 1 hora):

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [{"type": "text", "text": "You are an AI assistant"}]
    },
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "(long context)",
          "cache_control": {"type": "ephemeral", "ttl": "1h"}
        },
        {"type": "text", "text": "Hello"}
      ]
    }
  ]
}

Cache de mensagem com imagem:

{
  "role": "user",
  "content": [
    {
      "type": "image_url",
      "image_url": {"detail": "auto", "url": "data:image/jpeg;base64,/9j/4AAQ..."},
      "cache_control": {"type": "ephemeral"}
    },
    {"type": "text", "text": "What's this?"}
  ]
}

Cache de definição de ferramenta:

cache_control é colocado no nível superior do objeto da ferramenta (junto de type e function):

{
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Get current weather for a location",
      "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    },
    "cache_control": {"type": "ephemeral", "ttl": "1h"}
  }]
}

2.5 Verificando o Status do Cache

O campo usage da resposta retornará claude_cache_tokens_details, com informações detalhadas do cache:

Primeira requisição (criando o cache):

{
  "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
    }
  }
}

Requisições seguintes (cache atingido):

{
  "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
    }
  }
}
Campo Significado
cache_creation_input_tokens Quantidade de tokens gravados no cache nesta requisição
cache_read_input_tokens Quantidade de tokens lidos do cache nesta requisição
cache_write_5_minutes_input_tokens Quantidade de tokens gravados no cache com TTL de 5 minutos
cache_write_1_hour_input_tokens Quantidade de tokens gravados no cache com TTL de 1 hora
prompt_tokens_details.cached_tokens Quantidade de tokens em cache quando o cache é atingido, compatível com o formato da OpenAI

3. Cabeçalho de Requisição para anthropic-beta

Você pode ativar recursos beta do modelo Claude pelo cabeçalho HTTP anthropic-beta, que a AIHubMix repassa para a API da Anthropic.

Uso

Adicione anthropic-beta ao cabeçalho da requisição, com o valor sendo o identificador do recurso beta correspondente:

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": "You are an AI assistant"},
        {
          "type": "text",
          "text": "(long context)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {"role": "user", "content": [{"type": "text", "text": "hello"}]}
  ]
}'
Para os identificadores beta disponíveis, consulte a Documentação da API da Anthropic.

Última atualização: 2026-06-01

Um só gateway para vários provedores de IA. Feito para produção.

Acessar AIHubMix