Atualização da Interface Compatível com OpenAI: Suporte Profundo para Claude

31 de jul. de 2026 · AIHubMix · 8 min read · Notícias

Atualização da Interface Compatível com OpenAI: Suporte Profundo para 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 de múltiplas turnos agora é mais amigável ao usuário, permitindo uma integração perfeita sem parâmetros adicionais. Também suporta a ativação dos recursos beta oferecidos pela Anthropic.

1. Pensamento do Modelo (Pensamento Estendido)

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 uma turnada do assistente; as respostas subsequentes são geradas diretamente após receber os resultados da ferramenta, 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 cada vez que recebe um resultado da ferramenta, formando uma cadeia de raciocínio:

User → [Thinking] → Tool Call → Tool Result → [Thinking] → Response
                                                ↑ Pensamento Intercalado

Isso permite que o modelo:

  • Realize raciocínio secundário com base nos resultados da ferramenta, em vez de simplesmente concatenar saídas.
  • Encadeie raciocínios entre múltiplas chamadas de ferramentas, onde cada decisão é baseada na análise do passo anterior.
Referência: Pensamento Intercalado da Anthropic

1.2 Habilitando o Pensamento

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

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

Valores possíveis para esforço: 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 fácil exibição.
  • reasoning_details: Informações estruturadas completas sobre o pensamento, que precisam ser retornadas como estão em conversas de múltiplas turnos; a estrutura interna pode diferir entre os provedores.

Exemplo não streaming (omitindo campos não relacionados):

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "Olá! Como posso ajudá-lo hoje?",
      "reasoning_content": "O usuário está apenas dizendo olá...",
      "reasoning_details": {
        "type": "thinking",
        "thinking": "O usuário está apenas dizendo olá...",
        "signature": "Er8CCkYI..."
      }
    }
  }]
}

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

1.4 Retendo o Pensamento em Conversas de Múltiplas Turnos (O Pensamento Intercalado é embutido, sem parâmetros adicionais necessários)

Para permitir que o modelo continue suas capacidades de raciocínio em conversas de múltiplas turnos, basta colocar o reasoning_details retornado anteriormente como está na mensagem do assistente da próxima rodada:

messages = [
    {"role": "user", "content": "Como está o tempo em 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"}',
    }
]

AIHubMix habilitará automaticamente o pensamento intercalado quando detectar informações históricas de pensamento na solicitação, permitindo que o modelo continue o raciocínio profundo após receber os resultados da chamada da ferramenta sem exigir parâmetros adicionais.

1.5 Exemplo Completo

Os seguintes dois exemplos demonstram o processo completo de múltiplas turnos de Chamada de Ferramenta + pensamento intercalado: consulta do usuário → modelo pensa e chama uma ferramenta → injeta resultados da ferramenta (preservando reasoning_details) → modelo pensamento intercalado dá a resposta final.

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

# ── Definição da ferramenta ───────────────────────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Obter o clima atual para um local",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "Nome da cidade"}},
            "required": ["location"]
        }
    }
}]

# ── Execução simulada da ferramenta ───────────────────────────────────────
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 "{}"

# ── Loop de conversa de múltiplas turnos ─────────────────────────────
messages = [
    {"role": "user", "content": "Como está o tempo em Boston? Então recomende o que vestir."}
]

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

    # Imprimir processo de pensamento
    if msg.reasoning_content:
        label = "Pensamento Intercalado" if turn > 1 else "Pensamento"
        print(f"[{label}] {msg.reasoning_content}")

    # Imprimir conteúdo da resposta
    if msg.content:
        print(f"[Resposta] {msg.content}")

    # Imprimir chamadas de ferramentas
    if msg.tool_calls:
        for tc in msg.tool_calls:
            print(f"[Chamada de Ferramenta: {tc.function.name}] {tc.function.arguments}")

    # Construir mensagem do assistente, preservar reasoning_details (crítico!)
    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  # passar de volta não modificado
    messages.append(assistant_msg)

    # Sem tool_calls significa que a conversa terminou
    if not msg.tool_calls:
        break

    # Executar ferramentas e anexar resultados às mensagens
    for tc in msg.tool_calls:
        args = json.loads(tc.function.arguments)
        result = execute_tool(tc.function.name, args)
        print(f"[Resultado da Ferramenta: {tc.function.name}] {result}")
        messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})

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

# ── Definição da ferramenta & execução simulada ─────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Obter o clima atual para um local",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "Nome da cidade"}},
            "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 "{}"

# ── Coletor de resposta streaming ────────────────────────────────
def stream_and_collect(turn: int, **kwargs):
    """Transmitir resposta, imprimir pensamento/conteúdo em tempo real, acumular reasoning_details/tool_calls."""
    rd = {}            # detalhes de raciocínio acumulados
    content = ""       # texto de resposta acumulado
    tc_map = {}        # chamadas de ferramentas acumuladas (por índice)
    cur = "none"       # seção de saída atual: 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

        # ── Lidar com pensamento ──
        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
            # Imprimir pedaços de pensamento em tempo real
            thinking_chunk = rd_delta.get("thinking", "")
            if thinking_chunk:
                if cur != "thinking":
                    cur = "thinking"
                    label = "Pensamento Intercalado" if turn > 1 else "Pensamento"
                    sys.stdout.write(f"\n[{label}] ")
                sys.stdout.write(thinking_chunk)
                sys.stdout.flush()

        # ── Lidar com conteúdo ──
        if delta.content:
            if cur != "content":
                if cur == "thinking":
                    sys.stdout.write("\n")
                cur = "content"
                sys.stdout.write("\n[Resposta] ")
            sys.stdout.write(delta.content)
            sys.stdout.flush()
            content += delta.content

        # ── Lidar com 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 ""

    # Finalizar a seção de saída atual
    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,
    }

# ── Loop de conversa de múltiplas turnos ─────────────────────────────
messages = [
    {"role": "user", "content": "Como está o tempo em Boston? Então recomende o que vestir."}
]

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

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

    # Imprimir chamadas de ferramentas
    if result["tool_calls"]:
        for tc in result["tool_calls"]:
            print(f"[Chamada de Ferramenta: {tc['function']['name']}] {tc['function']['arguments']}")

    # Construir mensagem do assistente, preservar reasoning_details (crítico!)
    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"]  # passar de volta não modificado
    messages.append(assistant_msg)

    # Sem tool_calls significa que a conversa terminou
    if not result["tool_calls"]:
        break

    # Executar ferramentas e anexar resultados às mensagens
    for tc in result["tool_calls"]:
        args = json.loads(tc["function"]["arguments"])
        tool_result = execute_tool(tc["function"]["name"], args)
        print(f"[Resultado da Ferramenta: {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 de Esforço:

  • Opus 4.6 / Sonnet 4.6 e acima: mapeia para o nível de esforço Pensamento Adaptativo nativo da Anthropic.
  • Outros modelos: calculado usando a fórmula para budget_tokens:
budget_tokens = max(min(max_tokens × effort_ratio, 128000), 1024)
esforço esforço_ratio
xhigh 0.95
high 0.80
medium 0.50
low 0.20
minimal 0.10

Mapeamento de Esforço de Pensamento Adaptativo:

Esforço de Entrada Opus 4.6 Sonnet 4.6
xhigh máx alto
high alto alto
medium médio médio
low baixo baixo
minimal baixo baixo

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

-think sufixo: Opus/Sonnet 4.6+ usa pensamento adaptativo (esforço=médio); outros modelos definem budget_tokens = min(10240, max_tokens - 1), com um max_tokens padrão de 4096.


2. Cache de Prompt

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

Documentação Oficial do Claude: Cache de Prompt

2.1 Custos de Cache

Operação Multiplicador de Preço (relativo ao preço de entrada original)
Gravação de Cache (TTL de 5 minutos) 1.25x
Gravação de Cache (TTL de 1 hora) 2x
Leitura de Cache 0.1x

2.2 Modelos Suportados e Comprimento Mínimo do Cache

Modelo Contagem Mínima de Tokens de 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 (obsoleto) 1024
Claude Haiku 4.5 4096
Claude Haiku 3.5 (obsoleto) / Haiku 3 2048
Limite de Quantidade de Pontos de Interrupção: Um máximo de 4 cache_control pontos de interrupção por solicitação.

2.3 TTL de Cache

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

Os custos de gravação para TTL de 1 hora são mais altos, mas podem economizar despesas totais ao reduzir gravações repetidas em sessões longas. Todos os modelos a partir do Claude 4.5 e posteriores de todos os provedores (incluindo Anthropic, Amazon Bedrock, Google Vertex AI) suportam TTL de 1 hora.

2.4 Uso

Você pode definir pontos de interrupção de cache usando o campo cache_control em system, user (incluindo imagens) e tools. Os seguintes exemplos mostram apenas a estrutura da chave, omitindo grandes blocos de texto.

Cache de Mensagem do Sistema (TTL padrão de 5 minutos):

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [
        {"type": "text", "text": "Você é um assistente de IA"},
        {
          "type": "text",
          "text": "(contexto longo)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {
      "role": "user",
      "content": [{"type": "text", "text": "Olá"}]
    }
  ]
}

Cache de Mensagem do Usuário (TTL de 1 hora):

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [{"type": "text", "text": "Você é um assistente de IA"}]
    },
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "(contexto longo)",
          "cache_control": {"type": "ephemeral", "ttl": "1h"}
        },
        {"type": "text", "text": "Olá"}
      ]
    }
  ]
}

Cache de Mensagem de 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": "O que é isso?"}
  ]
}

Cache de Definição da Ferramenta:

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

{
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Obter o clima atual para um local",
      "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    },
    "cache_control": {"type": "ephemeral", "ttl": "1h"}
  }]
}

2.5 Visualizando o Status do Cache

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

Primeira Solicitação (Criando 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
    }
  }
}

Solicitações Subsequentes (Cache Hit):

{
  "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 Número de tokens gravados no cache nesta solicitação
cache_read_input_tokens Número de tokens lidos do cache nesta solicitação
cache_write_5_minutes_input_tokens Número de tokens gravados no cache de TTL de 5 minutos
cache_write_1_hour_input_tokens Número de tokens gravados no cache de TTL de 1 hora
prompt_tokens_details.cached_tokens Número de tokens em cache quando o cache é acessado, compatível com o formato da OpenAI

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

Você pode habilitar recursos beta do modelo Claude através do Cabeçalho HTTP anthropic-beta, que o AIHubMix passará para a API da Anthropic.

Uso

Adicione anthropic-beta ao cabeçalho da solicitaçã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": "Você é um assistente de IA"},
        {
          "type": "text",
          "text": "(contexto longo)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {"role": "user", "content": [{"type": "text", "text": "olá"}]}
  ]
}'
Para identificadores beta específicos disponíveis, consulte a Documentação da API da Anthropic.

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

More from the blog